Skip Menu |

This queue is for tickets about the Devel-Confess CPAN distribution.

Report information
The Basics
Id: 105362
Status: resolved
Priority: 0/
Queue: Devel-Confess

People
Owner: Nobody in particular
Requestors: PERLANCAR [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: (no value)
Fixed in: (no value)



Subject: Original references + stack trace when non-object references are thrown
For non-references: # trapping % perl -d:Confess -MData::Dump -E'sub f { die "Not found" }; eval { f }; dd $@' "Not found at -e line 1.\n\tmain::f() called at -e line 1\n\teval {...} called at -e line 1\n" # rethrowing % perl -d:Confess -MData::Dump -E'sub f { die "Not found" }; eval { f }; die' Not found at -e line 1. main::f() called at -e line 1 eval {...} called at -e line 1 ...propagated at -e line 1. For non-object references: # trapping (the original reference is returned, but no stack trace information) % perl -d:Confess -MData::Dump -E'sub f { die [404,"Not found"] }; eval { f }; dd $@' [404, "Not found"] % perl -d:Confess -MData::Dump -E'sub f { die [404,"Not found"] }; eval { f }; die' ARRAY(0xb6f160) at -e line 1. Is there a way to get both the stack trace and the original reference, so I can rethrow and still retain the original stack trace? Perhaps by wrapping the exception using on object (I see from the source code that Devel::Confess already does this kind of trick, albeit only for objects?). Imagined interface: # trapping (the original reference is returned, but no stack trace information) % perl -d:Confess=wrap_ref -MData::Dump -E'sub f { die [404,"Not found"] }; eval { f }; dd $@' bless({message>[400, "Not found"], stack_trace=>...}, "Devel::Confess::Wrapper") # rethrow % perl -d:Confess=wrap_ref -MData::Dump -E'sub f { die [404,"Not found"] }; eval { f }; die' ARRAY(0xb6f160) at -e line 1. main::f() called at -e line 1 eval {...} called at -e line 1 ...propagated at -e line 1. # rethrow after formatting the message % perl -d:Confess=wrap_ref -MData::Dump -E'sub f { die [404,"Not found"] }; eval { f }; $err = $@; $err->message = "<color>ERROR ".$err->message->[0].": ".$err->message->[1]."</color>"; die $err' "<color>ERROR 404: Not found</color>" at -e line 1. main::f() called at -e line 1 eval {...} called at -e line 1 ...propagated at -e line 1. Or do you think this use-case is too specific? Admittedly, I've very seldom encountered other people's code where it uses non-object refs as exception. I use this technique myself in my CLI framework (Perinci::CmdLine::Lite). Regards, perlancar
Upon reading your blog post http://blogs.perl.org/users/graham_knop/2013/09/carp-always-evenobjects.html and further look into the source code, it looks like this case is already handled. I can get the original exception from $Devel::Confess::EXCEPTIONS{ refaddr($ref) } and the stack trace in $Devel::Confess::MESSAGES{ refaddr($ref) }. Neat. Sorry for the noise.
There is a bit of a bug here. The code was intended to pick up the initial stack trace if a non-object reference was rethrown at the top level. I'll fix that. Turning non-object references into objects is too much of a change to the exception for me to do. So the only real option is to track the information externally. And that limits my options for retrieving the information. The only reliable option I can do for the general case is to check if we are not in an eval, in which case we can modify the output freely. I wouldn't recommend using the %MESSAGES hash. It isn't part of the API and could easily be changed in future releases. I would have kept it as a lexical, but I needed the longer lifetime that a package variable gives you. If you still need access to that information after I fix the initial issue, I'll have to think further about a proper way to provide it.
Ok noted. BTW, here's how I'm retrieving the stack trace: https://github.com/perlancar/perl-Perinci-CmdLine-Lite/commit/07d8c4616bfa042ed73e710ac5e85d12b88d0dcd Basically, it just appends the stack trace (from $MESSAGES{$id}) to the error message ($err->[1]). Example of how this plays out: * without -d:Confess: $ perl -MPerinci::CmdLine::Lite -E'Perinci::CmdLine::Lite->new(url=>"/Perinci/Examples/gen_array", subcommands=>sub{"blah"})->run' ERROR 500: BUG: Subcommands code didn't return a hashref $ perl -MPerinci::CmdLine::Lite -E'Perinci::CmdLine::Lite->new(url=>"/Perinci/Examples/gen_array", subcommands=>sub{"blah"})->run' -- --json [ 500, "BUG: Subcommands code didn't return a hashref", null, {} ] s$ perl -d:Confess -MPerinci::CmdLine::Lite -E'Perinci::CmdLine::Lite->new(url=>"/Perinci/Examples/gen_array", subcommands=>sub{"blah"})->run' ERROR 500: BUG: Subcommands code didn't return a hashref at /home/s1/perl5/perlbrew/perls/perl-5.22.0/lib/site_perl/5.22.0/Perinci/CmdLine/Base.pm line 406. Perinci::CmdLine::Base::list_subcommands(Perinci::CmdLine::Lite=HASH(0x1da0580)) called at /home/s1/perl5/perlbrew/perls/perl-5.22.0/lib/site_perl/5.22.0/Perinci/CmdLine/Lite.pm line 511 Perinci::CmdLine::Lite::action_help(Perinci::CmdLine::Lite=HASH(0x1da0580), HASH(0x1fc2248)) called at /home/s1/perl5/perlbrew/perls/perl-5.22.0/lib/site_perl/5.22.0/Perinci/CmdLine/Base.pm line 1185 eval {...} called at /home/s1/perl5/perlbrew/perls/perl-5.22.0/lib/site_perl/5.22.0/Perinci/CmdLine/Base.pm line 1143 Perinci::CmdLine::Base::run(Perinci::CmdLine::Lite=HASH(0x1da0580)) called at -e line 1 $ perl -d:Confess -MPerinci::CmdLine::Lite -E'Perinci::CmdLine::Lite->new(url=>"/Perinci/Examples/gen_array", subcommands=>sub{"blah"})->run' -- --json [ 500, "BUG: Subcommands code didn't return a hashref\n at /home/s1/perl5/perlbrew/perls/perl-5.22.0/lib/site_perl/5.22.0/Perinci/CmdLine/Base.pm line 406.\n\tPerinci::CmdLine::Base::list_subcommands(Perinci::CmdLine::Lite=HASH(0x1e78580)) called at /home/s1/perl5/perlbrew/perls/perl-5.22.0/lib/site_perl/5.22.0/Perinci/CmdLine/Lite.pm line 511\n\tPerinci::CmdLine::Lite::action_help(Perinci::CmdLine::Lite=HASH(0x1e78580), HASH(0x21d4cd0)) called at /home/s1/perl5/perlbrew/perls/perl-5.22.0/lib/site_perl/5.22.0/Perinci/CmdLine/Base.pm line 1185\n\teval {...} called at /home/s1/perl5/perlbrew/perls/perl-5.22.0/lib/site_perl/5.22.0/Perinci/CmdLine/Base.pm line 1143\n\tPerinci::CmdLine::Base::run(Perinci::CmdLine::Lite=HASH(0x1e78580)) called at -e line 1", null, {} ] Regards, perlancar
I've released 0.008000, which will include a stack trace with a ref that has been rethrown if the output is going to the screen. It will also dumper refs and objects without stringify overloads if the dump option is enabled and the output is going to the screen. I'm still hesitant to include an API for getting a stack trace for a given exception, as this module wasn't designed to really have an API at all. I'll still consider it for the future though.