Try-Tiny
view release on metacpan or search on metacpan
some cases during global destruction (Graham Knop, doy/Sub-Name/#9)
0.16 2013-07-10
- remove accidental Sub::Name test dep
0.15 2013-07-08
- optionally use Sub::Name to name the try/catch/finally blocks, if available
(Mark Fowler)
0.14 2013-07-05
- also throw an exception for catch/finally in scalar context (RT#81070)
0.13 2013-07-04
- fix tests failing on 5.6.x due to differing DESTROY semantics
- excise superfluous local($@) call - 7% speedup
- fix (fsvo) broken URLs (RT#55659)
- proper exception on erroneous usage of bare catch/finally (RT#81070)
- proper exception on erroneous use of multiple catch{} blocks
- clarify exception occuring on unterminated try block (RT#75712)
- fix the prototypes shown in docs to match code (RT#79590; thanks, Pushtaev
Vadim)
lib/Try/Tiny.pm view on Meta::CPAN
C<Try::Tiny::Catch> which allows try to decode correctly what to do
with this code reference.
catch { ... }
Inside the C<catch> block the caught error is stored in C<$_>, while previous
value of C<$@> is still available for use. This value may or may not be
meaningful depending on what happened before the C<try>, but it might be a good
idea to preserve it in an error stack.
For code that captures C<$@> when throwing new errors (i.e.
L<Class::Throwable>), you'll need to do:
local $@ = $_;
=item finally (&;@)
try { ... }
catch { ... }
finally { ... };
lib/Try/Tiny.pm view on Meta::CPAN
this is also how C<eval> works, but not how L<TryCatch> works):
sub parent_sub {
try {
die;
}
catch {
return;
};
say "this text WILL be displayed, even though an exception is thrown";
}
Instead, you should capture the return value:
sub parent_sub {
my $success = try {
die;
1;
};
return unless $success;
lib/Try/Tiny.pm view on Meta::CPAN
# do something with $_
return undef; #see note
};
return unless $success;
say "This text WILL NEVER appear!";
}
Note that if you have a C<catch> block, it must return C<undef> for this to work,
since if a C<catch> block exists, its return value is returned in place of C<undef>
when an exception is thrown.
=item *
C<try> introduces another caller stack frame. L<Sub::Uplevel> is not used. L<Carp>
will not report this when using full stack traces, though, because
C<%Carp::Internal> is used. This lack of magic is considered a feature.
=for stopwords unhygienically
=item *
The value of C<$_> in the C<catch> block is not guaranteed to be the value of
the exception thrown (C<$@>) in the C<try> block. There is no safe way to
ensure this, since C<eval> may be used unhygienically in destructors. The only
guarantee is that the C<catch> will be called if an exception is thrown.
=item *
The return value of the C<catch> block is not ignored, so if testing the result
of the expression for truth on success, be sure to return a false value from
the C<catch> block:
my $obj = try {
MightFail->new;
} catch {
lib/Try/Tiny.pm view on Meta::CPAN
return unless $obj;
=item *
C<$SIG{__DIE__}> is still in effect.
Though it can be argued that C<$SIG{__DIE__}> should be disabled inside of
C<eval> blocks, since it isn't people have grown to rely on it. Therefore in
the interests of compatibility, C<try> does not disable C<$SIG{__DIE__}> for
the scope of the error throwing code.
=item *
Lexical C<$_> may override the one set by C<catch>.
For example Perl 5.10's C<given> form uses a lexical C<$_>, creating some
confusing behavior:
given ($foo) {
when (...) {
lib/Try/Tiny.pm view on Meta::CPAN
L<Feature::Compat::Try> to automatically switch to the native C<try> syntax in newer perls (when
available). See also L<Try Catch Exception Handling|perlsyn/Try-Catch-Exception-Handling>.
=item L<TryCatch>
Much more feature complete, more convenient semantics, but at the cost of
implementation complexity.
=item L<autodie>
Automatic error throwing for builtin functions and more. Also designed to
work well with C<given>/C<when>.
=item L<Throwable>
A lightweight role for rolling your own exception classes.
=item L<Error>
Exception object implementation with a C<try> statement. Does not localize
C<$@>.
my ( $code, $desc ) = @_;
local $Test::Builder::Level = $Test::Builder::Level + 1;
my ( $ok, $error ) = _eval($code);
ok($ok, $desc );
diag "error: $@" unless $ok;
}
sub throws_ok (&$$) {
my ( $code, $regex, $desc ) = @_;
local $Test::Builder::Level = $Test::Builder::Level + 1;
my ( $ok, $error ) = _eval($code);
if ( $ok ) {
fail($desc);
} else {
like($error || '', $regex, $desc );
}
my $prev;
lives_ok {
try {
die "foo";
};
} "basic try";
throws_ok {
try {
die "foo";
} catch { die $_ };
} qr/foo/, "rethrow";
{
local $@ = "magic";
is( try { 42 }, 42, "try block evaluated" );
is( $@, "magic", '$@ untouched' );
}
{
local $@ = "magic";
try {
like $err, qr/foo/;
} catch {
fail("shouldn't happen");
};
pass "got here";
}
} "try in try catch block";
throws_ok {
try {
die "foo";
} catch {
my $err = shift;
try { } catch { };
die "rethrowing $err";
}
} qr/rethrowing foo/, "rethrow with try in catch block";
sub Evil::DESTROY {
eval { "oh noes" };
}
sub Evil::new { bless { }, $_[0] }
{
local $@ = "magic";
t/erroneous_usage.t view on Meta::CPAN
use Test::More tests => 8;
use Try::Tiny;
sub _eval {
local $@;
local $Test::Builder::Level = $Test::Builder::Level + 2;
return ( scalar(eval { $_[0]->(); 1 }), $@ );
}
sub throws_ok (&$$) {
my ( $code, $regex, $desc ) = @_;
local $Test::Builder::Level = $Test::Builder::Level + 1;
my ( $ok, $error ) = _eval($code);
if ( $ok ) {
fail($desc);
} else {
like($error || '', $regex, $desc );
}
}
throws_ok {
try { 1 }; catch { 2 };
} qr/\QUseless bare catch()/, 'Bare catch() detected';
throws_ok {
try { 1 }; finally { 2 };
} qr/\QUseless bare finally()/, 'Bare finally() detected';
throws_ok {
try { 1 }; catch { 2 } finally { 2 };
} qr/\QUseless bare catch()/, 'Bare catch()/finally() detected';
throws_ok {
try { 1 }; finally { 2 } catch { 2 };
} qr/\QUseless bare finally()/, 'Bare finally()/catch() detected';
throws_ok {
try { 1 } catch { 2 } catch { 3 } finally { 4 } finally { 5 }
} qr/\QA try() may not be followed by multiple catch() blocks/, 'Multi-catch detected';
throws_ok {
try { 1 } catch { 2 }
do { 2 }
} qr/\Qtry() encountered an unexpected argument (2) - perhaps a missing semi-colon before or at/,
'Unterminated try detected';
sub foo {
try { 0 }; catch { 2 }
}
throws_ok {
if (foo()) {
# ...
}
} qr/\QUseless bare catch/,
'Bare catch at the end of a function call';
sub bar {
try { 0 }; finally { 2 }
}
throws_ok {
if (bar()) {
# ...
}
} qr/\QUseless bare finally/,
'Bare finally at the end of a function call';
t/finally.t view on Meta::CPAN
use strict;
use warnings;
use Test::More tests => 30;
use Try::Tiny;
try {
my $a = 1+1;
} catch {
fail('Cannot go into catch block because we did not throw an exception')
} finally {
pass('Moved into finally from try');
};
try {
die('Die');
} catch {
ok($_ =~ /Die/, 'Error text as expected');
pass('Into catch block as we died in try');
} finally {
t/finally.t view on Meta::CPAN
die('Die');
} finally {
pass('Moved into finally from catch');
} catch {
ok($_ =~ /Die/, 'Error text as expected');
};
try {
die('Die');
} finally {
pass('Moved into finally block when try throws an exception and we have no catch block');
};
try {
die('Die');
} finally {
pass('First finally clause run');
} finally {
pass('Second finally clause run');
};
( run in 0.438 second using v1.01-cache-2.11-cpan-496ff517765 )