Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW

Try::Tiny catch block with $_ eq ''

by dd-b (Monk)
on May 16, 2013 at 21:12 UTC ( [id://1033905] : perlquestion . print w/replies, xml ) Need Help??

dd-b has asked for the wisdom of the Perl Monks concerning the following question:

I'm having trouble with ending up in my catch block without any indication of why. Code blocks like:

try { $self->_run($self, $row_id, $subject) } catch { $logger->error("run($row_id): $_\n") }; $self->_clear_subject;

(that $logger is from Log::Log4perl).

Something happens down in $self->_run() and I end up in the catch block, but with no indication of why.

So, what are the things that count as "errors" for Try::Tiny? The docs just say "error", which doesn't mean anything terribly precise. The obvious case I know about is die calls. I don't have any die calls with null strings as their arguments. (What I end up with in the catch block is a string that Data::Dumper prints as ''. I know that's not impossible; the error put in $_ may not be "true". But I don't have a die statement with a null string arg, and I wouldn't expect DBIC or any standard module to have a die call with a null string arg.)

I'm using DBIC database stuff in there, and I know it throws errors (in fact that's the point of this whole setup, to catch any errors it throws), but I don't expect it to throw null string errors; does it sometimes?

I may also be slightly wrong about how far my code got; I've run into at least one case (that I can't reproduce in a simple environment) where it seemed that $logger->trace("String with undefined $variable substituted"); would log nothing ($variable is undef; I'd expect it to log "String with undefined substituted" and give a warning on stderr about the undef, and that's what a simple program that does only that produces). Other trace level calls through the same $logger in the same module are logged. As I say, I can't reproduce this in a simple case, it may be another example of my getting confused.

Replies are listed 'Best First'.
Re: Try::Tiny catch block with $_ eq ''
by tobyink (Canon) on May 16, 2013 at 21:39 UTC

    The only "errors" that Try::Tiny cares about are die. (Though this also includes Carp::croak(), Carp::confess(), etc, because they call die internally.)

    The only way I can reproduce $_ eq "" is to call die($e) where $e is a blessed object with overloading, that overloads eq or "" to compare equal to the empty string.

    $ perl -MTry::Tiny -MData::Dumper -e'my $e = do { package E; use overl +oad q[""] => sub {""}, fallback=>1; bless [] }; try { die $e } catch +{ print Dumper($_ eq "") }'

    Update: oh yes; the other thing worth thinking about... are you using my $_ anywhere in this lexical scope? (Or given/when?) The lexical version of the $_ variable could be masking the global one (which is what Try::Tiny uses). Try starting your catch block with our $_; and see if that makes any difference.

    package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name

      The "other ways to die" I know of, have checked for. I am not overloading eq or stringifiction, or throwing objects directly myself, in this code. I have no explicit my $_;. Hadn't thought about that (but would be unlikely to do it, too). However, there is given/when around some in this general area. I'll check the details of what that does. I don't think I'm inside it, though, so probably not the cause; but hadn't thought how that might interact, so I need to check. I don't think the catch blocks contain any given/when, though; still, I hadn't thought of that, need to check the possible cases, thanks!

Re: Try::Tiny catch block with $_ eq ''
by vsespb (Chaplain) on May 16, 2013 at 21:29 UTC
    I don't know answer to your question, but you can try use perl 'sigtrap' module to get stacktrace, and possibly temporary replace try/catch/$_ with 'unless defined eval { }'/$@ , to debug your code.
    UPD: For example
    unless (defined eval { $self->_run($self, $row_id, $subject); 1 }) { $logger->error("run($row_id): $@\n") }; $self->_clear_subject;
    this way you'll check if that is the problem with "my $_" or no.

      I'm using Try::Tiny because they claim it's hard to really use eval correctly to do this, but I could switch back and hope I don't hit their problem cases, at least as a test. We shall see. I've been putting eleventy-dozen $logger->trace("point 17"); statements through the code it comes out of unexpectedly to narrow down where it is. Setting up the database for a test run and then analyzing the log is not as simple as I could hope it was (though far better than my test setup procedure two weeks ago :-) ).

        I think it's hard when there are nested evals. Otherwise it's pretty simple, if you read the docs carefully.
Re: Try::Tiny catch block with $_ eq ''
by sundialsvc4 (Abbot) on May 17, 2013 at 13:32 UTC

    What I prefer to use is Exception::Class, in conjunction with Try::Tiny.   You can die (that is, “throw an error”) with an object as well as with a string, and this gives you a well-defined way to communicate between the point of the error and its ultimate handler.   (Wrap “legacy code” sections in a handler that catches the (string ...) exceptions and throws an object containing, among other things, that string.)

    An exception class can not only carry any sort of a “payload” (including undef values as-needed), but it is also self-identifying you have not only the class-name but the entire class hierarchy that you’ve cooked up.