How do I conditionally make warnings fatal? - perl

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.

Related

Perl exit on warning

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: #_";
};

How can I suppress warnings from a Perl function?

In PHP you might use # in front of function call to suppress warnings returned.
Is there something similar in Perl?
This feature of PHP is crazy and should be avoided whenever possible.
Perl has two kinds of exceptions: Fatal errors and warnings. Warnings may either be emitted by Perl itself, or by user code.
Inside a certain static/lexical scope, Perl's builtin warnings can be switched off like:
use warnings;
foo();
sub foo {
no warnings 'uninitialized'; # recommended: only switch of specific categories
warn "hello\n";
1 + undef; # Otherwise: Use of uninitialized value in addition (+)
}
Output: hello on STDERR.
(A list of all warnings categories can be found here )
But this can't be used to remove warnings from code you are calling (dynamic scope). This also doesn't silence user-defined warnings.
In this case, you can write a handler for the __WARN__ pseudo-signal:
use warnings;
{
local $SIG{__WARN__} = sub { };
foo();
print "bye\n";
}
sub foo {
warn "hello\n";
1 + undef;
}
Output: bye on STDOUT.
We can abstract that into a function muffle:
sub muffle {
my $func = shift;
local $SIG{__WARN__} = sub { };
return $func->(#_);
}
muffle(\&foo, 1, 2, 3); # foo(1, 2, 3)
However, this is an incredibly dumb thing to do:
Warnings point to potential problems and bugs. Fix those instead of ignoring them.
Don't activate built-in warnings in the first place for categories you aren't interested in. E.g. many people are perfectly happy that an undef value stringifies to the empty string, and don't want any warning for that.
The strategies outlined here do not handle fatal exceptions, use Try::Tiny instead.
You could also just run perl -X and disable all warnings.
I think there are perfectly valid reasons for doing this FWIW.
Easier way is to use no warnings; See perldoc link for more information. Wrap the statement giving warning in curly braces to get rid of the warning message.
E.g.
{
no warnings;
<statement giving warning>
}

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.

Can 'use strict' warn instead of error

When using use strict perl will generate a runtime error on unsafe constructs. Now I am wondering if it is possible to have it only print a warning instead of causing a runtime error ? Or is use warnings (or -w) warning about the same issues ?
No, use strict can't be made to issue warnings rather than die. All it does is set a few bits in the magic $^H variable, which triggers various things in the guts of the Perl interpreter.
No, use warnings isn't warning about the same things as use strict kills you for. For instance, use warnings will warn you about variables used only once (which might be the result of typos).
I'm gonna take a stab at guessing the real motivation here. Feel free to tell me if I guessed wrong.
I suspect your trying to tackle a large, older code base and would like to enable strictures but you were hoping first to get a sense of where the errors will be (and how many there are) without breaking functionality. Unfortunately, since use strict functions by modifying the internal behavior of the perl parser and interpreter, there isn't a 'loose strict' or, by analogy to html, any kind of 'transitional' mode.
However, you can tease apart the functionality of use strict to start moving in the right direction. First, note that there are actually three separate parts:
use strict 'refs'; # no symbolic references
use strict 'vars'; # must declare variables
use strict 'subs'; # no barewords
and of those only 'refs' generates runtime errors. So you could easily add use strict qw(vars subs) to each of your files (scripts and modules) and test them with perl -c. If you encounter any error messages, then comment out the use strict, or at least whichever of the two checks failed, and add a comment as to the nature of the failure and move on. This way you can quickly (depending on the number of files) determine which files have compile-time errors and come back to address them later. (If you were more motivated than me at the moment, you could even automate this process). Unless you have code that does scary things inside of BEGIN blocks, this should be pretty safe to do.
The trickier part is checking for the runtime errors generated by use strict 'refs' and unfortunately, there really isn't an easy way to do this because the errors are triggered by symbolic references which can't be determined by any kind of static analysis so -c and/or Perl::Critic are both useless.
Hopefully that gets closer to addressing your real problem.
The warnings and strict pragmas are complementary, not overlapping. The strict pragma has both compile-time and run-time effects. You can't reduce the severity of strictures from errors to warnings, but you can disable them entirely. For example, if you're writing your own export routine you'll need to enable symbolic references in order to manipulate the symbol table.
{
no strict 'refs';
# symrefs okay within this block
}
Warnings can also be disabled lexically (assuming you did use warnings instead of the largely obsolete -w flag).
Strictures and warnings provide a safety net. That's why they're recommended to be used by default. If you disable them you should disable only what's necessary and limit the change to the smallest possible scope.
The preferred method:
use Carp;
sub foo {
croak "no args" unless #_;
}
eval foo();
if( $# ){
print "caught die: $#";
}
If you can't change your die's to croak's:
sub foo {
die "no args" unless #_;
}
{
my $prev_die = $SIG{__DIE__};
$SIG{__DIE__} = sub { print "caught die: $_[0]"; };
eval foo();
$SIG{__DIE__} = $prev_die;
}
The second method will print out the errors on STDERR.
See:
perldoc -f eval
perldoc perlvar and search for /\$\#/ and /__DIE__/
perldoc Carp
Warnings can be made fatal — see perllexwarn — but strict errors can't be made non-fatal.
Why do you want to do that? I suspect an XY problem.