Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Exception handling and "trap" vs. TRAPNAL()



    HI Bart :)

 * Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx> 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...



Messages sorted by: Reverse Date, Date, Thread, Author