[Perl.sig] Perl Tip - Stopping Silent Errors With Exceptions
Litss Coordinator
litss.coord at anu.edu.au
Wed Apr 13 12:00:23 EST 2005
==== News ====
Stas Bekman, leading mod_perl developer, and author of *Practical
mod_perl* will be presenting courses in Melbourne and Sydney during
June
2005. Places must be booked by ``29th May 2005''. See the end of this
tip for more information.
==== Stopping Silent Errors With Exceptions ====
Do you ever get sick of writing ``or die "Error: $!"'' after all of
your
calls to ``open''? Do you remember to check whether your call to
``close'' worked? Do you remember to check whether you were able to
``print'' successfully to file handles? Do you check the return value
of
every subroutine you import from an existing module? How do you handle
error situations for the many things that can go wrong when you use
functions and subroutines which return a false value upon failure?
One way is to write code very much like the following:
open(my $fh, "<", $filename) or die "Failed to open $filename:
$!";
print $fh "some text ..." or die "Failed to print to
filehandle: !";
do_something_else() or die "Failed to do something!";
close $fh or die "Failed to close filehandle: $!";
However, this is ugly and time consuming. Furthermore, most of the time
``print'' ``close'', and other similar functions don't fail. This leads
to the argument that writing your code as above, clutters your code to
handle errors that "never" occur. Of course when they *do* occur and
you're not checking the return values, you may find debugging the
problem next to impossible.
In this tip we'll cover what exceptions are in Perl, why your
subroutines should throw exceptions and, finally how to make Perl's
existing subroutines and functions automatically throw exceptions on
failure.
== What is an Exception? ==
Put simply, an exception is what happens when something ``exceptional''
occurs. These are almost exclusively error cases. An exception usually
interupts your program flow and, if not caught, causes your program to
stop with an error.
When we write ``or die'' in Perl we are using Perl's exception handling
system. Unless the ``die'' exception is caught, Perl will terminate the
program and print out an error to ``STDERR''.
We can also throw exceptions using the ``croak'' and ``confess''
subroutines from Perl's ``Carp'' module. These allow us to report
errors
from the perspective of the caller, which is useful if a subroutine has
been called incorrectly or with bad arguments.
== Catching Exceptions ==
We can catch the exception by using an ``eval'' block. Note that
``eval'' blocks are very different to a ``eval'' *strings* which we
don't recommend. An ``eval'' block is compiled at the same time as our
other code and runs in turn when it is reached. If an exception is
thrown within an ``eval'' block then the ``eval'' stops and the $@
variable it set to the value of the exception.
eval {
open(my $fh, "<", $filename)
or die "Failed to open $filename: $!";
print $fh "some text ..."
or die "Failed to print to filehandle: $!";
do_something_else()
or die "Failed to do something!";
close $fh
or die "Failed to close filehandle: $!";
};
# If an exception was thrown, catch it and do something
if($@) {
# log the error and do any other clean up...
# rethrow the exception so that the program will die
die "Failed to add data to file: $@";
}
This still looks as ugly as it did before, but now we'll be able to do
any cleanup we wanted to before the program terminates. This cleanup
might include rolling back changes to the database, cleanly closing
connections to other machines and sending an email to explain what went
wrong.
Of course, a lot of the time we don't want to catch exceptions. When
this is the case we can leave out the ``eval'' block as we did in the
first example.
== Using Exceptions ==
It's generally considered a good practice to write your subroutines to
throw an exception upon failure rather than silently returning a false
value. An exception is much more likely to grab the programmer's
attention when something goes wrong. Even if it doesn't, an exception
means that their program isn't going to carry on assuming that
everything is working perfectly even though they haven't managed to
make
the connection, connect to the database or open the file.
To use exceptions just change your subroutines from looking like this:
# Return the approximate pressure in atmospheres at a
# given depth in metres underwater.
sub pressure_at_depth {
my $depth = shift;
# We need to have an positive depth to return
# a sensible result.
return if not defined($depth)
return if $depth < 0;
return 1+($depth/10);
}
To look like this:
use Carp;
sub pressure_at_depth {
my $depth = shift;
# We need to have an positive depth to return
# a sensible result.
croak "Depth not defined" if not defined($depth)
croak "Positive depth required" if $depth < 0;
return 1+($depth/10);
}
Now if someone who is using your subroutines writes:
my $pressure = pressure_at_depth($max_depth);
and $max_depth is undefined, then an error will seen immediately,
rather
than appearing further down in the program when $pressure is next used.
== Using Exceptions Without Rewriting Code ==
It's not always possible or practical to rewrite existing code to use
exceptions rather than to return ``undef''. Doing so may break existing
code which does test for errors but not by using exception handling.
Further, these options don't help with Perl's built-in functions.
Fortunately it's possible to make Perl's built-in functions and
existing
subroutines thrown an exception on failure without having to rewrite
anything. Even better, you can use this functionality in your new code
without affecting the error handling of legacy programs. We can do this
through the Perl module ``Fatal''.
``Fatal'' replaces functions with equivalents which succeed (return a
true value) or die. It enables us to rewrite the code from our first
example as:
use Fatal qw(open print close);
open(my $fh, "<", $filename);
print $fh "some text ...";
do_something_else() or die "Failed to do something!";
close $fh;
Which is a huge step forward in readability while still ensuring
exceptions are raised when ``open'', ``print'' or ``close'' fail.
``Fatal'' comes as a standard module with all recent releases of Perl.
In addition, ``Fatal'' also allows us to do this for user-defined
functions (subroutines).
use Fatal qw(open print close);
# When using Fatal on a subroutine that's not exported
# by a module, we need to call Fatal->import at run-time.
Fatal->import("do_something_else");
open(my $fh, "<", $filename);
print $fh "some text ...";
do_something_else();
close $fh;
==== Upcoming Courses in Canberra ====
http://perltraining.com.au/bookings/Canberra.html
Introduction to Perl: 17th - 18th May
Intermediate Perl: 19th - 20th May
Database Programming with Perl: 23rd June
Perl Security: 24th June
Note that the ``Early Bird Date'' for the introduction courses is
Friday, this week: ``15 April 2005''!.
==== Upcoming Courses in Melbourne ====
http://perltraining.com.au/bookings/Melbourne.html
Database Programming with Perl: 26th May
Perl Security: 27th May
Getting started with mod_perl: 6th June
mod_perl 2.0, the next generation: 7th June
Object Oriented Perl: 7th - 8th July
Note that the ``Early Bird Date'' for the first four courses is
``29th April 2005''!.
==== Upcoming Courses in Sydney ====
http://perltraining.com.au/bookings/Sydney.html
Database Programming with Perl: 9th June
Perl Security: 10th June
Getting started with mod_perl: 13th June
mod_perl 2.0, the next generation: 14th June
Object Oriented Perl: 21st - 22nd July
Note that the ``Early Bird Date'' for the mod_perl courses is
``29th April 2005''!.
==== Corporate Courses ====
http://perltraining.com.au/corporate.html
Do you have a large group, or the need for training at a particular
time? Perl Training Australia is happy to arrange a course in the time
and place that best suits you. For more information read our page on
Corporate Courses at http://perltraining.com.au/corporate.html or call
us on +61-3-9354-6001.
_______________________________________________
This Perl tooltip and associated text is Copyright Perl Training Australia.
You may freely distribute this text so long as it is distributed in full
with this Copyright noticed attached.
If you have any questions please don't hesitate to contact us:
Email: contact at perltraining.com.au
Phone: 03 9354 6001
Perl-tips mailing list
To change your subscription details visit:
http://perltraining.com.au/cgi-bin/mailman/listinfo/perl-tips
More information about the Perl.sig
mailing list