-
-
Save belden/acab0f2ecb293fae1c6c to your computer and use it in GitHub Desktop.
| use strict; | |
| use warnings; | |
| package Deathly::Hallows; | |
| # Add a hook to force `use warnings (FATAL => 'all');` at the head of | |
| # all Adama modules. | |
| # | |
| # 'perldoc -f require' will tell you about adding coderef hooks to @INC. | |
| # | |
| # Note that this toy module doesn't keep itself at the head of @INC, which | |
| # makes it unsuitable for real-world debugging. (Modules may 'use lib ...' or | |
| # 'unshift @INC, qw(...)' which would move \&Deathly::Hallows::INC off the | |
| # head of @INC. See https://metacpan.org/source/BELDEN/Array-Sticky-0.01/lib/Array/Sticky/INC.pm | |
| # for a module that fixes that problem. | |
| sub BEGIN { | |
| unshift @INC, \&Deathly::Hallows::INC; | |
| } | |
| sub Deathly::Hallows::INC { | |
| my ($c, $filename) = @_; | |
| # find file somewhere else in @INC, or give up and let perl take over | |
| my ($match) = grep { -f "$_/$filename" } grep { "$_" ne "$c" } @INC; | |
| return () unless $match; | |
| # slurp the module into an array | |
| open my $fh, '<', "$match/$filename" or die "read $match/$filename $!\n"; | |
| my @contents = (<$fh>); | |
| close $fh; | |
| # add whatever we want to the head of this module | |
| if ($filename =~ /Adama/) { | |
| unshift @contents, ( | |
| "use warnings (FATAL => 'all');\n", | |
| ); | |
| } | |
| # return a generator function to that produces the source for our module | |
| return sub { | |
| $_ = shift @contents; | |
| return defined $_; | |
| }; | |
| } | |
| 1; |
Jay Hannah asks what the difference is between this module and use autodie. There's quite a big difference, actually.
use autodie lexically replaces core functions with versions that automatically terminate program execution ("die") when they encounter a failure. A failure at the core-function level generally means "the system" is not set up as the programmer expects.[1]
Deathly::Hallows seeks to elevate all warnings to exceptions. Warnings are generated by mistakes the developer has made in programming; making them exceptions gets the programmer's attention immediately.
[1]: For example, a program that reads a file might look like this:
open my $fh, '<', '/some/file' or die "/some/file: $!\n";
print while (<$fh>);Here, the programmer is defensibly guarding against the circumstance where /some/file does not exist on the system. The defense is simply to terminate the program's execution. With use autodie, the above code looks like this:
use autodie qw(open);
open my $fh, '<', '/some/file';
print while (<$fh>);The pay-off of use autodie comes when the programmer needs to open another file: now there's no need to add the redundant stanza or die "...$!\n" to the code.
If the programmer were to write code that generates a warning:
use autodie qw(open);
my $filename = '/some/file';
my $filename = '/some/file'; # perl warns that $filename has already been declared
open my $fh, '<', $filename;
print while (<$fh>);but which executes properly, then autodie will not be helpful in identifying that $filename has been declared twice in the same scope.
I wrote this because I wanted all modules that matched some regular expression (
/Adama/, line 33) to act as though they haduse warnings (FATAL => 'all');at the head of them.You might think something like this would work to enable fatal warnings everywhere:
However,
warningsis lexically scoped, which means that in the command line above, only the current package (which is undeclared, hence ispackage main;) will have fatal warnings enabled.There are several solutions to making warnings universally fatal.
$SIG{__WARN__} = sub { die @_ }would work, for example. I preferred this approach because signal handlers are not-too-uncommonly overridden in interesting Perl code, whereas load paths tend to be fairly static.To use this module: