http://qs1969.pair.com?node_id=459595

Dru has asked for the wisdom of the Perl Monks concerning the following question:

Monks,

Can someone point me in the right direction on error handling with packages? I have some die statements in a custom package, in case certain files don't exist, but it's preventing the rest of my script from executing when the die is encountered. Ideally, I would like to build in an error checking mechanism that if the package dies, I can catch this within the script and perform some action based on it. I thought something like this would work:
use strict; user warnings; use VPNUser qw(get_vpn_user $user $fullname $location); my $ip = shift; my $time = shift; my $status = get_vpn_user($ip, $time); print "$status\n";
but $status never prints anything if the package dies. I have "return 1" at the end of the package, so maybe that's my problem?

My error handling skills are limited to "die" and "warn". I would appreciate if someone could point me in the right direction on how to handle this.

Thanks,
Dru

Replies are listed 'Best First'.
Re: Error Handling with Packages
by holli (Abbot) on May 23, 2005 at 16:04 UTC
    use eval BLOCK
    use strict; use warnings; eval { &GWBush; }; print "Error: $@" if $@; sub GWBush { die "I'm a liar"; }


    holli, /regexed monk/
Re: Error Handling with Packages
by ghenry (Vicar) on May 23, 2005 at 16:05 UTC

    I don't mean to be insulting, but have you read through any of these:

    HTH.

    Walking the road to enlightenment... I found a penguin and a camel on the way.....
    Fancy a yourname@perl.me.uk? Just ask!!!
Re: Error Handling with Packages
by Forsaken (Friar) on May 23, 2005 at 16:06 UTC
    Carp is your friend. Personally, when writing custom modules, I use croak to indicate *what* went wrong and return 0/1 to make the calling script aware of what happens. If you insist onusing die, you'd be looking at the carp command..it also dies, but at least you know why.


    Remember rule one...

      Yes, I meant to put this in my comment above.

      He can read through Error-handling-and-messages in the perlmodstyle page.

      Specifically:

      croak() only when your module absolutely cannot figure out what to do. (croak() is a better version of die() for use within modules, which reports its errors from the perspective of the caller. See Carp for details of croak(), carp() and other useful routines.)/p>

      Walking the road to enlightenment... I found a penguin and a camel on the way.....
      Fancy a yourname@perl.me.uk? Just ask!!!
      it seems to me that you have swapped croak and carp! croak dies, carp just prints warnings.

      Anyway, usually, talking directly to the end user about some internals of the program is not a good idea... well, for scripts it is ok, specially if you are both the user and the programmer but for modules it's a bad idea, it doesn't escalate.

      For most errors, the best think to do is to croak() and let the module user wrap the call in eval if he wants to handle the error in some way or just ignore it.

      Sometimes, returning false and passing more information about the error in $! also makes sense.

        Yeah, you're right, looks like I got them the wrong way around . As for talking to the end-user from a module, I think using carp for that is an elegant solution, especially when it's something that can be toggled on and off using a flag. For example:
        package Foo; use strict; use warnings; sub new { my($class, $debug) = @_; my $self = {}; bless($self, $class); if($debug) { $self->{'Debug'} = 1; } return $self; } sub bar { my($self, $hashref, @arguments) = @_; unless(ref($hashref) eq 'HASH') { if($debug) { carp "ERROR: first argument to \'bar\' must be a hashref"; } return; } #insert code here return $result; }
        I find carp most useful for these cases. Personally I feel a module should never die on its own, that's my decision...


        Remember rule one...
Re: Error Handling with Packages
by sasikumar (Monk) on May 23, 2005 at 16:11 UTC
    Hi Dru,

    I had such problems. I tried to avaoid it this way. All my custom packages functions will return me two variable instead of one. For eg:
    #Function inside a package sub test() { ... ... open(FILE,"filename") || return (1,"Error while trying to open the fil +e filename"); .... return (0,UNDEF); } # Main Program my ($result,$err)=test(); Now based on the return value i decide to print the error or take corr +ective messures

    There are lot of other ways to do it too. But i found this to be easier for me.

    Thanks
    SasiKumar
      Returning the error as a second value is usually not a good idea. It requires too much discipline to always check the error status, after every call, and it doesn't allow for idiomatic checks as
      foo() or die "foo failed";
      so lazy programmers will just don't do it resulting on the worst kind of errors: unnoticed errors!

      Usually it's much better to croak(): module users would still be able to handle errors via eval if they wish, and when not, the final user will notice that something went wrong because the croak will reach them.

        I have another custom approach:

        This way the module user can still use idioms like

        $O->foo() or die "could not blabla: ".$O->error();

        Users can still set the error handler to croak() if they wish. This could be extended ad nauseam: $debug switches, $exec_handler switch, method to get errMsg without resetting it -- you name it. I should probably put it in its own module, too, and inherit from that. I'm still not sure how to escalate errors when one method calls another. Maybe simply

        sub bar { my ($self) = @_; foo() or return raiseError("bar failed to rhubarb: ".$self->error( +)); return 1; } # ... $O->bar() or die "could not blabla: ".$O->error(); # "could not blabla: bar failed to rhubarb: foo: could not do thisandt +hat"
Re: Error Handling with Packages
by Dru (Hermit) on May 23, 2005 at 19:38 UTC
    Thanks all. I'm using Carp in my package for my detail error messages and calling my subroutine with an "eval". That is what I was needing to do.