|
From: Sam H. <sh...@ma...> - 2005-07-29 17:01:41
|
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hi Mike,
Here is my assessment of what's going on with $SIG{__WARN__} and $SIG
{__DIE__} in Translator.pm.
evaluate_modules() uses:
local $SIG{__DIE__} = "DEFAULT";
to set the default handler for the duration of the subroutine. It
then goes onto use eval STRING to read in PG modules, and checks $@
for any error messages. This is not harmful, and it saves us a little
time since the handler defined in Apache::WeBWorK would waste time
generating a backtrace before calling the default handler. (But see
below for why I think we should just let the Apache::WeBWorK handlers
run.)
At the beginning of translate(), we see a few things:
# reset the error detection
$SIG{__WARN__} = sub {CORE::warn(@_) } unless ref($SIG
{__WARN__}) =~/CODE/;
my $save_SIG_warn_trap = $SIG{__WARN__};
#FIXME -- this may not work with the xmlrpc access
# this formats the error message within the existing warn message.
$SIG{__WARN__} = sub {&$save_SIG_warn_trap(PG_errorMessage
('message',@_))};
#$SIG{__WARN__} = sub {CORE::warn(PG_errorMessage('message',@_))};
my $save_SIG_die_trap = $SIG{__DIE__};
$SIG{__DIE__} = sub {CORE::die(PG_errorMessage('traceback',@_))};
This is a trouble spot, since it sets the handlers without using
"local". From what I can tell, what we WANT to do here is provide a
level of preprocessing to any warning or exception that comes out. We
may also want to avoid calling the Apache::WeBWorK warn/die handlers,
although I think we should leave them alone and assume our caller has
some way they want to collect errors and warnings. (For example,
WeBWorK::PG::Local has its own warn handler that accumulates warnings
and returns them as part of the WeBWorK::PG object.) It seems to me
that the simplest way to accomplish this is:
# reset the error detection
my $outer_sig_warn = $SIG{__WARN__};
local $SIG{__WARN__} = sub {
my $munged = PG_errorMessage('message', $_[0]);
ref $outer_sig_warn eq "CODE" ? &$outer_sig_warn($munged) :
warn $munged;
};
my $outer_sig_die = $SIG{__DIE__};
local $SIG{__DIE__} = sub {
my $munged = PG_errorMessage('traceback', $_[0]);
ref $outer_sig_die eq "CODE" ? &$outer_sig_die($munged) :
die $munged;
};
This doesn't deal with the case where the handlers are set to string
names of subroutines, but that could be added. It also doesn't handle
"IGNORE", but that could be added as well.
in process_answers(), we have:
local %errorTable;
$SIG{__DIE__} = sub {
#
# Get full traceback, but save it in local variable so that
# we can add it later. This is because some evaluators use
# eval to trap errors and then report them in the message
# column of the results table, and we don't want to include
# the traceback there.
#
my $fullerror = PG_errorMessage('traceback',@_);
my ($error,$traceback) = split /\n/, $fullerror, 2;
$fullerror =~ s/\n /<BR> /g; $fullerror
=~ s/\n/<BR>/g;
$error .= "\n";
$errorTable{$error} = $fullerror;
CORE::die($error);
};
# reset the error detection
my $save_SIG_warn_trap = $SIG{__WARN__};
$save_SIG_warn_trap = sub {CORE::warn @_} unless ref
($save_SIG_warn_trap) =~/CODE/;
$SIG{__WARN__} = sub {&$save_SIG_warn_trap(PG_errorMessage
('message',@_))};
my $save_SIG_die_trap = $SIG{__DIE__};
and then later on, after the answers are evaluated:
$SIG{__DIE__} = $save_SIG_die_trap;
$SIG{__WARN__} = $save_SIG_warn_trap;
First of all, restoring the die handler doesn't work at all, since
$save_SIG_die_trap is set to the custom die handler we just installed
a few lines before. In addition, I think we can do the same thing
here that we did up above, inserting the code that accumulates
exceptions. Also, I think %errorTable can be a lexical.
# reset the error detection
my $outer_sig_warn = $SIG{__WARN__};
local $SIG{__WARN__} = sub {
my $munged = PG_errorMessage('message', $_[0]);
ref $outer_sig_warn eq "CODE" ? &$outer_sig_warn($munged) :
warn $munged;
};
my %errorTable;
my $outer_sig_die = $SIG{__DIE__};
local $SIG{__DIE__} = sub {
my $fullerror = PG_errorMessage('traceback', $_[0]);
my ($error,$traceback) = split /\n/, $fullerror, 2;
$fullerror =~ s/\n /<BR> /g; $fullerror
=~ s/\n/<BR>/g;
$error .= "\n";
$errorTable{$error} = $fullerror;
ref $outer_sig_die eq "CODE" ? &$outer_sig_die($error) : die
$error;
};
There are three more places where warn/die handlers get messed with
- -- PG_restricted_eval(), PG_macro_file_eval(), and PG_answer_eval().
In each case, subroutine begins with:
my $save_SIG_warn_trap = $SIG{__WARN__};
$SIG{__WARN__} = sub { CORE::die @_};
my $save_SIG_die_trap = $SIG{__DIE__};
$SIG{__DIE__}= sub {CORE::die @_};
then some code gets passed to eval STRING, and then:
$SIG{__DIE__} = $save_SIG_die_trap;
$SIG{__WARN__} = $save_SIG_warn_trap;
We can replace these with the same type of code that's used in
evaluate_modules():
local $SIG{__WARN__} = "DEFAULT";
local $SIG{__DIE__} = "DEFAULT";
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (Darwin)
iD8DBQFC6mDE14CQX+2WvVgRAmifAKCf2Mjte5IUWjE/FVINpt+CMZkeVwCePwGh
8EQzltAV6e4ObIZN8iRpLvU=
=LagG
-----END PGP SIGNATURE-----
|