Exception::Base(3pm)
π Exception::Base(3pm) β Lightweight exceptions π
Okay bestie, welcome to Exception::Base(3pm) from User Contributed Perl Documentation. Weβre talking lightweight exceptions, but make it fashion. π
πΈ NAME πΈ
Exception::Base β Lightweight exceptions
π¦ SYNOPSIS π¦
Hereβs the whole demo, bestie β code stays exactly as it is. No cap. β¨
# Use module and create needed exceptions
use Exception::Base
'Exception::Runtime', # create new module
'Exception::System', # load existing module
'Exception::IO', => {
isa => 'Exception::System' }, # create new based on existing
'Exception::FileNotFound' => {
isa => 'Exception::IO', # create new based on previous
message => 'File not found', # override default message
has => [ 'filename' ], # define new rw attribute
string_attributes => [ 'message', 'filename' ],
}; # output message and filename
# eval is used as "try" block
eval {
open my $file, '/etc/passwd'
or Exception::FileNotFound->throw(
message=>'Something wrong',
filename=>'/etc/passwd');
};
# syntax for Perl >= 5.10
use feature 'switch';
if ($@) {
given (my $e = Exception::Base->catch) {
when ($e->isa('Exception::IO')) { warn "IO problem"; }
when ($e->isa('Exception::Eval')) { warn "eval died"; }
when ($e->isa('Exception::Runtime')) { warn "some runtime was caught"; }
when ($e->matches({value=>9})) { warn "something happened"; }
when ($e->matches(qr/^Error/)) { warn "some error based on regex"; }
default { $e->throw; } # rethrow the exception
}
}
# standard syntax for older Perl
if ($@) {
my $e = Exception::Base->catch; # convert $@ into exception
if ($e->isa('Exception::IO')) { warn "IO problem"; }
elsif ($e->isa('Exception::Eval')) { warn "eval died"; }
elsif ($e->isa('Exception::Runtime')) { warn "some runtime was caught"; }
elsif ($e->matches({value=>9})) { warn "something happened"; }
elsif ($e->matches(qr/^Error/)) { warn "some error based on regex"; }
else { $e->throw; } # rethrow the exception
}
# $@ has to be recovered ASAP!
eval { die "this die will be caught" };
my $e = Exception::Base->catch;
eval { die "this die will be ignored" };
if ($e) {
(...)
}
# the exception can be thrown later
my $e = Exception::Base->new;
# (...)
$e->throw;
# ignore our package in stack trace
package My::Package;
use Exception::Base '+ignore_package' => __PACKAGE__;
# define new exception in separate module
package Exception::My;
use Exception::Base (__PACKAGE__) => {
has => ['myattr'],
};
# run Perl with changed verbosity for debugging purposes
$ perl -MException::Base=verbosity,4 script.pl
π DESCRIPTION π
Okay so this class implements a fully OO exception mechanism, and itβs giving the same energy as Exception::Class or Class::Throwable. It gives programmers a simple interface for declaring exception classes. Those classes can be thrown and caught. Each uncaught exception prints a full stack trace if the default verbosity is increased for debugging purposes. Periodt. π
The features of Exception::Base are literally this iconic list:
- fast implementation of the exception class
- fully OO without closures and source code filtering
- does not mess with
$SIG{__DIE__}and$SIG{__WARN__} - no external run-time modules dependencies, requires core Perl modules only
- the default behavior of exception class can be changed globally or just for the thrown exception
- matching the exception by class, message or other attributes
- matching with string, regex or closure function
- creating automatically the derived exception classes (
usein perlfunc interface) - easily expendable, see
Exception::Systemclass for example - prints just an error message or dumps full stack trace
- can propagate (rethrow) an exception
- can ignore some packages for stack trace output
- some defaults (i.e. verbosity) can be different for different exceptions
π OVERLOADS π
π Boolean context π
True value. See to_bool method.
eval { Exception::Base->throw( message=>"Message", value=>123 ) };
if ($@) {
# the exception object is always true
}
π₯ Numeric context π₯
Content of attribute pointed by numeric_attribute attribute. See to_number method.
eval { Exception::Base->throw( message=>"Message", value=>123 ) };
print 0+$@; # 123
π String context π
Content of attribute which is combined from string_attributes attributes with additional information, depended on verbosity setting. See to_string method.
eval { Exception::Base->throw( message=>"Message", value=>123 ) };
print "$@"; # "Message at -e line 1.\n"
π¦ ~~ π¦
Smart matching operator. See matches method.
eval { Exception::Base->throw( message=>"Message", value=>123 ) };
print "Message" ~~ $@; # 1
print qr/message/i ~~ $@; # 1
print ['Exception::Base'] ~~ $@; # 1
print 123 ~~ $@; # 1
print {message=>"Message", value=>123} ~~ $@; # 1
β οΈ Warning: The smart operator requires that the exception object is a second argument.
π CONSTANTS π
π ATTRS π
Declaration of class attributes as reference to hash.
The attributes are listed as name => {properties}, where properties is a list of attribute properties:
isβ Can be'rw'for read-write attributes or'ro'for read-only attributes. The attribute is read-only and does not have an accessor created if'is'property is missed.defaultβ Optional property with the default value if the attribute value is not defined.
The read-write attributes can be set with new constructor. Read-only attributes and unknown attributes are ignored.
The constant have to be defined in derived class if it brings additional attributes.
package Exception::My;
use base 'Exception::Base';
# Define new class attributes
use constant ATTRS => {
%{Exception::Base->ATTRS}, # base's attributes have to be first
readonly => { is=>'ro' }, # new ro attribute
readwrite => { is=>'rw', default=>'blah' }, # new rw attribute
};
package main;
use Exception::Base ':all';
eval {
Exception::My->throw( readwrite => 2 );
};
if ($@) {
my $e = Exception::Base->catch;
print $e->readwrite; # = 2
print $e->defaults->{readwrite}; # = "blah"
}
π ATTRIBUTES π
Okay bestie, so class attributes here are literally just values sitting in a blessed hash, and they also show up as accessor methods. Two ways to reach the same thing, no cap.
π message (rw, default: 'Unknown exception')
This is the whole personality of your exception β the message. It's the part that actually shows up in the string when you stringify the object. Periodt.
eval { Exception::Base->throw( message=>"Message" ); };
print $@->message if $@;
Plot twist: it can also be an array reference full of strings, and then the "perlfunc" from sprintf kicks in to build the message. Slay.
Exception::Base->throw( message => ["%s failed", __PACKAGE__] );
π’ value (rw, default: 0)
The numeric main-character energy of your exception. In numeric context, this is what the object evaluates to.
eval { Exception::Base->throw( value=>2 ); };
print "Error 2" if $@ == 2;
π£οΈ verbosity (rw, default: 2)
This one controls how extra the exception gets when it turns itself into a string. Iconic little dial. The vibes, level by level:
0β Empty string. Nothing. Silence. π1β Message.2β Message at %s line %d.
Exactly the same as what die() spits out. Small detail bestie: it does not include the "at %s line %d." string when the message ends with a "\n" character. And this is the default, so it's what you get out of the box.
3β Full trace time:
Class: Message at %s line %d
%c_ = %s::%s() called in package %s at %s line %d
...propagated in package %s at %s line %d.
...
This output has the full trace of the error stack, minus the first "ignore_level" lines, and minus the packages listed in the "ignore_package" and "ignore_class" settings.
4β Full trace of the error stack, no filtering. At this level the"ignore_level","ignore_package"and"ignore_class"settings only matter for the first line of the exception's message.
If verbosity is undef, then the default verbosity for exception objects gets used instead. Gentle reminder.
If the verbosity you set through the constructor ("new" or "throw") is lower than 3, the full stack trace won't be collected at all. Frugal queen.
If the verbosity is lower than 2, the full system data (time, pid, tid, uid, euid, gid, egid) won't be collected either.
You can change this setting through the import interface:
use Exception::Base verbosity => 4;
Or flex it for the whole Perl interpreter instance, e.g. for debugging purposes:
sh$ perl -MException::Base=verbosity,4 script.pl
π ignore_package (rw)
Holds the name (scalar or regexp) or names (as a reference array) of packages that get ignored in the error stack trace. Super useful when some package throws an exception but this module shouldn't be listed in the stack trace. We don't need that kind of negativity in the call stack.
package My::Package;
use Exception::Base;
sub my_function {
do_something() or throw Exception::Base ignore_package=>__PACKAGE__;
throw Exception::Base ignore_package => [ "My", qr/^My::Modules::/ ];
}
Can be changed with the import interface too:
use Exception::Base ignore_package => __PACKAGE__;
π«₯ ignore_class (rw)
Holds the name (scalar) or names (as a reference array) of packages that are base classes for ignored packages in the error stack trace. Translation: some packages get ignored even when the derived class was the one called. Sneaky but valid.
package My::Package;
use Exception::Base;
Exception::Base->throw( ignore_class => "My::Base" );
Changed via the import interface:
use Exception::Base ignore_class => "My::Base";
πͺ ignore_level (rw)
The number of levels on the stack trace to ignore. Handy when some package throws an exception but this module shouldn't be listed in the stack trace. Works with or without the ignore_package attribute β your call, bestie.
# Convert warning into exception. The signal handler ignores itself.
use Exception::Base 'Exception::My::Warning';
$SIG{__WARN__} = sub {
Exception::My::Warning->throw( message => $_[0], ignore_level => 1 );
};
β° time (ro)
The timestamp of when the exception got thrown. Collected if the verbosity at throw time was greater than 1.
eval { Exception::Base->throw( message=>"Message" ); };
print scalar localtime $@->time;
π pid (ro)
The PID of the Perl process at the moment of the thrown exception. Collected if the verbosity at throw time was greater than 1.
eval { Exception::Base->throw( message=>"Message" ); };
kill 10, $@->pid;
π§΅ tid (ro)
The tid of the thread, or undef if threads aren't being used. Collected if the verbosity at throw time was greater than 1.
π€ uid (ro), euid (ro), gid (ro), egid (ro)
The real and effective uid and gid of the Perl process at the moment of the thrown exception. All collected if the verbosity at throw time was greater than 1. Group project, all four of them.
π₯ caller_stack (ro)
The error stack as an array of arrays, each row stuffed with info about caller functions. The first 8 elements of each row are the same as the first 8 elements of the "caller" function's output. Anything past that is optional and consists of the arguments of the called function. Collected if the verbosity at throw time was greater than 1, and it only contains the first element of the caller stack if verbosity was lower than 3.
Also, if the arguments of the called function are references and the "Scalar::Util::weaken" function is available, then the reference gets weakened. Considerate king behavior.
eval { Exception::Base->throw( message=>"Message" ); };
($package, $filename, $line, $subroutine, $hasargs, $wantarray,
$evaltext, $is_require, @args) = $@->caller_stack->[0];
π’ propagated_stack (ro)
An array of arrays used for generating the "...propagated at" message. Each row's elements are the same as the first 3 elements of the "caller" function's output.
βοΈ max_arg_len (rw, default: 64)
The maximum length of an argument for functions in backtrace output. Zero means no limit at all β go off, length queen.
sub a { Exception::Base->throw( max_arg_len=>5 ) }
a("123456789");
π’ max_arg_nums (rw, default: 8)
The maximum number of arguments for functions in backtrace output. Zero means no limit on arguments.
sub a { Exception::Base->throw( max_arg_nums=>1 ) }
a(1,2,3);
π max_eval_len (rw, default: 0)
The maximum length of eval strings in backtrace output. Zero means no limit on length.
eval "Exception->throw( max_eval_len=>10 )";
print "$@";
𧬠defaults
Meta-attribute holding the list of default values. Very meta of it.
my $e = Exception::Base->new;
print defined $e->{verbosity}
? $e->{verbosity}
: $e->{defaults}->{verbosity};
π― default_attribute (default: 'message')
Meta-attribute holding the name of the default attribute. This is the one that gets set for the one-argument throw method. This attribute actually has meaning for derived classes, bestie.
use Exception::Base 'Exception::My' => {
has => 'myattr',
default_attribute => 'myattr',
};
eval { Exception::My->throw("string") };
print $@->myattr; # "string"
π’ numeric_attribute (default: 'value')
Meta-attribute holding the name of the attribute that contains the numeric value of the exception object. This is the one used to represent the exception in numeric context.
use Exception::Base 'Exception::My' => {
has => 'myattr',
numeric_attribute => 'myattr',
};
eval { Exception::My->throw(myattr=>123) };
print 0 + $@; # 123
πͺ eval_attribute (default: 'message')
Meta-attribute holding the name of the attribute that gets filled if the error stack is empty. That attribute ends up containing the value of the $@ variable. Meaningful for derived classes.
use Exception::Base 'Exception::My' => {
has => 'myattr',
eval_attribute => 'myattr'
};
eval { die "string" };
print $@->myattr; # "string"
π§΅ string_attributes (default: ['message'])
Meta-attribute holding the array of names of attributes with a defined value, which get joined into the string returned by the "to_string" method. And if none of the attributes are defined? Then the string is created from the first default value of the attributes listed in the opposite order. Little reverse-order moment, iykyk.
use Exception::Base 'Exception::My' => {
has => 'myattr',
myattr => 'default',
string_attributes => ['message', 'myattr'],
};
eval { Exception::My->throw( message=>"string", myattr=>"foo" ) };
print $@->myattr; # "string: foo"
eval { Exception::My->throw() };
print $@->myattr; # "default"
π IMPORTS π
Okay bestie, this is the part where you teach use Exception::Base some new tricks at compile time. Periodt. π
use Exception::Base 'attribute' => value;β this changes the default value for that attribute. If the attribute name has no special prefix, its old default gets straight-up swapped for the new value. Out with the old, in with the new. π¦
use Exception::Base verbosity => 4;
But if the attribute name starts with + or -, then the new value builds on top of the previous one instead of replacing it. It's giving "character development" β¨:
- πΈ If the original value was a reference to an array, the new value can be included in or removed from the original array. And if you need to add or remove more than one element, just hand it an array reference. Slay.
use Exception::Base
"+ignore_packages" => [ __PACKAGE__, qr/^Moose::/ ],
"-ignore_class" => "My::Good::Class";
- πΈ If the original value was a number, it gets incremented or decremented by the new value. Math but make it cute.
use Exception::Base "+ignore_level" => 1;
- πΈ If the original value was a string, the new value gets included (appended). Glow-up for strings, literally.
use Exception::Base "+message" => ": The incuded message";
use Exception::Base 'Exception', ...;β loads an additional exception class module for you. And if that module isn't available? No stress β it creates the exception class automatically at compile time, based on theException::Baseclass. Iconic auto-pilot behaviour. π
use Exception::Base qw{ Exception::Custom Exception::SomethingWrong };
Exception::Custom->throw;
use Exception::Base 'Exception' => { isa => BaseException, version => version, ... };β also loads an additional exception class module. But here's the vibe: if the module's version is lower than the given parameter, or the module can't be loaded at all, then it creates the exception class automatically at compile time. The newly created class is based on the given class and gets the given$VERSIONvariable. Bestie, it finesses it for you. π₯isaβ the newly created class will be based on the given class. Passing down the family traits π
use Exception::Base
'Exception::My',
'Exception::Nested' => { isa => 'Exception::My };
versionβ the class will be created only if the module's version is lower than the given parameter, and it will have the version given in the argument. Version check but make it protective π«Ά
use Exception::Base
'Exception::My' => { version => 1.23 };
hasβ gives the class new attributes. If the parameter is a string, you get one new rw attribute. If it's a reference to an array of strings, you get multiple new rw attributes. And if it's a reference to a hash of array of strings (withrwandroas hash keys), you get new rw or ro attributes. Choose your fighter. β
use Exception::Base
'Exception::Simple' => { has => 'field' },
'Exception::More' => { has => [ 'field1', 'field2' ] },
'Exception::Advanced' => { has => {
ro => [ 'field1', 'field2' ],
rw => [ 'field3' ]
} };
messageverbositymax_arg_lenmax_arg_numsmax_eval_len- any other attribute having a default property β the class will simply have the default property for whatever attribute you named. Effortless. π
use Exception::Base
'Exception::WithDefault' => { message => 'Default message' },
'Exception::Reason' => {
has => [ 'reason' ],
string_attributes => [ 'message', 'reason' ] };
π§ CONSTRUCTORS π§
new([%args])β creates the exception object so you can throw it later. Heads up though: the system data attributes liketime,pid,uid,gid,euid,egidare not filled in. They're on their own little vacation, ngl. π
If the key of the argument is a read-write attribute, that attribute gets filled. Otherwise? The argument is just ignored. Not every key gets to be main character energy, and that's okay. π
$e = Exception::Base->new(
message=>"Houston, we have a problem",
unknown_attr => "BIG"
);
print $e->{message};
Small flex for the performance girlies: the constructor reads the list of class attributes from the ATTRS constant function and stores it in the internal cache for performance reasons. The default values for the class are also stored in that internal cache. Efficient queen behavior. β‘
CLASS->throw([%args]]) β creates the exception object and immediately throws it using thediesystem function. No chill, instant action. π₯
open my $fh, $file
or Exception::Base->throw( message=>"Can not open file: $file" );
Fun fact: throw is also exported as a function, so you can just call it like a normal person:
open my $fh, $file
or throw 'Exception::Base' => message=>"Can not open file: $file";
- Oh and also β
throwcan be used as a method too. Versatile icon. π
π οΈ METHODS π οΈ
okay bestie, this is the good part β all the stuff your exception object can actually do π
$obj->throw([%args])β throws the exception object, like, immediately. no waiting. it's also the move for rethrowing an existing exception object, and any extra args you pass will override the attributes on that existing object. slay.
$e = Exception::Base->new;
# (...)
$e->throw( message=>"thrown exception with overridden message" );
eval { Exception::Base->throw( message=>"Problem", value=>1 ) };
$@->throw if $@->value;
$obj->throw(message, [%args])β so if the number of args in the list is odd, the first argument is a message. and heads up: that message can be overridden by amessagefrom the args list. lowkey chaotic but iconic.
Exception::Base->throw( "Problem", message=>"More important" );
eval { die "Bum!" };
Exception::Base->throw( $@, message=>"New message" );
CLASS->throw($exception, [%args])β rethrows an existing exception object as a different exception class, right away. glow-up via reclassification β¨
eval { open $f, "w", "/etc/passwd" or Exception::System->throw };
# convert Exception::System into Exception::Base
Exception::Base->throw($@);
CLASS->catch([$variable])β recovers the exception from the variable argument, or from the$@variable if that argument was empty. then it also replaces$@with an empty string so you don't get stuck in an endless loop. not me preventing infinite loops π
The method returns an exception object if an exception was caught, or an undefined value otherwise.
eval { Exception::Base->throw; };
if ($@) {
my $e = Exception::Base->catch;
print $e->to_string;
}
And if the value is not empty but does not contain an Exception::Base object? a new exception object gets created with class CLASS, and its message is based on the previous value with the " at file line 123." string removed, plus the last end of line (LF) stripped too.
eval { die "Died\n"; };
my $e = Exception::Base->catch;
print ref $e; # "Exception::Base"
matches(that)β checks whether the exception object matches whatever you handed it. thematchesmethod overloads the~~smart matching operator, btw. β οΈ Warning: the second argument for the smart matching operator needs to be a scalar. just scalar. that's the vibe check.
If the argument is a reference to an array, it checks whether the object is a given class:
use Exception::Base
'Exception::Simple',
'Exception::Complex' => { isa => 'Exception::Simple };
eval { Exception::Complex->throw() };
print $@->matches( ['Exception::Base'] ); # matches
print $@->matches( ['Exception::Simple', 'Exception::X'] ); # matches
print $@->matches( ['NullObject'] ); # doesn't
If the argument is a reference to a hash, the attributes of the exception object are what get matched:
eval { Exception::Base->throw( message=>"Message", value=>123 ) };
print $@->matches( { message=>"Message" } ); # matches
print $@->matches( { value=>123 } ); # matches
print $@->matches( { message=>"Message", value=>45 } ); # doesn't
If the argument is a single string, a regexp, a code reference, or it's undefined, then the default attribute of the exception object gets matched (usually that's the message attribute, iykyk):
eval { Exception::Base->throw( message=>"Message" ) };
print $@->matches( "Message" ); # matches
print $@->matches( qr/Message/ ); # matches
print $@->matches( qr/[0-9]/ ); # doesn't
print $@->matches( sub{/Message/} ); # matches
print $@->matches( sub{0} ); # doesn't
print $@->matches( undef ); # doesn't
If the argument is a numeric value, it matches when the value attribute matches:
eval { Exception::Base->throw( value=>123, message=>456 ) } );
print $@->matches( 123 ); # matches
print $@->matches( 456 ); # doesn't
And if an attribute contains an array reference, that array gets sprintf-ed before matching. cute little preprocessing moment π
eval { Exception::Base->throw( message=>["%s", "Message"] ) };
print $@->matches( "Message" ); # matches
print $@->matches( qr/Message/ ); # matches
print $@->matches( qr/[0-9]/ ); # doesn't
The match method matches for special keywords too β these are the VIP keys:
-isaβ matches if the object is a given class.
eval { Exception::Base->new( message=>"Message" ) };
print $@->matches( { -isa=>"Exception::Base" } ); # matches
print $@->matches( { -isa=>["X::Y", "Exception::Base"] } ); # matches
-hasβ matches if the object has a given attribute.
eval { Exception::Base->new( message=>"Message" ) };
print $@->matches( { -has=>"Message" } ); # matches
-defaultβ matches against the default attribute, usually themessageattribute.
eval { Exception::Base->new( message=>"Message" ) };
print $@->matches( { -default=>"Message" } ); # matches
to_stringβ returns the string representation of the exception object. it gets called automatically whenever the exception object is used in string scalar context, but you can also totally call it yourself, no gatekeeping.
eval { Exception::Base->throw; };
$@->{verbosity} = 1;
print "$@";
$@->verbosity = 4;
print $@->to_string;
to_numberβ returns the numeric representation of the exception object. auto-called when the object lands in numeric scalar context. explicit calls work too π
eval { Exception::Base->throw( value => 42 ); };
print 0+$@; # 42
print $@->to_number; # 42
to_boolβ returns the boolean representation of the exception object. auto-called in boolean context, and yes you can call it straight up.
eval { Exception::Base->throw; };
print "ok" if $@; # ok
print "ok" if $@->to_bool; # ok
get_caller_stacktraceβ returns an array of strings, or a string, with the caller stack trace. it's implicitly used by theto_stringmethod. the receipts, basically π§ΎPROPAGATEβ checks the caller stack and fills thepropagated_stackattribute. usually this one comes out when thediesystem function was called without any arguments. messy but valid._collect_system_dataβ collects system data and fills the attributes of the exception object. this method is called automatically if the exception is thrown or created by thenewconstructor. and it can be overridden by a derived class, which is lowkey the whole point π
package Exception::Special;
use base 'Exception::Base';
use constant ATTRS => {
%{Exception::Base->ATTRS},
'special' => { is => 'ro' },
};
sub _collect_system_data {
my $self = shift;
$self->SUPER::_collect_system_data(@_);
$self->{special} = get_special_value();
return $self;
}
BEGIN {
__PACKAGE__->_make_accessors;
}
1;
The method returns the reference to the self object. πͺ
_make_accessorsβ creates accessors for each attribute. this is a static method, and it should be called in each derived class that defines new attributes. don't skip it, fr fr.
package Exception::My;
# (...)
BEGIN {
__PACKAGE__->_make_accessors;
}
packageβ returns the package name of the subroutine which thrown an exception.fileβ returns the file name of the subroutine which thrown an exception.lineβ returns the line number for the file of the subroutine which thrown an exception.subroutineβ returns the subroutine name which thrown an exception.
π SEE ALSO π
Repository: <http://github.com/dex4er/perl-Exception-Base> β go give it a star bestie β
There are more implementations of exception objects available on CPAN, if you're shopping around. but first, please note that Perl has a built-in implementation of pseudo-exceptions:
eval { die { message => "Pseudo-exception", package => __PACKAGE__,
file => __FILE__, line => __LINE__ };
};
if ($@) {
print $@->{message}, " at ", $@->{file}, " in line ", $@->{line}, ".\n";
}
The more complex implementation of exception mechanism provides more features. here's the roster:
- Error β complete implementation of try/catch/finally/otherwise mechanism. it uses nested closures with a lot of syntactic sugar. it's slightly faster than the
Exception::Basemodule for the failure scenario, and much slower for the success scenario. it doesn't provide a simple way to create user defined exceptions. it doesn't collect system data and stack trace on error. π - Exception::Class β more Perlish way to do OO exceptions. it's similar to the
Exception::Basemodule and provides similar features, but it is 10x slower for the failure scenario. ten times. π - Exception::Class::TryCatch β additional try/catch mechanism for Exception::Class. it is 15x slower for the success scenario.
- Class::Throwable β elegant OO exceptions, similar to Exception::Class and
Exception::Base. it might be missing some features found inException::Baseand Exception::Class. - Exceptions β not recommended. abandoned. modifies
%SIGhandlers. π© - TryCatch β a module which gives new try/catch keywords without a source filter.
- Try::Tiny β smaller, simpler and slower version of the TryCatch module.
The Exception::Base does not depend on other modules like Exception::Class, and it is more powerful than Class::Throwable. also it does not use closures like Error, and it does not pollute the namespace like Exception::Class::TryCatch. it is also much faster than Exception::Class::TryCatch and Error for the success scenario. main character energy, honestly π
The Exception::Base is compatible with syntax sugar modules like TryCatch and Try::Tiny. π
The Exception::Base is also a base class for enhanced classes:
- Exception::System β the exception class for system or library calls which modifies the
$!variable. - Exception::Died β the exception class for eval blocks with simple
diein perlfunc. it can also handle the$SIG{__DIE__}hook and convert a simplediein perlfunc into an exception object. - Exception::Warning β the exception class which handles the
$SIG{__WARN__}hook and converts a simplewarnin perlfunc into an exception object. π
πΈ EXAMPLES πΈ
π New exception classes π
The Exception::Base module lets you create new exception classes so easily, bestie. You can use the import in perlfunc interface or the base module to do it.
The import in perlfunc interface lets you create a new class with new read-write attributes. Slay:
package Exception::Simple;
use Exception::Base (__PACKAGE__) => {
has => qw{ reason method },
string_attributes => qw{ message reason method },
};
For more complex exceptions you can redefine the ATTRS constant. It's giving main-character customization:
package Exception::Complex;
use base 'Exception::Base';
use constant ATTRS => {
%{ Exception::Base->ATTRS }, # SUPER::ATTRS
hostname => { is => 'ro' },
string_attributes => qw{ hostname message },
};
sub _collect_system_data {
my $self = shift;
my $hostname = `hostname`;
chomp $hostname;
$self->{hostname} = $hostname;
return $self->SUPER::_collect_system_data(@_);
}
π₯ PERFORMANCE π₯
There are two scenarios for eval in perlfunc block: success or failure. The success scenario should have no penalty on speed. The failure scenario is usually more complex to handle and can be significantly slower. π
Any other code than simple if ($@) is really slow and shouldn't be used if speed is important. That means any module which provides try/catch syntax sugar should be avoided: Error, Exception::Class::TryCatch, TryCatch, Try::Tiny. Ngl, avoid the extra sprinkle if you're chasing speed.
Be careful because simple if ($@) has many gotchas which are described in Try::Tiny's documentation. π«Ά
The Exception::Base module was benchmarked with other implementations for simple try/catch scenario. The results (Perl 5.10.1 x86_64-linux-thread-multi) are following:
eval/die stringβ success:3715708sub/s, failure:408951sub/seval/die objectβ success:4563524sub/s, failure:191664sub/sException::Base eval/ifβ success:4903857sub/s, failure:11291sub/sException::Base eval/if verbosity=1β success:4790762sub/s, failure:18833sub/sErrorβ success:117475sub/s, failure:26694sub/sClass::Throwableβ success:4618545sub/s, failure:12678sub/sException::Classβ success:643901sub/s, failure:3493sub/sException::Class::TryCatchβ success:307825sub/s, failure:3439sub/sTryCatchβ success:690784sub/s, failure:294802sub/sTry::Tinyβ success:268780sub/s, failure:158383sub/s
The Exception::Base module was written to be as fast as it is possible. It does not use internally i.e. accessor functions which are slower about 6 times than standard variables. It is slower than pure die/eval for success scenario because it uses OO mechanisms which are slow in Perl. It can be a little faster if some features are disabled, i.e. the stack trace and higher verbosity. π
You can find the benchmark script in this package distribution. β¨
π BUGS π
If you find the bug or want to implement new features, please report it at <https://github.com/dex4er/perl-Exception-Base/issues>.
The code repository is available at <http://github.com/dex4er/perl-Exception-Base>.
π AUTHOR π
Piotr Roszatycki <dexter@cpan.org>
π LICENSE π
Copyright (c) 2007-2015 Piotr Roszatycki <dexter@cpan.org>.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
See <http://dev.perl.org/licenses/artistic.html>.
Okay bestie, that's the whole page! π This one comes from perl v5.42.0, dated 2023-08-08, and the page is Exception::Base(3pm). Byeeee! πβ¨π¦
π¦ where this page comes from
- opensuse-leap 16.0: perl-Exception-Base 0.2501-bp160.1.4 Β· Artistic-1.0 OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms