Class::Throwable(3pm)
πΈ Class::Throwable πΈ
okay bestie, so this is the manual page for Class::Throwable β that's Class::Throwable(3pm) if you're being formal about it π
β and it's a minimal lightweight exception class for Perl. tiny, cute, does exactly what it says. let's get into it π¦
β¨ NAME β¨
Class::Throwableβ A minimal lightweight exception class π
β¨ SYNOPSIS β¨
like, here's the whole vibe in code form, no notes:
use Class::Throwable;
# simple usage
eval {
# code code code,
if ($something_goes_wrong) {
throw Class::Throwable "Something has gone wrong";
}
};
if ($@) {
# we just print out the exception message here
print "There has been an exception: " $@->getMessage();
# but if we are debugging we get the whole
# stack trace as well
if (DEBUG) {
print $@->getStackTraceAsString();
}
}
# it can be used to catch perl exceptions
# and wrap them in a Class::Throwable exception
eval {
# generate a perl exception
eval "2 / 0";
# then throw our own with the
# perl exception as a sub-exception
throw Class::Throwable "Throwing an exception" => $@ if $@;
};
if ($@) {
# setting the verbosity to
# 2 gives a full stack trace
# including any sub-exceptions
# (see below for examples of
# this output format)
$@->toString(2);
}
# you can also declare inline exceptions
use Class::Throwable qw(My::App::Exception::IllegalOperation);
# set their global verbosity as well
# with the class method
My::App::Exception::IllegalOperation->setVerbosity(2);
eval {
throw My::App::Exception::IllegalOperation "Bad, real bad";
};
# can also declare subclasses of Class::Throwable
# in other files, then when you import them, you
# can set their verbosity
use My::Sub::Class::In::A::Separate::File (VERBOSE => 1);
throw My::Sub::Class::In::A::Separate::File "This exception will use a verbosity of 1";
# you can even create exceptions, then throw them later
my $e = Class::Throwable->new("Things have gone bad, but I need to do something first", $@);
# do something else ...
# then throw the exception we created earlier
throw $e
ate, no crumbs π
β¨ DESCRIPTION β¨
so, this module implements a minimal lightweight exception object. it's literally the middle child on purpose β a compromise between the two extremes:
- the more basic solutions like Carp, which can only print info and cannot handle exception objects π
- and the more complex ones like Exception::Class, which can define complex inline exceptions but comes dragging a whole pile of module dependencies behind it π
so like. it's giving "best of both, drama of neither". periodt. π
π Inline Exceptions π
you can easily create new exception classes inline by passing them along with the use statement, like this bestie:
use Class::Throwable ('My::InlineException', 'My::Other::InlineException');
this is the quick 'n' easy way to define arbitrary exception classes without manually making separate files or packages for each one. slay. β¨
β οΈ but listen β subclasses of Class::Throwable cannot be used to define inline exceptions. if you try it, an exception will be thrown. do not test her, she will catch you π
π Exception Verbosity π
Class::Throwable offers a bunch of different types of diagnostic output to suit your needs, and most of it is controlled through the verbosity levels. here's the breakdown, no cap:
- if the verbosity level is set to
0or below, an empty string is returned π«₯ - if the value is set to
1, then the exception's message is returned π¬ - if the value is set to
2or above, a full stack trace along with full stack traces for all sub-exceptions are returned, in the format shown instackTraceToStringπ§
and the default verbosity setting? that's 1. iconic. π
there are a number of ways you can set the verbosity of the exceptions produced by Class::Throwable. the simplest way is as the argument to the toString method. using this method will override any other settings you may have, and insure that the output of this method is exactly as you ask it to be. queen behaviour honestly π
$@->toString(2);
however β to use this style properly, you have to test the value of $@ to be sure it's a Class::Throwable object. sometimes that's not an issue, but sometimes it lowkey makes more sense to set verbosity on a wider scale. πΈ
for instance, if you define inline exceptions, then the simplest way to set a verbosity level for a particular inline exception is through the class method setVerbosity. π
use Class::Throwable qw(My::InlineException);
My::InlineException->setVerbosity(2);
this means that unless the toString verbosity argument overrides it, all My::InlineException exceptions will use a verbosity setting of 2. this method means you can easily "print" the value of $@ and then any My::InlineException exceptions will be automatically stringified with a verbosity level of 2. this can simplify exception catching by reducing the need to inspect the value of $@. less peeking, more living π¦
if you defined your exceptions as subclasses of Class::Throwable and stored them in separate files, then another means of setting the verbosity level is to assign it in the use statement:
use My::SeparateFileSubClass::Exception (VERBOSE => 2);
this has the same effect as the setVerbosity class method β in fact, there is nothing to stop you from using the setVerbosity class method in this case if you like. you do you π
this method can also be used on Class::Throwable itself, however, this does not set the verbosity level for all subclasses, only for Class::Throwable exceptions. narrow scope, bestie, respect it. π
there's one last method, and it has the widest scope of them all. the variable $Class::Throwable::DEFAULT_VERBOSITY can be set. setting this value will take effect if:
- there is no value passed to the
toStringmethod, and - no verbosity level has been set for the particular class, either through
setVerbosityor theusestatement.
so it's the fallback of fallbacks. the final boss. the main character of defaults π
π§ Module exception retro-fitting π§
yesss, it is possible to retrofit a module to use Class::Throwable exceptions if you want to. basically this allows modules which die with either strings or some other value to throw Class::Throwable based exceptions instead. this feature is relatively new and should be considered experimental β any feedback on it is greatly appreciated π«Ά
β οΈ NOTE: it's important to do module retrofitting at the earliest possible moment (preferrably before the module you are retrofitting is compiled), because it will override die within a specified package. timing matters, bestie. β°
other than all that, retrofitting is actually super simple. here's a basic example:
use Class::Throwable retrofit => 'My::Class';
now anytime die is called within My::Class, the calls will get converted to a Class::Throwable instance. β¨
you can also control how exceptions are converted, like so:
use Class::Throwable retrofit => 'My::Class' => sub { My::Exception->throw(@_) };
now anytime die is called within My::Class, the calls get converted to a My::Exception instance instead. or, a slightly more complex example like this:
use Class::Throwable retrofit => (
'My::Class' => sub {
My::IllegalOperation->throw(@_) if $_[0] =~ /^Illegal Operation/;
My::Exception->throw(@_);
});
now anytime die is called within My::Class, the calls get converted to a My::Exception instance unless the exception matches the reg-exp β in which case a My::IllegalOperation exception is thrown instead. conditional queen π
there are a couple of points to be made regarding this functionality:
- first, it will add another stack frame to your exceptions (the retrofit routine, basically). this is probably avoidable, but since this is still experimental, the author wanted to keep things somewhat simple. π
- and second, if you supply a custom
diehandler, you should be sure that it willdiesomewhere within that routine. π if you do not, you may have many un-intended consequences. like, many. do not do this to yourself.
and that's part one bestie β there's literally more, so don't go anywhere π
π METHODS π
ok so here's where the actual doing stuff happens, bestie. buckle up π
πΈ Constructor πΈ
throw ($message, $sub_exception)β the moment you've been waiting for. this is the classic way to make an exception object: it builds the object, scoops up everything from the call stack, and then just goes ahead anddies. dramatic? yes. necessary? also yes π
the optional $message arg is your lil custom note you can attach to the exception object. usually it's a string, but this module literally does not care and will not enforce that β it just needs to be a scalar. so like, go off, pass in fancier references or objects if that's your vibe. if you pass in no $message at all, it makes a default one for you π«Ά
second optional arg, $sub_exception, is for when you caught an exception but it's not the right move to re-throw it raw β better to wrap it up all cozy inside a brand new exception object. this will commonly be another Class::Throwable object, but again, no enforcement, so plain string-based perl exceptions are totally welcome too.
heads up: if you call this as an instance method on an exception object that was pre-built with new, only then does the stack trace info get populated, and then the exception gets handed off to die.
new ($message, $sub_exception)β the alternate route, lowkey the same energy asthrow, except it does not collect stack trace info and does notdie. it just stores$messageand$sub_exception, hands you back the exception instance, and lets you throw it whenever you feel like it. delayed gratification, iconic behavior β¨
π Class Methods π
setVerbosity ($verbosity)β this one's a class method, so listen: if you call it on an instance, an exception gets thrown, fr. use it to set the verbosity level for a particular class. more deets live up in the "Exception Verbosity" section above β¬οΈ
π¦ Accessors π¦
getMessageβ lets you peek at the message inside the exception, so you can do more granular exception reporting. nosy in the best way πgetStackTraceβ hands you the raw stack trace info as an array of arrays.callerreturns 10 values β$package,$filename,$line,$subroutine,$hasargs,$wantarray,$evaltext,$is_require,$hints,$bitmaskβ and we do not bother capturing the last two, because they're subject to change and meant for internal use. all the others get retained, in the exact ordercallerreturned them.hasSubExceptionβ returns true (1) if this exception has a sub-exception, and false (0) otherwise. simple. clean. slay.getSubExceptionβ gives you access to the stored sub-exception π
π Output Methods π
this object overloads the stringification operator, and it calls the toString method to do that stringifying for it. yes, it's giving main character energy.
toString ($verbosity)β prints out the exception object's info at a variable level of verbosity, which you set with the optional$verbosityargument. check the "Exception Verbosity" section above for the tea βstringValueβ returns the normal perl stringified value of the object, without going through thetoStringmethod. the no-frills version β¨stackTraceToStringβ prints the stack trace info, and the trace comes out looking like this:
|--[ main::foo called in my_script.pl line 12 ]
|--[ main::bar called in my_script.pl line 14 ]
|--[ main::baz called in my_script.pl line 16 ]
π EXAMPLE OUTPUT π
ok so picture this code, bestie:
{
package Foo;
sub foo { eval { Bar::bar() }; throw Class::Throwable "Foo!!", $@ }
package Bar;
sub bar { eval { Baz::baz() }; throw Class::Throwable "Bar!!", $@ }
package Baz;
sub baz { throw Class::Throwable "Baz!!" }
}
eval { Foo::foo() };
print $@->toString($verbosity) if $@;
if you printed the exception with verbosity of 0, you'd get no output at all. zero. nada. this mode is there so you can suppress exception output if you need to π
if you printed it with verbosity of 1, you'd get this:
Class::Throwable : Foo!!
and if you printed it with verbosity of 2, you'd get this gorgeous nested moment π
Class::Throwable : Foo!!
|--[ Foo::foo called in test.pl line 26 ]
|--[ main::(eval) called in test.pl line 26 ]
+ Class::Throwable : Bar!!
|--[ Bar::bar called in test.pl line 19 ]
|--[ Foo::(eval) called in test.pl line 19 ]
|--[ Foo::foo called in test.pl line 26 ]
|--[ main::(eval) called in test.pl line 26 ]
+ Class::Throwable : Baz!!
|--[ Baz::baz called in test.pl line 21 ]
|--[ Bar::(eval) called in test.pl line 21 ]
|--[ Bar::bar called in test.pl line 19 ]
|--[ Foo::(eval) called in test.pl line 19 ]
|--[ Foo::foo called in test.pl line 26 ]
|--[ main::(eval) called in test.pl line 26 ]
like, the nesting?? the little + signs?? obsessed ππ
π BUGS π
none that the author is aware of, bestie. but of course, if you find one, let them know and they'll be sure to fix it. this is based on code that's been used heavily in production sites for over 2 years now, without incident. that's a glow up fr fr β¨
π CODE COVERAGE π
Devel::Cover is used to test the code coverage of the tests, and below is the Devel::Cover report on this module's test suite. numbers go brrr:
---------------------------- ------ ------ ------ ------ ------ ------ ------
File stmt branch cond sub pod time total
---------------------------- ------ ------ ------ ------ ------ ------ ------
Class/Throwable.pm 100.0 98.0 63.6 100.0 100.0 100.0 95.7
---------------------------- ------ ------ ------ ------ ------ ------ ------
Total 100.0 98.0 63.6 100.0 100.0 100.0 95.7
---------------------------- ------ ------ ------ ------ ------ ------ ------
the pod is at 100.0, the docs ate and left no crumbs π
π«Ά SEE ALSO π«Ά
there are a bunch of ways to do exceptions with perl, and the author wasn't really satisfied with how anybody else did them, so this module was born. but hey β if this one isn't your thing, maybe check these out:
- Throwable β
Throwableis a role for classes that are meant to be thrown as exceptions to standard program flow. - Exception::Class β one of the more common exception classes out there. it does an excellent job with its default behavior, and allows a number of complex options which can likely serve any needs you might have. the reason the author didn't use this module is that those extra options made things feel more complex than they needed to be, plus it introduced a number of dependencies. not saying it's bloated at all, just that for them it was far more than they found they needed. if you have heavy duty exception needs, this is your module π₯
- Error β the classic perl exception module, complete with a
try/catchmechanism. this module has a lot of bad karma attached to it because of the obscure nested closure memory leak thattry/catchhas. the author never really liked the way its exception objectError::Simpledid things either. - Exception β the author has never really experimented with this one, so take their opinion with a large grain of salt. their problem with it was always that it seemed to want to do too much. it attempts to make perl into a language with real exceptions, but messes with
%SIGhandlers and other such things. that can be dangerous territory sometimes, and for them, far more than their needs. β οΈ
π AUTHOR π
stevan little, <stevan@iinteractive.com>
π COPYRIGHT AND LICENSE π
Copyright 2004 by Infinity Interactive, Inc.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
aaaaand that's the whole page, bestie!! π¦ this was perl v5.36.0, dated 2022-10-15, and the manpage was Class::Throwable(3pm) β go forth and throw some beautifully-nested exceptions, you absolute icon πβ¨πΈ
π¦ where this page comes from
- ubuntu noble: libclass-throwable-perl 0.13-3 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libclass-throwable-perl 0.13-3 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libclass-throwable-perl 0.13-3 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms