* Exception handling and "trap" vs. TRAPNAL() @ 2005-10-01 8:01 DervishD 0 siblings, 0 replies; 25+ messages in thread From: DervishD @ 2005-10-01 8:01 UTC (permalink / raw) To: Zsh Workers Hi all :) I've tried twice to forward this message from zsh-users to zsh-workers, with no success. Has this list any kind of limitation for forwarded material? Well, anyway, here is the message again. Let's say we have this script, using exception handling: --- cut here --- #!/bin/zsh emulate -L zsh [[ "$1" = "trap" ]] && trap 'throw DEFAULT' ZERR [[ "$1" = "TRAPZERR" ]] && function TRAPZERR() { throw DEFAULT ; } function throw() { typeset -g EXCEPTION="$1" readonly THROW=0 if (( TRY_BLOCK_ERROR == 0 )); then (( TRY_BLOCK_ERROR = 1 )) fi THROW= 2>/dev/null } function catch() { typeset -g CAUGHT if [[ $TRY_BLOCK_ERROR -gt 0 && $EXCEPTION = ${~1} ]]; then (( TRY_BLOCK_ERROR = 0 )) CAUGHT="$EXCEPTION" unset EXCEPTION return 0 fi return 1 } alias catch="noglob catch" { print "Before throwing" # This should throw "DEFAULT" exception false throw EXCEPTION # This shouldn't be shown print "After throwing" } always { catch * && print "Caught exception $CAUGHT" return 0 } --- cut here --- Sorry for the size, but I want to make sure it is as self-contained as possible. Well, I do the following: $ ./script trap Before throwing Caught exception EXCEPTION $ ./script TRAPZERR Before throwing Caught exception DEFAULT $ ./script Before throwing Caught exception EXCEPTION I'm puzzled. The "trap" trap is executed in the current environment, so I assume it would throw "DEFAULT", as intended, as soon as we hit the "false". It doesn't and I don't know why. OTOH, the TRAPZERR function, which runs "throw" in its own environment, works ok :??? WHY? AFAIK the "trap" builtin runs its arguments in the current environment, so to say it's like an alias, like cutting and pasting the code, but anyway that shouldn't be the problem, since "throw" is using globals and so it affects running in any environment. The only thing that could be happening is that the ZERR trap using "trap" is not setting "TRY_BLOCK_ERROR" to "1", or it sets the variable but the variable gets reset upon exiting the trap :??? Can anybody (and I really mean anybody, not Bart XD) explain this to me? I would like to use something similar and I need to use "trap" traps and not "TRAPNAL()" traps because I want to use LINENO and probably a couple of variables which are local to the functions where the ZERR trap can be raised. I'm not sure if this is an intended behaviour or a zsh bug, since there have been bugs related to traps when saving/restoring shell state in the past, and this may be one of those bugs. Thanks a lot in advance :) Raúl Núñez de Arenas Coronado -- Linux Registered User 88736 | http://www.dervishd.net http://www.pleyades.net & http://www.gotesdelluna.net It's my PC and I'll cry if I want to... ^ permalink raw reply [flat|nested] 25+ messages in thread
[parent not found: <20050929200741.GA1156@DervishD>]
[parent not found: <20050930124130.45eb0463.pws@csr.com>]
[parent not found: <20051001153756.GA12183@DervishD>]
* Re: Exception handling and "trap" vs. TRAPNAL() [not found] ` <20051001153756.GA12183@DervishD> @ 2005-10-01 18:38 ` Bart Schaefer 2005-10-01 19:10 ` Peter Stephenson 2005-10-01 20:28 ` DervishD 0 siblings, 2 replies; 25+ messages in thread From: Bart Schaefer @ 2005-10-01 18:38 UTC (permalink / raw) To: Peter Stephenson; +Cc: DervishD, zsh-workers On Oct 1, 5:37pm, DervishD wrote: } Subject: Re: Exception handling and "trap" vs. TRAPNAL() } } Hi Peter :) } * Peter Stephenson <pws@csr.com> dixit: } > Add some "print"s to the trap to see when it's triggering. } } The trap is triggering just in the "false" statement, and the } TRAPZERR works perfectly, it's the "trap" kind which is failing. I ran this with "set -x" (much better than inserting print statements) and it's clear that the "trap" command version of the trap is not tripping the "always" block: +./script:34> false +./script:34> throw DEFAULT +throw:34> typeset -g 'EXCEPTION=DEFAULT' +throw:34> readonly 'THROW=0' +throw:34> (( TRY_BLOCK_ERROR == 0 )) throw:34: read-only variable: THROW +throw:34> THROW='' +./script:35> throw EXCEPTION The problem is this snippet in signals.c: } else if (errflag) trapret = 1; execrestore(); lexrestore(); if (trapret > 0) { if (isfunc) { breaks = loops; errflag = 1; } else { lastval = trapret-1; } We start that section with errflag == 1 and set trapret = 1, but then at lexrestore(), errflag is reset to zero, and because isfunc is false, it's not set back to 1 again. After unwinding out of the trap handler, execution of the try block finds errflag == 0, and proceeds rather than skipping ahead to the always block. So the question is: Why ignore errors that occur in inline traps? } Can I do anything to make it work (except patching zsh)? I don't think so. } Is there any other way of throwing exceptions automagically when a } command returns a non-zero status No. ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-01 18:38 ` Bart Schaefer @ 2005-10-01 19:10 ` Peter Stephenson 2005-10-01 20:41 ` DervishD 2005-10-01 22:44 ` Bart Schaefer 2005-10-01 20:28 ` DervishD 1 sibling, 2 replies; 25+ messages in thread From: Peter Stephenson @ 2005-10-01 19:10 UTC (permalink / raw) To: zsh-workers [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #1: Type: text/plain, Size: 1908 bytes --] Bart Schaefer wrote: > So the question is: Why ignore errors that occur in inline traps? I'm glad you looked at this rather than me... Correct me if I'm wrong, but I don't think that's quite the question, which is in two parts: (1) why ignore errors in traps (the save/restore behaviour seen regardless of the trap style) (2) why flag an error only when returning a non-zero status from a function-style trap (the explicit setting of "errflag" when returning with non-zero status from such a trap). The answer to (1) is that traps are present as error handlers (of some sort and for some debatable value of "error") and to avoid confusing matters the error is taken from the code surrounding the trap, not the trap itself. Traps are supposed to operate in a sort of semi-permeable sandbox. (Yes, I know that's meaningless.) The answer to (2) is this, from the manual regarding function-style traps: The return status from the function is handled specially. If it is zero, the signal is assumed to have been handled, and execu‐ tion continues normally. Otherwise, the normal effect of the signal is produced; if this causes execution to terminate, the status returned to the shell is the status returned from the function. So the whole behaviour that DervishD is seeing is a side affect of the fact that this: TRAPZERR() { throw DEFAULT; } finishes by executing a command with non-zero status, the throw, causing the special behaviour described above. You can certainly argue that the "normal effect" of a ZERR trap is not to cause an error, and it's certainly possible to argue that yet more hacked-up special cases and additional bug-prone complexity in the trap code are warranted. However, I'm still not convinced this is going anywhere I want to go. -- Peter Stephenson <pws@pwstephenson.fsnet.co.uk> Work: pws@csr.com Web: http://www.pwstephenson.fsnet.co.uk ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-01 19:10 ` Peter Stephenson @ 2005-10-01 20:41 ` DervishD 2005-10-01 22:44 ` Bart Schaefer 1 sibling, 0 replies; 25+ messages in thread From: DervishD @ 2005-10-01 20:41 UTC (permalink / raw) To: Peter Stephenson; +Cc: zsh-workers Hi Peter :) * Peter Stephenson <pws@pwstephenson.fsnet.co.uk> dixit: > The answer to (2) is this, from the manual regarding function-style traps: > > The return status from the function is handled specially. If it > is zero, the signal is assumed to have been handled, and execuâ > tion continues normally. Otherwise, the normal effect of the > signal is produced; if this causes execution to terminate, the > status returned to the shell is the status returned from the > function. > > So the whole behaviour that DervishD is seeing is a side affect of the > fact that this: > > TRAPZERR() { throw DEFAULT; } > > finishes by executing a command with non-zero status, the throw, causing > the special behaviour described above. But I didn't see that as a side effect, since the trap is not returning. I assumed that the "throw" broke execution flow, and jumped to the start of the "always" block, that is, no return value from TRAPZERR was involved. I know, the code I was proposing is weird, and ZERR is intended to *handle* errors, not to throwing exceptions, but if inline traps won't cause an "always" block to be run, it should be stated in the manual (IMHO). Probably ZERR is a bad example, but as soon as I started to mess with exceptions and "always" blocks I thought about a trap for SIGINT to do cleanup, using an exception and some "always" blocks. Probably people is not using exception handling in shell script, but if the feature exits, it should work seamlessly with traps or at least document the problem. On the other hand, I don't see any problem in traps not causing exceptions to be thrown, but the behaviour should be consistent and both kinds of traps should behave the same. Otherwise you can write code using function traps and throwing exceptions from the trap, and as soon as you need the trap to be inline (for using $LINENO or any other similar variable) your code will no longer work :( Anyway, thanks a lot for explaining the problem and the decisions behind the behaviour. As I suspected and told to Bart, I wasn't sure this was a bug. Raúl Núñez de Arenas Coronado -- Linux Registered User 88736 | http://www.dervishd.net http://www.pleyades.net & http://www.gotesdelluna.net It's my PC and I'll cry if I want to... ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-01 19:10 ` Peter Stephenson 2005-10-01 20:41 ` DervishD @ 2005-10-01 22:44 ` Bart Schaefer 2005-10-02 8:06 ` DervishD 1 sibling, 1 reply; 25+ messages in thread From: Bart Schaefer @ 2005-10-01 22:44 UTC (permalink / raw) To: Peter Stephenson, zsh-workers On Oct 1, 8:10pm, Peter Stephenson wrote: } } Bart Schaefer wrote: } > So the question is: Why ignore errors that occur in inline traps? } } The answer to (1) is that traps are present as error handlers (of some } sort and for some debatable value of "error") and to avoid confusing } matters the error is taken from the code surrounding the trap, not the } trap itself. Yes, but the "error" of a non-zero command status is not the same as a shell-level error (syntax or what have you). } So the whole behaviour that DervishD is seeing is a side affect of the } fact that this: } } TRAPZERR() { throw DEFAULT; } } } finishes by executing a command with non-zero status, the throw It could be argued that, in the presence of an "always" block, the function should not be considered to have "finished" at all. The shell-level error is supposed to have stopped execution; the implict "return $?" never happened, the return status of the function is not defined, and we're in no-man's land. The trouble, of course, is that shell-level errors are not really exceptions, an "always" block is not really an exception handler, and nothing is actually "thrown" by "throw", so none of the assumptions are valid that Raúl is making based on the names "throw" and "catch". And the implementation simply can't stop execution and unwind to the "always" block the way a real exception system would. } You can certainly argue that the "normal effect" of a ZERR trap is not } to cause an error, and it's certainly possible to argue that yet more } hacked-up special cases and additional bug-prone complexity in the trap } code are warranted. Well, that's just it. An even stronger argument than the no-man's land proposition, is that errflag should *not* be propagated out of a ZERR trap. This would not accomplish what Raúl wants, because it would mean that *neither* TRAPZERR nor "trap ... ZERR" would be capable of "raising an exception" -- in effect we'd be removing a special-case, not adding one. } else if (trapreturn >= 0 && !isfunc) { /* * Context was an inline trap. If an explicit return value * was used, we need to set `lastval'. Otherwise we use the * value restored by execrestore. In this case, all return * values indicate an explicit return from the current function, * so always handle specially. trapreturn is always restored by * execrestore. */ trapret = trapreturn + 1; } else if (errflag) trapret = 1; I'm not sure, though, that there isn't some other kind of real signal that depends on that snippet. } However, I'm still not convinced this is going anywhere I want to go. The behavior should be consistent, is all. ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-01 22:44 ` Bart Schaefer @ 2005-10-02 8:06 ` DervishD 0 siblings, 0 replies; 25+ messages in thread From: DervishD @ 2005-10-02 8:06 UTC (permalink / raw) To: Bart Schaefer; +Cc: Peter Stephenson, zsh-workers Hi Bart :) * Bart Schaefer <schaefer@brasslantern.com> dixit: > } You can certainly argue that the "normal effect" of a ZERR trap is not > } to cause an error, and it's certainly possible to argue that yet more > } hacked-up special cases and additional bug-prone complexity in the trap > } code are warranted. > Well, that's just it. An even stronger argument than the no-man's > land proposition, is that errflag should *not* be propagated out of > a ZERR trap. This would not accomplish what Raúl wants, because it > would mean that *neither* TRAPZERR nor "trap ... ZERR" would be > capable of "raising an exception" -- in effect we'd be removing a > special-case, not adding one. But as I told in one of my last messages, that's ok too. My problem is not really about not being able to throw an exception from a trap, but having one kind of trap that works and another that doesn't with no special reason. I will be equally happy if this is shown in the documentation or the special-case removed. This said, being able to throw an exception from a trap is IMHO desirable. In the end, both traps and exceptions are tools for doing cleanup or recovering from errors, so common usage is not a bad idea. Of course, even in the case of not being able to throw exceptions from traps, is more or less easy (assuming that shell scripts are short snippets of code in the common case) to implement a way of throwing exceptions if some trap is invoked, by setting a variable and doing a busy loop or something like that, so... Not the best solution but it's a suitable one. Raúl Núñez de Arenas Coronado -- Linux Registered User 88736 | http://www.dervishd.net http://www.pleyades.net & http://www.gotesdelluna.net It's my PC and I'll cry if I want to... ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-01 18:38 ` Bart Schaefer 2005-10-01 19:10 ` Peter Stephenson @ 2005-10-01 20:28 ` DervishD 2005-10-02 4:40 ` Bart Schaefer 1 sibling, 1 reply; 25+ messages in thread From: DervishD @ 2005-10-01 20:28 UTC (permalink / raw) To: Bart Schaefer; +Cc: Peter Stephenson, zsh-workers Hi Bart :) * Bart Schaefer <schaefer@brasslantern.com> dixit: > On Oct 1, 5:37pm, DervishD wrote: > } Subject: Re: Exception handling and "trap" vs. TRAPNAL() > } > } Hi Peter :) > } * Peter Stephenson <pws@csr.com> dixit: > } > Add some "print"s to the trap to see when it's triggering. > } > } The trap is triggering just in the "false" statement, and the > } TRAPZERR works perfectly, it's the "trap" kind which is failing. > I ran this with "set -x" (much better than inserting print statements) I did exactly the same, and the "trap" didn't caused the always block to run but the TRAPZERR bit did. > } Can I do anything to make it work (except patching zsh)? > I don't think so. Well, then I'll use explicit throw commands instead of ZERR. The code will be a bit messier but I'll try to use TRAPZERR whenever possible until the ¿bug? is fixed. I am still not sure whether this is a bug or a feature: trap 'false' DEBUG true [[ $? -eq 1 ]] && print "What?" This snippet doesn't print anything, and won't do even if using TRAPDEBUG and "return 1" instead of "false". So ignoring errors from inline traps make those traps and the TRAPNAL ones semantically identical (TRAPNAL is a function, but its return value seems to be ignored). Apart from this, I cannot think of a reason :? Raúl Núñez de Arenas Coronado -- Linux Registered User 88736 | http://www.dervishd.net http://www.pleyades.net & http://www.gotesdelluna.net It's my PC and I'll cry if I want to... ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-01 20:28 ` DervishD @ 2005-10-02 4:40 ` Bart Schaefer 2005-10-02 8:13 ` DervishD 2005-10-02 19:09 ` Peter Stephenson 0 siblings, 2 replies; 25+ messages in thread From: Bart Schaefer @ 2005-10-02 4:40 UTC (permalink / raw) To: DervishD; +Cc: Peter Stephenson, zsh-workers On Oct 1, 10:28pm, DervishD wrote: } } I am still not sure whether this is a bug or a feature: } } trap 'false' DEBUG } } true } [[ $? -eq 1 ]] && print "What?" } } This snippet doesn't print anything, and won't do even if using } TRAPDEBUG and "return 1" instead of "false". Guess what. The bit of documentation that PWS quoted earlier is significantly at odds with the actual implementation. Here's that paragraph again: The return status from the function is handled specially. If it is zero, the signal is assumed to have been handled, and execution continues normally. So far we're right on the mark. Otherwise, the normal effect of the signal is produced; This is clearly inaccurate. If it were accurate, a non-zero return from TRAPHUP, TRAPABRT, or TRAPALRM (among others) ought to cause the shell to exit. This demonstrably does not happen. What *does* happen is that all levels of nested loop are broken (as if by the "break N" command with a sufficiently large N) and the shell is forced to behave as if a shell-level error occurred. (I'll note in passing that it actually has to be a GREATER THAN zero return value, not just non-zero; "return -1" doesn't cause any of the effects discussed here. But this bit of doc is already so far wrong that signedness makes little difference.) However, this effect is only achieved in two cases: (1) An explicit "return" command is used; it's a side-effect of the "return" builtin that the value of $? is recorded as the return value of the trap. (2) The TRAPNAL function itself exits with a shell-level error, such as a syntax error or an assignment to a read-only variable. Merely falling off the end of the function with $? > 0 is not sufficient. It's my contention that case (2) above is an oddball. It ought to behave the same way for "TRAPNAL() { ... }" and "trap ... NAL". That it does not is because the same local variable is overloaded to mean both "$? > 0 was true" and "error condition was true", not because of some strange special-case. if this causes execution to terminate, the status returned to the shell is the status returned from the function. This is also demonstrably wrong. Both cases above cause the return status to be set via the "behave as if a shell-level error occurred" reaction, so usually $? == 1. The side-effect of "return" on $? lasts only long enough to set up the forced error, then is erased as part of unwinding the trap context. In fact, ONLY in the case of "trap 'return X' NAL" is $? = X caused to happen, and that's because the trap executes in the context of the caller and forces the caller itself to return. This behavior of the return builtin in an "inline trap" is not documented anywhere as far as I can tell, but you can watch it in action (and badly cripple your shell) by writing -- trap return DEBUG -- and then attempting to execute any function that has more than one command in the body. A much more accurate description of the behavior of TRAPNAL can be found under the "return" command entry: If return was executed from a trap in a TRAPNAL function, the effect is different for zero and non-zero return status. With zero status (or after an implicit return at the end of the trap), the shell will return to whatever it was previously processing; with a non-zero status, the shell will behave as interrupted except that the return status of the trap is retained. Aha! "Behave as if interrupted" NOT "the normal effect of the signal." The doc under "TRAPNAL" needs to be repaired. (Here's another case of zsh code overloading a variable; "interrupt" and "shell-level error" are indistinguishable after a certain point.) ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-02 4:40 ` Bart Schaefer @ 2005-10-02 8:13 ` DervishD 2005-10-02 19:09 ` Peter Stephenson 1 sibling, 0 replies; 25+ messages in thread From: DervishD @ 2005-10-02 8:13 UTC (permalink / raw) To: Bart Schaefer; +Cc: Peter Stephenson, zsh-workers Hi Bart :) * Bart Schaefer <schaefer@brasslantern.com> dixit: > Otherwise, the normal effect of the signal is produced; > > This is clearly inaccurate. If it were accurate, a non-zero return > from TRAPHUP, TRAPABRT, or TRAPALRM (among others) ought to cause the > shell to exit. This demonstrably does not happen. What *does* happen > is that all levels of nested loop are broken (as if by the "break N" > command with a sufficiently large N) and the shell is forced to behave > as if a shell-level error occurred. I haven't tested that. But I see your point. I'm starting to think the same: it's a documentation problem, not a code problem (although eliminating the special case for TRAPNAL doesn't sound bad...). Raúl Núñez de Arenas Coronado -- Linux Registered User 88736 | http://www.dervishd.net http://www.pleyades.net & http://www.gotesdelluna.net It's my PC and I'll cry if I want to... ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-02 4:40 ` Bart Schaefer 2005-10-02 8:13 ` DervishD @ 2005-10-02 19:09 ` Peter Stephenson 2005-10-02 19:55 ` Bart Schaefer 1 sibling, 1 reply; 25+ messages in thread From: Peter Stephenson @ 2005-10-02 19:09 UTC (permalink / raw) To: zsh-workers Bart Schaefer wrote: > The doc under "TRAPNAL" needs to be repaired. OK, I've just copied the other chunk. I'm not sure there's any code change I would feel comfortable with. It took quite a lot of effort in numerous different discussions working out how traps should behave and I don't see anything that's important enough to risk breaking something else, nor anything that's obviously inconsistent provided the manual is written correctly... except for the odd case of the error in the TRAP function, which I'm currently too confused even to think about straight. -- Peter Stephenson <pws@pwstephenson.fsnet.co.uk> Work: pws@csr.com Web: http://www.pwstephenson.fsnet.co.uk ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-02 19:09 ` Peter Stephenson @ 2005-10-02 19:55 ` Bart Schaefer 2005-10-02 23:00 ` DervishD 0 siblings, 1 reply; 25+ messages in thread From: Bart Schaefer @ 2005-10-02 19:55 UTC (permalink / raw) To: zsh-workers On Oct 2, 8:09pm, Peter Stephenson wrote: } } I'm not sure there's any code change I would feel comfortable with. } [...] I don't see anything that's [...] obviously inconsistent } provided the manual is written correctly... except for the } odd case of the error in the TRAP function, which I'm currently too } confused even to think about straight. It's really pretty simple. The whole problem is the "else if (errflag)" branch in dotrapargs(). It's a no-op for inline traps because of later tests of (isfunc). If we want to suppress errors/interrupts from trap functions, so they behave the same as inline traps, we simply delete that branch: ------------------------------------------------------------------- Index: Src/signals.c =================================================================== RCS file: /extra/cvsroot/zsh/zsh-4.0/Src/signals.c,v retrieving revision 1.13 diff -c -r1.13 signals.c --- Src/signals.c 28 May 2005 04:32:49 -0000 1.13 +++ Src/signals.c 2 Oct 2005 19:31:00 -0000 @@ -1097,8 +1097,7 @@ * execrestore. */ trapret = trapreturn + 1; - } else if (errflag) - trapret = 1; + } execrestore(); lexrestore(); ------------------------------------------------------------------- Note that an effect of suppressing errors in this way is that it becomes difficult to send a keyboard interrupt to the shell, because interrupts that arrive during execution of a trap will be ignored. If we want to propagate errors that occur inside traps, and thereby make it possible for inline traps to "throw exceptions" in "always" blocks, we do this instead (more discussion below patch): ------------------------------------------------------------------- Index: Src/signals.c =================================================================== diff -c -r1.13 signals.c --- Src/signals.c 28 May 2005 04:32:49 -0000 1.13 +++ Src/signals.c 2 Oct 2005 19:22:57 -0000 @@ -1003,6 +1003,7 @@ int trapret = 0; int obreaks = breaks; int isfunc; + int traperr; /* if signal is being ignored or the trap function * * is NULL, then return * @@ -1097,8 +1098,8 @@ * execrestore. */ trapret = trapreturn + 1; - } else if (errflag) - trapret = 1; + } + traperr = errflag; execrestore(); lexrestore(); @@ -1110,6 +1111,7 @@ lastval = trapret-1; } } else { + errflag |= traperr; breaks += obreaks; if (breaks > loops) breaks = loops; ------------------------------------------------------------------- The |= there is to retain any true value of errflag from the context restored by lexrestore(). That may be impossible in the first place, but I'm not certain. Note that the "if" for the "else" in that third hunk is: if (trapret > 0) { With the change in the second hunk, (trapret > 0) is true if and only if an explicit "return" statement was executed within the trap (either inline or function). When (traperr) is true an error/interrupt must have occurred in the trap, and it therefore did not call "return", and therefore (trapret > 0) must be false. ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-02 19:55 ` Bart Schaefer @ 2005-10-02 23:00 ` DervishD 2005-10-03 1:37 ` Bart Schaefer 0 siblings, 1 reply; 25+ messages in thread From: DervishD @ 2005-10-02 23:00 UTC (permalink / raw) To: Bart Schaefer; +Cc: zsh-workers Hi Bart :) * Bart Schaefer <schaefer@brasslantern.com> dixit: > If we want to propagate errors that occur inside traps, and thereby > make it possible for inline traps to "throw exceptions" in "always" > blocks, we do this instead (more discussion below patch): [...] I insist: while ZERR may not be the place for throwing exceptions (I think it's a perfect place, but that's another matter), signals like SIGTERM, SIGINT, etc. or even SIGALRM, are very good candidates to do exception handling. The best point about EH is that it allows to do better error handling and cleanup, and traps are mostly used exactly for that, so using the two things together is not as weird as it may look. Propagating "errflag" may break current code only if that code is using an inline trap which "returns" a value and that error value is ignored on purpose. Weird behaviour, to return a value you are going to ignore as if it never existed. Moreover, simulating a "return" from an inline trap is something difficult to do accidentally. It would require messing with "$?" directly or something like that. Of course, there are other code that may break: inline traps which generate syntax errors (due to "eval" or the like, so the error cannot be picked up always because it is produced only when the "eval" parameters are ill formed or something like that) that previously didn't cause any problem except maybe print an error message and now will jump into the "always" block. But I don't think there is much code out there using "always" blocks that can break. I cannot think about any other code that may break due to the last change you're proposing, but anyway your first patch seems reasonable too: if inline traps cannot throw exceptions, do not let function traps do it and document the problem (not about exceptions, but about return values and syntax errors). Raúl Núñez de Arenas Coronado -- Linux Registered User 88736 | http://www.dervishd.net http://www.pleyades.net & http://www.gotesdelluna.net It's my PC and I'll cry if I want to... ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-02 23:00 ` DervishD @ 2005-10-03 1:37 ` Bart Schaefer 2005-10-03 8:57 ` Peter Stephenson 2005-10-03 9:01 ` DervishD 0 siblings, 2 replies; 25+ messages in thread From: Bart Schaefer @ 2005-10-03 1:37 UTC (permalink / raw) To: DervishD; +Cc: zsh-workers Having written most of what follows, I just discovered something that may make a lot of it moot: In zsh prior to 4.1.something, an error condition in an inline trap *WAS* passed through to the calling context. That it now is not, is apparently either a broken behavior or an undocumented change, and in any case no code written for older zsh could have relied upon errors in traps being inconsequential. Compare zsh-4.0.6: schaefer[505] trap 'readonly BAR; BAR= ; print ok' ZERR schaefer[506] foo() { false; print more } schaefer[507] foo foo: read-only variable: BAR schaefer[508] print $? 1 Now here's zsh-4.2.5: schaefer[501] trap 'readonly BAR; BAR= ; print ok' ZERR schaefer[502] foo() { false; print more } schaefer[503] foo foo: read-only variable: BAR more schaefer[504] print $? 0 This gets even stranger if you compare to bash2. With an ERR trap, bash2 behaves like zsh-4.2.5 (except that the ERR trap is reset on function entry, so you have to put the trap command inside the body). So maybe this is a standards-compliance thing? [ASIDE: The bash2 behavior implies that traps set globally outside of a function are not supposed to apply within the function context. E.g., in bash2 a global INT trap does not prevent the function from being interrupted by a SIGINT; instead it handles the signal in the context where the trap command was run. Similarly an ERR trap stops being tripped during the body of the function, but remains in effect so that it is tripped if/when the function itself returns nonzero. This is, almost, the inverse of zsh's "setopt localtraps" behavior.] Nevertheless ... On Oct 3, 1:00am, DervishD wrote: } } I insist: while ZERR may not be the place for throwing exceptions } (I think it's a perfect place, but that's another matter), signals } like SIGTERM, SIGINT, etc. or even SIGALRM, are very good candidates } to do exception handling. Raúl ... the important point that you're missing is that zsh doesn't really have or handle exceptions. Peter's throw/catch functions are simulations of exception behavior using a mechanism that is far less powerful than true exception handling. The zsh "always" syntax could -- and perhaps even should -- have been implemented equally well as a new syntax for the "eval" builtin, much like Perl's "eval" can be followed by a curly-bracketed block instead of a string. Zsh's TRY_BLOCK_ERROR is a crude approximation of Perl's $@, in that $TRY_BLOCK_ERROR captures only the presence of an error and not the error message. (In fact, a very useful extension would be to add an EVAL_ERROR variable that is 1 if an "eval" stopped with an error, and 0 if "eval" finished normally, so that an error could be distinguished from "eval false".) The important distinction, though, is that zsh lacks even an approximation of Perl's "die" builtin. } Propagating "errflag" may break current code only if that code is } using an inline trap which "returns" a value and that error value is } ignored on purpose. That's not quite correct. Remember, an inline trap using the "return" builtin actually causes the surrounding/calling context to return, so it's imposible to provide a value to the calling context that way; "return" in an inline trap is roughly analogous to using "break 2" in a nested loop. When "return" is not used, the value of $? from the calling context is always restored when the trap finishes, so it never makes sense for the calling context to expect to get a value from an inline trap. } Of course, there are other code that may break: inline traps } which generate syntax errors [...] that } previously didn't cause any problem except maybe print an error } message and now will jump into the "always" block. But I don't think } there is much code out there using "always" blocks that can break. The trouble is not with code that is using an "always" block, it's with code that is NOT using an "always" block. Which would be the majority of existing code, because "always" is a very new feature. So propagating errflag may break current code if that code is using an inline trap, that code does not have an "always" block, and that code expects to keep going no matter what happens in the trap. } I cannot think about any other code that may break due to the } last change you're proposing, but anyway your first patch seems } reasonable too: if inline traps cannot throw exceptions, do not let } function traps do it and document the problem (not about exceptions, } but about return values and syntax errors). Here's another way to think about it: Presently (that is, without either of my patches), a TRAPNAL function handles error conditions like a function call, whereas an inline trap handles error conditions like an "eval" statement. That's defensible, in a way, because a "trap" command *looks* like an "eval" statement; it contains a string that is evaluated as commands. It could even be explained that way in the docs, so that you would have understood why your sample script didn't work. Given all of this plus the bash2 behavior, I'm inclined to add a few more words to the documentation and apply *neither* of the patches from workers/21804. Further, *IF* we were going to choose one of those patches to apply, I'd say it should be the first one, to make TRAPNAL ignore errors too. Here's how to use throw/catch from a TRAPNAL function in the event that 21804 part 1 is applied: TRAPZERR() { eval 'throw DEFAULT'; return 1 } That is, call throw in the eval to set the variables, and then return a positive $? to interrupt into the always block. ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-03 1:37 ` Bart Schaefer @ 2005-10-03 8:57 ` Peter Stephenson 2005-10-03 14:51 ` Bart Schaefer 2005-10-03 9:01 ` DervishD 1 sibling, 1 reply; 25+ messages in thread From: Peter Stephenson @ 2005-10-03 8:57 UTC (permalink / raw) To: zsh-workers Bart Schaefer <schaefer@brasslantern.com> wrote: > In zsh prior to 4.1.something, an error condition in an inline trap > *WAS* passed through to the calling context. That it now is not, is > apparently either a broken behavior or an undocumented change, and in > any case no code written for older zsh could have relied upon errors > in traps being inconsequential. I think this is 19575: reading the "-" path in the diff the old behaviour of setting trapret if errflag is set was there, and a subsequent "errflag = 1" depended solely on that, not being in a function. Given the description it doesn't look like the change was intentional, even though arguably it should be the way it now is. > [ASIDE: The bash2 behavior implies that traps set globally outside > of a function are not supposed to apply within the function context. > E.g., in bash2 a global INT trap does not prevent the function from > being interrupted by a SIGINT; instead it handles the signal in the > context where the trap command was run. Similarly an ERR trap stops > being tripped during the body of the function, but remains in effect > so that it is tripped if/when the function itself returns nonzero. > This is, almost, the inverse of zsh's "setopt localtraps" behavior.] This is presumably implementable without too much horror, since we can already save and restore traps. It would probably now need to be an option, though. > Raúl ... the important point that you're missing is that zsh doesn't > really have or handle exceptions. Peter's throw/catch functions are > simulations of exception behavior using a mechanism that is far less > powerful than true exception handling. Yes, it's all just based on testing a single variable and relying on the fact that zsh has to unwind the state during an error anyway. Maybe the zshcontrib entry needs more cautionary wording. > The zsh "always" syntax could -- and perhaps even should -- have been > implemented equally well as a new syntax for the "eval" builtin, much > like Perl's "eval" can be followed by a curly-bracketed block instead > of a string. That's not particularly natural in zsh. eval { ... } would mean that "eval" could no longer be a true builtin but would have to be treated as a reserved word early in parsing. We can second-guess the later stages in processing (is eval disabled?), so probably it can be done without major grief, but the zsh syntax isn't really comparable to Perl. > (In fact, a very useful extension would > be to add an EVAL_ERROR variable that is 1 if an "eval" stopped with > an error, and 0 if "eval" finished normally, so that an error could be > distinguished from "eval false".) This, presumably, is simple. > Presently (that is, without either of my patches), a TRAPNAL function > handles error conditions like a function call, whereas an inline trap > handles error conditions like an "eval" statement. That's defensible, > in a way, because a "trap" command *looks* like an "eval" statement; > it contains a string that is evaluated as commands. It could even be > explained that way in the docs, so that you would have understood why > your sample script didn't work. Yes, this all sounds rational. > Given all of this plus the bash2 behavior, I'm inclined to add a few > more words to the documentation and apply *neither* of the patches > from workers/21804. Further, *IF* we were going to choose one of > those patches to apply, I'd say it should be the first one, to make > TRAPNAL ignore errors too. It looks like the reason for the error to be propagated is receding into history and I'd be perfectly happy with that patch. -- Peter Stephenson <pws@csr.com> Software Engineer CSR PLC, Churchill House, Cambridge Business Park, Cowley Road Cambridge, CB4 0WZ, UK Tel: +44 (0)1223 692070 This message has been scanned for viruses by BlackSpider MailControl - www.blackspider.com ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-03 8:57 ` Peter Stephenson @ 2005-10-03 14:51 ` Bart Schaefer 2005-10-03 15:10 ` Peter Stephenson 0 siblings, 1 reply; 25+ messages in thread From: Bart Schaefer @ 2005-10-03 14:51 UTC (permalink / raw) To: zsh-workers On Oct 3, 9:57am, Peter Stephenson wrote: } Subject: Re: Exception handling and "trap" vs. TRAPNAL() } } Bart Schaefer <schaefer@brasslantern.com> wrote: } > In zsh prior to 4.1.something, an error condition in an inline trap } > *WAS* passed through to the calling context. } } it doesn't look like the change was intentional, even though arguably it } should be the way it now is. It could also be argued that, if the trap should behave like an "eval", it ought to set $? = 1 when an error occurs inside the trap (but still not cause an interrupt condition). The example of bash2 contradicts that position. Can anyone who is reading this try ksh? } > E.g., in bash2 a global INT trap does not prevent the function from } > being interrupted by a SIGINT; instead it handles the signal in the } > context where the trap command was run. Similarly an ERR trap stops } > being tripped during the body of the function, but remains in effect } } This is presumably implementable without too much horror, since we } can already save and restore traps. It's worse than just saving and restoring traps. It requires treating a function call like a subshell, in that both the function and the caller get the SIGINT, the function is interrupted, and the caller's trap is trigged. } It would probably now need to be an option, though. Yes. } > The zsh "always" syntax could -- and perhaps even should -- have been } > implemented equally well as a new syntax for the "eval" builtin, much } > like Perl's "eval" can be followed by a curly-bracketed block instead } > of a string. } } That's not particularly natural in zsh. I didn't really mean that the syntax would be exactly like Perl's, just that the behavior would be. If you wanted to implement something as close as possible to "always" in an older version of zsh, you'd do something like: eval "print this is the try block" TRY_ERR=$? print "this is the always block" if (( TRY_ERR == 0 )) then print "this is what comes after the always block" else # This is a stupid way to reset $? without exiting from the # parent shell script when we aren't in a function body. ( return TRY_ERR ) fi } > Given all of this plus the bash2 behavior, I'm inclined to add a few } > more words to the documentation and apply *neither* of the patches } > from workers/21804. Further, *IF* we were going to choose one of } > those patches to apply, I'd say it should be the first one, to make } > TRAPNAL ignore errors too. } } It looks like the reason for the error to be propagated is receding into } history and I'd be perfectly happy with that patch. As I said, I'm actually leaning towards no patch at all. However, the reason for propagating the error isn't actually receding, because Raúl says he needs that behavior, and given "always" it really is useful. Here's a possible compromise: Use my second patch, but propagate the error if and only if we're in the try-block of an always-construct. That's guaranteed not to break old code, and continues to behave like bash2 in the absence of "always". Can dotrapargs() determine that it is in "always" context without too much trouble? ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-03 14:51 ` Bart Schaefer @ 2005-10-03 15:10 ` Peter Stephenson 2005-10-03 16:50 ` Bart Schaefer 0 siblings, 1 reply; 25+ messages in thread From: Peter Stephenson @ 2005-10-03 15:10 UTC (permalink / raw) To: zsh-workers Bart Schaefer wrote: > It could also be argued that, if the trap should behave like an "eval", > it ought to set $? = 1 when an error occurs inside the trap (but still > not cause an interrupt condition). The example of bash2 contradicts > that position. Can anyone who is reading this try ksh? ksh appears to set the status to 1. > Here's a possible compromise: Use my second patch, but propagate the > error if and only if we're in the try-block of an always-construct. > That's guaranteed not to break old code, and continues to behave like > bash2 in the absence of "always". Can dotrapargs() determine that it > is in "always" context without too much trouble? It can test try_errflag, which is usually -1 when not in an always block and >= 0 otherwise. However that's the value of TRY_BLOCK_ERROR which can be set by the user, so isn't actually guaranteed to be -1 outside. Either adding another flag not tied to a variable or rejecting user attempts to set TRY_BLOCK_ERROR when it's current value is less than zero (and therefore presumably only let it be set to such a number) would work around this. But do you mean how can it test if it's in the try block part? There's no explicit test for that at the moment, but it's easy to add a variable. pws This message has been scanned for viruses by BlackSpider MailControl - www.blackspider.com ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-03 15:10 ` Peter Stephenson @ 2005-10-03 16:50 ` Bart Schaefer 0 siblings, 0 replies; 25+ messages in thread From: Bart Schaefer @ 2005-10-03 16:50 UTC (permalink / raw) To: zsh-workers On Oct 3, 4:10pm, Peter Stephenson wrote: } } Bart Schaefer wrote: } > It could also be argued that, if the trap should behave like an "eval", } > it ought to set $? = 1 when an error occurs inside the trap (but still } > not cause an interrupt condition). The example of bash2 contradicts } > that position. Can anyone who is reading this try ksh? } } ksh appears to set the status to 1. Oh, goody. } > Here's a possible compromise: Use my second patch, but propagate the } > error if and only if we're in the try-block of an always-construct. } } But do you mean how can it test if it's in the try block part? There's } no explicit test for that at the moment, but it's easy to add a variable. Yes, that is what I meant. That gives us something like this (except I'm only half serious about the emulation part): Index: Src/loop.c =================================================================== diff -c -r1.10 loop.c --- Src/loop.c 18 Feb 2005 17:05:17 -0000 1.10 +++ Src/loop.c 3 Oct 2005 16:31:57 -0000 @@ -627,13 +627,17 @@ try_errflag = -1; /**/ +zlong +try_tryflag = 0; + +/**/ int exectry(Estate state, int do_exec) { Wordcode end, always; int endval; int save_retflag, save_breaks, save_loops, save_contflag; - zlong save_try_errflag; + zlong save_try_errflag, save_try_tryflag; end = state->pc + WC_TRY_SKIP(state->pc[-1]); always = state->pc + 1 + WC_TRY_SKIP(*state->pc); @@ -642,7 +646,12 @@ cmdpush(CS_CURSH); /* The :try clause */ + save_try_tryflag = try_tryflag; + try_tryflag = 1; + execlist(state, 1, do_exec); + + try_tryflag = save_try_tryflag; /* Don't record errflag here, may be reset. */ endval = lastval; Index: Src/signals.c =================================================================== RCS file: /extra/cvsroot/zsh/zsh-4.0/Src/signals.c,v retrieving revision 1.13 diff -c -r1.13 signals.c --- Src/signals.c 28 May 2005 04:32:49 -0000 1.13 +++ Src/signals.c 3 Oct 2005 16:48:34 -0000 @@ -1003,6 +1003,7 @@ int trapret = 0; int obreaks = breaks; int isfunc; + int traperr; /* if signal is being ignored or the trap function * * is NULL, then return * @@ -1097,8 +1098,8 @@ * execrestore. */ trapret = trapreturn + 1; - } else if (errflag) - trapret = 1; + } + traperr = errflag; execrestore(); lexrestore(); @@ -1110,6 +1111,10 @@ lastval = trapret-1; } } else { + if (traperr && emulation != EMULATE_SH) + lastval = 1; + if (try_tryflag) + errflag = traperr; breaks += obreaks; if (breaks > loops) breaks = loops; ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-03 1:37 ` Bart Schaefer 2005-10-03 8:57 ` Peter Stephenson @ 2005-10-03 9:01 ` DervishD 2005-10-03 16:21 ` Bart Schaefer 1 sibling, 1 reply; 25+ messages in thread From: DervishD @ 2005-10-03 9:01 UTC (permalink / raw) To: Bart Schaefer; +Cc: zsh-workers HI Bart :) * Bart Schaefer <schaefer@brasslantern.com> dixit: > In zsh prior to 4.1.something, an error condition in an inline trap > *WAS* passed through to the calling context. That it now is not, is > apparently either a broken behavior or an undocumented change, and in > any case no code written for older zsh could have relied upon errors > in traps being inconsequential. :(((( > This gets even stranger if you compare to bash2. With an ERR trap, > bash2 behaves like zsh-4.2.5 (except that the ERR trap is reset on > function entry, so you have to put the trap command inside the > body). So maybe this is a standards-compliance thing? "Traps shall remain in place for a given shell until explictly changed with another trap command", says SUSv3. But it also says that "Shell Functions" form another shell execution environment... I haven't found anything in SUSv3 saying that traps must be reset when entering a shell function, but it says they are reset when spawning a subshell (which is very logical anyway). This looks more like a bashism. > On Oct 3, 1:00am, DervishD wrote: > } I insist: while ZERR may not be the place for throwing exceptions > } (I think it's a perfect place, but that's another matter), signals > } like SIGTERM, SIGINT, etc. or even SIGALRM, are very good candidates > } to do exception handling. > Raúl ... the important point that you're missing is that zsh doesn't > really have or handle exceptions. Peter's throw/catch functions are > simulations of exception behavior using a mechanism that is far less > powerful than true exception handling. I know, I know, but AFAIK the "always" block works more or less the same as exception handling does in other languages. It's far less powerful, that's ok, but it's powerful enough to do what I was proposing and using traps together with always blocks. > } Propagating "errflag" may break current code only if that code is > } using an inline trap which "returns" a value and that error value is > } ignored on purpose. > That's not quite correct. Remember, an inline trap using the "return" > builtin actually causes the surrounding/calling context to return, so > it's imposible to provide a value to the calling context that way; That's why I was quoting "return": I didn't mean a literal "return" command, but a way of returning a value to the current code. If $? is restored, then there is not easy way of returning the error, but that's good because then propagatin "errflag" shouldn't break current code, am I wrong? > } Of course, there are other code that may break: inline traps > } which generate syntax errors [...] that > } previously didn't cause any problem except maybe print an error > } message and now will jump into the "always" block. But I don't think > } there is much code out there using "always" blocks that can break. > The trouble is not with code that is using an "always" block, it's with > code that is NOT using an "always" block. Which would be the majority > of existing code, because "always" is a very new feature. > > So propagating errflag may break current code if that code is using an > inline trap, that code does not have an "always" block, and that code > expects to keep going no matter what happens in the trap. But if you propagate the error, you won't break such code. I mean, even with your patch, this code... trap 'readonly VAR; VAR=0' ZERR print "HERE" false print "HERE AGAIN" ...will print both strings. I must admit I may be misinterpreting your patch, and assuming that errflag doesn't do what I think it does O:)) As far as I understan, "errflag" just signals the error and propagate it to the current execution environment (for inline traps, I mean), but doesn't make the shell abort :? > Presently (that is, without either of my patches), a TRAPNAL > function handles error conditions like a function call, whereas an > inline trap handles error conditions like an "eval" statement. > That's defensible, in a way, because a "trap" command *looks* like > an "eval" statement; it contains a string that is evaluated as > commands. It could even be explained that way in the docs, so that > you would have understood why your sample script didn't work. It sounds quite sensible, yes. In fact SUS says that inline traps (the only ones supported by SUS) do an implicit "eval" on the string you pass to it. Moreover this is, obviously, the less intrusive change and won't break existing code ;) > Here's how to use throw/catch from a TRAPNAL function in the event > that 21804 part 1 is applied: > > TRAPZERR() { eval 'throw DEFAULT'; return 1 } > > That is, call throw in the eval to set the variables, and then return > a positive $? to interrupt into the always block. But this is useless to me, since I already have that behaviour without patching. My problem are inline traps. I need to use some variables that the shell sets automatically, like LINENO, so I must go for an inline trap and it won't make the try block to stop and jump into the always block. This is not a big deal if using ZERR, because you can get rid of the trap and substitute each "command" by "command && throw whatever", but it's a problem with, for example, SIGQUIT. I would like to handle SIGQUIT specially on always blocks, but I need "throw" to work inside all kind of traps for that. You're proposing a quite intelligent way of have consequent behaviour (that is, no trap can throw exceptions) for TRAPNAL() traps, but is there any similar solution for inline traps? After reading your message I'm starting to think that zsh should stick to current behaviour but reflect that behaviour in the docs. Any patch will need redocumenting, too, and can break existing code, just to fix a problem that can be seen as proper behaviour. If no patch is applied I can still use TRAPNAL's for throwing exceptions, but I must think a way of doing that with inline traps, which I think it's impossible because the value of TRY_BLOCK_ERROR is reset. I've tried a partial solution, adding a variable to "throw" and "catch" which is set up independently of the value of TRY_BLOCK_ERROR, but the problem is jumping out the try block into the always block :((( With the variable, the exception is caught as soon as the always block is run, but execution of the try block doesn't stop... Thanks a lot, Bart, for your explanation and your valuable insights. The problem is much more complex than I thought. Raúl Núñez de Arenas Coronado -- Linux Registered User 88736 | http://www.dervishd.net http://www.pleyades.net & http://www.gotesdelluna.net It's my PC and I'll cry if I want to... ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-03 9:01 ` DervishD @ 2005-10-03 16:21 ` Bart Schaefer 2005-10-03 17:59 ` DervishD 0 siblings, 1 reply; 25+ messages in thread From: Bart Schaefer @ 2005-10-03 16:21 UTC (permalink / raw) To: DervishD; +Cc: zsh-workers On Oct 3, 11:01am, DervishD wrote: } } I know, I know, but AFAIK the "always" block works more or less } the same as exception handling does in other languages. Less, not more. The problem is that in a language with real exceptions, the difference between an error and an exception is well-defined, and it is usually possible to have errors without exceptions and vice-versa. Zsh's "always" block is an error handler, not an exception handler, and PWS's functions are overloading the meaning of "error". } > } Propagating "errflag" may break current code only if that code is } > } using an inline trap which "returns" a value and that error value is } > } ignored on purpose. } > That's not quite correct. Remember, an inline trap using the "return" } > builtin actually causes the surrounding/calling context to return } } That's why I was quoting "return": I didn't mean a literal } "return" command, but a way of returning a value to the current code. Yes, but there *isn't* any way of returning a value to the surrounding code -- unless you mean something like setting $REPLY. So your basis for the statement is a practical impossibility. } If $? is restored, then there is not easy way of returning the error, } but that's good because then propagating "errflag" shouldn't break } current code, am I wrong? Yes, unfortunately, you're wrong. } But if you propagate the error, you won't break such code. I } mean, even with your patch, this code... } } trap 'readonly VAR; VAR=0' ZERR } } print "HERE" } false } print "HERE AGAIN" } } ...will print both strings. In 4.0.6, or with my second potential patch, that prints only "HERE" (plus the error message about the readonly assignment). In 4.2.5, it prints both strings. In bash2, it also prints both strings. Anything that relies on the 4.2.5 or bash2 behavior will break if we go back to propagating the error as was done in 4.0.6. } O:)) As far as I understan, "errflag" just signals the error and } propagate it to the current execution environment (for inline traps, } I mean), but doesn't make the shell abort :? It doesn't make the shell *exit*, but it does stop execution in the way that would jump into an "always" block. (If it did not, how would it solve your problem?) In the absence of the "always", it simply bails out of the current context. The "always" block is a way to intercept the "bail out" action, but the bail-out happens whether "always" is there or not. } If no patch is applied I can still use TRAPNAL's for throwing } exceptions, but I must think a way of doing that with inline traps, } which I think it's impossible because the value of TRY_BLOCK_ERROR is } reset. You're right, it's impossible. You'll have to think about how you would write the code if you never had "always" in the first place, and write it that way. ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-03 16:21 ` Bart Schaefer @ 2005-10-03 17:59 ` DervishD 2005-10-04 16:31 ` Bart Schaefer 0 siblings, 1 reply; 25+ messages in thread From: DervishD @ 2005-10-03 17:59 UTC (permalink / raw) To: Bart Schaefer; +Cc: zsh-workers Hi Bart :) * Bart Schaefer <schaefer@brasslantern.com> dixit: > On Oct 3, 11:01am, DervishD wrote: > } I know, I know, but AFAIK the "always" block works more or less > } the same as exception handling does in other languages. > Less, not more. The problem is that in a language with real > exceptions, the difference between an error and an exception is > well-defined, and it is usually possible to have errors without > exceptions and vice-versa. Zsh's "always" block is an error > handler, not an exception handler, and PWS's functions are > overloading the meaning of "error". But always blocks handle errors that would cause the shell to abort execution. In a certain way, they are exceptions, they're different from errors (with "error" I mean the usual "non-zero exit code"). > } > } Propagating "errflag" may break current code only if that code is > } > } using an inline trap which "returns" a value and that error value is > } > } ignored on purpose. > } > That's not quite correct. Remember, an inline trap using the "return" > } > builtin actually causes the surrounding/calling context to return > } > } That's why I was quoting "return": I didn't mean a literal > } "return" command, but a way of returning a value to the current code. > > Yes, but there *isn't* any way of returning a value to the surrounding > code -- unless you mean something like setting $REPLY. So your basis > for the statement is a practical impossibility. I was thinking exactly that, using $REPLY or something similar. I just wasn't sure if the trap restored all the environment or not. > } But if you propagate the error, you won't break such code. I > } mean, even with your patch, this code... > } > } trap 'readonly VAR; VAR=0' ZERR > } > } print "HERE" > } false > } print "HERE AGAIN" > } > } ...will print both strings. > > In 4.0.6, or with my second potential patch, that prints only "HERE" > (plus the error message about the readonly assignment). In 4.2.5, it > prints both strings. In bash2, it also prints both strings. Anything > that relies on the 4.2.5 or bash2 behavior will break if we go back to > propagating the error as was done in 4.0.6. It doesn't seem to be a good idea, then... > } O:)) As far as I understan, "errflag" just signals the error and > } propagate it to the current execution environment (for inline traps, > } I mean), but doesn't make the shell abort :? > It doesn't make the shell *exit*, but it does stop execution in the > way that would jump into an "always" block. (If it did not, how > would it solve your problem?) In the absence of the "always", it > simply bails out of the current context. The "always" block is a way > to intercept the "bail out" action, but the bail-out happens whether > "always" is there or not. OK, I just was assuming that without the always block the execution would go on undisturbed, which is false. I don't know why I got that false impression, because I really know that code like this: readonly VAR VAR= will stop execution. Sorry, my fault. > } If no patch is applied I can still use TRAPNAL's for throwing > } exceptions, but I must think a way of doing that with inline traps, > } which I think it's impossible because the value of TRY_BLOCK_ERROR is > } reset. > You're right, it's impossible. You'll have to think about how you > would write the code if you never had "always" in the first place, > and write it that way. The problem is that I would like to use "always" blocks with the "nonzeroexitcode" kind of errors (that's why I use ZERR, it helps me to convert the normal errors into errors as defined in a try block context, a condition which causes the shell to stop execution). Obviously this doesn't need nor exceptions nor always blocks, a simple "goto" will suffice because what I want is a way of avoiding handling similar errors in a per-command basis. I mean, I want to replace this code: command1 && {# handle here some possible error probably exiting} command2 && {# handle here exactly the same error as before} ... commandn && {# incredible, here we must handle a similar error} with this one: trap 'throw commonerror$LINENO' ZERR { command1 command2 command3 } always { # Here we catch and handle the common error # In the exception name we have the line number, # just in case we want to fine tune error handling } I face this kind of code frequently: some tightly coupled commands that cause the same kind of errors, or whose failures can be seen as a single error, and execution *must* stop at that point. That's the whole point. I maybe am using the wrong shell construct for solving the problem, and obviously I can always use the first syntax and handle errors one-by-one (and not, something like this: { command1 || command 2 || command 3 } && {#handle error here} won't always work for me), but I find the always block construct much better for this task. If I cannot use the ZERR trap and I must add an explicit throw call after each command, it still is far better than handling the error in place, better even than using a function to handle the error instead of the throw call, because of rethrowing. With all the information you've given to me, I think now that it's not a good idea to patch zsh to alter current behaviour, because I see that current behaviour makes sense, but nonetheless being able to cause 'alwaysable' errors from inline traps looks very good to me O:)) Thanks again for all, Bart. Raúl Núñez de Arenas Coronado -- Linux Registered User 88736 | http://www.dervishd.net http://www.pleyades.net & http://www.gotesdelluna.net It's my PC and I'll cry if I want to... ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-03 17:59 ` DervishD @ 2005-10-04 16:31 ` Bart Schaefer 2005-10-04 17:29 ` DervishD 0 siblings, 1 reply; 25+ messages in thread From: Bart Schaefer @ 2005-10-04 16:31 UTC (permalink / raw) To: DervishD; +Cc: zsh-workers On Oct 3, 7:59pm, DervishD wrote: : : I want to replace this code: : : command1 && {# handle here some possible error probably exiting} : command2 && {# handle here exactly the same error as before} : ... : commandn && {# incredible, here we must handle a similar error} : : with this one: : : trap 'throw commonerror$LINENO' ZERR : { : command1 : command2 : command3 : } always { : # Here we catch and handle the common error : # In the exception name we have the line number, : # just in case we want to fine tune error handling : } First of all, note that unless command3 is return, the always block is going to execute regardless of whether there has been an error. How about: function common_error() { # Here we catch and handle the common error # In the ERROR_LINE variable we have the line number, # and in the ERROR variable we have the $? status, # just in case we want to fine tune error handling } trap 'ERROR=$?; ERROR_LINE=$LINENO; return $ERROR' ZERR trap common_error EXIT command1 command2 command3 trap - EXIT ZERR Note also for reference that trap 'return $?' ZERR is roughly equivalent to setopt ERR_RETURN ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-04 16:31 ` Bart Schaefer @ 2005-10-04 17:29 ` DervishD 2005-10-04 17:34 ` Peter Stephenson 0 siblings, 1 reply; 25+ messages in thread From: DervishD @ 2005-10-04 17:29 UTC (permalink / raw) To: Bart Schaefer; +Cc: zsh-workers Hi Bart :) * Bart Schaefer <schaefer@brasslantern.com> dixit: > : I want to replace this code: > : > : command1 && {# handle here some possible error probably exiting} > : command2 && {# handle here exactly the same error as before} > : ... > : commandn && {# incredible, here we must handle a similar error} > : > : with this one: > : > : trap 'throw commonerror$LINENO' ZERR > : { > : command1 > : command2 > : command3 > : } always { > : # Here we catch and handle the common error > : # In the exception name we have the line number, > : # just in case we want to fine tune error handling > : } > First of all, note that unless command3 is return, the always block > is going to execute regardless of whether there has been an error. Well, I was going to use a test in the always block to see if an exception was thrown, using catch or testing for "EXCEPTION" and "TRY_BLOCK_ERROR" manually. That's not an issue. > How about: > > function common_error() { > # Here we catch and handle the common error > # In the ERROR_LINE variable we have the line number, > # and in the ERROR variable we have the $? status, > # just in case we want to fine tune error handling > } > > trap 'ERROR=$?; ERROR_LINE=$LINENO; return $ERROR' ZERR > trap common_error EXIT > > command1 > command2 > command3 > > trap - EXIT ZERR > > Note also for reference that > trap 'return $?' ZERR > is roughly equivalent to > setopt ERR_RETURN Man, you're the f. boss. I NEVER thought about this, which doesn't need throw nor an always block. Thanks a lot for the help, Bart, this is a very good solution, much better, even, than using always blocks. Just one note: why should be always blocks needed at all if you can use this kind of solutions? As long as you don't cause ZERR to be raised in "common_error()", this solution is much better because you can fine tune (using the "trap" builtin) when do you want to do common handling and when you don't. The only difference I can see is that code in an always block is executed in the current environment and code in "common_error()" is not. Raúl Núñez de Arenas Coronado -- Linux Registered User 88736 | http://www.dervishd.net http://www.pleyades.net & http://www.gotesdelluna.net It's my PC and I'll cry if I want to... ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-04 17:29 ` DervishD @ 2005-10-04 17:34 ` Peter Stephenson 2005-10-04 17:46 ` DervishD 0 siblings, 1 reply; 25+ messages in thread From: Peter Stephenson @ 2005-10-04 17:34 UTC (permalink / raw) To: zsh-workers DervishD wrote: > Just one note: why should be always blocks needed at all if you > can use this kind of solutions? As long as you don't cause ZERR to be > raised in "common_error()", this solution is much better because you > can fine tune (using the "trap" builtin) when do you want to do > common handling and when you don't. The only difference I can see is > that code in an always block is executed in the current environment > and code in "common_error()" is not. TRAP_RETURN triggers on any non-zero status, while an "always" block is used to stop actual errors which would usually cause everything in sight(*) to abort. If you're really concerned only with non-zero status and don't care about errors there's every reason to go with something like TRAP_RETURN. (*) for some values of "everything" pws This message has been scanned for viruses by BlackSpider MailControl - www.blackspider.com ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Exception handling and "trap" vs. TRAPNAL() 2005-10-04 17:34 ` Peter Stephenson @ 2005-10-04 17:46 ` DervishD 0 siblings, 0 replies; 25+ messages in thread From: DervishD @ 2005-10-04 17:46 UTC (permalink / raw) To: Peter Stephenson; +Cc: zsh-workers Hi Peter :) * Peter Stephenson <pws@csr.com> dixit: > DervishD wrote: > > Just one note: why should be always blocks needed at all if you > > can use this kind of solutions? As long as you don't cause ZERR to be > > raised in "common_error()", this solution is much better because you > > can fine tune (using the "trap" builtin) when do you want to do > > common handling and when you don't. The only difference I can see is > > that code in an always block is executed in the current environment > > and code in "common_error()" is not. > TRAP_RETURN triggers on any non-zero status, while an "always" block > is used to stop actual errors which would usually cause everything in > sight(*) to abort. If you're really concerned only with non-zero status > and don't care about errors there's every reason to go with something > like TRAP_RETURN. But you can check for those kind of errors like this: ( #whatever would have caused an abortive-error ) OK, you must run them in a subshell, but the fact is that this is doable. But I see, an always block here is far more readable and easy to use (apart from being more optimal if the 'abortive-error' can happen in a loop). OTOH, code like this is very annoying when trapping ZERR: # If the file doesn't exist, this will raise ZERR # We probably will want ZERR raised if "do_something" fails, # but certainly not if the test fails. We must use "if"... [[ -r "$filename" ]] && do_something Thanks for the explanation :) Raúl Núñez de Arenas Coronado -- Linux Registered User 88736 | http://www.dervishd.net http://www.pleyades.net & http://www.gotesdelluna.net It's my PC and I'll cry if I want to... ^ permalink raw reply [flat|nested] 25+ messages in thread
* Exception handling and "trap" vs. TRAPNAL() @ 2005-10-01 7:45 DervishD 0 siblings, 0 replies; 25+ messages in thread From: DervishD @ 2005-10-01 7:45 UTC (permalink / raw) To: Zsh Workers Hi all :)) I forwarded this message yesterday, but it hasn't made its way into the list, don't know why :? so I'm posting it here again. Let's say we have this script, using exception handling: --- cut here --- #!/bin/zsh emulate -L zsh [[ "$1" = "trap" ]] && trap 'throw DEFAULT' ZERR [[ "$1" = "TRAPZERR" ]] && function TRAPZERR() { throw DEFAULT ; } function throw() { typeset -g EXCEPTION="$1" readonly THROW=0 if (( TRY_BLOCK_ERROR == 0 )); then (( TRY_BLOCK_ERROR = 1 )) fi THROW= 2>/dev/null } function catch() { typeset -g CAUGHT if [[ $TRY_BLOCK_ERROR -gt 0 && $EXCEPTION = ${~1} ]]; then (( TRY_BLOCK_ERROR = 0 )) CAUGHT="$EXCEPTION" unset EXCEPTION return 0 fi return 1 } alias catch="noglob catch" { print "Before throwing" # This should throw "DEFAULT" exception false throw EXCEPTION # This shouldn't be shown print "After throwing" } always { catch * && print "Caught exception $CAUGHT" return 0 } --- cut here --- Sorry for the size, but I want to make sure it is as self-contained as possible. Well, I do the following: $ ./script trap Before throwing Caught exception EXCEPTION $ ./script TRAPZERR Before throwing Caught exception DEFAULT $ ./script Before throwing Caught exception EXCEPTION I'm puzzled. The "trap" trap is executed in the current environment, so I assume it would throw "DEFAULT", as intended, as soon as we hit the "false". It doesn't and I don't know why. OTOH, the TRAPZERR function, which runs "throw" in its own environment, works ok :??? WHY? AFAIK the "trap" builtin runs its arguments in the current environment, so to say it's like an alias, like cutting and pasting the code, but anyway that shouldn't be the problem, since "throw" is using globals and so it affects running in any environment. The only thing that could be happening is that the ZERR trap using "trap" is not setting "TRY_BLOCK_ERROR" to "1", or it sets the variable but the variable gets reset upon exiting the trap :??? Can anybody (and I really mean anybody, not Bart XD) explain this to me? I would like to use something similar and I need to use "trap" traps and not "TRAPNAL()" traps because I want to use LINENO and probably a couple of variables which are local to the functions where the ZERR trap can be raised. Thanks a lot in advance :) Raúl Núñez de Arenas Coronado -- Linux Registered User 88736 | http://www.dervishd.net http://www.pleyades.net & http://www.gotesdelluna.net It's my PC and I'll cry if I want to... ^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2005-10-04 17:45 UTC | newest] Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2005-10-01 8:01 Exception handling and "trap" vs. TRAPNAL() DervishD [not found] <20050929200741.GA1156@DervishD> [not found] ` <20050930124130.45eb0463.pws@csr.com> [not found] ` <20051001153756.GA12183@DervishD> 2005-10-01 18:38 ` Bart Schaefer 2005-10-01 19:10 ` Peter Stephenson 2005-10-01 20:41 ` DervishD 2005-10-01 22:44 ` Bart Schaefer 2005-10-02 8:06 ` DervishD 2005-10-01 20:28 ` DervishD 2005-10-02 4:40 ` Bart Schaefer 2005-10-02 8:13 ` DervishD 2005-10-02 19:09 ` Peter Stephenson 2005-10-02 19:55 ` Bart Schaefer 2005-10-02 23:00 ` DervishD 2005-10-03 1:37 ` Bart Schaefer 2005-10-03 8:57 ` Peter Stephenson 2005-10-03 14:51 ` Bart Schaefer 2005-10-03 15:10 ` Peter Stephenson 2005-10-03 16:50 ` Bart Schaefer 2005-10-03 9:01 ` DervishD 2005-10-03 16:21 ` Bart Schaefer 2005-10-03 17:59 ` DervishD 2005-10-04 16:31 ` Bart Schaefer 2005-10-04 17:29 ` DervishD 2005-10-04 17:34 ` Peter Stephenson 2005-10-04 17:46 ` DervishD -- strict thread matches above, loose matches on Subject: below -- 2005-10-01 7:45 DervishD
Code repositories for project(s) associated with this public inbox https://git.vuxu.org/mirror/zsh/ This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).