Skip Menu |

This queue is for tickets about the Try-Tiny CPAN distribution.

Report information
The Basics
Id: 81070
Status: resolved
Priority: 0/
Queue: Try-Tiny

People
Owner: Nobody in particular
Requestors: allter [...] gmail.com
Cc: ribasushi [...] leporine.io
AdminCc:

Bug Information
Severity: Wishlist
Broken in: (no value)
Fixed in: (no value)



Subject: Accidental semicolon (;) in the middle of try/catch/finally statement is silently ignored
I've discovered the nice Try::Tiny as a means of fighting the loss of time when debugging issues concerning scope and values of $@. But as soon as I started using it I discovered that if you accidently add a semicolon in the middle of different try/catch/finally clauses, Perl silently ignores subsequent catch/finally. I.e. exception is not caught and it is not processed. :-o I suggest to add some warning (probably, controlled via "use warnings" and implemented using "warnings::warnif(..)") to help to diagnose this case. In a patch that I attach with this issue, I've implemented this using DESTROY methods in Try::Tiny::Catch and Try::Tiny::Finally packages. When you call "catch(..);" or "finally(..);" not as an argument to "try(..)", the object returned from them destroys before the next statement and a warning is written. But when you chain them to "try {} ..", then when "sub try" code processes arguments they are reblessed to Try::Tiny::CatchProcessed and Try::Tiny::FinallyProcessed and the DESTROY destructor is not called after the entire statement. Feel free to discuss this issue (do we need to use warnif (and thus call warnings::register() on a module level) to allow FATAL warnings? warning message format? etc.). :) Andrey
Subject: try_tiny.patch
--- a/Try/Tiny.pm +++ b/Try/Tiny.pm @@ -40,8 +40,10 @@ sub try (&;@) { if ( $ref eq 'Try::Tiny::Catch' ) { $catch = ${$code_ref}; + bless $code_ref, 'Try::Tiny::CatchProcessed'; # To not trigger a warning/error from no-standalone catch } elsif ( $ref eq 'Try::Tiny::Finally' ) { push @finally, ${$code_ref}; + bless $code_ref, 'Try::Tiny::FinallyProcessed'; # To not trigger a warning/error from no-standalone finally } else { use Carp; confess("Unknown code ref type given '${ref}'. Check your usage & try again"); @@ -120,6 +122,16 @@ sub catch (&;@) { ); } +{ + package # hide from PAUSE + Try::Tiny::Catch; + sub DESTROY + { + my @caller = caller(); + warn "catch without try, check semicolons in try/catch/finally statement BEFORE the line at ".$caller[1]." line ".$caller[2]."\n"; + } +} + sub finally (&;@) { my ( $block, @rest ) = @_; @@ -130,6 +142,16 @@ sub finally (&;@) { } { + package # hide from PAUSE + Try::Tiny::Finally; + sub DESTROY + { + my @caller = caller(); + warn "finally without try, check semicolons in try/catch/finally statement BEFORE the line at ".$caller[1]." line ".$caller[2]."\n"; + } +} + +{ package # hide from PAUSE Try::Tiny::ScopeGuard;
On Fri Nov 09 05:59:19 2012, allter wrote: Show quoted text
> I've discovered the nice Try::Tiny as a means of fighting the loss of > time when debugging issues concerning scope and values of $@. > > But as soon as I started using it I discovered that if you accidently > add a semicolon in the middle of different try/catch/finally clauses, > Perl silently ignores subsequent catch/finally. I.e. exception is not > caught and it is not processed. :-o > > I suggest to add some warning (probably, controlled via "use warnings" > and implemented using "warnings::warnif(..)") to help to diagnose this case. > > In a patch that I attach with this issue, I've implemented this using > DESTROY methods in Try::Tiny::Catch and Try::Tiny::Finally packages. > When you call "catch(..);" or "finally(..);" not as an argument to > "try(..)", the object returned from them destroys before the next > statement and a warning is written. But when you chain them to "try {} > ..", then when "sub try" code processes arguments they are reblessed to > Try::Tiny::CatchProcessed and Try::Tiny::FinallyProcessed and the > DESTROY destructor is not called after the entire statement. > > Feel free to discuss this issue (do we need to use warnif (and thus call > warnings::register() on a module level) to allow FATAL warnings? warning > message format? etc.). :)
This approach seems overly complex. What do you think about this simple and straightforward solution: https://github.com/ribasushi/try-tiny/commit/9d0e0466 Cheers Show quoted text
> Andrey
Fixed in 0.13.
From: allter [...] gmail.com
1. Not barking if wantarray is defined means no barking on catch {}/ finally{} at the end of sub {} body. This is frequently the case at the stage when try/catch block is introduced in the project. 2. Dying when user most probably wanted a perl to not to die is not DWIM. I think this should be a warning by default, probably controllable via use warnings qw(FATAL). (Ideally this check should be at compile-time, but this needs changes to Perl at this moment) Чтв Июл 04 05:01:15 2013, RIBASUSHI писал: Show quoted text
> On Fri Nov 09 05:59:19 2012, allter wrote:
> > I've discovered the nice Try::Tiny as a means of fighting the loss
> of
> > time when debugging issues concerning scope and values of $@. > > > > But as soon as I started using it I discovered that if you
> accidently
> > add a semicolon in the middle of different try/catch/finally
> clauses,
> > Perl silently ignores subsequent catch/finally. I.e. exception is
> not
> > caught and it is not processed. :-o > > > > I suggest to add some warning (probably, controlled via "use
> warnings"
> > and implemented using "warnings::warnif(..)") to help to diagnose
> this case.
> > > > In a patch that I attach with this issue, I've implemented this
> using
> > DESTROY methods in Try::Tiny::Catch and Try::Tiny::Finally packages. > > When you call "catch(..);" or "finally(..);" not as an argument to > > "try(..)", the object returned from them destroys before the next > > statement and a warning is written. But when you chain them to "try
> {}
> > ..", then when "sub try" code processes arguments they are reblessed
> to
> > Try::Tiny::CatchProcessed and Try::Tiny::FinallyProcessed and the > > DESTROY destructor is not called after the entire statement. > > > > Feel free to discuss this issue (do we need to use warnif (and thus
> call
> > warnings::register() on a module level) to allow FATAL warnings?
> warning
> > message format? etc.). :)
> > This approach seems overly complex. What do you think about this > simple and straightforward solution: https://github.com/ribasushi/try- > tiny/commit/9d0e0466 > > Cheers >
> > Andrey
>
From: allter [...] gmail.com
Thank you for bringing initiative back to the module! But could such changes be delayed for a week (feature-freeze) so that patches could be discussed before the CPAN release? Many modules update their dependencies from CPAN and it is not fun to see your script dies (see a critic above) when it runs unattended just because your sysadmin updated a secondary perl module which also uses backwards-incompatible modules. That's the reason I keep versions of my used modules with my projects, not using system-wide setup. This actually slows the development of utility modules because tech leads like me stop on specific versions that they are ok with. And then they don't contribute. :( Чтв Июл 04 12:26:27 2013, DOY писал: Show quoted text
> Fixed in 0.13.
On Thu Jul 04 14:56:06 2013, allter wrote: Show quoted text
> 2. Dying when user most probably wanted a perl to not to die is not > DWIM. I think this should be a warning by default, probably > controllable via use warnings qw(FATAL). (Ideally this check should > be at compile-time, but this needs changes to Perl at this moment)
My 2c on this issue: I do not think dying is negotiable here. The whole point is that your code stops working the way you intended because of a syntax error (stray q{;}). If there was an easy way to make it a compile-time error - we absolutely would. However by making it not die, but merely warn, and allowing for silencing of the warning (via the utterly broken warnings::register) - well... might as well not change anything then ;)
Subject: Re: [rt.cpan.org #81070] Accidental semicolon (;) in the middle of try/catch/finally statement is silently ignored
Date: Fri, 5 Jul 2013 09:24:08 -0400
To: Peter Rabbitson via RT <bug-Try-Tiny [...] rt.cpan.org>
From: Jesse Luehrs <doy [...] tozt.net>
On Fri, Jul 05, 2013 at 01:59:27AM -0400, Peter Rabbitson via RT wrote: Show quoted text
> Queue: Try-Tiny > Ticket <URL: https://rt.cpan.org/Ticket/Display.html?id=81070 > > > On Thu Jul 04 14:56:06 2013, allter wrote:
> > 2. Dying when user most probably wanted a perl to not to die is not > > DWIM. I think this should be a warning by default, probably > > controllable via use warnings qw(FATAL). (Ideally this check should > > be at compile-time, but this needs changes to Perl at this moment)
> > My 2c on this issue: > > I do not think dying is negotiable here. The whole point is that your > code stops working the way you intended because of a syntax error > (stray q{;}). If there was an easy way to make it a compile-time error > - we absolutely would. > > However by making it not die, but merely warn, and allowing for > silencing of the warning (via the utterly broken warnings::register) - > well... might as well not change anything then ;)
I agree. If something is always going to be incorrect, it should die, not warn. There is never a case where calling `catch` or `finally` in void context is anything other than a bug, so making this warn is not particularly useful. As for the issue with try blocks at the end of a function, they will inherit the function's context, and if that function is called in non-void context, you'll probably notice when the value you get doesn't make any sense. If the function is called in void context, the catch block will also be called in void context, so the error will still work. The only case where that isn't true is for predicate functions, where you just do something like "if (my_function()) { ... }", but we can solve this by making it an error to call `catch` or `finally` in scalar context too (since when they are used correctly with `try`, they will always be called in list context). Does that sound reasonable? -doy
CC: allter [...] gmail.com
Subject: Re: [rt.cpan.org #81070] Accidental semicolon (;) in the middle of try/catch/finally statement is silently ignored
Date: Fri, 5 Jul 2013 23:44:31 +1000
To: Jesse Luehrs via RT <bug-Try-Tiny [...] rt.cpan.org>
From: Peter Rabbitson <ribasushi [...] cpan.org>
On Fri, Jul 05, 2013 at 09:24:35AM -0400, Jesse Luehrs via RT wrote: Show quoted text
> solve this by making it an error to call `catch` or `finally` in scalar > context too (since when they are used correctly with `try`, they will > always be called in list context). Does that sound reasonable?
I am not sure why I didn't do so originally... +1
From: allter [...] gmail.com
Perl is not a "exception or correct" math problems solving tool. It's a practical scripting language. That's why perl doesn't die with a big noise immediately when open, print, or close fails. If you force a user to always catch any possible exception, perl programs will transform to java programs (with the difference in that Java finds more problems at design stage). Recently I've read a story in our local IT slashdot-clone about a 3D printing enthusiast who found his DIY printer with extruder and heating bed working in dangerous proximity to wooden table surface when he woke up in morning. They just melted down because a Windows machine rebooted because of buggy driver and the printed controller did not turn off the heating. Many perl scripts control misc. aspects of human life ("duct tape for the Internet"). Some code wrapped in "try" will not get executed for weeks (for example, if it is in other exception handling code). I think that it is safer for humans if perl scripts will forgive any single mistake of a programmer. 3D printer extruders should not cause fire and real human deaths just because a module designer thinks that syntactically-correct code should cause a process termination in this case. For those who want "fail early" approach there are many options like autodie or fatal warnings. You should not terminate user processes if they just want to not hassle with $@ scope issues. These processes will most likely fail later or spam with garbled data, but at least user wont find his script killed at the place he did not ask for it. Птн Июл 05 01:59:27 2013, RIBASUSHI писал: Show quoted text
> On Thu Jul 04 14:56:06 2013, allter wrote:
> > 2. Dying when user most probably wanted a perl to not to die is not > > DWIM. I think this should be a warning by default, probably > > controllable via use warnings qw(FATAL). (Ideally this check
> should
> > be at compile-time, but this needs changes to Perl at this
> moment) > > My 2c on this issue: > > I do not think dying is negotiable here. The whole point is that your > code stops working the way you intended because of a syntax error > (stray q{;}). If there was an easy way to make it a compile-time > error - we absolutely would. > > However by making it not die, but merely warn, and allowing for > silencing of the warning (via the utterly broken > warnings::register) - well... might as well not change anything > then ;) >
Subject: Re: [rt.cpan.org #81070] Accidental semicolon (;) in the middle of try/catch/finally statement is silently ignored
Date: Fri, 5 Jul 2013 11:31:11 -0400
To: "Andrey M. Smirnov via RT" <bug-Try-Tiny [...] rt.cpan.org>
From: Jesse Luehrs <doy [...] tozt.net>
On Fri, Jul 05, 2013 at 11:15:48AM -0400, Andrey M. Smirnov via RT wrote: Show quoted text
> Queue: Try-Tiny > Ticket <URL: https://rt.cpan.org/Ticket/Display.html?id=81070 > > > Perl is not a "exception or correct" math problems solving tool. It's > a practical scripting language. That's why perl doesn't die with a big > noise immediately when open, print, or close fails. If you force a > user to always catch any possible exception, perl programs will > transform to java programs (with the difference in that Java finds > more problems at design stage). > > Recently I've read a story in our local IT slashdot-clone about a 3D > printing enthusiast who found his DIY printer with extruder and > heating bed working in dangerous proximity to wooden table surface > when he woke up in morning. They just melted down because a Windows > machine rebooted because of buggy driver and the printed controller > did not turn off the heating. > > Many perl scripts control misc. aspects of human life ("duct tape for > the Internet"). Some code wrapped in "try" will not get executed for > weeks (for example, if it is in other exception handling code). I > think that it is safer for humans if perl scripts will forgive any > single mistake of a programmer. 3D printer extruders should not cause > fire and real human deaths just because a module designer thinks that > syntactically-correct code should cause a process termination in this > case. > > For those who want "fail early" approach there are many options like > autodie or fatal warnings. You should not terminate user processes if > they just want to not hassle with $@ scope issues. These processes > will most likely fail later or spam with garbled data, but at least > user wont find his script killed at the place he did not ask for it.
Sorry, but I'm not convinced. Unexpected incorrect behavior is at least as dangerous as unexpected termination, and in many cases, far more so. I'm not likely to change my mind about this. -doy
From: allter [...] gmail.com
Птн Июл 05 11:31:29 2013, doy@tozt.net писал: Show quoted text
> On Fri, Jul 05, 2013 at 11:15:48AM -0400, Andrey M. Smirnov via RT wrote:
> > Queue: Try-Tiny > > Ticket <URL: https://rt.cpan.org/Ticket/Display.html?id=81070 > > > > > Perl is not a "exception or correct" math problems solving tool. It's > > a practical scripting language. That's why perl doesn't die with a big > > noise immediately when open, print, or close fails. If you force a > > user to always catch any possible exception, perl programs will > > transform to java programs (with the difference in that Java finds > > more problems at design stage). > > > > Recently I've read a story in our local IT slashdot-clone about a 3D > > printing enthusiast who found his DIY printer with extruder and > > heating bed working in dangerous proximity to wooden table surface > > when he woke up in morning. They just melted down because a Windows > > machine rebooted because of buggy driver and the printed controller > > did not turn off the heating. > > > > Many perl scripts control misc. aspects of human life ("duct tape for > > the Internet"). Some code wrapped in "try" will not get executed for > > weeks (for example, if it is in other exception handling code). I > > think that it is safer for humans if perl scripts will forgive any > > single mistake of a programmer. 3D printer extruders should not cause > > fire and real human deaths just because a module designer thinks that > > syntactically-correct code should cause a process termination in this > > case. > > > > For those who want "fail early" approach there are many options like > > autodie or fatal warnings. You should not terminate user processes if > > they just want to not hassle with $@ scope issues. These processes > > will most likely fail later or spam with garbled data, but at least > > user wont find his script killed at the place he did not ask for it.
> > Sorry, but I'm not convinced. Unexpected incorrect behavior is at least > as dangerous as unexpected termination, and in many cases, far more so. > I'm not likely to change my mind about this.
It's OK (at least doc mentions autodie in SEE ALSO so it's in line with "fail early" motto). But then you divert your module from a path of minimalism. When you implement several issues of such kind, then your nice module will became the TryCatch. :)
On 2013-7月-05 金 11:15:47, allter wrote: Show quoted text
> Perl is not a "exception or correct" math problems solving tool. It's > a practical scripting language. That's why perl doesn't die with a > big noise immediately when open, print, or close fails.
On the other hand, there are plenty of examples where Perl will die at runtime (instead of warn or silently do nothing) when it encounters a problematic situation, like: perl -e '@{ \"foo" }' # Runtime error: Not an ARRAY reference perl -e 'require "Doesnt::Exist"' # Runtime error: Can't locate Doesnt::Exist in @INC perl -e '1 / @arr' # Runtime error: Illegal division by zero The existence of exceptions certainly does not imply a slippery slope to Java. No one has the expectation that a cleanly-compiling Perl program will never terminate abruptly. On 2013-7月-05 金 12:26:04, allter wrote: Show quoted text
> It's OK (at least doc mentions autodie in SEE ALSO so it's in line > with "fail early" motto). But then you divert your module from a > path of minimalism. When you implement several issues of such kind, > then your nice module will became the TryCatch. :)
You're the one who created this ticket and sent in the initial patch! Why are you now spreading FUD about this error checking? Try::Tiny has always favored correctness over whipuptitude; that's why it was created in the first place. Throwing exceptions because of programmer error is absolutely in line with Try::Tiny's original and present goals. Unexpected behavior is exactly what is wrong with eval, and that's exactly where we'd return to if we demoted this exception to a warning. Shawn
From: allter [...] gmail.com
Птн Июл 05 15:42:35 2013, SARTAK писал: Show quoted text
> On the other hand, there are plenty of examples where Perl will die at > runtime (instead of warn or silently do nothing) when it encounters
When you write certain operator you always think that its operand might be out of the domain of acceptable values, or be of unacceptable type. Operator also may want certain global conditions prior to operate. It's obvious that operator that loads perl source file dynamically must check if it exists, is readable and is a correct perl program. Imagine a perl without auto-vivification of data structures. Any non-trivial data access will be a boilerplate of code. BTW, I want the option to turn this auto-vivification off, at least for rvalues - but I understand that such an option will result in both a compile-time and a runtime performance penalty - that's why I want it as an option and not as a Perl "bugfix" or globally-acting magic hint module. Show quoted text
> On 2013-7月-05 金 12:26:04, allter wrote: > You're the one who created this ticket and sent in the initial patch! > Why are you now spreading FUD about this error checking?
1. I really think that issuing a warning is sufficient in this case. If your catch/finally block does non-trivial things, your script will most likely fail later. And when the script fails, you just read all the STDERR vomit and trace back to where bad things just started to happen. That's what all warnings for (like 'undefined value in eq/=') - and that's why everyone suggests to 'use warnings' in your script. Warnings are a way of tracing bad things without requiring extra verbosity in your code (open/autovificiation) or extra analysis at the compile-time (that's our case). 2. Backwards-compatibility. For the duration of 12 prior versions people were ok if they wrote a lone 'catch'. Issuing a warning is just a backwards-compatible way to show they were wrong. I think that modules should be backwards-compatible unless the reason of incompatible change is a fix of obvious and indisputable bug. The current issue is not an obvious bug. 3. Trust to hot-hot-fixed code. Where I work, we frequently employ hot-hot-fixes on a running system. I frequently think that it is the reason we chose Perl over Java or C++ or PHP as a main environment (or, maybe, it won in the process of natural selection as we use all of them but in different areas). The hot fixed code may be executed right after its deployment, or hours later, or days, or years later. Testing doesn't help because you cannot simulate all of the combinations of a real world properties that surround the script. I don't want that this code that is introduced in a hurry will be a reason why the fixed business logic fails. 4. Ability to recommend to novice programmers. I chose Try::Tiny because it added minumum of the complexity to do a single task: "work around $@ localization issue". When programmers come to my project, all I can rely is that they know CORE perl. Any non-standard framework that is added to project must be predictable and traceable with only CORE knowledge. I think that if scripts will fail because of bad knowledge of using a non-standard framework by novices then I'd better teach them about issues of $@ and will still continue to use "eval {}; if ( $@ ) { #..." idioma. 5. Most of 'catch' code in my projects are just pretty printing of exception conditions. They don't matter much to business logic. When they do, the code is triple-checked so the missing semicolon is impossible. I also think that Java and C++ idioma of rethrowing some exceptions by first catching it is not employed in Perl programs frequently. Show quoted text
> Try::Tiny has always favored correctness over whipuptitude; that's why > it was created in the first place. Throwing exceptions because of > programmer error is absolutely in line with Try::Tiny's original > and present goals. Unexpected behavior is exactly what is wrong > with eval, and that's exactly where we'd return to if we demoted > this exception to a warning.
I thought that Try::Tiny also wanted to achieve its goals simply, by using only standard Perl constructs and practices. Without adding too much non-intuitive syntax sugar and without using source filters. A lone 'catch' had a perfectly correct behavior according to a syntax of Perl and a minimum knowledge of how Try::Tiny works: do nothing. By making it to die when the programmer doesn't expect this, you started to drift to the line of Moose and other modern frameworks which heavily use syntax sugar in such a way that even if you know Perl you can't write a script without reading docs and examples while you write and then triple-checking the results (because some non-intuitive actions are hidden in syntax sugar implementation and in frameworks and combined together they introduce lots of complexity and problems).