in reply to Re: Can I catch a die from someone else's module?
in thread Can I catch a die from someone else's module?

Yes, eval definitely works!

Quick followup:

Do I need the 1; ? It works with and without it, just wanted to make sure it might not be needed for something internal, or was it there for an example?

eval { Module::That::Dies::die_here(); 1; } or do { warn "Error from Module::That::Dies:\n$@\n"; };

Replies are listed 'Best First'.
Re^3: Can I catch a die from someone else's module?
by haukex (Archbishop) on Apr 19, 2022 at 18:57 UTC
    Do I need the 1; ?

    Generally, yes, you should include it. The reason is that eval returns undef on failure, which is what the or do { ... } is intended to react to. However, the or would also be triggered if the eval block were to return any other false value, such as 0 or the empty string. For example, the following incorrectly prints "Error from Module::That::Dies:". So unless you can be absolutely sure that the eval block always returns a true value, you should include the 1 (or any other true value) as the last statement of the block.

    sub Module::That::Dies::die_here { return 0 } eval { Module::That::Dies::die_here(); #1; } or do { warn "Error from Module::That::Dies:\n$@\n"; };

    Update: I tend to write it like the following, since the 1 on a line by itsself can look out of place and might be accidentally deleted:

    eval { ... ;1 } or do { # catch ... }

      I, too, use the eval { ...; 1; };. I'm literally just writing unit tests with eval right now, so I thought I'd share how I often write them:

      my $missing_opt_code_succeeded = eval { $tesla->option_codes('NON_EXIST'); 1; }; is $missing_opt_code_succeeded, undef, "If an option code doesn't exist, we croak ok"; like $@, qr/does not exist/, "...and error message is sane";

        G'day stevieb,

        It's your module and you can test it in whatever way you want; however, I'd have a problem with the separation of the eval block and $@ several lines later.

        Here's a real-world ($work) example I wrote a few years ago that similarly tests for failure. In this instance, it checks that an abstract class can't be instantiated. I just grabbed it from git and only changed the class name to Abstract::Work::Class; otherwise, this is a verbatim copy of 01-abstract.t:

        #!perl -T use 5.016; use warnings; use Test::More tests => 3; use Abstract::Work::Class; my $obj; my $no_instantiation = 0; my $eval_message = ''; eval { $obj = Abstract::Work::Class::->new(); 1; } or do { $eval_message = $@; $no_instantiation = 1; }; ok(! defined($obj), 'Test potential object remains undefined'); ok($no_instantiation, 'Test no instantiation of abstract class'); ok(index($eval_message, 'FATAL! Attempt to instantiate an abstract cla +ss.') == 0, 'Test error message from attempting instantiation of abstract clas +s');

        I normally attempt to capture the value of $@ as the first statement in '... or do { ... };'.

        As a testament to the robustness of that code, it's from one of the core modules of a large 30+ module framework. The framework has been modified, extended and enhanced many times, and built on many platforms, over the last four years; 01-abstract.t was last modified in May 2018. It's a solid solution and you're welcome to adapt it to your needs if you want.

        — Ken

Re^3: Can I catch a die from someone else's module?
by kcott (Archbishop) on Apr 20, 2022 at 00:00 UTC
    "Do I need the 1;?"

    ++ Good question.

    I would always include it. If you think other maintainers may not understand its importance, add a comment like:

    eval { ...; 1; # IMPORTANT! Do not remove. See: https://www.perlmonks.org/?nod +e_id=11143055 } or do { ...; };

    Even in situations when the last statement in the eval block evaluates to 1, or some other TRUE value, I would include it:

    eval { ...; $x = 1; 1; } ...;

    Otherwise, if someone removes the '$x = 1;' line, or changes the value, but does not know, or forgets, to add the '1;' statement, the 'eval {...} or do {...};' construct may be broken.

    — Ken

Re^3: Can I catch a die from someone else's module?
by perlfan (Parson) on May 02, 2022 at 01:39 UTC
    I think it depends; for this module I would do this since it returns the reference if successful.
    local $@; my $book = eval { Spreadsheet::Read->new($file); };
    or
    local $@; my $book; my $ok = eval { $book = Spreadsheet::Read->new($file); 1; };
    The second example seems like "more work" to handle the error and is a lot messier. So in this case adding the "1" is superfulous since the called sub actually returns something (or doesn't).

    A case where I'd use 1; with an or undef thrown in:

    local $@; my $ok = eval { doSomethingButDontWantReturnValButCouldDie() or undef; 1; };
    I think ultimately, the answer is like I stated above, it depends - on what you're doing and expecting; and how you tend to check for/handle exceptions.

    I've also done this; but usually it's a game time decision for me.

    local $@; my $ok = eval { doSomethingButDontWantReturnValButCouldDie(); 1; } or undef;
Re^3: Can I catch a die from someone else's module?
by perlfan (Parson) on May 02, 2022 at 01:46 UTC
    I'd like to add as a separate comment, while the use if the 1; is "it depends"; I would absolutely always localize $@ as close in scope to the eval {} as make sense; usually right before it (unless it's already been done in the same scope and if there are a series of eval{}s that have already been successfully executed.
    local $@; my $ok = eval { doSomethingButDontWantReturnValButCouldDie() or undef; 1; };