How do I ignore a die() that occurs in a Perl END block?
As it is now I get
END failed--call queue aborted
And the error bubbles up the calling script.
Put your end block inside an eval { .... } - this should prevent the behaviour you describe.
#!/usr/bin/perl
print("hi there!\n");
END {
eval{
print("goodbye\n");
die("this can't hurt....");
};
#detect if an error occurred in the eval
if ( $# ){
print ("an error occurred: $#\n");
}
}
Place your code inside eval block and if you want to retrieve the error message that die provides you can capture using if condition outside the block.
#!/usr/bin/perl
my $val=1;
eval
{
print "Inside Eval\n";
if($val == 1)
{
die "hello world\n";
}
print "End of Eval\n";
};
if ( $# )
{
print "Error message - $#" . "\n";
}
Try::Tiny is an excellent wrapper for eval, allowing you to explicitly handle run-time exceptions.
Related
According to the perldoc -f die, which documents $SIG{__DIE__}
Although this feature was to be run only right before your program was to exit, this is not currently so: the $SIG{__DIE__} hook is currently called even inside evaled blocks/strings! If one wants the hook to do nothing in such situations, put die #_ if $^S; as the first line of the handler (see $^S in perlvar). Because this promotes strange action at a distance, this counterintuitive behavior may be fixed in a future release.
So let's take a basic signal handler which will trigger with eval { die 42 },
package Stupid::Insanity {
BEGIN { $SIG{__DIE__} = sub { print STDERR "ERROR"; exit; }; }
}
We make this safe with
package Stupid::Insanity {
BEGIN { $SIG{__DIE__} = sub { return if $^S; print STDERR "ERROR"; exit; }; }
}
Now this will NOT trigger with eval { die 42 }, but it will trigger when that same code is in a BEGIN {} block like
BEGIN { eval { die 42 } }
This may seem obscure but it's rather real-world as you can see it being used in this method here (where the require fails and it's caught by an eval), or in my case specifically here Net::DNS::Parameters. You may think you can catch the compiler phase too, like this,
BEGIN {
$SIG{__DIE__} = sub {
return if ${^GLOBAL_PHASE} eq 'START' || $^S;
print STDERR "ERROR";
exit;
};
}
Which will work for the above case, but alas it will NOT work for a require of a document which has a BEGIN statement in it,
eval "BEGIN { eval { die 42 } }";
Is there anyway to solve this problem and write a $SIG{__DIE__} handler that doesn't interfere with eval?
Check caller(1)
Just a bit further down the rabbit hole with [caller(1)]->[3] eq '(eval)'
return if [caller(1)]->[3] eq '(eval)' || ${^GLOBAL_PHASE} eq 'START' || $^S;
Crawl entire call stack
You can crawl the entire stack and be sure you're NOT deeply in an eval with,
for ( my $i = 0; my #frame = caller($i); $i++ ) {
return if $frame[3] eq '(eval)'
}
Yes, this is total insanity. Thanks to mst on irc.freenode.net/#perl for the pointer.
I know that in java language ,if an exception is catched successfully ,the code after the try-catch-clause will still run.In perl ,it uses eval to catch exception.So ,I write two simple programs to test it.
testEval1.pl:
$exp = '$i = 3; die "error message"; $k = $i + $j';
push ( #program, '$i = 3; die "error message"; $k = $i + $j');
$rtn =eval($exp);
if ( ! defined ( $rtn))
{
print "Exception: " , $#,"\n";
}
else
{
print $rtn,"\n";
}
output of testEval1.pl:
code continue to run after die!
Exception: error message at (eval 1) line 1.
testEval2.pl
$baseDir = "/home/wuchang/newStore1";
my $eval_rtn = eval(opendir(BASEDIR,$baseDir) or die "dir doesn't exist!\n");
print "code continue to run after die!\n";
if(!defined($eval_rtn)){
print $#;
}
else
{
print $rtn,"\n";
}
output of testEval2.pl:
dir doesn't exist!
you can see that in the two code examples , the code block of eval both has die expressions.But in testEval1.pl,the code after eval can be excuted,while in testEval2.pl,it's not!
So ,my question is ,what's the difference ?
What can I do to make the program continue to run even if a "dir doesn't exist" exception happeded ?
thank you.
You're evaling result of
opendir(BASEDIR,$baseDir) or die "dir doesn't exist!\n"
code. If it would succeed that would be equivalent of eval(1).
What you want is eval BLOCK:
my $eval_rtn = eval{ opendir(BASEDIR,$baseDir) or die "dir doesn't exist!\n" };
Check perldoc -f eval for difference between eval EXPR and eval BLOCK
To answer your question title:
Will code after eval(die “some error message”) continue to be executed?
The answer is "No". But please read on, because this is not a problem, but a misunderstanding about the Perl syntax involved.
The line:
my $eval_rtn = eval( opendir(BASEDIR,$baseDir) or die "dir doesn't exist!\n" );
Does not get as far as running the eval. The syntax you have used with (..) brackets takes a scalar value, and before the eval does anything at all, it is waiting for the opendir...or die expression to return a string (which will then be evaluated). To make it equivalent to your other example, you could make the param a string:
my $eval_rtn = eval( q{opendir(BASEDIR,$baseDir) or die "dir doesn't exist!\n"} );
You could also use the block form instead:
my $eval_rtn = eval { opendir(BASEDIR,$baseDir) or die "dir doesn't exist!\n"; };
I would recommend using the block form where possible, it is usually easier to debug, and in your case better matches the exception handling semantics that you want to achieve.
If I set RaiseError = 1, what exception is raised when connecting or executing?
If I were to surround my execute() method in try catch, what exception should I be catching?
The Perl Error.pm module, which provides try and catch, is deprecated. Exceptions, inasmuch as they exist in Perl, are untyped, and here's how you catch one:
eval {
do_something_which_may_throw_exception();
};
if ($#) {
print "Exception: $#\n";
};
In short, the eval { ... } block acts as a "try", and the if ($#) { ... } acts as a "catch", wherein the exception text is contained in the special variable $#.
The DBI documentation lists and explains a lot of options, many of which relate to error handling.
Perl has two major error handling idioms:
Returning a false value. The reason for the error is in some global variable.
die with some error message (fatal).
By default, DBI uses the first idiom. The error reason is in $DBI::errstr. For this to work, you have to check the return values of each and every call to the DBI API.
When you feel lazy, you can use exceptions. Setting RaiseError in the handle constructor will make DBI methods throw an exception. From the docs:
RaiseError
Type: boolean, inherited
The RaiseError attribute can be used to force errors to raise exceptions rather than simply return error codes in the normal way. It is "off" by default. When set "on", any method which results in an error will cause the DBI to effectively do a die("$class $method failed: $DBI::errstr"), where $class is the driver class and $method is the name of the method that failed. E.g.,
DBD::Oracle::db prepare failed: ... error text here ...
[…]
Typically RaiseError is used in conjunction with eval { ... } to catch the exception that's been thrown and followed by an if ($#) { ... } block to handle the caught exception. For example:
eval {
...
$sth->execute();
...
};
if ($#) {
# $sth->err and $DBI::err will be true if error was from DBI
warn $#; # print the error
... # do whatever you need to deal with the error
}
In that eval block the $DBI::lasth variable can be useful for diagnosis and reporting if you can't be sure which handle triggered the error.
As you can see, exceptions in Perl aren't handled with try/catch, but with eval { ... }. After an eval that dies, the $# error variable will be set to that error, and you are free to handle it. Note that DBI does not use exception objects.
If you'd like to get back formal exception objects from DBI, you can use the HandleError attribute and Exception::Class::DBI. I use it myself. From the Synopsis:
use DBI;
use Exception::Class::DBI;
my $dbh = DBI->connect($dsn, $user, $pass, {
PrintError => 0,
RaiseError => 0,
HandleError => Exception::Class::DBI->handler,
});
eval { $dbh->do($sql) };
if (my $ex = $#) {
print STDERR "DBI Exception:\n";
print STDERR " Exception Type: ", ref $ex, "\n";
print STDERR " Error: ", $ex->error, "\n";
print STDERR " Err: ", $ex->err, "\n";
print STDERR " Errstr: ", $ex->errstr, "\n";
print STDERR " State: ", $ex->state, "\n";
print STDERR " Return Value: ", ($ex->retval || 'undef'), "\n";
}
I am using Module::Pluggable to load modules from a given directory:
for my $module ( plugins() ) {
eval "use $module";
if ($#) {
my $error = (split(/\n/, $#))[0];
push #rplugin_errors, $error;
print STDOUT "Failed to load $module: $error\n";
} else {
print STDOUT "Loaded: $module\n";
my $mod = $module->new();
my $module_name = $mod->{name};
$classes{$module_name} = $mod;
}
}
This function can be called via a reload method elsewhere. But if a one of the modules I am trying to "use" throws an errors it's not loaded and the script is somewhat crippled.
I'd like to validate each module in plugins() before executing use. So Ideally I could do something like:
$error = 0;
for my $module ( plugins() ) {
eval TEST $module;
if ($#) {
print STDERR "$module failed. Will not continue";
$error = 1;
last;
}
}
if ($error == 0) {
for my $module ( plugins() ) {
use $module;
}
}
Change
eval TEST $module;
back to
eval "use $module";
Well, importing probably doesn't make sense here (or in your original code), so the following would be better:
eval "require $module";
I think you're overcomplicating this. Your code already includes a clause to test for errors in the use and report on them if any occur. (if ($#)... print STDOUT "Failed to load $module: $error\n";) According to your comment on ikegami's answer, your goal is that "If one fails, we halt and send a message stating the reload could not take place because of a module error." (Yes, I know you said your goal is to validate the modules before loading them. It isn't. Your goal is to halt if there's an error; you've just decided that pre-validation is the way to accomplish that. This is what we call an X-Y Problem.)
You're already detecting and reporting any errors that occur... You want to halt on error... So, when you detect an error, halt after reporting it.
if ($#) {
my $error = (split(/\n/, $#))[0];
push #rplugin_errors, $error;
die "Failed to load $module: $error\n";
} else {
This is a followup to "How can I get around a ‘die’ call in a Perl library I can’t modify?".
I have a subroutine that calls a Library-Which-Crashes-Sometimes many times. Rather than couch each call within this subroutine with an eval{}, I just allow it to die, and use an eval{} on the level that calls my subroutine:
my $status=eval{function($param);};
unless($status){print $#; next;}; # print error and go to
# next file if function() fails
However, there are error conditions that I can and do catch in function(). What is the most proper/elegant way to design the error-catching in the subroutine and the calling routine so that I get the correct behavior for both caught and uncaught errors?
Block eval can be nested:
sub function {
eval {
die "error that can be handled\n";
1;
} or do {
#propagate the error if it isn't the one we expect
die $# unless $# eq "error that can be handled\n";
#handle the error
};
die "uncaught error";
}
eval { function(); 1 } or do {
warn "caught error $#";
};
I'm not completely sure what you want to do, but I think you can do it with a handler.
$SIG{__DIE__} = sub { print $# } ;
eval{ function($param); 1 } or next;