[Chicago-talk] Errors. And the reporting thereof.

Steven Lembark lembark at wrkhors.com
Fri Nov 14 11:18:06 CST 2003


> 1) We have to be able to roll it out piece-meal into
> the system. Anything that would require an immediate
> and total re-write of everything to handle something
> new really isn't appropriate.
> 2) It always has to report the correct error back.
> (duh, but I'll address that in a moment).
> 3) It can't break any of the existing code. Virtually
> all of the error handling is done by returning
> something true upon success and undef upon failure.
>
> Easy, right?

Yes, actually.

> So far, here's what I've investigated and my results.
>
> I looked into using Error.pm (and the equivalents) but
> have decided against it because it violates condition
> 2, unless we violate condition 1 as well. The issue is
> that it stores the error in a third-party location
> ("prior Error"), which is fine as long as everything
> is always setting an error there. But my concern is
> that a method will fail, set an error, and return
> undef. Then later, another method will fail, not set
> an error, and return undef. Then if I try to report on
> that second failure, I'll end up with the error
> message from the first. Very bad, since we're now
> reporting the wrong thing instead of something
> generic.

One thing you can do at the #! level is to trap the
unhandled exceptions so that you have a better idea
where they happen. This can be implemented in stages
by $SIG{__DIE__} = sub { ... } to existing pieces of
code you can trap aptosis in your sub's -- that or
wrapping the top-level call in a block eval.

You can then start adding die clauses where undef's
are assigned to the returns:

	$foo = undef;

becomes

	die "Undefined $foo";


At which point the die handler (or top-level eval) can
deal with reporting the cause of death.

One way to handle this is:

	#!/blah/perl

	eval
	{
		your old code here...
	};

	die "$0: $@" if $@;

	exit 0;

[useing perl -i to automate this if you like :-].

This will not affect the running code since none if it
throws exceptions.

Now go in and either modify the routines that see undef
with something like:

	croak "Undef returned from foobar call";

or replace the return undef's with similar croaks:

	croak "Useful Error Message Here" unless $var;

to die instead of return undef at all.

Personally, I prefer using exceptions since it
simplifies the calling code also: all it gets back
are values the called function considers legit. You
can handle the exceptions if you like via:

	sub foo
	{
		for(;;)
		{
			eval
			{
				some call;
				another call;
			};

			if( $@ =~ /errstring 1/ )
			{
				# deal with whatever caused error
				# number one.
			}
			elsif( $@ =~ /blah/ )
			{
				# deal with blah errors
			}
			elsif( $@ =~ /roadkill/ )
			{
				# redispatch the exception

				die "Roadkill: foo unable to handle $@ from bar";
			}
			else
			{
				# no errors, we're done.

				last
			}
		}
	}

If this looks pretty much like textbook exception handling,
you're right.

The nice thing about it is that the top-level handler reports
all errors and you can update the existing code incrementally
to handle the errors properly.



>   my $self = shift;
>   return $self->error("We always fail. Sigh.") unless
> 0;
> };

	sub slurp
	{
		my $item = shift or die "Bogus foo: false object";

		my $path = $item->{path};

		die "Nonexistant: $path" unless -e $path;
		die "Unreadable: $path"  unless -r _;

		open my $fh, '<', $path or die "Roadkill: $path: $!";

		local $/;

		# caller gets back the item's buffer

		$item->{buffer} = <$fh>
	}


	for my $file ( @filz )
	{
		# ignore anything other than roadkill.

		eval
		{
			$file->slurp or warn "Empty $file->{path}";
		};

		die "Fatal read: $@" if $@ && $@ =~ /Roadkill:/;
	}


The error method can be modified to die with a useful message
rather than return undef easily enough, after which you start
putting eval's in the proper places.

--
Steven Lembark                               2930 W. Palmer
Workhorse Computing                       Chicago, IL 60647
                                            +1 888 359 3508



More information about the Chicago-talk mailing list