0
|
1 #-----------------------------------------------------------------
|
|
2 # $Id: Exception.pm,v 1.14 2002/06/29 00:42:17 sac Exp $
|
|
3 #
|
|
4 # BioPerl module Bio::Root::Exception
|
|
5 #
|
|
6 # Cared for by Steve Chervitz <sac@bioperl.org>
|
|
7 #
|
|
8 # You may distribute this module under the same terms as perl itself
|
|
9 #-----------------------------------------------------------------
|
|
10
|
|
11 =head1 NAME
|
|
12
|
|
13 Bio::Root::Exception - Generic exception objects for Bioperl
|
|
14
|
|
15 =head1 SYNOPSIS
|
|
16
|
|
17 =head2 Throwing exceptions using B<Error::throw()>:
|
|
18
|
|
19 use Bio::Root::Exception;
|
|
20 use Error;
|
|
21
|
|
22 # Set Error::Debug to include stack trace data in the error messages
|
|
23 $Error::Debug = 1;
|
|
24
|
|
25 $file = shift;
|
|
26 open (IN, $file) ||
|
|
27 throw Bio::Root::FileOpenException ( "Can't open file $file for reading", $!);
|
|
28
|
|
29 =head2 Throwing exceptions using B<Bio::Root::Root::throw()>:
|
|
30
|
|
31 # Here we have an object that ISA Bio::Root::Root, so it inherits throw().
|
|
32
|
|
33 open (IN, $file) ||
|
|
34 $object->throw(-class => 'Bio::Root::FileOpenException',
|
|
35 -text => "Can't open file $file for reading",
|
|
36 -value => $!);
|
|
37
|
|
38 =head2 Catching and handling exceptions using B<Error::try()>:
|
|
39
|
|
40 use Bio::Root::Exception;
|
|
41 use Error qw(:try);
|
|
42
|
|
43 # Note that we need to import the 'try' tag from Error.pm
|
|
44
|
|
45 # Set Error::Debug to include stack trace data in the error messages
|
|
46 $Error::Debug = 1;
|
|
47
|
|
48 $file = shift;
|
|
49 try {
|
|
50 open (IN, $file) ||
|
|
51 throw Bio::Root::FileOpenException ( "Can't open file $file for reading", $!);
|
|
52 }
|
|
53 catch Bio::Root::FileOpenException with {
|
|
54 my $err = shift;
|
|
55 print STDERR "Using default input file: $default_file\n";
|
|
56 open (IN, $default_file) || die "Can't open $default_file";
|
|
57 }
|
|
58 otherwise {
|
|
59 my $err = shift;
|
|
60 print STDERR "An unexpected exception occurred: \n$err";
|
|
61
|
|
62 # By placing an the error object reference within double quotes,
|
|
63 # you're invoking its stringify() method.
|
|
64 }
|
|
65 finally {
|
|
66 # Any code that you want to execute regardless of whether or not
|
|
67 # an exception occurred.
|
|
68 };
|
|
69 # the ending semicolon is essential!
|
|
70
|
|
71
|
|
72 =head2 Defining a new Exception type as a subclass of Bio::Root::Exception:
|
|
73
|
|
74 @Bio::TestException::ISA = qw( Bio::Root::Exception );
|
|
75
|
|
76
|
|
77 =head1 DESCRIPTION
|
|
78
|
|
79 =head2 Exceptions defined in B<Bio::Root::Exception>
|
|
80
|
|
81 These are generic exceptions for typical problem situations that could arise
|
|
82 in any module or script.
|
|
83
|
|
84 =over 8
|
|
85
|
|
86 =item Bio::Root::Exception()
|
|
87
|
|
88 =item Bio::Root::NotImplemented()
|
|
89
|
|
90 =item Bio::Root::IOException()
|
|
91
|
|
92 =item Bio::Root::FileOpenException()
|
|
93
|
|
94 =item Bio::Root::SystemException()
|
|
95
|
|
96 =item Bio::Root::BadParameter()
|
|
97
|
|
98 =item Bio::Root::OutOfRange()
|
|
99
|
|
100 =item Bio::Root::NoSuchThing()
|
|
101
|
|
102 =back
|
|
103
|
|
104 Using defined exception classes like these is a good idea because it
|
|
105 indicates the basic nature of what went wrong in a convenient,
|
|
106 computable way.
|
|
107
|
|
108 If there is a type of exception that you want to throw
|
|
109 that is not covered by the classes listed above, it is easy to define
|
|
110 a new one that fits your needs. Just write a line like the following
|
|
111 in your module or script where you want to use it (or put it somewhere
|
|
112 that is accessible to your code):
|
|
113
|
|
114 @NoCanDoException::ISA = qw( Bio::Root::Exception );
|
|
115
|
|
116 All of the exceptions defined in this module inherit from a common
|
|
117 base class exception, Bio::Root::Exception. This allows a user to
|
|
118 write a handler for all Bioperl-derived exceptions as follows:
|
|
119
|
|
120 use Bio::Whatever;
|
|
121 use Error qw(:try);
|
|
122
|
|
123 try {
|
|
124 # some code that depends on Bioperl
|
|
125 }
|
|
126 catch Bio::Root::Exception with {
|
|
127 my $err = shift;
|
|
128 print "A Bioperl exception occurred:\n$err\n";
|
|
129 };
|
|
130
|
|
131 So if you do create your own exceptions, just be sure they inherit
|
|
132 from Bio::Root::Exception directly, or indirectly by inheriting from a
|
|
133 Bio::Root::Exception subclass.
|
|
134
|
|
135 The exceptions in Bio::Root::Exception are extensions of Graham Barr's
|
|
136 B<Error.pm> module available from CPAN. Despite this dependency, the
|
|
137 Bio::Root::Exception module does not explicitly C<require Error>.
|
|
138 This permits Bio::Root::Exception to be loaded even when
|
|
139 Error.pm is not available.
|
|
140
|
|
141 =head2 Throwing exceptions within Bioperl modules
|
|
142
|
|
143 Error.pm is not part of the Bioperl distibution, and may not be
|
|
144 present within any given perl installation. So, when you want to
|
|
145 throw an exception in a Bioperl module, the safe way to throw it
|
|
146 is to use B<Bio::Root::Root::throw()> which can use Error.pm
|
|
147 when it's available. See documentation in Bio::Root::Root for details.
|
|
148
|
|
149 =head1 SEE ALSO
|
|
150
|
|
151 See the C<examples/exceptions> directory of the Bioperl distribution for
|
|
152 working demo code.
|
|
153
|
|
154 B<Bio::Root::Root::throw()> for information about throwing
|
|
155 Bio::Root::Exception-based exceptions.
|
|
156
|
|
157 B<Error.pm> (available from CPAN, author: GBARR)
|
|
158
|
|
159 Error.pm is helping to guide the design of exception handling in Perl 6.
|
|
160 See these RFC's:
|
|
161
|
|
162 http://dev.perl.org/rfc/63.pod
|
|
163
|
|
164 http://dev.perl.org/rfc/88.pod
|
|
165
|
|
166
|
|
167 =head1 AUTHOR
|
|
168
|
|
169 Steve Chervitz E<lt>sac@bioperl.orgE<gt>
|
|
170
|
|
171 =head1 COPYRIGHT
|
|
172
|
|
173 Copyright (c) 2001 Steve Chervitz. All Rights Reserved.
|
|
174
|
|
175 This library is free software; you can redistribute it and/or modify
|
|
176 it under the same terms as Perl itself.
|
|
177
|
|
178 =head1 DISCLAIMER
|
|
179
|
|
180 This software is provided "as is" without warranty of any kind.
|
|
181
|
|
182 =head1 EXCEPTIONS
|
|
183
|
|
184 =cut
|
|
185
|
|
186 # Define some generic exceptions.'
|
|
187
|
|
188 package Bio::Root::Exception;
|
|
189
|
|
190 use strict;
|
|
191
|
|
192 my $debug = $Error::Debug; # Prevents the "used only once" warning.
|
|
193 my $DEFAULT_VALUE = "__DUMMY__"; # Permits eval{} based handlers to work
|
|
194
|
|
195 =head2 B<Bio::Root::Exception>
|
|
196
|
|
197 Purpose : A generic base class for all BioPerl exceptions.
|
|
198 By including a "catch Bio::Root::Exception" block, you
|
|
199 should be able to trap all BioPerl exceptions.
|
|
200 Example : throw Bio::Root::Exception("A generic exception", $!);
|
|
201
|
|
202 =cut
|
|
203
|
|
204 #---------------------------------------------------------
|
|
205 @Bio::Root::Exception::ISA = qw( Error );
|
|
206 #---------------------------------------------------------
|
|
207
|
|
208 =head2 Methods defined by Bio::Root::Exception
|
|
209
|
|
210 =over 4
|
|
211
|
|
212 =item B< new() >
|
|
213
|
|
214 Purpose : Guarantees that -value is set properly before
|
|
215 calling Error::new().
|
|
216
|
|
217 Arguments: key-value style arguments same as for Error::new()
|
|
218
|
|
219 You can also specify plain arguments as ($message, $value)
|
|
220 where $value is optional.
|
|
221
|
|
222 -value, if defined, must be non-zero and not an empty string
|
|
223 in order for eval{}-based exception handlers to work.
|
|
224 These require that if($@) evaluates to true, which will not
|
|
225 be the case if the Error has no value (Error overloads
|
|
226 numeric operations to the Error::value() method).
|
|
227
|
|
228 It is OK to create Bio::Root::Exception objects without
|
|
229 specifing -value. In this case, an invisible dummy value is used.
|
|
230
|
|
231 If you happen to specify a -value of zero (0), it will
|
|
232 be replaced by the string "The number zero (0)".
|
|
233
|
|
234 If you happen to specify a -value of empty string (""), it will
|
|
235 be replaced by the string "An empty string ("")".
|
|
236
|
|
237 =cut
|
|
238
|
|
239 sub new {
|
|
240 my ($class, @args) = @_;
|
|
241 my ($value, %params);
|
|
242 if( @args % 2 == 0 && $args[0] =~ /^-/) {
|
|
243 %params = @args;
|
|
244 $value = $params{'-value'};
|
|
245 }
|
|
246 else {
|
|
247 $params{-text} = $args[0];
|
|
248 $value = $args[1];
|
|
249 }
|
|
250
|
|
251 if( defined $value and not $value) {
|
|
252 $value = "The number zero (0)" if $value == 0;
|
|
253 $value = "An empty string (\"\")" if $value eq "";
|
|
254 }
|
|
255 else {
|
|
256 $value ||= $DEFAULT_VALUE;
|
|
257 }
|
|
258 $params{-value} = $value;
|
|
259
|
|
260 my $self = $class->SUPER::new( %params );
|
|
261 return $self;
|
|
262 }
|
|
263
|
|
264 =item pretty_format()
|
|
265
|
|
266 Purpose : Get a nicely formatted string containing information about the
|
|
267 exception. Format is similar to that produced by
|
|
268 Bio::Root::Root::throw(), with the addition of the name of
|
|
269 the exception class in the EXCEPTION line and some other
|
|
270 data available via the Error object.
|
|
271 Example : print $error->pretty_format;
|
|
272
|
|
273 =cut
|
|
274
|
|
275 sub pretty_format {
|
|
276 my $self = shift;
|
|
277 my $msg = $self->text;
|
|
278 my $stack = '';
|
|
279 if( $Error::Debug ) {
|
|
280 $stack = $self->_reformat_stacktrace();
|
|
281 }
|
|
282 my $value_string = $self->value ne $DEFAULT_VALUE ? "VALUE: ".$self->value."\n" : "";
|
|
283 my $class = ref($self);
|
|
284
|
|
285 my $title = "------------- EXCEPTION: $class -------------";
|
|
286 my $footer = "\n" . '-' x CORE::length($title);
|
|
287 my $out = "\n$title\n" .
|
|
288 "MSG: $msg\n". $value_string. $stack. $footer . "\n";
|
|
289 return $out;
|
|
290 }
|
|
291
|
|
292
|
|
293 # Reformatting of the stack performed by _reformat_stacktrace:
|
|
294 # 1. Shift the file:line data in line i to line i+1.
|
|
295 # 2. change xxx::__ANON__() to "try{} block"
|
|
296 # 3. skip the "require" and "Error::subs::try" stack entries (boring)
|
|
297 # This means that the first line in the stack won't have any file:line data
|
|
298 # But this isn't a big issue since it's for a Bio::Root::-based method
|
|
299 # that doesn't vary from exception to exception.
|
|
300
|
|
301 sub _reformat_stacktrace {
|
|
302 my $self = shift;
|
|
303 my $msg = $self->text;
|
|
304 my $stack = $self->stacktrace();
|
|
305 $stack =~ s/\Q$msg//;
|
|
306 my @stack = split( /\n/, $stack);
|
|
307 my @new_stack = ();
|
|
308 my ($method, $file, $linenum, $prev_file, $prev_linenum);
|
|
309 my $stack_count = 0;
|
|
310 foreach my $i( 0..$#stack ) {
|
|
311 # print "STACK-ORIG: $stack[$i]\n";
|
|
312 if( ($stack[$i] =~ /^\s*([^(]+)\s*\(.*\) called at (\S+) line (\d+)/) ||
|
|
313 ($stack[$i] =~ /^\s*(require 0) called at (\S+) line (\d+)/)) {
|
|
314 ($method, $file, $linenum) = ($1, $2, $3);
|
|
315 $stack_count++;
|
|
316 }
|
|
317 else{
|
|
318 next;
|
|
319 }
|
|
320 if( $stack_count == 1 ) {
|
|
321 push @new_stack, "STACK: $method";
|
|
322 ($prev_file, $prev_linenum) = ($file, $linenum);
|
|
323 next;
|
|
324 }
|
|
325
|
|
326 if( $method =~ /__ANON__/ ) {
|
|
327 $method = "try{} block";
|
|
328 }
|
|
329 if( ($method =~ /^require/ and $file =~ /Error\.pm/ ) ||
|
|
330 ($method =~ /^Error::subs::try/ ) ) {
|
|
331 last;
|
|
332 }
|
|
333 push @new_stack, "STACK: $method $prev_file:$prev_linenum";
|
|
334 ($prev_file, $prev_linenum) = ($file, $linenum);
|
|
335 }
|
|
336 push @new_stack, "STACK: $prev_file:$prev_linenum";
|
|
337
|
|
338 return join "\n", @new_stack;
|
|
339 }
|
|
340
|
|
341 =item B< stringify() >
|
|
342
|
|
343 Purpose : Overrides Error::stringify() to call pretty_format().
|
|
344 This is called automatically when an exception object
|
|
345 is placed between double quotes.
|
|
346 Example : catch Bio::Root::Exception with {
|
|
347 my $error = shift;
|
|
348 print "$error";
|
|
349 }
|
|
350
|
|
351 See Also: L<pretty_format()|pretty_format>
|
|
352
|
|
353 =cut
|
|
354
|
|
355 sub stringify {
|
|
356 my ($self, @args) = @_;
|
|
357 return $self->pretty_format( @args );
|
|
358 }
|
|
359
|
|
360
|
|
361
|
|
362 =back
|
|
363
|
|
364 =head1 Subclasses of Bio::Root::Exception
|
|
365
|
|
366
|
|
367 =head2 B<Bio::Root::NotImplemented>
|
|
368
|
|
369 Purpose : Indicates that a method has not been implemented.
|
|
370 Example : throw Bio::Root::NotImplemented(
|
|
371 -text => "Method \"foo\" not implemented in module FooBar.",
|
|
372 -value => "foo" );
|
|
373
|
|
374 =cut
|
|
375
|
|
376 #---------------------------------------------------------
|
|
377 @Bio::Root::NotImplemented::ISA = qw( Bio::Root::Exception );
|
|
378 #---------------------------------------------------------
|
|
379
|
|
380 =head2 B<Bio::Root::IOException>
|
|
381
|
|
382 Purpose : Indicates that some input/output-related trouble has occurred.
|
|
383 Example : throw Bio::Root::IOException(
|
|
384 -text => "Can't save data to file $file.",
|
|
385 -value => $! );
|
|
386
|
|
387 =cut
|
|
388
|
|
389 #---------------------------------------------------------
|
|
390 @Bio::Root::IOException::ISA = qw( Bio::Root::Exception );
|
|
391 #---------------------------------------------------------
|
|
392
|
|
393
|
|
394 =head2 B<Bio::Root::FileOpenException>
|
|
395
|
|
396 Purpose : Indicates that a file could not be opened.
|
|
397 Example : throw Bio::Root::FileOpenException(
|
|
398 -text => "Can't open file $file for reading.",
|
|
399 -value => $! );
|
|
400
|
|
401 =cut
|
|
402
|
|
403 #---------------------------------------------------------
|
|
404 @Bio::Root::FileOpenException::ISA = qw( Bio::Root::IOException );
|
|
405 #---------------------------------------------------------
|
|
406
|
|
407
|
|
408 =head2 B<Bio::Root::SystemException>
|
|
409
|
|
410 Purpose : Indicates that a system call failed.
|
|
411 Example : unlink($file) or throw Bio::Root::SystemException(
|
|
412 -text => "Can't unlink file $file.",
|
|
413 -value => $! );
|
|
414
|
|
415 =cut
|
|
416
|
|
417 #---------------------------------------------------------
|
|
418 @Bio::Root::SystemException::ISA = qw( Bio::Root::Exception );
|
|
419 #---------------------------------------------------------
|
|
420
|
|
421
|
|
422 =head2 B<Bio::Root::BadParameter>
|
|
423
|
|
424 Purpose : Indicates that one or more parameters supplied to a method
|
|
425 are invalid, unspecified, or conflicting.
|
|
426 Example : throw Bio::Root::BadParameter(
|
|
427 -text => "Required parameter \"-foo\" was not specified",
|
|
428 -value => "-foo" );
|
|
429
|
|
430 =cut
|
|
431
|
|
432 #---------------------------------------------------------
|
|
433 @Bio::Root::BadParameter::ISA = qw( Bio::Root::Exception );
|
|
434 #---------------------------------------------------------
|
|
435
|
|
436
|
|
437 =head2 B<Bio::Root::OutOfRange>
|
|
438
|
|
439 Purpose : Indicates that a specified (start,end) range or
|
|
440 an index to an array is outside the permitted range.
|
|
441 Example : throw Bio::Root::OutOfRange(
|
|
442 -text => "Start coordinate ($start) cannot be less than zero.",
|
|
443 -value => $start );
|
|
444
|
|
445 =cut
|
|
446
|
|
447 #---------------------------------------------------------
|
|
448 @Bio::Root::OutOfRange::ISA = qw( Bio::Root::Exception );
|
|
449 #---------------------------------------------------------
|
|
450
|
|
451
|
|
452 =head2 B<Bio::Root::NoSuchThing>
|
|
453
|
|
454 Purpose : Indicates that a requested thing cannot be located
|
|
455 and therefore could possibly be bogus.
|
|
456 Example : throw Bio::Root::NoSuchThing(
|
|
457 -text => "Accession M000001 could not be found.",
|
|
458 -value => "M000001" );
|
|
459
|
|
460 =cut
|
|
461
|
|
462 #---------------------------------------------------------
|
|
463 @Bio::Root::NoSuchThing::ISA = qw( Bio::Root::Exception );
|
|
464 #---------------------------------------------------------
|
|
465
|
|
466
|
|
467 1;
|
|
468
|