Do-It-Yourself warnings categories

One of the reasons I have not "moved on" from Perl to some other more "modern" language is that Perl gives me such great access to its inner workings. The Do-It-Yourself Lexical Pragmas post from a couple weeks ago is an example of this. Another example is that Perl lets you tie your own code into its warnings system.

Tying into the warnings machinery requires a module. That is, the interface assumes you are reporting problems relative to another name space that invoked your code. Your module can either add diagnostics to existing Perl warning categories or actually create new categories. In either case your diagnostics are sensitive to the enablement or disablement of the category, as well as its fatalization.

In addition to enabling or disabling warning categories, use warnings ... and no warnings ...; make some subroutines available which can be used to issue your own diagnostics. These are reported relative to the file and line that called into your module (sort of like carp()). A useful subset of the warnings:: subroutines is:

warnings::enabled( $category )

This subroutine returns a true value if the given warnings category is enabled by the calling package, or a false value if it is not.

warnings::warn( $category, $message )

This subroutine issues the given $message as a warning in the given $category, or as a fatal (though trappable) error if the category has been fatalized.

warnings::warnif( $category, $message )

This convenience subroutine is equivalent to warnings::warn( $category, $message ) if warnings::enabled( $category ).

The deprecated warning was the subject of one of my blog posts a couple months ago. That post described its use by Perl itself, but module authors need to deprecate code as well. You can issue your own deprecated diagnostics like this:

warnings::warnif( deprecated => 'This feature is deprecated' );

This will be fatal if deprecated has been fatalized, or suppressed if it has been disabled. The file and line number appended will be those of the most-recent call into your module, not necessarily the caller of the subroutine that contains this code. This means you can centralize your deprecation code without worrying about the depth of the call tree.

In order to create a new warning category named after your module, all you have to do is

use warnings::register;

Once Perl compiles this, you can treat it just like a built-in category. If your module is named My::Module, users of it can

use warnings 'My::Module';

or

no warnings 'My::Module';

The latter example may be more to the point, since use warnings; enables custom categories as well.

Your module generates diagnostics in the new category using the same interface as for the built-ins. For example,

warnings::warnif( __PACKAGE__, 'Danger, Will Robinson!' );

You can actually omit the $category argument in this case, simplifying the above to

warnings::warnif( 'Danger, Will Robinson!' );

If your module needs more than one new warning category, you can give their base names as arguments to use warnings::register;.

package My::Module;
use warnings::register qw{ fu bar };

will create warning categories My::Module, My::Module::fu, and My::Module::bar.

Somewhat to my surprise, I found no documented restrictions on the names of packages that can be made into warnings categories. From the standpoint of Perl's warning machinery,

package deprecated;
use warnings::register;

is not a problem, at least under Perl 5.34.0. Nevertheless, from the standpoint of both users and maintainers of such a module, this looks to me like a Very Bad Thing.

2 Comments

worth noting is that warnings::register taking a list of subcategories is 5.14+ only, and that warnings::register_categories(@categories) can be used to register categories without needing them to be attached to a module (also 5.14+)

Leave a comment

About Tom Wyant

user-pic I blog about Perl.