From: Karen Etheridge Date: Fri, 11 Dec 2015 04:05:22 +0000 (-0800) Subject: Try-Tiny-0.23 X-Git-Tag: v0.23^0 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=cb57845a7aaed3a94a4d0bc74b46626756111659;p=p5sagit%2FTry-Tiny.git Try-Tiny-0.23 - fix syntax of example code (Rudolf Leermakers, PR#22) - 'perl' removed from prerequisite recommendations, to avoid tripping up CPAN clients (Graham Knop) - Sub::Util is used preferentially to Sub::Name in most cases (Graham Knop, PR#27) --- diff --git a/CONTRIBUTING b/CONTRIBUTING new file mode 100644 index 0000000..a4cf967 --- /dev/null +++ b/CONTRIBUTING @@ -0,0 +1,96 @@ + +CONTRIBUTING + +Thank you for considering contributing to this distribution. This file +contains instructions that will help you work with the source code. + +PLEASE NOTE that if you have any questions or difficulties, you can reach the +maintainer(s) through the bug queue described later in this document +(preferred), or by emailing the releaser directly. You are not required to +follow any of the steps in this document to submit a patch or bug report; +these are just recommendations, intended to help you (and help us help you +faster). + +The distribution is managed with Dist::Zilla (https://metacpan.org/release/Dist-Zilla). +This means than many of the usual files you might expect are not in the +repository, but are generated at release time (e.g. Makefile.PL). + +However, you can run tests directly using the 'prove' tool: + + $ prove -l + $ prove -lv t/some_test_file.t + $ prove -lvr t/ + +In most cases, 'prove' is entirely sufficent for you to test any +patches you have. + +You may need to satisfy some dependencies. The easiest way to satisfy +dependencies is to install the last release -- this is available at +https://metacpan.org/release/Try-Tiny + +If you use cpanminus, you can do it without downloading the tarball first: + + $ cpanm --reinstall --installdeps --with-recommends Try::Tiny + +Dist::Zilla is a very powerful authoring tool, but requires a number of +author-specific plugins. If you would like to use it for contributing, +install it from CPAN, then run one of the following commands, depending on +your CPAN client: + + $ cpan `dzil authordeps --missing` +or + $ dzil authordeps --missing | cpanm + +You should then also install any additional requirements not needed by the +dzil build but may be needed by tests or other development: + + $ cpan `dzil listdeps --author --missing` +or + $ dzil listdeps --author --missing | cpanm + +Or, you can use the 'dzil stale' command to install all requirements at once: + + $ cpan Dist::Zilla::App::Command::stale + $ cpan `dzil stale --all` +or + $ cpanm Dist::Zilla::App::Command::stale + $ dzil stale --all | cpanm + +You can also do this via cpanm directly: + + $ cpanm --reinstall --installdeps --with-develop --with-recommends Try::Tiny + +Once installed, here are some dzil commands you might try: + + $ dzil build + $ dzil test + $ dzil test --release + $ dzil xtest + $ dzil listdeps --json + $ dzil build --notgz + +You can learn more about Dist::Zilla at http://dzil.org/. + +The code for this distribution is hosted at GitHub. The repository is: +https://github.com/karenetheridge/Try-Tiny +You can submit code changes by forking the repository, pushing your code +changes to your clone, and then submitting a pull request. Detailed +instructions for doing that is available here: + +https://help.github.com/articles/creating-a-pull-request + +If you have found a bug, but do not have an accompanying patch to fix it, you +can submit an issue report here: +https://rt.cpan.org/Public/Dist/Display.html?Name=Try-Tiny +or via email: bug-Try-Tiny@rt.cpan.org +This is a good place to send your questions about the usage of this distribution. + +If you send me a patch or pull request, your name and email address will be +included in the documentation as a contributor (using the attribution on the +commit or patch), unless you specifically request for it not to be. If you +wish to be listed under a different name or address, you should submit a pull +request to the .mailmap file to contain the correct mapping. + + +This file was generated via Dist::Zilla::Plugin::GenerateFile::FromShareDir 0.009 from a +template file originating in Dist-Zilla-PluginBundle-Author-ETHER-0.109. diff --git a/Changes b/Changes index 3be4a07..711e69e 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,6 @@ Revision history for Try-Tiny -{{$NEXT}} +0.23 2015-12-11 04:04:35Z - fix syntax of example code (Rudolf Leermakers, PR#22) - 'perl' removed from prerequisite recommendations, to avoid tripping up CPAN clients (Graham Knop) diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..f171741 --- /dev/null +++ b/INSTALL @@ -0,0 +1,43 @@ +This is the Perl distribution Try-Tiny. + +Installing Try-Tiny is straightforward. + +## Installation with cpanm + +If you have cpanm, you only need one line: + + % cpanm Try::Tiny + +If you are installing into a system-wide directory, you may need to pass the +"-S" flag to cpanm, which uses sudo to install the module: + + % cpanm -S Try::Tiny + +## Installing with the CPAN shell + +Alternatively, if your CPAN shell is set up, you should just be able to do: + + % cpan Try::Tiny + +## Manual installation + +As a last resort, you can manually install it. Download the tarball, untar it, +then build it: + + % perl Makefile.PL + % make && make test + +Then install it: + + % make install + +If you are installing into a system-wide directory, you may need to run: + + % sudo make install + +## Documentation + +Try-Tiny documentation is available as POD. +You can run perldoc from a shell to read the documentation: + + % perldoc Try::Tiny diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..fa48736 --- /dev/null +++ b/LICENCE @@ -0,0 +1,32 @@ +This software is Copyright (c) 2009 by יובל קוג'מן (Yuval Kogman). + +This is free software, licensed under: + + The MIT (X11) License + +The MIT License + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to +whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall +be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT +WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.pod b/README.pod new file mode 100644 index 0000000..5f79a47 --- /dev/null +++ b/README.pod @@ -0,0 +1,600 @@ +=pod + +=encoding UTF-8 + +=head1 NAME + +Try::Tiny - minimal try/catch with proper preservation of $@ + +=head1 VERSION + +version 0.23 + +=head1 SYNOPSIS + +You can use Try::Tiny's C and C to expect and handle exceptional +conditions, avoiding quirks in Perl and common mistakes: + + # handle errors with a catch handler + try { + die "foo"; + } catch { + warn "caught error: $_"; # not $@ + }; + +You can also use it like a standalone C to catch and ignore any error +conditions. Obviously, this is an extreme measure not to be undertaken +lightly: + + # just silence errors + try { + die "foo"; + }; + +=head1 DESCRIPTION + +This module provides bare bones C/C/C statements that are designed to +minimize common mistakes with eval blocks, and NOTHING else. + +This is unlike L which provides a nice syntax and avoids adding +another call stack layer, and supports calling C from the C block to +return from the parent subroutine. These extra features come at a cost of a few +dependencies, namely L and L which are +occasionally problematic, and the additional catch filtering uses L +type constraints which may not be desirable either. + +The main focus of this module is to provide simple and reliable error handling +for those having a hard time installing L, but who still want to +write correct C blocks without 5 lines of boilerplate each time. + +It's designed to work as correctly as possible in light of the various +pathological edge cases (see L) and to be compatible with any style +of error values (simple strings, references, objects, overloaded objects, etc). + +If the C block dies, it returns the value of the last statement executed in +the C block, if there is one. Otherwise, it returns C in scalar +context or the empty list in list context. The following examples all +assign C<"bar"> to C<$x>: + + my $x = try { die "foo" } catch { "bar" }; + my $x = try { die "foo" } || "bar"; + my $x = (try { die "foo" }) // "bar"; + + my $x = eval { die "foo" } || "bar"; + +You can add C blocks, yielding the following: + + my $x; + try { die 'foo' } finally { $x = 'bar' }; + try { die 'foo' } catch { warn "Got a die: $_" } finally { $x = 'bar' }; + +C blocks are always executed making them suitable for cleanup code +which cannot be handled using local. You can add as many C blocks to a +given C block as you like. + +Note that adding a C block without a preceding C block +suppresses any errors. This behaviour is consistent with using a standalone +C, but it is not consistent with C/C patterns found in +other programming languages, such as Java, Python, Javascript or C#. If you +learnt the C/C pattern from one of these languages, watch out for +this. + +=head1 EXPORTS + +All functions are exported by default using L. + +If you need to rename the C, C or C keyword consider using +L to get L's flexibility. + +=over 4 + +=item try (&;@) + +Takes one mandatory C subroutine, an optional C subroutine and C +subroutine. + +The mandatory subroutine is evaluated in the context of an C block. + +If no error occurred the value from the first block is returned, preserving +list/scalar context. + +If there was an error and the second subroutine was given it will be invoked +with the error in C<$_> (localized) and as that block's first and only +argument. + +C<$@> does B contain the error. Inside the C block it has the same +value it had before the C block was executed. + +Note that the error may be false, but if that happens the C block will +still be invoked. + +Once all execution is finished then the C block, if given, will execute. + +=item catch (&;@) + +Intended to be used in the second argument position of C. + +Returns a reference to the subroutine it was given but blessed as +C which allows try to decode correctly what to do +with this code reference. + + catch { ... } + +Inside the C 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, 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), you'll need to do: + + local $@ = $_; + +=item finally (&;@) + + try { ... } + catch { ... } + finally { ... }; + +Or + + try { ... } + finally { ... }; + +Or even + + try { ... } + finally { ... } + catch { ... }; + +Intended to be the second or third element of C. C blocks are always +executed in the event of a successful C or if C is run. This allows +you to locate cleanup code which cannot be done via C e.g. closing a file +handle. + +When invoked, the C block is passed the error that was caught. If no +error was caught, it is passed nothing. (Note that the C block does not +localize C<$_> with the error, since unlike in a C block, there is no way +to know if C<$_ == undef> implies that there were no errors.) In other words, +the following code does just what you would expect: + + try { + die_sometimes(); + } catch { + # ...code run in case of error + } finally { + if (@_) { + print "The try block died with: @_\n"; + } else { + print "The try block ran without error.\n"; + } + }; + +B block>. C will +not do anything about handling possible errors coming from code located in these +blocks. + +Furthermore B blocks are not trappable and are unable +to influence the execution of your program>. This is due to limitation of +C-based scope guards, which C is implemented on top of. This +may change in a future version of Try::Tiny. + +In the same way C blesses the code reference this subroutine does the same +except it bless them as C. + +=back + +=head1 BACKGROUND + +There are a number of issues with C. + +=head2 Clobbering $@ + +When you run an C block and it succeeds, C<$@> will be cleared, potentially +clobbering an error that is currently being caught. + +This causes action at a distance, clearing previous errors your caller may have +not yet handled. + +C<$@> must be properly localized before invoking C in order to avoid this +issue. + +More specifically, C<$@> is clobbered at the beginning of the C, which +also makes it impossible to capture the previous error before you die (for +instance when making exception objects with error stacks). + +For this reason C will actually set C<$@> to its previous value (the one +available before entering the C block) in the beginning of the C +block. + +=head2 Localizing $@ silently masks errors + +Inside an C block, C behaves sort of like: + + sub die { + $@ = $_[0]; + return_undef_from_eval(); + } + +This means that if you were polite and localized C<$@> you can't die in that +scope, or your error will be discarded (printing "Something's wrong" instead). + +The workaround is very ugly: + + my $error = do { + local $@; + eval { ... }; + $@; + }; + + ... + die $error; + +=head2 $@ might not be a true value + +This code is wrong: + + if ( $@ ) { + ... + } + +because due to the previous caveats it may have been unset. + +C<$@> could also be an overloaded error object that evaluates to false, but +that's asking for trouble anyway. + +The classic failure mode is: + + sub Object::DESTROY { + eval { ... } + } + + eval { + my $obj = Object->new; + + die "foo"; + }; + + if ( $@ ) { + + } + +In this case since C is not localizing C<$@> but still uses +C, it will set C<$@> to C<"">. + +The destructor is called when the stack is unwound, after C sets C<$@> to +C<"foo at Foo.pm line 42\n">, so by the time C is evaluated it has +been cleared by C in the destructor. + +The workaround for this is even uglier than the previous ones. Even though we +can't save the value of C<$@> from code that doesn't localize, we can at least +be sure the C was aborted due to an error: + + my $failed = not eval { + ... + + return 1; + }; + +This is because an C that caught a C will always return a false +value. + +=head1 SHINY SYNTAX + +Using Perl 5.10 you can use L. + +=for stopwords topicalizer + +The C block is invoked in a topicalizer context (like a C block), +but note that you can't return a useful value from C using the C +blocks without an explicit C. + +This is somewhat similar to Perl 6's C blocks. You can use it to +concisely match errors: + + try { + require Foo; + } catch { + when (/^Can't locate .*?\.pm in \@INC/) { } # ignore + default { die $_ } + }; + +=head1 CAVEATS + +=over 4 + +=item * + +C<@_> is not available within the C block, so you need to copy your +argument list. In case you want to work with argument values directly via C<@_> +aliasing (i.e. allow C<$_[1] = "foo">), you need to pass C<@_> by reference: + + sub foo { + my ( $self, @args ) = @_; + try { $self->bar(@args) } + } + +or + + sub bar_in_place { + my $self = shift; + my $args = \@_; + try { $_ = $self->bar($_) for @$args } + } + +=item * + +C returns from the C block, not from the parent sub (note that +this is also how C works, but not how L 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; + + say "This text WILL NEVER appear!"; + } + # OR + sub parent_sub_with_catch { + my $success = try { + die; + 1; + } + catch { + # do something with $_ + return undef; #see note + }; + return unless $success; + + say "This text WILL NEVER appear!"; + } + +Note that if you have a C block, it must return C for this to work, +since if a C block exists, its return value is returned in place of C +when an exception is thrown. + +=item * + +C introduces another caller stack frame. L is not used. L +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 block is not guaranteed to be the value of +the exception thrown (C<$@>) in the C block. There is no safe way to +ensure this, since C may be used unhygienically in destructors. The only +guarantee is that the C will be called if an exception is thrown. + +=item * + +The return value of the C 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 block: + + my $obj = try { + MightFail->new; + } catch { + ... + + return; # avoid returning a true value; + }; + + 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 blocks, since it isn't people have grown to rely on it. Therefore in +the interests of compatibility, C does not disable C<$SIG{__DIE__}> for +the scope of the error throwing code. + +=item * + +Lexical C<$_> may override the one set by C. + +For example Perl 5.10's C form uses a lexical C<$_>, creating some +confusing behavior: + + given ($foo) { + when (...) { + try { + ... + } catch { + warn $_; # will print $foo, not the error + warn $_[0]; # instead, get the error like this + } + } + } + +Note that this behavior was changed once again in L. +However, since the entirety of lexical C<$_> is now L, it +is unclear whether the new version 18 behavior is final. + +=back + +=head1 SEE ALSO + +=over 4 + +=item L + +Much more feature complete, more convenient semantics, but at the cost of +implementation complexity. + +=item L + +Automatic error throwing for builtin functions and more. Also designed to +work well with C/C. + +=item L + +A lightweight role for rolling your own exception classes. + +=item L + +Exception object implementation with a C statement. Does not localize +C<$@>. + +=item L + +Provides a C statement, but properly calling C is your +responsibility. + +The C keyword pushes C<$@> onto an error stack, avoiding some of the +issues with C<$@>, but you still need to localize to prevent clobbering. + +=back + +=head1 LIGHTNING TALK + +I gave a lightning talk about this module, you can see the slides (Firefox +only): + +L + +Or read the source: + +L + +=head1 VERSION CONTROL + +L + +=head1 SUPPORT + +Bugs may be submitted through L +(or L). + +=head1 AUTHORS + +=over 4 + +=item * + +יובל קוג'מן (Yuval Kogman) + +=item * + +Jesse Luehrs + +=back + +=head1 CONTRIBUTORS + +=for stopwords Peter Rabbitson Karen Etheridge Ricardo Signes Mark Fowler Graham Knop Dagfinn Ilmari Mannsåker Paul Howarth Rudolf Leermakers anaxagoras awalker chromatic Alex cm-perl Andrew Yates David Lowe Glenn Hans Dieter Pearcey Jonathan Yu Marc Mims Stosberg + +=over 4 + +=item * + +Peter Rabbitson + +=item * + +Karen Etheridge + +=item * + +Ricardo Signes + +=item * + +Mark Fowler + +=item * + +Graham Knop + +=item * + +Dagfinn Ilmari Mannsåker + +=item * + +Paul Howarth + +=item * + +Rudolf Leermakers + +=item * + +anaxagoras + +=item * + +awalker + +=item * + +chromatic + +=item * + +Alex + +=item * + +cm-perl + +=item * + +Andrew Yates + +=item * + +David Lowe + +=item * + +Glenn Fowler + +=item * + +Hans Dieter Pearcey + +=item * + +Jonathan Yu + +=item * + +Marc Mims + +=item * + +Mark Stosberg + +=back + +=head1 COPYRIGHT AND LICENCE + +This software is Copyright (c) 2009 by יובל קוג'מן (Yuval Kogman). + +This is free software, licensed under: + + The MIT (X11) License + +=cut