http://qs321.pair.com?node_id=388614

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

Fellow Monks,

Please shine your blessed light on this:

I was writing some code using Image::Magick and found myself repeating lines like:

$err = $image->DoSomething; if("$err") { # one must evaluate the return value # in string context, according to the # PerlMagick docs die ("Can't do Something: $err\n"); }

Which I find less than esthetical for several reasons. And even now I::M sometimes printed a warning in stead of giving an error message.

I'd rather do:

eval { $image->DoSomething; $image->DoSomethingElse; $image->Save; }; if($@) { handle_the_error; }

So I started Image::Magick::Exceptions, borrowing from Simon Cozens' Class::Wrap:

package Image::Magick::Exceptions; use warnings; use strict; use Exception::Class qw/Image::Magick::Exception/; { no strict; no warnings qw/redefine prototype/; my $class = "Image::Magick"; for my $method (keys %{$class."::"}) { my $cmethod = "${class}::$method"; if ( defined *{$cmethod}{CODE} and $method ne 'new' and $method !~ /^[A-Z]+$/ ) { *{"hidden::$cmethod"} = *{$cmethod}{CODE}; *{$cmethod} = sub { my $err = &{"hidden::$cmethod"}; if("$err") { Image::Magick::Exception->throw (error => "$err"); }; $err; }; } } } package main; use Image::Magick; use Image::Magick::Exceptions; etc();

As a proof of concept this works. BUT, things start going wrong when a function is *supposed* to return a string, like the Get/GetAttribute (why all these aliases, by the way?). Reading the documentation again, it says you need to handle methods differently based on what they are supposed to return.

Numerical methods should be tested like:

$ret = $image->Write; if ("$ret") { error("$ret") } print $ret . " images written";
Here, evaluating $ret in string context apparently returns something different than evaluating $ret in numerical context.

"Void" methods can be tested the same. If the return value is not empty, there's an error.

Image operations must be tested like:

$clone = $image->Clone; if (!ref $clone) { error($clone) }

And the documentation fails to mention functions that are *supposed* to return string values.

I could fix my Image::Magick::Exceptions module, by keeping a list of method names and return types.

But where to find these? Even the I::M manual is not clear on what methods return what. Or even fails to mention all methods.

And even this doesn't handle the warn()'s that I::M throws every now and then.

Finally, and thanks for bearing with my long winded post, the question:

Is there a better (non-documented) way to catch errors from I::M?
Or else, is there a better manual than the one provided with I::M?

--
Lyon

Replies are listed 'Best First'.
Re: Exception handling for Image::Magick
by simonm (Vicar) on Sep 06, 2004 at 00:42 UTC
    I think that you can take advantage of the fact that the error messages all look like "Error 300: Couldn't foo the bar." The code below should work unless the string returned by GetAttribute happens to be formatted to look like an error message.
    use Exception::Class ( 'Image::Magick::Exception' => { fields => [ 'type', 'errno', 'msg' ] + } ); ... my $result = &{"hidden::$cmethod"}; if( "$result" =~ /^(\w+)\s(\d\d\d):\s(.*)/ ) { Image::Magick::Exception->throw( error => "$result", type => $1, errno => $2, msg => $3 ); }; return $result

    More generally, it looks like the really proper fix to this would involve twiddling the glue code in Magick.xs. I took a look at the code and I think a global flag could be added that changed the behavior of MagickErrorHandler and MagickWarningHandler, equivalent to DBI's RaiseError flag, but I don't have enough XS experience to know the right way to implement this.

      Brilliantly simple!! If it looks like a duck and quacks like a duck, it probably is a duck.

      Cheers,

      --
      Lyon

Re: Exception handling for Image::Magick
by Zaxo (Archbishop) on Sep 05, 2004 at 16:42 UTC

    The conventional way of writing your first snippet is:

    $err = $image->DoSomething or die "Can't do Something: ", $err;
    Perhaps that is adequate and simpler than exception handling.

    After Compline,
    Zaxo

      the problem here is that I::M methods return a non-empty string on failure. So that would be:

      $err = $image->DoSomething and die $err;

      But sometimes methods are *meant* to return someting and then you have to test agains context to see if it's an error or not.

      --
      Lyon

Re: Exception handling for Image::Magick
by grantm (Parson) on Sep 06, 2004 at 00:50 UTC
    I also find Image::Magick's error handling a pain to deal with . Like you, I thought a wrapper class to take care of error handling would be a good thing. I didn't get too far down the track before I decided that it really belongs in the core Image::Magick module. Perhaps an option like DBI's 'RaiseError' to enable the die-on-error behaviour would be a good idea. It might be worth asking the package maintainer if they'd accept a patch to do something like that.