line
stmt
bran
cond
sub
pod
time
code
1
#!/usr/bin/perl -c
2
3
package Exception::Base;
4
5
=head1 NAME
6
7
Exception::Base - Lightweight exceptions
8
9
=head1 SYNOPSIS
10
11
# Use module and create needed exceptions
12
use Exception::Base
13
'Exception::Runtime', # create new module
14
'Exception::System', # load existing module
15
'Exception::IO', => {
16
isa => 'Exception::System' }, # create new based on existing
17
'Exception::FileNotFound' => {
18
isa => 'Exception::IO', # create new based on previous
19
message => 'File not found', # override default message
20
has => [ 'filename' ], # define new rw attribute
21
string_attributes => [ 'message', 'filename' ],
22
}; # output message and filename
23
24
# eval is used as "try" block
25
eval {
26
open my $file, '/etc/passwd'
27
or Exception::FileNotFound->throw(
28
message=>'Something wrong',
29
filename=>'/etc/passwd');
30
};
31
# syntax for Perl >= 5.10
32
use feature 'switch';
33
if ($@) {
34
given (my $e = Exception::Base->catch) {
35
when ($e->isa('Exception::IO')) { warn "IO problem"; }
36
when ($e->isa('Exception::Eval')) { warn "eval died"; }
37
when ($e->isa('Exception::Runtime')) { warn "some runtime was caught"; }
38
when ($e->matches({value=>9})) { warn "something happened"; }
39
when ($e->matches(qr/^Error/)) { warn "some error based on regex"; }
40
default { $e->throw; } # rethrow the exception
41
}
42
}
43
# standard syntax for older Perl
44
if ($@) {
45
my $e = Exception::Base->catch; # convert $@ into exception
46
if ($e->isa('Exception::IO')) { warn "IO problem"; }
47
elsif ($e->isa('Exception::Eval')) { warn "eval died"; }
48
elsif ($e->isa('Exception::Runtime')) { warn "some runtime was caught"; }
49
elsif ($e->matches({value=>9})) { warn "something happened"; }
50
elsif ($e->matches(qr/^Error/)) { warn "some error based on regex"; }
51
else { $e->throw; } # rethrow the exception
52
}
53
54
# $@ has to be recovered ASAP!
55
eval { die "this die will be caught" };
56
my $e = Exception::Base->catch;
57
eval { die "this die will be ignored" };
58
if ($e) {
59
(...)
60
}
61
62
# the exception can be thrown later
63
my $e = Exception::Base->new;
64
# (...)
65
$e->throw;
66
67
# ignore our package in stack trace
68
package My::Package;
69
use Exception::Base '+ignore_package' => __PACKAGE__;
70
71
# define new exception in separate module
72
package Exception::My;
73
use Exception::Base (__PACKAGE__) => {
74
has => ['myattr'],
75
};
76
77
# run Perl with changed verbosity for debugging purposes
78
$ perl -MException::Base=verbosity,4 script.pl
79
80
=head1 DESCRIPTION
81
82
This class implements a fully OO exception mechanism similar to
83
L or L. It provides a simple interface
84
allowing programmers to declare exception classes. These classes can be
85
thrown and caught. Each uncaught exception prints full stack trace if the
86
default verbosity is increased for debugging purposes.
87
88
The features of C:
89
90
=over 2
91
92
=item *
93
94
fast implementation of the exception class
95
96
=item *
97
98
fully OO without closures and source code filtering
99
100
=item *
101
102
does not mess with C<$SIG{__DIE__}> and C<$SIG{__WARN__}>
103
104
=item *
105
106
no external run-time modules dependencies, requires core Perl modules only
107
108
=item *
109
110
the default behavior of exception class can be changed globally or just for
111
the thrown exception
112
113
=item *
114
115
matching the exception by class, message or other attributes
116
117
=item *
118
119
matching with string, regex or closure function
120
121
=item *
122
123
creating automatically the derived exception classes (L
124
interface)
125
126
=item *
127
128
easily expendable, see L class for example
129
130
=item *
131
132
prints just an error message or dumps full stack trace
133
134
=item *
135
136
can propagate (rethrow) an exception
137
138
=item *
139
140
can ignore some packages for stack trace output
141
142
=item *
143
144
some defaults (i.e. verbosity) can be different for different exceptions
145
146
=back
147
148
=for readme stop
149
150
=cut
151
152
1
1
3227
use 5.006;
1
4
153
154
1
1
5
use strict;
1
2
1
21
155
1
1
19
use warnings;
1
2
1
114
156
157
our $VERSION = '0.2501';
158
159
160
# Safe operations on symbol stash
161
BEGIN {
162
1
1
2
eval {
163
1
4
require Symbol;
164
1
5
Symbol::qualify_to_ref('Symbol::qualify_to_ref');
165
};
166
1
50
18
if (not $@) {
167
1
132
*_qualify_to_ref = \*Symbol::qualify_to_ref;
168
}
169
else {
170
1
1
5
*_qualify_to_ref = sub ($;) { no strict 'refs'; \*{ $_[0] } };
1
2
1
58
0
0
0
0
0
0
171
};
172
};
173
174
175
# Use weaken ref on stack if available
176
BEGIN {
177
1
1
3
eval {
178
1
5
require Scalar::Util;
179
1
3
my $ref = \1;
180
1
8
Scalar::Util::weaken($ref);
181
};
182
1
50
4
if (not $@) {
183
1
75
*_HAVE_SCALAR_UTIL_WEAKEN = sub () { !! 1 };
184
}
185
else {
186
0
0
*_HAVE_SCALAR_UTIL_WEAKEN = sub () { !! 0 };
187
};
188
};
189
190
191
BEGIN {
192
1
1
57
my %OVERLOADS = (fallback => 1);
193
194
=head1 OVERLOADS
195
196
=over
197
198
=item Boolean context
199
200
True value. See C method.
201
202
eval { Exception::Base->throw( message=>"Message", value=>123 ) };
203
if ($@) {
204
# the exception object is always true
205
}
206
207
=cut
208
209
1
2
$OVERLOADS{'bool'} = 'to_bool';
210
211
=item Numeric context
212
213
Content of attribute pointed by C attribute. See
214
C method.
215
216
eval { Exception::Base->throw( message=>"Message", value=>123 ) };
217
print 0+$@; # 123
218
219
=cut
220
221
1
2
$OVERLOADS{'0+'} = 'to_number';
222
223
=item String context
224
225
Content of attribute which is combined from C attributes
226
with additional information, depended on C setting. See
227
C method.
228
229
eval { Exception::Base->throw( message=>"Message", value=>123 ) };
230
print "$@"; # "Message at -e line 1.\n"
231
232
=cut
233
234
1
2
$OVERLOADS{'""'} = 'to_string';
235
236
=item "~~"
237
238
Smart matching operator. See C method.
239
240
eval { Exception::Base->throw( message=>"Message", value=>123 ) };
241
print "Message" ~~ $@; # 1
242
print qr/message/i ~~ $@; # 1
243
print ['Exception::Base'] ~~ $@; # 1
244
print 123 ~~ $@; # 1
245
print {message=>"Message", value=>123} ~~ $@; # 1
246
247
Warning: The smart operator requires that the exception object is a second
248
argument.
249
250
=back
251
252
=cut
253
254
1
50
5
$OVERLOADS{'~~'} = 'matches' if ($] >= 5.010);
255
256
1
1
5
use overload;
1
2
1
8
257
1
4
overload->import(%OVERLOADS);
258
};
259
260
261
# Constant regexp for numerify value check
262
1
1
148
use constant _RE_NUM_INT => qr/^[+-]?\d+$/;
1
2
1
466
263
264
265
=head1 CONSTANTS
266
267
=over
268
269
=item ATTRS
270
271
Declaration of class attributes as reference to hash.
272
273
The attributes are listed as I => {I}, where I is a
274
list of attribute properties:
275
276
=over
277
278
=item is
279
280
Can be 'rw' for read-write attributes or 'ro' for read-only attributes. The
281
attribute is read-only and does not have an accessor created if 'is' property
282
is missed.
283
284
=item default
285
286
Optional property with the default value if the attribute value is not
287
defined.
288
289
=back
290
291
The read-write attributes can be set with C constructor. Read-only
292
attributes and unknown attributes are ignored.
293
294
The constant have to be defined in derived class if it brings additional
295
attributes.
296
297
package Exception::My;
298
use base 'Exception::Base';
299
300
# Define new class attributes
301
use constant ATTRS => {
302
%{Exception::Base->ATTRS}, # base's attributes have to be first
303
readonly => { is=>'ro' }, # new ro attribute
304
readwrite => { is=>'rw', default=>'blah' }, # new rw attribute
305
};
306
307
package main;
308
use Exception::Base ':all';
309
eval {
310
Exception::My->throw( readwrite => 2 );
311
};
312
if ($@) {
313
my $e = Exception::Base->catch;
314
print $e->readwrite; # = 2
315
print $e->defaults->{readwrite}; # = "blah"
316
}
317
318
=back
319
320
=cut
321
322
BEGIN {
323
1
1
2
my %ATTRS = ();
324
325
=head1 ATTRIBUTES
326
327
Class attributes are implemented as values of blessed hash. The attributes
328
are also available as accessors methods.
329
330
=over
331
332
=cut
333
334
=item message (rw, default: 'Unknown exception')
335
336
Contains the message of the exception. It is the part of the string
337
representing the exception object.
338
339
eval { Exception::Base->throw( message=>"Message" ); };
340
print $@->message if $@;
341
342
It can also be an array reference of strings and then the L
343
is used to get a message.
344
345
Exception::Base->throw( message => ["%s failed", __PACKAGE__] );
346
347
=cut
348
349
1
6
$ATTRS{message} = { is => 'rw', default => 'Unknown exception' };
350
351
=item value (rw, default: 0)
352
353
Contains the value which represents numeric value of the exception object in
354
numeric context.
355
356
eval { Exception::Base->throw( value=>2 ); };
357
print "Error 2" if $@ == 2;
358
359
=cut
360
361
1
2
$ATTRS{value} = { is => 'rw', default => 0 };
362
363
=item verbosity (rw, default: 2)
364
365
Contains the verbosity level of the exception object. It allows to change the
366
string representing the exception object. There are following levels of
367
verbosity:
368
369
=over 2
370
371
=item C<0>
372
373
Empty string
374
375
=item C<1>
376
377
Message
378
379
=item C<2>
380
381
Message at %s line %d.
382
383
The same as the standard output of die() function. It doesn't include
384
"at %s line %d." string if message ends with C<"\n"> character. This is
385
the default option.
386
387
=item C<3>
388
389
Class: Message at %s line %d
390
%c_ = %s::%s() called in package %s at %s line %d
391
...propagated in package %s at %s line %d.
392
...
393
394
The output contains full trace of error stack without first C
395
lines and those packages which are listed in C and
396
C settings.
397
398
=item S<4>
399
400
The output contains full trace of error stack. In this case the
401
C, C and C settings are meaning
402
only for first line of exception's message.
403
404
=back
405
406
If the verbosity is undef, then the default verbosity for exception objects is
407
used.
408
409
If the verbosity set with constructor (C or C) is lower than 3,
410
the full stack trace won't be collected.
411
412
If the verbosity is lower than 2, the full system data (time, pid, tid, uid,
413
euid, gid, egid) won't be collected.
414
415
This setting can be changed with import interface.
416
417
use Exception::Base verbosity => 4;
418
419
It can be also changed for Perl interpreter instance, i.e. for debugging
420
purposes.
421
422
sh$ perl -MException::Base=verbosity,4 script.pl
423
424
=cut
425
426
1
3
$ATTRS{verbosity} = { is => 'rw', default => 2 };
427
428
=item ignore_package (rw)
429
430
Contains the name (scalar or regexp) or names (as references array) of
431
packages which are ignored in error stack trace. It is useful if some package
432
throws an exception but this module shouldn't be listed in stack trace.
433
434
package My::Package;
435
use Exception::Base;
436
sub my_function {
437
do_something() or throw Exception::Base ignore_package=>__PACKAGE__;
438
throw Exception::Base ignore_package => [ "My", qr/^My::Modules::/ ];
439
}
440
441
This setting can be changed with import interface.
442
443
use Exception::Base ignore_package => __PACKAGE__;
444
445
=cut
446
447
1
3
$ATTRS{ignore_package} = { is => 'rw', default => [ ] };
448
449
=item ignore_class (rw)
450
451
Contains the name (scalar) or names (as references array) of packages which
452
are base classes for ignored packages in error stack trace. It means that
453
some packages will be ignored even the derived class was called.
454
455
package My::Package;
456
use Exception::Base;
457
Exception::Base->throw( ignore_class => "My::Base" );
458
459
This setting can be changed with import interface.
460
461
use Exception::Base ignore_class => "My::Base";
462
463
=cut
464
465
1
2
$ATTRS{ignore_class} = { is => 'rw', default => [ ] };
466
467
=item ignore_level (rw)
468
469
Contains the number of level on stack trace to ignore. It is useful if some
470
package throws an exception but this module shouldn't be listed in stack
471
trace. It can be used with or without I attribute.
472
473
# Convert warning into exception. The signal handler ignores itself.
474
use Exception::Base 'Exception::My::Warning';
475
$SIG{__WARN__} = sub {
476
Exception::My::Warning->throw( message => $_[0], ignore_level => 1 );
477
};
478
479
=cut
480
481
1
4
$ATTRS{ignore_level} = { is => 'rw', default => 0 };
482
483
=item time (ro)
484
485
Contains the timestamp of the thrown exception. Collected if the verbosity on
486
throwing exception was greater than 1.
487
488
eval { Exception::Base->throw( message=>"Message" ); };
489
print scalar localtime $@->time;
490
491
=cut
492
493
1
2
$ATTRS{time} = { is => 'ro' };
494
495
=item pid (ro)
496
497
Contains the PID of the Perl process at time of thrown exception. Collected
498
if the verbosity on throwing exception was greater than 1.
499
500
eval { Exception::Base->throw( message=>"Message" ); };
501
kill 10, $@->pid;
502
503
=cut
504
505
1
3
$ATTRS{pid} = { is => 'ro' };
506
507
=item tid (ro)
508
509
Contains the tid of the thread or undef if threads are not used. Collected
510
if the verbosity on throwing exception was greater than 1.
511
512
=cut
513
514
1
2
$ATTRS{tid} = { is => 'ro' };
515
516
=item uid (ro)
517
518
=cut
519
520
1
2
$ATTRS{uid} = { is => 'ro' };
521
522
=item euid (ro)
523
524
=cut
525
526
1
3
$ATTRS{euid} = { is => 'ro' };
527
528
529
=item gid (ro)
530
531
=cut
532
533
1
2
$ATTRS{gid} = { is => 'ro' };
534
535
=item egid (ro)
536
537
Contains the real and effective uid and gid of the Perl process at time of
538
thrown exception. Collected if the verbosity on throwing exception was
539
greater than 1.
540
541
=cut
542
543
1
2
$ATTRS{egid} = { is => 'ro' };
544
545
=item caller_stack (ro)
546
547
Contains the error stack as array of array with information about caller
548
functions. The first 8 elements of the array's row are the same as first 8
549
elements of the output of C function. Further elements are optional
550
and are the arguments of called function. Collected if the verbosity on
551
throwing exception was greater than 1. Contains only the first element of
552
caller stack if the verbosity was lower than 3.
553
554
If the arguments of called function are references and
555
C::weaken> function is available then reference is weakened.
556
557
eval { Exception::Base->throw( message=>"Message" ); };
558
($package, $filename, $line, $subroutine, $hasargs, $wantarray,
559
$evaltext, $is_require, @args) = $@->caller_stack->[0];
560
561
=cut
562
563
1
2
$ATTRS{caller_stack} = { is => 'ro' };
564
565
=item propagated_stack (ro)
566
567
Contains the array of array which is used for generating "...propagated at"
568
message. The elements of the array's row are the same as first 3 elements of
569
the output of C function.
570
571
=cut
572
573
1
2
$ATTRS{propagated_stack} = { is => 'ro' };
574
575
=item max_arg_len (rw, default: 64)
576
577
Contains the maximal length of argument for functions in backtrace output.
578
Zero means no limit for length.
579
580
sub a { Exception::Base->throw( max_arg_len=>5 ) }
581
a("123456789");
582
583
=cut
584
585
1
9
$ATTRS{max_arg_len} = { is => 'rw', default => 64 };
586
587
=item max_arg_nums (rw, default: 8)
588
589
Contains the maximal number of arguments for functions in backtrace output.
590
Zero means no limit for arguments.
591
592
sub a { Exception::Base->throw( max_arg_nums=>1 ) }
593
a(1,2,3);
594
595
=cut
596
597
1
3
$ATTRS{max_arg_nums} = { is => 'rw', default => 8 };
598
599
=item max_eval_len (rw, default: 0)
600
601
Contains the maximal length of eval strings in backtrace output. Zero means
602
no limit for length.
603
604
eval "Exception->throw( max_eval_len=>10 )";
605
print "$@";
606
607
=cut
608
609
1
8
$ATTRS{max_eval_len} = { is => 'rw', default => 0 };
610
611
=item defaults
612
613
Meta-attribute contains the list of default values.
614
615
my $e = Exception::Base->new;
616
print defined $e->{verbosity}
617
? $e->{verbosity}
618
: $e->{defaults}->{verbosity};
619
620
=cut
621
622
1
2
$ATTRS{defaults} = { };
623
624
=item default_attribute (default: 'message')
625
626
Meta-attribute contains the name of the default attribute. This attribute
627
will be set for one argument throw method. This attribute has meaning for
628
derived classes.
629
630
use Exception::Base 'Exception::My' => {
631
has => 'myattr',
632
default_attribute => 'myattr',
633
};
634
635
eval { Exception::My->throw("string") };
636
print $@->myattr; # "string"
637
638
=cut
639
640
1
22
$ATTRS{default_attribute} = { default => 'message' };
641
642
=item numeric_attribute (default: 'value')
643
644
Meta-attribute contains the name of the attribute which contains numeric value
645
of exception object. This attribute will be used for representing exception
646
in numeric context.
647
648
use Exception::Base 'Exception::My' => {
649
has => 'myattr',
650
numeric_attribute => 'myattr',
651
};
652
653
eval { Exception::My->throw(myattr=>123) };
654
print 0 + $@; # 123
655
656
=cut
657
658
1
3
$ATTRS{numeric_attribute} = { default => 'value' };
659
660
=item eval_attribute (default: 'message')
661
662
Meta-attribute contains the name of the attribute which is filled if error
663
stack is empty. This attribute will contain value of C<$@> variable. This
664
attribute has meaning for derived classes.
665
666
use Exception::Base 'Exception::My' => {
667
has => 'myattr',
668
eval_attribute => 'myattr'
669
};
670
671
eval { die "string" };
672
print $@->myattr; # "string"
673
674
=cut
675
676
1
2
$ATTRS{eval_attribute} = { default => 'message' };
677
678
=item string_attributes (default: ['message'])
679
680
Meta-attribute contains the array of names of attributes with defined value
681
which are joined to the string returned by C method. If none of
682
attributes are defined, the string is created from the first default value of
683
attributes listed in the opposite order.
684
685
use Exception::Base 'Exception::My' => {
686
has => 'myattr',
687
myattr => 'default',
688
string_attributes => ['message', 'myattr'],
689
};
690
691
eval { Exception::My->throw( message=>"string", myattr=>"foo" ) };
692
print $@->myattr; # "string: foo"
693
694
eval { Exception::My->throw() };
695
print $@->myattr; # "default"
696
697
=back
698
699
=cut
700
701
1
7
$ATTRS{string_attributes} = { default => [ 'message' ] };
702
703
1
95
651
*ATTRS = sub () { \%ATTRS };
95
1672
704
};
705
706
707
# Cache for class' ATTRS
708
my %Class_Attributes;
709
710
711
# Cache for class' defaults
712
my %Class_Defaults;
713
714
715
# Cache for $obj->isa(__PACKAGE__)
716
my %Isa_Package;
717
718
719
=head1 IMPORTS
720
721
=over
722
723
=item C' => I;>
724
725
Changes the default value for I. If the I name has no
726
special prefix, its default value is replaced with a new I.
727
728
use Exception::Base verbosity => 4;
729
730
If the I name starts with "C<+>" or "C<->" then the new I
731
is based on previous value:
732
733
=over
734
735
=item *
736
737
If the original I was a reference to array, the new I can
738
be included or removed from original array. Use array reference if you
739
need to add or remove more than one element.
740
741
use Exception::Base
742
"+ignore_packages" => [ __PACKAGE__, qr/^Moose::/ ],
743
"-ignore_class" => "My::Good::Class";
744
745
=item *
746
747
If the original I was a number, it will be incremented or
748
decremented by the new I.
749
750
use Exception::Base "+ignore_level" => 1;
751
752
=item *
753
754
If the original I was a string, the new I will be
755
included.
756
757
use Exception::Base "+message" => ": The incuded message";
758
759
=back
760
761
=item C', ...;>
762
763
Loads additional exception class module. If the module is not available,
764
creates the exception class automatically at compile time. The newly created
765
class will be based on C class.
766
767
use Exception::Base qw{ Exception::Custom Exception::SomethingWrong };
768
Exception::Custom->throw;
769
770
=item C' => { isa => I, version => I, ... };>
771
772
Loads additional exception class module. If the module's version is lower
773
than given parameter or the module can't be loaded, creates the exception
774
class automatically at compile time. The newly created class will be based on
775
given class and has the given $VERSION variable.
776
777
=over
778
779
=item isa
780
781
The newly created class will be based on given class.
782
783
use Exception::Base
784
'Exception::My',
785
'Exception::Nested' => { isa => 'Exception::My };
786
787
=item version
788
789
The class will be created only if the module's version is lower than given
790
parameter and will have the version given in the argument.
791
792
use Exception::Base
793
'Exception::My' => { version => 1.23 };
794
795
=item has
796
797
The class will contain new rw attribute (if parameter is a string) or new rw
798
attributes (if parameter is a reference to array of strings) or new rw or ro
799
attributes (if parameter is a reference to hash of array of strings with rw
800
and ro as hash key).
801
802
use Exception::Base
803
'Exception::Simple' => { has => 'field' },
804
'Exception::More' => { has => [ 'field1', 'field2' ] },
805
'Exception::Advanced' => { has => {
806
ro => [ 'field1', 'field2' ],
807
rw => [ 'field3' ]
808
} };
809
810
=item message
811
812
=item verbosity
813
814
=item max_arg_len
815
816
=item max_arg_nums
817
818
=item max_eval_len
819
820
=item I
821
822
The class will have the default property for the given attribute.
823
824
=back
825
826
use Exception::Base
827
'Exception::WithDefault' => { message => 'Default message' },
828
'Exception::Reason' => {
829
has => [ 'reason' ],
830
string_attributes => [ 'message', 'reason' ] };
831
832
=back
833
834
=cut
835
836
# Create additional exception packages
837
sub import {
838
54
54
4170
my $class = shift;
839
840
54
146
while (defined $_[0]) {
841
52
84
my $name = shift @_;
842
52
100
265
if ($name eq ':all') {
100
843
# do nothing for backward compatibility
844
}
845
elsif ($name =~ /^([+-]?)([a-z0-9_]+)$/) {
846
# Lower case: change default
847
21
59
my ($modifier, $key) = ($1, $2);
848
21
29
my $value = shift;
849
21
73
$class->_modify_default($key, $value, $modifier);
850
}
851
else {
852
# Try to use external module
853
30
46
my $param = {};
854
30
100
66
161
$param = shift @_ if defined $_[0] and ref $_[0] eq 'HASH';
855
856
30
100
71
my $version = defined $param->{version} ? $param->{version} : 0;
857
858
30
100
117
if (caller ne $name) {
859
29
100
33
next if eval { $name->VERSION($version) };
29
407
860
861
# Package is needed
862
{
863
27
40
local $SIG{__DIE__};
27
98
864
27
43
eval {
865
27
77
$class->_load_package($name, $version);
866
};
867
};
868
27
100
96
if ($@) {
869
# Die unless can't load module
870
26
100
98
if ($@ !~ /Can\'t locate/) {
871
3
16
Exception::Base->throw(
872
message => ["Can not load available %s class: %s", $name, $@],
873
verbosity => 1
874
);
875
};
876
}
877
else {
878
# Module is loaded: go to next
879
1
5
next;
880
};
881
};
882
883
24
50
58
next if $name eq __PACKAGE__;
884
885
# Package not found so it have to be created
886
24
100
64
if ($class ne __PACKAGE__) {
887
1
6
Exception::Base->throw(
888
message => ["Exceptions can only be created with %s class", __PACKAGE__],
889
verbosity => 1
890
);
891
};
892
23
65
$class->_make_exception($name, $version, $param);
893
}
894
}
895
896
45
3955
return $class;
897
};
898
899
900
=head1 CONSTRUCTORS
901
902
=over
903
904
=item new([%I])
905
906
Creates the exception object, which can be thrown later. The system data
907
attributes like C, C, C, C, C, C are not
908
filled.
909
910
If the key of the argument is read-write attribute, this attribute will be
911
filled. Otherwise, the argument will be ignored.
912
913
$e = Exception::Base->new(
914
message=>"Houston, we have a problem",
915
unknown_attr => "BIG"
916
);
917
print $e->{message};
918
919
The constructor reads the list of class attributes from ATTRS constant
920
function and stores it in the internal cache for performance reason. The
921
defaults values for the class are also stored in internal cache.
922
923
=item C-Ethrow([%I]])
924
925
Creates the exception object and immediately throws it with C system
926
function.
927
928
open my $fh, $file
929
or Exception::Base->throw( message=>"Can not open file: $file" );
930
931
The C is also exported as a function.
932
933
open my $fh, $file
934
or throw 'Exception::Base' => message=>"Can not open file: $file";
935
936
=back
937
938
The C can be also used as a method.
939
940
=cut
941
942
# Constructor
943
sub new {
944
72
72
1
14223
my ($self, %args) = @_;
945
946
72
66
270
my $class = ref $self || $self;
947
948
72
87
my $attributes;
949
my $defaults;
950
951
# Use cached value if available
952
72
100
168
if (not defined $Class_Attributes{$class}) {
953
22
78
$attributes = $Class_Attributes{$class} = $class->ATTRS;
954
$defaults = $Class_Defaults{$class} = {
955
286
629
map { $_ => $attributes->{$_}->{default} }
956
22
126
grep { defined $attributes->{$_}->{default} }
522
787
957
(keys %$attributes)
958
};
959
}
960
else {
961
50
70
$attributes = $Class_Attributes{$class};
962
50
82
$defaults = $Class_Defaults{$class};
963
};
964
965
72
175
my $e = {};
966
967
# If the attribute is rw, initialize its value. Otherwise: ignore.
968
1
1
9
no warnings 'uninitialized';
1
2
1
300
969
72
188
foreach my $key (keys %args) {
970
50
100
146
if ($attributes->{$key}->{is} eq 'rw') {
971
46
116
$e->{$key} = $args{$key};
972
};
973
};
974
975
# Defaults for this object
976
72
571
$e->{defaults} = { %$defaults };
977
978
72
201
bless $e => $class;
979
980
# Collect system data and eval error
981
72
220
$e->_collect_system_data;
982
983
72
299
return $e;
984
};
985
986
987
=head1 METHODS
988
989
=over
990
991
=item C<$obj>-Ethrow([%I])
992
993
Immediately throws exception object. It can be used for rethrowing existing
994
exception object. Additional arguments will override the attributes in
995
existing exception object.
996
997
$e = Exception::Base->new;
998
# (...)
999
$e->throw( message=>"thrown exception with overridden message" );
1000
1001
eval { Exception::Base->throw( message=>"Problem", value=>1 ) };
1002
$@->throw if $@->value;
1003
1004
=item C<$obj>-Ethrow(I, [%I])
1005
1006
If the number of I list for arguments is odd, the first argument is a
1007
message. This message can be overridden by message from I list.
1008
1009
Exception::Base->throw( "Problem", message=>"More important" );
1010
eval { die "Bum!" };
1011
Exception::Base->throw( $@, message=>"New message" );
1012
1013
=item I-Ethrow($I, [%I])
1014
1015
Immediately rethrows an existing exception object as an other exception class.
1016
1017
eval { open $f, "w", "/etc/passwd" or Exception::System->throw };
1018
# convert Exception::System into Exception::Base
1019
Exception::Base->throw($@);
1020
1021
=cut
1022
1023
# Create the exception and throw it or rethrow existing
1024
sub throw {
1025
36
36
1
849
my $self = shift;
1026
1027
36
66
134
my $class = ref $self || $self;
1028
1029
36
39
my $old_e;
1030
1031
36
100
65
if (not ref $self) {
1032
# CLASS->throw
1033
34
100
59
if (not ref $_[0]) {
1034
# Throw new exception
1035
33
100
71
if (scalar @_ % 2 == 0) {
1036
# Throw normal error
1037
30
111
die $self->new(@_);
1038
}
1039
else {
1040
# First argument is a default attribute; it can be overridden with normal args
1041
3
5
my $argument = shift;
1042
3
11
my $e = $self->new(@_);
1043
3
7
my $default_attribute = $e->{defaults}->{default_attribute};
1044
3
100
11
$e->{$default_attribute} = $argument if not defined $e->{$default_attribute};
1045
3
13
die $e;
1046
};
1047
}
1048
else {
1049
# First argument is an old exception
1050
1
2
$old_e = shift;
1051
};
1052
}
1053
else {
1054
# $e->throw
1055
2
3
$old_e = $self;
1056
};
1057
1058
# Rethrow old exception with replaced attributes
1059
1
1
5
no warnings 'uninitialized';
1
1
1
672
1060
3
7
my %args = @_;
1061
3
7
my $attrs = $old_e->ATTRS;
1062
3
8
foreach my $key (keys %args) {
1063
2
100
8
if ($attrs->{$key}->{is} eq 'rw') {
1064
1
3
$old_e->{$key} = $args{$key};
1065
};
1066
};
1067
3
9
$old_e->PROPAGATE;
1068
3
100
10
if (ref $old_e ne $class) {
1069
# Rebless old object for new class
1070
1
2
bless $old_e => $class;
1071
};
1072
1073
3
13
die $old_e;
1074
};
1075
1076
1077
=item I-Ecatch([$I])
1078
1079
The exception is recovered from I argument or C<$@> variable if
1080
I argument was empty. Then also C<$@> is replaced with empty string
1081
to avoid an endless loop.
1082
1083
The method returns an exception object if exception is caught or undefined
1084
value otherwise.
1085
1086
eval { Exception::Base->throw; };
1087
if ($@) {
1088
my $e = Exception::Base->catch;
1089
print $e->to_string;
1090
}
1091
1092
If the value is not empty and does not contain the C object,
1093
new exception object is created with class I and its message is based
1094
on previous value with removed C<" at file line 123."> string and the last end
1095
of line (LF).
1096
1097
eval { die "Died\n"; };
1098
my $e = Exception::Base->catch;
1099
print ref $e; # "Exception::Base"
1100
1101
=cut
1102
1103
# Recover $@ variable and return exception object
1104
sub catch {
1105
19
19
1
794
my ($self) = @_;
1106
1107
19
66
78
my $class = ref $self || $self;
1108
1109
19
23
my $e;
1110
my $new_e;
1111
1112
1113
19
100
42
if (@_ > 1) {
1114
# Recover exception from argument
1115
1
2
$e = $_[1];
1116
}
1117
else {
1118
# Recover exception from $@ and clear it
1119
18
22
$e = $@;
1120
18
31
$@ = '';
1121
};
1122
1123
19
100
66
64
if (ref $e and do { local $@; local $SIG{__DIE__}; eval { $e->isa(__PACKAGE__) } }) {
4
100
5
4
14
4
7
4
25
1124
# Caught exception
1125
3
6
$new_e = $e;
1126
}
1127
elsif ($e eq '') {
1128
# No error in $@
1129
2
3
$new_e = undef;
1130
}
1131
else {
1132
# New exception based on error from $@. Clean up the message.
1133
14
58
while ($e =~ s/\t\.\.\.propagated at (?!.*\bat\b.*).* line \d+( thread \d+)?\.\n$//s) { };
1134
14
106
$e =~ s/( at (?!.*\bat\b.*).* line \d+( thread \d+)?\.)?\n$//s;
1135
14
52
$new_e = $class->new;
1136
14
26
my $eval_attribute = $new_e->{defaults}->{eval_attribute};
1137
14
30
$new_e->{$eval_attribute} = $e;
1138
};
1139
1140
19
49
return $new_e;
1141
};
1142
1143
1144
=item matches(I)
1145
1146
Checks if the exception object matches the given argument.
1147
1148
The C method overloads C<~~> smart matching operator. Warning: The
1149
second argument for smart matching operator needs to be scalar.
1150
1151
If the argument is a reference to array, it is checked if the object is a
1152
given class.
1153
1154
use Exception::Base
1155
'Exception::Simple',
1156
'Exception::Complex' => { isa => 'Exception::Simple };
1157
eval { Exception::Complex->throw() };
1158
print $@->matches( ['Exception::Base'] ); # matches
1159
print $@->matches( ['Exception::Simple', 'Exception::X'] ); # matches
1160
print $@->matches( ['NullObject'] ); # doesn't
1161
1162
If the argument is a reference to hash, attributes of the exception
1163
object is matched.
1164
1165
eval { Exception::Base->throw( message=>"Message", value=>123 ) };
1166
print $@->matches( { message=>"Message" } ); # matches
1167
print $@->matches( { value=>123 } ); # matches
1168
print $@->matches( { message=>"Message", value=>45 } ); # doesn't
1169
1170
If the argument is a single string, regexp or code reference or is undefined,
1171
the default attribute of the exception object is matched (usually it is a
1172
"message" attribute).
1173
1174
eval { Exception::Base->throw( message=>"Message" ) };
1175
print $@->matches( "Message" ); # matches
1176
print $@->matches( qr/Message/ ); # matches
1177
print $@->matches( qr/[0-9]/ ); # doesn't
1178
print $@->matches( sub{/Message/} ); # matches
1179
print $@->matches( sub{0} ); # doesn't
1180
print $@->matches( undef ); # doesn't
1181
1182
If argument is a numeric value, the argument matches if C attribute
1183
matches.
1184
1185
eval { Exception::Base->throw( value=>123, message=>456 ) } );
1186
print $@->matches( 123 ); # matches
1187
print $@->matches( 456 ); # doesn't
1188
1189
If an attribute contains array reference, the array will be C-ed
1190
before matching.
1191
1192
eval { Exception::Base->throw( message=>["%s", "Message"] ) };
1193
print $@->matches( "Message" ); # matches
1194
print $@->matches( qr/Message/ ); # matches
1195
print $@->matches( qr/[0-9]/ ); # doesn't
1196
1197
The C method matches for special keywords:
1198
1199
=over
1200
1201
=item -isa
1202
1203
Matches if the object is a given class.
1204
1205
eval { Exception::Base->new( message=>"Message" ) };
1206
print $@->matches( { -isa=>"Exception::Base" } ); # matches
1207
print $@->matches( { -isa=>["X::Y", "Exception::Base"] } ); # matches
1208
1209
=item -has
1210
1211
Matches if the object has a given attribute.
1212
1213
eval { Exception::Base->new( message=>"Message" ) };
1214
print $@->matches( { -has=>"Message" } ); # matches
1215
1216
=item -default
1217
1218
Matches against the default attribute, usually the C attribute.
1219
1220
eval { Exception::Base->new( message=>"Message" ) };
1221
print $@->matches( { -default=>"Message" } ); # matches
1222
1223
=back
1224
1225
=cut
1226
1227
# Smart matching.
1228
sub matches {
1229
159
159
1
461
my ($self, $that) = @_;
1230
1231
159
187
my @args;
1232
1233
159
314
my $default_attribute = $self->{defaults}->{default_attribute};
1234
159
238
my $numeric_attribute = $self->{defaults}->{numeric_attribute};
1235
1236
159
100
100
800
if (ref $that eq 'ARRAY') {
100
100
100
100
100
1237
7
16
@args = ( '-isa' => $that );
1238
}
1239
elsif (ref $that eq 'HASH') {
1240
100
285
@args = %$that;
1241
}
1242
elsif (ref $that eq 'Regexp' or ref $that eq 'CODE' or not defined $that) {
1243
24
51
@args = ( $that );
1244
}
1245
elsif (ref $that) {
1246
3
13
return '';
1247
}
1248
elsif ($that =~ _RE_NUM_INT) {
1249
13
30
@args = ( $numeric_attribute => $that );
1250
}
1251
else {
1252
12
24
@args = ( $that );
1253
};
1254
1255
156
50
363
return '' unless @args;
1256
1257
# Odd number of arguments - first is default attribute
1258
156
100
376
if (scalar @args % 2 == 1) {
1259
36
52
my $val = shift @args;
1260
36
50
66
203
if (ref $val eq 'ARRAY') {
100
50
100
1261
0
0
my $arrret = 0;
1262
0
0
foreach my $arrval (@{ $val }) {
0
0
1263
0
0
0
0
if (not defined $arrval) {
0
0
1264
0
0
0
$arrret = 1 if not $self->_string_attributes;
1265
}
1266
elsif (not ref $arrval and $arrval =~ _RE_NUM_INT) {
1267
1
1
6
no warnings 'numeric', 'uninitialized';
1
1
1
178
1268
0
0
0
$arrret = 1 if $self->{$numeric_attribute} == $arrval;
1269
}
1270
elsif (not $self->_string_attributes) {
1271
0
0
next;
1272
}
1273
else {
1274
0
0
local $_ = join ': ', $self->_string_attributes;
1275
0
0
0
if (ref $arrval eq 'CODE') {
0
1276
0
0
0
$arrret = 1 if $arrval->();
1277
}
1278
elsif (ref $arrval eq 'Regexp') {
1279
0
0
0
$arrret = 1 if /$arrval/;
1280
}
1281
else {
1282
0
0
0
$arrret = 1 if $_ eq $arrval;
1283
};
1284
};
1285
0
0
0
last if $arrret;
1286
};
1287
# Fail unless at least one condition is true
1288
0
0
0
return '' if not $arrret;
1289
}
1290
elsif (not defined $val) {
1291
8
100
23
return '' if $self->_string_attributes;
1292
}
1293
elsif (not ref $val and $val =~ _RE_NUM_INT) {
1294
1
1
6
no warnings 'numeric', 'uninitialized';
1
17
1
562
1295
0
0
0
return '' if $self->{$numeric_attribute} != $val;
1296
}
1297
elsif (not $self->_string_attributes) {
1298
7
35
return '';
1299
}
1300
else {
1301
21
49
local $_ = join ': ', $self->_string_attributes;
1302
21
100
73
if (ref $val eq 'CODE') {
100
1303
6
100
18
return '' if not $val->();
1304
}
1305
elsif (ref $val eq 'Regexp') {
1306
6
100
42
return '' if not /$val/;
1307
}
1308
else {
1309
9
100
47
return '' if $_ ne $val;
1310
};
1311
};
1312
17
50
142
return 1 unless @args;
1313
};
1314
1315
120
253
my %args = @args;
1316
120
360
while (my($key,$val) = each %args) {
1317
126
100
265
if ($key eq '-default') {
1318
6
11
$key = $default_attribute;
1319
};
1320
1321
126
100
100
675
if ($key eq '-isa') {
100
100
100
100
100
1322
11
100
23
if (ref $val eq 'ARRAY') {
1323
9
18
my $arrret = 0;
1324
9
9
foreach my $arrval (@{ $val }) {
9
20
1325
21
50
39
next if not defined $arrval;
1326
21
100
81
$arrret = 1 if $self->isa($arrval);
1327
21
100
43
last if $arrret;
1328
};
1329
9
100
47
return '' if not $arrret;
1330
}
1331
else {
1332
2
100
16
return '' if not $self->isa($val);
1333
};
1334
}
1335
elsif ($key eq '-has') {
1336
4
100
18
if (ref $val eq 'ARRAY') {
1337
2
17
my $arrret = 0;
1338
2
3
foreach my $arrval (@{ $val }) {
2
5
1339
5
50
12
next if not defined $arrval;
1340
5
100
11
$arrret = 1 if exists $self->ATTRS->{$arrval};
1341
5
100
14
last if $arrret;
1342
};
1343
2
100
20
return '' if not $arrret;
1344
}
1345
else {
1346
2
100
7
return '' if not $self->ATTRS->{$val};
1347
};
1348
}
1349
elsif (ref $val eq 'ARRAY') {
1350
38
46
my $arrret = 0;
1351
38
44
foreach my $arrval (@{ $val }) {
38
80
1352
77
100
182
if (not defined $arrval) {
100
1353
17
100
45
$arrret = 1 if not defined $self->{$key};
1354
}
1355
elsif (not defined $self->{$key}) {
1356
24
49
next;
1357
}
1358
else {
1359
local $_ = ref $self->{$key} eq 'ARRAY'
1360
? sprintf(
1361
9
20
@{$self->{$key}}[0],
1362
9
25
@{$self->{$key}}[1..$#{$self->{$key}}]
9
20
1363
)
1364
36
100
91
: $self->{$key};
1365
36
100
94
if (ref $arrval eq 'CODE') {
100
1366
8
100
22
$arrret = 1 if $arrval->();
1367
}
1368
elsif (ref $arrval eq 'Regexp') {
1369
12
100
61
$arrret = 1 if /$arrval/;
1370
}
1371
else {
1372
16
100
49
$arrret = 1 if $_ eq $arrval;
1373
};
1374
};
1375
53
100
159
last if $arrret;
1376
};
1377
38
100
217
return '' if not $arrret;
1378
}
1379
elsif (not defined $val) {
1380
12
100
100
93
return '' if exists $self->{$key} && defined $self->{$key};
1381
}
1382
elsif (not ref $val and $val =~ _RE_NUM_INT) {
1383
1
1
5
no warnings 'numeric', 'uninitialized';
1
1
1
561
1384
17
100
173
return '' if $self->{$key} != $val;
1385
}
1386
elsif (not defined $self->{$key}) {
1387
10
52
return '';
1388
}
1389
else {
1390
local $_ = ref $self->{$key} eq 'ARRAY'
1391
? sprintf(
1392
10
24
@{$self->{$key}}[0],
1393
10
33
@{$self->{$key}}[1..$#{$self->{$key}}]
10
16
1394
)
1395
34
100
96
: $self->{$key};
1396
1397
34
100
95
if (ref $val eq 'CODE') {
100
1398
12
100
31
return '' if not $val->();
1399
}
1400
elsif (ref $val eq 'Regexp') {
1401
12
100
103
return '' if not /$val/;
1402
}
1403
else {
1404
10
100
60
return '' if $_ ne $val;
1405
};
1406
};
1407
};
1408
1409
62
388
return 1;
1410
}
1411
1412
1413
=item to_string
1414
1415
Returns the string representation of exception object. It is called
1416
automatically if the exception object is used in string scalar context. The
1417
method can be used explicitly.
1418
1419
eval { Exception::Base->throw; };
1420
$@->{verbosity} = 1;
1421
print "$@";
1422
$@->verbosity = 4;
1423
print $@->to_string;
1424
1425
=cut
1426
1427
# Convert an exception to string
1428
sub to_string {
1429
58
58
1
566
my ($self) = @_;
1430
1431
my $verbosity = defined $self->{verbosity}
1432
? $self->{verbosity}
1433
58
100
153
: $self->{defaults}->{verbosity};
1434
1435
58
135
my $message = join ': ', $self->_string_attributes;
1436
1437
58
100
153
if ($message eq '') {
1438
4
4
foreach (reverse @{ $self->{defaults}->{string_attributes} }) {
4
15
1439
4
7
$message = $self->{defaults}->{$_};
1440
4
50
12
last if defined $message;
1441
};
1442
};
1443
1444
58
100
205
if ($verbosity == 1) {
100
100
1445
18
100
74
return $message if $message =~ /\n$/;
1446
1447
14
77
return $message . "\n";
1448
}
1449
elsif ($verbosity == 2) {
1450
20
100
56
return $message if $message =~ /\n$/;
1451
1452
19
44
my @stacktrace = $self->get_caller_stacktrace;
1453
19
112
return $message . $stacktrace[0] . ".\n";
1454
}
1455
elsif ($verbosity >= 3) {
1456
16
51
return ref($self) . ': ' . $message . $self->get_caller_stacktrace;
1457
};
1458
1459
4
19
return '';
1460
};
1461
1462
1463
=item to_number
1464
1465
Returns the numeric representation of exception object. It is called
1466
automatically if the exception object is used in numeric scalar context. The
1467
method can be used explicitly.
1468
1469
eval { Exception::Base->throw( value => 42 ); };
1470
print 0+$@; # 42
1471
print $@->to_number; # 42
1472
1473
=cut
1474
1475
# Convert an exception to number
1476
sub to_number {
1477
9
9
1
44
my ($self) = @_;
1478
1479
9
16
my $numeric_attribute = $self->{defaults}->{numeric_attribute};
1480
1481
1
1
5
no warnings 'numeric';
1
7
1
2198
1482
9
100
34
return 0+ $self->{$numeric_attribute} if defined $self->{$numeric_attribute};
1483
6
100
28
return 0+ $self->{defaults}->{$numeric_attribute} if defined $self->{defaults}->{$numeric_attribute};
1484
2
7
return 0;
1485
};
1486
1487
1488
=item to_bool
1489
1490
Returns the boolean representation of exception object. It is called
1491
automatically if the exception object is used in boolean context. The method
1492
can be used explicitly.
1493
1494
eval { Exception::Base->throw; };
1495
print "ok" if $@; # ok
1496
print "ok" if $@->to_bool; # ok
1497
1498
=cut
1499
1500
# Convert an exception to bool (always true)
1501
sub to_bool {
1502
1
1
1
38
return !! 1;
1503
};
1504
1505
1506
=item get_caller_stacktrace
1507
1508
Returns an array of strings or string with caller stack trace. It is
1509
implicitly used by C method.
1510
1511
=cut
1512
1513
# Stringify caller backtrace. Stolen from Carp
1514
sub get_caller_stacktrace {
1515
35
35
1
50
my ($self) = @_;
1516
1517
35
45
my @stacktrace;
1518
1519
35
43
my $tid_msg = '';
1520
35
50
72
$tid_msg = ' thread ' . $self->{tid} if $self->{tid};
1521
1522
my $verbosity = defined $self->{verbosity}
1523
? $self->{verbosity}
1524
35
100
84
: $self->{defaults}->{verbosity};
1525
1526
my $ignore_level = defined $self->{ignore_level}
1527
? $self->{ignore_level}
1528
: defined $self->{defaults}->{ignore_level}
1529
? $self->{defaults}->{ignore_level}
1530
35
50
85
: 0;
100
1531
1532
# Skip some packages for first line
1533
35
37
my $level = 0;
1534
35
86
while (my %c = $self->_caller_info($level++)) {
1535
79
100
209
next if $self->_skip_ignored_package($c{package});
1536
# Skip ignored levels
1537
36
100
71
if ($ignore_level > 0) {
1538
5
6
--$ignore_level;
1539
5
25
next;
1540
};
1541
push @stacktrace, sprintf " at %s line %s%s",
1542
defined $c{file} && $c{file} ne '' ? $c{file} : 'unknown',
1543
31
50
33
271
$c{line} || 0,
50
1544
$tid_msg;
1545
31
86
last;
1546
};
1547
# First line have to be filled even if everything was skipped
1548
35
100
121
if (not @stacktrace) {
1549
4
9
my %c = $self->_caller_info(0);
1550
push @stacktrace, sprintf " at %s line %s%s",
1551
defined $c{file} && $c{file} ne '' ? $c{file} : 'unknown',
1552
4
100
66
46
$c{line} || 0,
100
1553
$tid_msg;
1554
};
1555
35
100
79
if ($verbosity >= 3) {
1556
# Reset the stack trace level only if needed
1557
16
100
33
if ($verbosity >= 4) {
1558
4
42
$level = 0;
1559
};
1560
# Dump the caller stack
1561
16
40
while (my %c = $self->_caller_info($level++)) {
1562
24
50
66
64
next if $verbosity == 3 and $self->_skip_ignored_package($c{package});
1563
24
186
push @stacktrace, "\t$c{wantarray}$c{sub_name} called in package $c{package} at $c{file} line $c{line}";
1564
};
1565
# Dump the propagated stack
1566
16
26
foreach (@{ $self->{propagated_stack} }) {
16
49
1567
24
54
my ($package, $file, $line) = @$_;
1568
# Skip ignored package
1569
24
100
100
81
next if $verbosity <= 3 and $self->_skip_ignored_package($package);
1570
19
50
33
177
push @stacktrace, sprintf "\t...propagated in package %s at %s line %d.",
50
1571
$package,
1572
defined $file && $file ne '' ? $file : 'unknown',
1573
$line || 0;
1574
};
1575
};
1576
1577
35
100
241
return wantarray ? @stacktrace : join("\n", @stacktrace) . "\n";
1578
};
1579
1580
1581
=item PROPAGATE
1582
1583
Checks the caller stack and fills the C attribute. It is
1584
usually used if C system function was called without any arguments.
1585
1586
=cut
1587
1588
# Propagate exception if it is rethrown
1589
sub PROPAGATE {
1590
3
3
1
5
my ($self) = @_;
1591
1592
# Fill propagate stack
1593
3
4
my $level = 1;
1594
3
24
while (my @c = caller($level++)) {
1595
# Skip own package
1596
next if ! defined $Isa_Package{$c[0]}
1597
0
0
? $Isa_Package{$c[0]} = do { local $@; local $SIG{__DIE__}; eval { $c[0]->isa(__PACKAGE__) } }
0
0
0
0
0
0
1598
3
50
12
: $Isa_Package{$c[0]};
50
1599
# Collect the caller stack
1600
3
4
push @{ $self->{propagated_stack} }, [ @c[0..2] ];
3
10
1601
3
7
last;
1602
};
1603
1604
3
6
return $self;
1605
};
1606
1607
1608
# Return a list of values of default string attributes
1609
sub _string_attributes {
1610
115
115
161
my ($self) = @_;
1611
1612
111
100
511
return map { ref $_ eq 'ARRAY'
1613
? sprintf(@$_[0], @$_[1..$#$_])
1614
: $_ }
1615
136
100
100
717
grep { defined $_ and (ref $_ or $_ ne '') }
1616
136
334
map { $self->{$_} }
1617
115
138
@{ $self->{defaults}->{string_attributes} };
115
276
1618
};
1619
1620
1621
=item _collect_system_data
1622
1623
Collects system data and fills the attributes of exception object. This
1624
method is called automatically if exception if thrown or created by
1625
C constructor. It can be overridden by derived class.
1626
1627
package Exception::Special;
1628
use base 'Exception::Base';
1629
use constant ATTRS => {
1630
%{Exception::Base->ATTRS},
1631
'special' => { is => 'ro' },
1632
};
1633
sub _collect_system_data {
1634
my $self = shift;
1635
$self->SUPER::_collect_system_data(@_);
1636
$self->{special} = get_special_value();
1637
return $self;
1638
}
1639
BEGIN {
1640
__PACKAGE__->_make_accessors;
1641
}
1642
1;
1643
1644
Method returns the reference to the self object.
1645
1646
=cut
1647
1648
# Collect system data and fill the attributes and caller stack.
1649
sub _collect_system_data {
1650
73
73
125
my ($self) = @_;
1651
1652
# Collect system data only if verbosity is meaning
1653
73
100
1331
my $verbosity = defined $self->{verbosity} ? $self->{verbosity} : $self->{defaults}->{verbosity};
1654
73
100
182
if ($verbosity >= 2) {
1655
62
116
$self->{time} = CORE::time();
1656
62
50
144
$self->{tid} = threads->tid if defined &threads::tid;
1657
62
101
@{$self}{qw < pid uid euid gid egid >} =
62
467
1658
( $$, $<, $>, $(, $) );
1659
1660
# Collect stack info
1661
62
99
my @caller_stack;
1662
62
79
my $level = 1;
1663
1664
62
96
while (my @c = do { package DB; caller($level++) }) {
102
941
1665
# Skip own package
1666
102
100
404
next if ! defined $Isa_Package{$c[0]} ? $Isa_Package{$c[0]} = do { local $@; local $SIG{__DIE__}; eval { $c[0]->isa(__PACKAGE__) } } : $Isa_Package{$c[0]};
3
100
4
3
12
3
6
3
34
1667
# Collect the caller stack
1668
62
134
my @args = @DB::args;
1669
62
66
if (_HAVE_SCALAR_UTIL_WEAKEN) {
1670
62
114
foreach (@args) {
1671
131
100
340
Scalar::Util::weaken($_) if ref $_;
1672
};
1673
};
1674
62
240
my @stacktrace_element = ( @c[0 .. 7], @args );
1675
62
110
push @caller_stack, \@stacktrace_element;
1676
# Collect only one entry if verbosity is lower than 3 and skip ignored packages
1677
62
50
33
294
last if $verbosity == 2 and not $self->_skip_ignored_package($stacktrace_element[0]);
1678
};
1679
62
253
$self->{caller_stack} = \@caller_stack;
1680
};
1681
1682
73
123
return $self;
1683
};
1684
1685
1686
# Check if package should be ignored
1687
sub _skip_ignored_package {
1688
185
185
274
my ($self, $package) = @_;
1689
1690
my $ignore_package = defined $self->{ignore_package}
1691
? $self->{ignore_package}
1692
185
100
468
: $self->{defaults}->{ignore_package};
1693
1694
my $ignore_class = defined $self->{ignore_class}
1695
? $self->{ignore_class}
1696
185
100
482
: $self->{defaults}->{ignore_class};
1697
1698
185
50
372
if (defined $ignore_package) {
1699
185
100
418
if (ref $ignore_package eq 'ARRAY') {
1700
140
100
160
if (@{ $ignore_package }) {
140
391
1701
20
100
66
23
do { return 1 if defined $_ and (ref $_ eq 'Regexp' and $package =~ $_ or ref $_ ne 'Regexp' and $package eq $_) } foreach @{ $ignore_package };
20
33
47
40
443
1702
};
1703
}
1704
else {
1705
45
100
306
return 1 if ref $ignore_package eq 'Regexp' ? $package =~ $ignore_package : $package eq $ignore_package;
100
1706
};
1707
}
1708
147
50
317
if (defined $ignore_class) {
1709
147
100
292
if (ref $ignore_class eq 'ARRAY') {
1710
138
100
154
if (@{ $ignore_class }) {
138
334
1711
14
100
17
return 1 if grep { do { local $@; local $SIG{__DIE__}; eval { $package->isa($_) } } } @{ $ignore_class };
42
77
42
41
42
107
42
60
42
292
14
22
1712
};
1713
}
1714
else {
1715
9
100
12
return 1 if do { local $@; local $SIG{__DIE__}; eval { $package->isa($ignore_class) } };
9
12
9
25
9
13
9
126
1716
};
1717
};
1718
1719
133
640
return '';
1720
};
1721
1722
1723
# Return info about caller. Stolen from Carp
1724
sub _caller_info {
1725
160
160
258
my ($self, $i) = @_;
1726
160
187
my %call_info;
1727
160
248
my @call_info = ();
1728
1729
138
421
@call_info = @{ $self->{caller_stack}->[$i] }
1730
160
100
66
841
if defined $self->{caller_stack} and defined $self->{caller_stack}->[$i];
1731
1732
@call_info{
1733
160
727
qw{ package file line subroutine has_args wantarray evaltext is_require }
1734
} = @call_info[0..7];
1735
1736
160
100
418
unless (defined $call_info{package}) {
1737
22
101
return ();
1738
};
1739
1740
138
318
my $sub_name = $self->_get_subname(\%call_info);
1741
138
100
318
if ($call_info{has_args}) {
1742
74
150
my @args = map {$self->_format_arg($_)} @call_info[8..$#call_info];
307
746
1743
74
100
308
my $max_arg_nums = defined $self->{max_arg_nums} ? $self->{max_arg_nums} : $self->{defaults}->{max_arg_nums};
1744
74
100
100
333
if ($max_arg_nums > 0 and $#args+1 > $max_arg_nums) {
1745
25
77
$#args = $max_arg_nums - 2;
1746
25
47
push @args, '...';
1747
};
1748
# Push the args onto the subroutine
1749
74
303
$sub_name .= '(' . join (', ', @args) . ')';
1750
}
1751
138
100
311
$call_info{file} = 'unknown' unless $call_info{file};
1752
138
100
271
$call_info{line} = 0 unless $call_info{line};
1753
138
262
$call_info{sub_name} = $sub_name;
1754
138
100
310
$call_info{wantarray} = $call_info{wantarray} ? '@_ = ' : '$_ = ';
1755
1756
138
100
1291
return wantarray() ? %call_info : \%call_info;
1757
};
1758
1759
1760
# Figures out the name of the sub/require/eval. Stolen from Carp
1761
sub _get_subname {
1762
146
146
223
my ($self, $info) = @_;
1763
146
100
314
if (defined($info->{evaltext})) {
1764
26
40
my $eval = $info->{evaltext};
1765
26
100
55
if ($info->{is_require}) {
1766
2
8
return "require $eval";
1767
}
1768
else {
1769
24
56
$eval =~ s/([\\\'])/\\$1/g;
1770
return
1771
"eval '" .
1772
24
100
78
$self->_str_len_trim($eval, defined $self->{max_eval_len} ? $self->{max_eval_len} : $self->{defaults}->{max_eval_len}) .
1773
"'";
1774
};
1775
};
1776
1777
120
100
387
return ($info->{subroutine} eq '(eval)') ? 'eval {...}' : $info->{subroutine};
1778
};
1779
1780
1781
# Transform an argument to a function into a string. Stolen from Carp
1782
sub _format_arg {
1783
327
327
585
my ($self, $arg) = @_;
1784
1785
327
100
664
return 'undef' if not defined $arg;
1786
1787
325
100
100
382
if (do { local $@; local $SIG{__DIE__}; eval { $arg->isa(__PACKAGE__) } } or ref $arg) {
325
389
325
925
325
494
325
2954
1788
22
65
return q{"} . overload::StrVal($arg) . q{"};
1789
};
1790
1791
303
452
$arg =~ s/\\/\\\\/g;
1792
303
366
$arg =~ s/"/\\"/g;
1793
303
346
$arg =~ s/`/\\`/g;
1794
303
100
982
$arg = $self->_str_len_trim($arg, defined $self->{max_arg_len} ? $self->{max_arg_len} : $self->{defaults}->{max_arg_len});
1795
1796
303
100
1050
$arg = "\"$arg\"" unless $arg =~ /^-?[\d.]+\z/;
1797
1798
1
1
12
no warnings 'once', 'utf8'; # can't disable critic for utf8...
1
2
1
661
1799
303
50
33
874
if (not defined *utf8::is_utf{CODE} or utf8::is_utf8($arg)) {
1800
303
100
745
$arg = join('', map { $_ > 255
761
100
3404
1801
? sprintf("\\x{%04x}", $_)
1802
: chr($_) =~ /[[:cntrl:]]|[[:^ascii:]]/
1803
? sprintf("\\x{%02x}", $_)
1804
: chr($_)
1805
} unpack("U*", $arg));
1806
}
1807
else {
1808
0
0
$arg =~ s/([[:cntrl:]]|[[:^ascii:]])/sprintf("\\x{%02x}",ord($1))/eg;
0
0
1809
};
1810
1811
303
901
return $arg;
1812
};
1813
1814
1815
# If a string is too long, trims it with ... . Stolen from Carp
1816
sub _str_len_trim {
1817
369
369
896
my (undef, $str, $max) = @_;
1818
369
100
775
$max = 0 unless defined $max;
1819
369
100
100
1436
if ($max > 2 and $max < length($str)) {
1820
66
115
substr($str, $max - 3) = '...';
1821
};
1822
1823
369
881
return $str;
1824
};
1825
1826
1827
# Modify default values for ATTRS
1828
sub _modify_default {
1829
21
21
38
my ($self, $key, $value, $modifier) = @_;
1830
1831
21
33
84
my $class = ref $self || $self;
1832
1833
# Modify entry in ATTRS constant. Its elements are not constant.
1834
21
48
my $attributes = $class->ATTRS;
1835
1836
21
100
85
if (not exists $attributes->{$key}->{default}) {
1837
1
8
Exception::Base->throw(
1838
message => ["%s class does not implement default value for `%s' attribute", $class, $key],
1839
verbosity => 1
1840
);
1841
};
1842
1843
# Make a new anonymous hash reference for attribute
1844
20
22
$attributes->{$key} = { %{ $attributes->{$key} } };
20
80
1845
1846
# Modify default value of attribute
1847
20
100
54
if ($modifier eq '+') {
100
1848
7
15
my $old = $attributes->{$key}->{default};
1849
7
100
66
33
if (ref $old eq 'ARRAY' or ref $value eq 'Regexp') {
100
1850
5
50
9
my @new = ref $old eq 'ARRAY' ? @{ $old } : $old;
5
14
1851
5
100
13
foreach my $v (ref $value eq 'ARRAY' ? @{ $value } : $value) {
3
7
1852
9
50
18
next if grep { $v eq $_ } ref $old eq 'ARRAY' ? @{ $old } : $old;
28
100
46
9
16
1853
5
10
push @new, $v;
1854
};
1855
5
21
$attributes->{$key}->{default} = [ @new ];
1856
}
1857
elsif ($old =~ /^\d+$/) {
1858
1
4
$attributes->{$key}->{default} += $value;
1859
}
1860
else {
1861
1
10
$attributes->{$key}->{default} .= $value;
1862
};
1863
}
1864
elsif ($modifier eq '-') {
1865
6
12
my $old = $attributes->{$key}->{default};
1866
6
100
66
33
if (ref $old eq 'ARRAY' or ref $value eq 'Regexp') {
100
1867
4
50
8
my @new = ref $old eq 'ARRAY' ? @{ $old } : $old;
4
17
1868
4
100
10
foreach my $v (ref $value eq 'ARRAY' ? @{ $value } : $value) {
3
7
1869
7
13
@new = grep { $v ne $_ } @new;
20
34
1870
};
1871
4
13
$attributes->{$key}->{default} = [ @new ];
1872
}
1873
elsif ($old =~ /^\d+$/) {
1874
1
3
$attributes->{$key}->{default} -= $value;
1875
}
1876
else {
1877
1
5
$attributes->{$key}->{default} = $value;
1878
};
1879
}
1880
else {
1881
7
15
$attributes->{$key}->{default} = $value;
1882
};
1883
1884
# Redeclare constant
1885
{
1886
1
1
6
no warnings 'redefine';
1
2
1
188
20
26
1887
20
69
*{_qualify_to_ref("${class}::ATTRS")} = sub () {
1888
32
32
614
+{ %$attributes };
1889
20
62
};
1890
};
1891
1892
# Reset cache
1893
20
391
%Class_Attributes = %Class_Defaults = ();
1894
1895
20
79
return $self;
1896
};
1897
1898
1899
=item _make_accessors
1900
1901
Creates accessors for each attribute. This static method should be called in
1902
each derived class which defines new attributes.
1903
1904
package Exception::My;
1905
# (...)
1906
BEGIN {
1907
__PACKAGE__->_make_accessors;
1908
}
1909
1910
=cut
1911
1912
# Create accessors for this class
1913
sub _make_accessors {
1914
20
20
33
my ($self) = @_;
1915
1916
20
33
95
my $class = ref $self || $self;
1917
1918
1
1
4
no warnings 'uninitialized';
1
2
1
1332
1919
20
57
my $attributes = $class->ATTRS;
1920
20
56
foreach my $key (keys %{ $attributes }) {
20
103
1921
470
50
1467
next if ref $attributes->{$key} ne 'HASH';
1922
470
100
3504
if (not $class->can($key)) {
1923
128
100
347
next if not defined $attributes->{$key}->{is};
1924
28
100
64
if ($attributes->{$key}->{is} eq 'rw') {
1925
16
58
*{_qualify_to_ref($class . '::' . $key)} = sub :lvalue {
1926
@_ > 1 ? $_[0]->{$key} = $_[1]
1927
16
100
16
223
: $_[0]->{$key};
1928
16
55
};
1929
}
1930
else {
1931
12
42
*{_qualify_to_ref($class . '::' . $key)} = sub {
1932
4
4
79
$_[0]->{$key};
1933
12
43
};
1934
};
1935
};
1936
};
1937
1938
20
125
return $self;
1939
};
1940
1941
1942
=item package
1943
1944
Returns the package name of the subroutine which thrown an exception.
1945
1946
=item file
1947
1948
Returns the file name of the subroutine which thrown an exception.
1949
1950
=item line
1951
1952
Returns the line number for file of the subroutine which thrown an exception.
1953
1954
=item subroutine
1955
1956
Returns the subroutine name which thrown an exception.
1957
1958
=back
1959
1960
=cut
1961
1962
# Create caller_info() accessors for this class
1963
sub _make_caller_info_accessors {
1964
1
1
2
my ($self) = @_;
1965
1966
1
33
6
my $class = ref $self || $self;
1967
1968
1
2
foreach my $key (qw{ package file line subroutine }) {
1969
4
50
69
if (not $class->can($key)) {
1970
4
15
*{_qualify_to_ref($class . '::' . $key)} = sub {
1971
12
12
29
my $self = shift;
1972
my $ignore_level = defined $self->{ignore_level}
1973
? $self->{ignore_level}
1974
: defined $self->{defaults}->{ignore_level}
1975
? $self->{defaults}->{ignore_level}
1976
12
50
43
: 0;
100
1977
12
18
my $level = 0;
1978
12
29
while (my %c = $self->_caller_info($level++)) {
1979
24
100
54
next if $self->_skip_ignored_package($c{package});
1980
# Skip ignored levels
1981
20
100
44
if ($ignore_level > 0) {
1982
8
10
$ignore_level --;
1983
8
48
next;
1984
};
1985
12
71
return $c{$key};
1986
};
1987
4
28
};
1988
};
1989
};
1990
1991
1
118
return $self;
1992
};
1993
1994
1995
# Load another module without eval q{}
1996
sub _load_package {
1997
28
28
45
my ($class, $package, $version) = @_;
1998
1999
28
50
60
return unless $package;
2000
2001
28
57
my $file = $package . '.pm';
2002
28
123
$file =~ s{::}{/}g;
2003
2004
28
10826
require $file;
2005
2006
# Check version if first element on list is a version number.
2007
4
50
33
133
if (defined $version and $version =~ m/^\d/) {
2008
4
68
$package->VERSION($version);
2009
};
2010
2011
1
7
return $class;
2012
};
2013
2014
2015
# Create new exception class
2016
sub _make_exception {
2017
23
23
41
my ($class, $package, $version, $param) = @_;
2018
2019
23
50
45
return unless $package;
2020
2021
23
100
116
my $isa = defined $param->{isa} ? $param->{isa} : __PACKAGE__;
2022
23
100
51
$version = 0.01 if not $version;
2023
2024
23
100
79
my $has = defined $param->{has} ? $param->{has} : { rw => [ ], ro => [ ] };
2025
23
100
71
if (ref $has eq 'ARRAY') {
100
2026
3
12
$has = { rw => $has, ro => [ ] };
2027
}
2028
elsif (not ref $has) {
2029
2
7
$has = { rw => [ $has ], ro => [ ] };
2030
};
2031
23
44
foreach my $mode ('rw', 'ro') {
2032
46
100
126
if (not ref $has->{$mode}) {
2033
6
100
24
$has->{$mode} = [ defined $has->{$mode} ? $has->{$mode} : () ];
2034
};
2035
};
2036
2037
# Base class is needed
2038
23
100
27
if (not defined do { local $SIG{__DIE__}; eval { $isa->VERSION } }) {
23
70
23
37
23
237
2039
1
2
eval {
2040
1
3
$class->_load_package($isa);
2041
};
2042
1
50
6
if ($@) {
2043
1
4
Exception::Base->throw(
2044
message => ["Base class %s for class %s can not be found", $isa, $package],
2045
verbosity => 1
2046
);
2047
};
2048
};
2049
2050
# Handle defaults for object attributes
2051
22
56
my $attributes;
2052
{
2053
22
23
local $SIG{__DIE__};
22
57
2054
22
28
eval {
2055
22
56
$attributes = $isa->ATTRS;
2056
};
2057
};
2058
22
50
65
if ($@) {
2059
0
0
Exception::Base->throw(
2060
message => ["%s class is based on %s class which does not implement ATTRS", $package, $isa],
2061
verbosity => 1
2062
);
2063
};
2064
2065
# Create the hash with overridden attributes
2066
22
23
my %overridden_attributes;
2067
# Class => { has => { rw => [ "attr1", "attr2", "attr3", ... ], ro => [ "attr4", ... ] } }
2068
22
41
foreach my $mode ('rw', 'ro') {
2069
42
43
foreach my $attribute (@{ $has->{$mode} }) {
42
179
2070
12
100
66
99
if ($attribute =~ /^(isa|version|has)$/ or $isa->can($attribute)) {
2071
2
9
Exception::Base->throw(
2072
message => ["Attribute name `%s' can not be defined for %s class", $attribute, $package],
2073
);
2074
};
2075
10
37
$overridden_attributes{$attribute} = { is => $mode };
2076
};
2077
};
2078
# Class => { message => "overridden default", ... }
2079
20
28
foreach my $attribute (keys %{ $param }) {
20
53
2080
14
100
58
next if $attribute =~ /^(isa|version|has)$/;
2081
4
50
66
18
if (not exists $attributes->{$attribute}->{default}
2082
and not exists $overridden_attributes{$attribute})
2083
{
2084
1
5
Exception::Base->throw(
2085
message => ["%s class does not implement default value for `%s' attribute", $isa, $attribute],
2086
verbosity => 1
2087
);
2088
};
2089
3
7
$overridden_attributes{$attribute} = {};
2090
3
8
$overridden_attributes{$attribute}->{default} = $param->{$attribute};
2091
3
5
foreach my $property (keys %{ $attributes->{$attribute} }) {
3
11
2092
6
100
19
next if $property eq 'default';
2093
3
9
$overridden_attributes{$attribute}->{$property} = $attributes->{$attribute}->{$property};
2094
};
2095
};
2096
2097
# Create the new package
2098
19
30
*{_qualify_to_ref("${package}::VERSION")} = \$version;
19
69
2099
19
335
*{_qualify_to_ref("${package}::ISA")} = [ $isa ];
19
59
2100
19
61
*{_qualify_to_ref("${package}::ATTRS")} = sub () {
2101
43
43
50
+{ %{ $isa->ATTRS }, %overridden_attributes };
43
94
2102
19
383
};
2103
19
365
$package->_make_accessors;
2104
2105
19
99
return $class;
2106
};
2107
2108
2109
# Module initialization
2110
BEGIN {
2111
1
1
8
__PACKAGE__->_make_accessors;
2112
1
3
__PACKAGE__->_make_caller_info_accessors;
2113
};
2114
2115
2116
1;
2117
2118
2119
=begin plantuml
2120
2121
class Exception::Base <> {
2122
+ignore_class : ArrayRef = []
2123
+ignore_level : Int = 0
2124
+ignore_package : ArrayRef = []
2125
+max_arg_len : Int = 64
2126
+max_arg_nums : Int = 8
2127
+max_eval_len : Int = 0
2128
+message : Str|ArrayRef[Str] = "Unknown exception"
2129
+value : Int = 0
2130
+verbosity : Int = 2
2131
..
2132
+caller_stack : ArrayRef
2133
+egid : Int
2134
+euid : Int
2135
+gid : Int
2136
+pid : Int
2137
+propagated_stack : ArrayRef
2138
+tid : Int
2139
+time : Int
2140
+uid : Int
2141
..
2142
#defaults : HashRef
2143
#default_attribute : Str = "message"
2144
#numeric_attribute : Str = "value"
2145
#eval_attribute : Str = "message"
2146
#string_attributes : ArrayRef[Str] = ["message"]
2147
==
2148
+new( args : Hash ) <>
2149
+throw( args : Hash = undef ) <>
2150
+throw( message : Str, args : Hash = undef ) <>
2151
..
2152
+catch() : Exception::Base
2153
+catch( variable : Any ) : Exception::Base
2154
+matches( that : Any ) : Bool {overload="~~"}
2155
+to_string() : Str {overload='""'}
2156
+to_number() : Num {overload="0+"}
2157
+to_bool() : Bool {overload="bool"}
2158
+get_caller_stacktrace() : Array[Str]|Str
2159
+PROPAGATE()
2160
..
2161
+ATTRS() : HashRef <>
2162
..
2163
#_collect_system_data()
2164
#_make_accessors() <>
2165
#_make_caller_info_accessors() <>
2166
}
2167
2168
=end plantuml
2169
2170
=head1 SEE ALSO
2171
2172
Repository: L
2173
2174
There are more implementation of exception objects available on CPAN. Please
2175
note that Perl has built-in implementation of pseudo-exceptions:
2176
2177
eval { die { message => "Pseudo-exception", package => __PACKAGE__,
2178
file => __FILE__, line => __LINE__ };
2179
};
2180
if ($@) {
2181
print $@->{message}, " at ", $@->{file}, " in line ", $@->{line}, ".\n";
2182
}
2183
2184
The more complex implementation of exception mechanism provides more features.
2185
2186
=over
2187
2188
=item L
2189
2190
Complete implementation of try/catch/finally/otherwise mechanism. Uses nested
2191
closures with a lot of syntactic sugar. It is slightly faster than
2192
C module for failure scenario and is much slower for success
2193
scenario. It doesn't provide a simple way to create user defined exceptions.
2194
It doesn't collect system data and stack trace on error.
2195
2196
=item L
2197
2198
More Perlish way to do OO exceptions. It is similar to C
2199
module and provides similar features but it is 10x slower for failure
2200
scenario.
2201
2202
=item L
2203
2204
Additional try/catch mechanism for L. It is 15x slower for
2205
success scenario.
2206
2207
=item L
2208
2209
Elegant OO exceptions similar to L and C.
2210
It might be missing some features found in C and
2211
L.
2212
2213
=item L
2214
2215
Not recommended. Abandoned. Modifies C<%SIG> handlers.
2216
2217
=item L
2218
2219
A module which gives new try/catch keywords without source filter.
2220
2221
=item L
2222
2223
Smaller, simpler and slower version of L module.
2224
2225
=back
2226
2227
The C does not depend on other modules like
2228
L and it is more powerful than L. Also it
2229
does not use closures as L and does not pollute namespace as
2230
L. It is also much faster than
2231
L and L for success scenario.
2232
2233
The C is compatible with syntax sugar modules like
2234
L and L.
2235
2236
The C is also a base class for enhanced classes:
2237
2238
=over
2239
2240
=item L
2241
2242
The exception class for system or library calls which modifies C<$!> variable.
2243
2244
=item L
2245
2246
The exception class for eval blocks with simple L. It can also
2247
handle L<$SIG{__DIE__}|perlvar/%SIG> hook and convert simple L
2248
into an exception object.
2249
2250
=item L
2251
2252
The exception class which handle L<$SIG{__WARN__}|pervar/%SIG> hook and
2253
convert simple L into an exception object.
2254
2255
=back
2256
2257
=head1 EXAMPLES
2258
2259
=head2 New exception classes
2260
2261
The C module allows to create new exception classes easily.
2262
You can use L interface or L module to do it.
2263
2264
The L interface allows to create new class with new
2265
read-write attributes.
2266
2267
package Exception::Simple;
2268
use Exception::Base (__PACKAGE__) => {
2269
has => qw{ reason method },
2270
string_attributes => qw{ message reason method },
2271
};
2272
2273
For more complex exceptions you can redefine C constant.
2274
2275
package Exception::Complex;
2276
use base 'Exception::Base';
2277
use constant ATTRS => {
2278
%{ Exception::Base->ATTRS }, # SUPER::ATTRS
2279
hostname => { is => 'ro' },
2280
string_attributes => qw{ hostname message },
2281
};
2282
sub _collect_system_data {
2283
my $self = shift;
2284
my $hostname = `hostname`;
2285
chomp $hostname;
2286
$self->{hostname} = $hostname;
2287
return $self->SUPER::_collect_system_data(@_);
2288
}
2289
2290
=head1 PERFORMANCE
2291
2292
There are two scenarios for L block: success or failure.
2293
Success scenario should have no penalty on speed. Failure scenario is usually
2294
more complex to handle and can be significantly slower.
2295
2296
Any other code than simple C is really slow and shouldn't be used if
2297
speed is important. It means that any module which provides try/catch syntax
2298
sugar should be avoided: L, L, L,
2299
L. Be careful because simple C has many gotchas which are
2300
described in L's documentation.
2301
2302
The C module was benchmarked with other implementations for
2303
simple try/catch scenario. The results
2304
(Perl 5.10.1 x86_64-linux-thread-multi) are following:
2305
2306
-----------------------------------------------------------------------
2307
| Module | Success sub/s | Failure sub/s |
2308
-----------------------------------------------------------------------
2309
| eval/die string | 3715708 | 408951 |
2310
-----------------------------------------------------------------------
2311
| eval/die object | 4563524 | 191664 |
2312
-----------------------------------------------------------------------
2313
| Exception::Base eval/if | 4903857 | 11291 |
2314
-----------------------------------------------------------------------
2315
| Exception::Base eval/if verbosity=1 | 4790762 | 18833 |
2316
-----------------------------------------------------------------------
2317
| Error | 117475 | 26694 |
2318
-----------------------------------------------------------------------
2319
| Class::Throwable | 4618545 | 12678 |
2320
-----------------------------------------------------------------------
2321
| Exception::Class | 643901 | 3493 |
2322
-----------------------------------------------------------------------
2323
| Exception::Class::TryCatch | 307825 | 3439 |
2324
-----------------------------------------------------------------------
2325
| TryCatch | 690784 | 294802 |
2326
-----------------------------------------------------------------------
2327
| Try::Tiny | 268780 | 158383 |
2328
-----------------------------------------------------------------------
2329
2330
The C module was written to be as fast as it is
2331
possible. It does not use internally i.e. accessor functions which are
2332
slower about 6 times than standard variables. It is slower than pure
2333
die/eval for success scenario because it is uses OO mechanisms which are slow
2334
in Perl. It can be a little faster if some features are disables, i.e. the
2335
stack trace and higher verbosity.
2336
2337
You can find the benchmark script in this package distribution.
2338
2339
=head1 BUGS
2340
2341
If you find the bug or want to implement new features, please report it at
2342
L
2343
2344
The code repository is available at
2345
L
2346
2347
=for readme continue
2348
2349
=head1 AUTHOR
2350
2351
Piotr Roszatycki
2352
2353
=head1 LICENSE
2354
2355
Copyright (c) 2007-2015 Piotr Roszatycki .
2356
2357
This program is free software; you can redistribute it and/or modify it
2358
under the same terms as Perl itself.
2359
2360
See L