Perl exit on warning - perl

I'd like a perl script to exit with error code immediately on any kind of warning.
For example, on an "argument ... isn't numeric in addition" warning.
How can one do this?
Thank you in advance.

The warnings pragma has the FATAL option:
use warnings FATAL => 'all';

toolic's answer of use warnings FATAL => 'all'; is correct, but there are some caveats. There are some warnings emitted by internal perl functions that really don't expect to be dying. There's a list of those unsafe-to-fatalize warnings in perldoc strictures.
As of version 2.000003 of strictures, it enables warnings as follows:
use warnings FATAL => 'all';
use warnings NONFATAL => qw(
exec
recursion
internal
malloc
newline
experimental
deprecated
portable
);
no warnings 'once';
See https://metacpan.org/pod/strictures#CATEGORY-SELECTIONS for the full rationale.
Instead of copy/pasting the above lines into your code, you could of course just
use strictures 2;
which also enables strict for you.
(You might have to install strictures first, though.)

Not mentioned yet, but you can set a __WARN__ handler and do what you like there.
$SIG{__WARN__} = sub {
die "This program does not tolerate warnings like: #_";
};

Related

Suppress uninitailized perl warning

Is there a way I can suppress uninitialized Perl variable warning for only few libraries out of all the libraries used in a Perl file?
I can't think of any reason this would be a good idea, surely use of an uninitialised variable is a bug which ever angle you look at it from?
However, you can turn the warnings off with:
no warnings 'uninitialized';
And on again with:
use warnings 'uninitialized';
You can disable the warnings for a single statement if you surround it with these two statements.
You probably used -w, which enables warnings throughout the interpreter (though it can be overridden using local $^W = 0; or no warnings;).
It's not a bad thing to use -w. All modules should tolerate this or defend against it (using no warnings;). But some aren't "warnings-safe".
It's more conventional to place use warnings; in each of your scripts and modules. This is method of enabling warnings is less intrusive since it won't affect other third-party modules used by your script and modules.
If you're able to modify the offending module(s), you can add no warnings 'uninitialized'; to the module itself, outside of any sub declarations, and it will disable that category of warnings for all code in the module, as demonstrated with these two bits of code:
In warntest:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
use lib '.';
use UndefMod;
my $x;
say "Main before: $x";
UndefMod::test_mod;
say "Main after: $x";
In UndefMod.pm (in the same directory):
package UndefMod;
use strict;
use warnings;
use 5.010;
no warnings 'uninitialized';
sub test_mod {
my $x;
say "Module: $x";
}
1;
When run, this produces "Use of uninitialized value" warnings for both says in warntest, but no warning for the say in UndefMod, thus showing that either file's warnings setting has no effect on the other.
Of course, you may not be able to modify the module source, in which case you may be out of luck. Perl's scoping rules don't generally allow you to modify the warnings/no warnings state of a scope from outside that scope. (I say "may" and "generally" because there may be some black magic to hack around that, probably at least related to the magic behind PadWalker, but such things are beyond my ken.)

How do I conditionally make warnings fatal?

I want to have fatal errors that can be demoted to warnings (or conversely, warnings that can be promoted to fatal errors) depending on the user's preference. At present, I'm using die and overriding it like this:
my $force;
BEGIN {
no warnings 'once';
Getopt::Long::Configure('pass_through');
GetOptions('force', \$force);
*CORE::GLOBAL::die = sub { warn #_ } if $force;
Getopt::Long::Configure('no_pass_through');
}
use My::Module;
...
die "maybe";
...
exit(1) if $force;
exit(0); # success!
I don't like this approach, mostly because there are a few places where I still want to die (e.g. missing command-line arguments). I'd rather change (most) instances of die to warn and do a conditional use warnings FATAL => 'all'1. The problem is that use warnings is lexically scoped, so this is ineffective:
if (!$force) {
use warnings FATAL => 'all';
}
Attempting to use a postfix conditional is a syntax error:
use warnings FATAL => 'all' unless $force;
and use can't be used in an expression:
$force or use warnings FATAL => 'all'; # syntax error
I can think of various distasteful work-arounds (Defining my own warn/die, duplicating my main script code in an if/else, etc.) but am seeking an elegant solution that conditionally applies use warnings in the current lexical scope. (Applying it in the parent scope would work as well but would likely require dark magic.)
1: Actually, I want to add use warnings::register to my modules and use warnings FATAL => 'My::Module' to my script, but the distinction is unimportant for the purposes of this question.
You could try:
use if !$force, warnings => qw( FATAL all );
Yes, there's a module called if that is distributed with Perl to make it really easy to do conditional imports.
Note that this is evaluated at compile time, so $force needs to be defined at compile time. (Probably within a BEGIN { ... } block.) Based on the code samples in your question, you seem to have figured that part out already though.
Alternatively, more manual:
BEGIN {
require warnings;
warnings->import(qw/ FATAL all /) unless $force;
}
A completely different approach would be to use a signal handler $SIG{__WARN__} or $SIG{__DIE__} to decide to die or not at run-time.

Why doesn't use warnings FATAL => 'all' seem to work?

This script:
use warnings FATAL => 'all';
warn 'warning';
die 'death';
...results in 'warning' getting logged, but doesn't die at that point, instead dying with 'death'.
I have a mod_perl module that overrides $main::SIG{__DIE__} to basically spit out die() messages to the browser during development, but it does not touch SIG{__WARN__}, so what is happening here?
This is on perl 5.10.1 on Windows Server 2003, with "-w" in PerlSwitches.
It doesn't seem to work because your test isn't testing what you want to test. Try this:
use warnings FATAL => 'all';
print undef;
die 'death';
Just as a no warnings will not prevent warn from working, warnings FATAL will not make warn die.
You misunderstood the purpose of the warnings pragma. It is there to emit a warning message for completely legal, but possibly wrong operations. Example:
use warnings;
my ($x, $y) = ("hello", "world");
say "same" if $x == $y;
→ Argument "world" isn't numeric…. We can make certain categories produce fatal errors with use warnings FATAL => $CATEGORY with categories like numeric or uninitialized. The all category represents all categories.
It does not change the semantics of every warn to die. You can do this yourself, e.g. with overriding a local $SIG{__WARN__}, or creating a warn function that does croak #_. You can even override CORE::GLOBAL::warn to change all warns, even if they are in other modules.
The CGI::Carp module has a warningsToBrowser option; you may want to look at the source code to see how it's implemented.

How can I use Test::NoWarnings to catch redefinition errors for EXPORT()ed subroutines when loading modules in sequence

I'm working with a Test::More-based unit test for part of our codebase that loads all of our in-house modules in sequence with use_ok(). I'd like to make warnings fatal to the unit test so that we can more easily catch regressions.
It was suggested here that I use Test::NoWarnings, and indeed that seems to be what I am looking for.
The script is here:
#!/usr/bin/perl -w
use File::Find;
use File::Spec;
use Test::More;
use Test::NoWarnings;
# Determine the filepaths of every .pm file in the lib directory
my #files;
File::Find::find(
sub
{
if (/\.pm$/)
{
push(#files, $File::Find::name);
}
},
"$ENV{'CODE_ROOT'}/lib"
);
done_testing(scalar(#files) + 1);
use lib "$ENV{'CODE_ROOT'}/lib";
foreach my $file (#files)
{
# <code removed for brevity: format file name into Module::Hierarchy::For::Use>
use_ok($includeString);
}
1;
I have come across a few cases in which subroutines EXPORT()ed by different modules raise redefinition warnings, but these warnings are not caught by the Test::NoWarnings module. In contrast, a warning due to a lower-case attribute declaration is caught correctly.
To be clear, I have read the caveats about use of EXPORT() vs EXPORT_OK(), and the sources of the warnings themselves are being dealt with. I am specifically wondering whether my assumptions about the behaviour of Test::NoWarnings are correct, and if so, how I can alter the unit tests so that such things are caught.
If you want to make warnings fatal, what about just turning that on?
use strict;
use warnings FATAL => 'all';
Yes, the POD for warningspragma does caution strongly against the use of this option:
FATAL warnings should be used with care, particularly FATAL => 'all'
and
use of FATAL => 'all' is discouraged
But I think use in a regression or unit test is a valid use case. I've been using this in all of my unit and regression tests for a long time now with no unintended consequences (yet). I think Test::Warn and Test::NoWarnings are best used for catching your modules' own warnings, both expected and unexpected.
use strict;
use warnings FATAL => 'all';
use Test::More;
use Test::Warn; # test for our own expected warnings
require Test::NoWarnings; # REQUIRE to suppress auto test at program exit
my $tCnt = 0;
ok(1, "first test"); $tCnt++;
warning_like { warn("my warn") } (qr/my warn/), "caught warning"; $tCnt++;
ok(1, "made it to the end"); $tCnt++;
Test::NoWarnings::had_no_warnings(); $tCnt++;
done_testing($tCnt);
If you throw in a statement like length("123"); (void context) the compile-time warning will boot you right out with the FATALS => 'all'in effect. Without it, the all the tests run and pass, with no inkling of a potential problem.

warnings::warn and FATAL categories in Perl

I must be understanding the warnings documentation wrong. The way I read it, this code:
use warnings;
use warnings FATAL => 'all';
warnings::warn('numeric', 'blarg');
print "finished\n";
Should print the 'blarg' warning and die since I've asked for all warnings to be fatal. However, when I run the code I get:
$> /opt/local/bin/perl x.pl
blarg at x.pl line 3
finished
Can somone help me understand why I can't get warn to die?
Okay. This is ugly. I had a post half-prepared explaining this as a bug in warnings, and then I realized it's not, it's just a really evil subtlety in the way warnings works.
Warnings starts looking for a relevant stack frame to get the warning bits from in warnings::warn's caller's caller. The idea is that you're writing some module and you use warnings::warn or warnings::warnif in your functions, and whether or not the warning is printed (or fatal) depends on the use warnings setting in scope in the code that uses your module. There's no option provided to have it start at caller(1) instead of caller(2), so the effect you want isn't possible.
An example of code that does work (and demonstrates how this interface was expected to be used by whoever wrote it):
package Foo;
require warnings;
sub bail {
warnings::warnif('numeric', "You fool! You divided by zero!");
}
package main;
use warnings FATAL => all;
Foo::bail();
print "Will never be reached\n";
And you can't defeat the way it works by just adding another level of subroutines, because it takes the flags from the first caller that's in a different package from the caller of warn/warnif/enable/etc.