* Trigger action at exit?
@ 2008-02-29 22:30 John Trammell
2008-03-02 17:25 ` Ludovic Courtès
0 siblings, 1 reply; 12+ messages in thread
From: John Trammell @ 2008-02-29 22:30 UTC (permalink / raw)
To: guile-user
Hi all:
I'm trying to trigger an action at script exit, similar to Perl's "END
{...}" construct.
After a little searching I've stumbled across "add-hook! exit-hook
...", but for the life of me I can't seem to get it to do what I want.
Here's a taste of what I'm looking for:
(define foo (lambda () (display "foo") (newline)))
(add-hook! exit-hook foo)
(run-hook exit-hook)
I'd like something like this, that doesn't need the explicit
"run-hook" at the end. Ideas?
Thanks,
JT
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Trigger action at exit?
2008-02-29 22:30 Trigger action at exit? John Trammell
@ 2008-03-02 17:25 ` Ludovic Courtès
2008-03-03 20:44 ` Neil Jerram
2008-03-03 21:22 ` John Trammell
0 siblings, 2 replies; 12+ messages in thread
From: Ludovic Courtès @ 2008-03-02 17:25 UTC (permalink / raw)
To: guile-user
Hi,
"John Trammell" <johntrammell@gmail.com> writes:
> After a little searching I've stumbled across "add-hook! exit-hook
> ...", but for the life of me I can't seem to get it to do what I want.
> Here's a taste of what I'm looking for:
>
> (define foo (lambda () (display "foo") (newline)))
> (add-hook! exit-hook foo)
> (run-hook exit-hook)
>
> I'd like something like this, that doesn't need the explicit
> "run-hook" at the end. Ideas?
`exit-hook' is only run by the REPL, so it's not useful in programs.
One possibility is to catch the `quit' exception:
guile> (catch 'quit
(lambda ()
;; the function that may `exit'
(exit 1))
(lambda (key . args)
;; the handler
(format #t "quit: ~a~%" args)))
quit: (1)
Thanks,
Ludovic.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Trigger action at exit?
2008-03-02 17:25 ` Ludovic Courtès
@ 2008-03-03 20:44 ` Neil Jerram
2008-03-03 21:53 ` Ludovic Courtès
2008-03-03 21:22 ` John Trammell
1 sibling, 1 reply; 12+ messages in thread
From: Neil Jerram @ 2008-03-03 20:44 UTC (permalink / raw)
To: Ludovic Courtès; +Cc: guile-user
ludo@gnu.org (Ludovic Courtès) writes:
> Hi,
>
> "John Trammell" <johntrammell@gmail.com> writes:
>
>> After a little searching I've stumbled across "add-hook! exit-hook
>> ...", but for the life of me I can't seem to get it to do what I want.
>> Here's a taste of what I'm looking for:
>>
>> (define foo (lambda () (display "foo") (newline)))
>> (add-hook! exit-hook foo)
>> (run-hook exit-hook)
>>
>> I'd like something like this, that doesn't need the explicit
>> "run-hook" at the end. Ideas?
>
> `exit-hook' is only run by the REPL, so it's not useful in programs.
>
> One possibility is to catch the `quit' exception:
>
> guile> (catch 'quit
> (lambda ()
> ;; the function that may `exit'
> (exit 1))
> (lambda (key . args)
> ;; the handler
> (format #t "quit: ~a~%" args)))
> quit: (1)
In the light of this thread, and another one about atexit in AutoGen,
perhaps we should look at providing a Scheme-level at-exit primitive?
The main point, I think, would be to guarantee that anything
registered by (at-exit ...) or scm_at_exit (...) would run _before_
Guile starts doing its own cleanups.
Regards,
Neil
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Trigger action at exit?
2008-03-02 17:25 ` Ludovic Courtès
2008-03-03 20:44 ` Neil Jerram
@ 2008-03-03 21:22 ` John Trammell
2008-03-03 21:40 ` Ludovic Courtès
1 sibling, 1 reply; 12+ messages in thread
From: John Trammell @ 2008-03-03 21:22 UTC (permalink / raw)
To: Ludovic Courtès, guile-user
On Sun, Mar 2, 2008 at 11:25 AM, Ludovic Courtès <ludo@gnu.org> wrote:
> One possibility is to catch the `quit' exception:
>
> guile> (catch 'quit
> (lambda ()
> ;; the function that may `exit'
> (exit 1))
> (lambda (key . args)
> ;; the handler
> (format #t "quit: ~a~%" args)))
> quit: (1)
>
At first blush this looked like a good answer, but it appears to be
calling the handler at the *beginning* of the script, not the end:
(define index 0)
(define has-plan #f)
(define (cleanup)
(simple-format #t "has-plan: ~a~%" has-plan)
(simple-format #t "1..~a~%" index))
(catch 'quit
(lambda () (exit 1))
(lambda (key . args)
(begin (format #t "in handler~%")
(format #t "key:~a args:~a~%" key args)
(cleanup))))
(set! has-plan #t)
(format #t "fiddle dee dee~%")
; output is:
; in handler
; key:quit args:(1)
; has-plan: #f
; 1..0
; fiddle dee dee
I find it interesting that "quit" appears to be a special key to
"catch", but I can't find it documented anywhere (I did find other
keys documented at
http://home.thezone.net/~gharvey/guile/qdocs/html/qdocs_6.html).
Googling for "guile catch key quit" (plus similar variations on
"exception(s)" and "atexit") brings no enlightenment.
Also various attempts to flush stdout do not affect the output
sequence. Am I doing something wrong? Should this work?
Thanks for the help,
JT
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Trigger action at exit?
2008-03-03 21:22 ` John Trammell
@ 2008-03-03 21:40 ` Ludovic Courtès
2008-03-03 22:27 ` John Trammell
0 siblings, 1 reply; 12+ messages in thread
From: Ludovic Courtès @ 2008-03-03 21:40 UTC (permalink / raw)
To: guile-user
Hi,
"John Trammell" <johntrammell@gmail.com> writes:
> (define (cleanup)
> (simple-format #t "has-plan: ~a~%" has-plan)
> (simple-format #t "1..~a~%" index))
>
> (catch 'quit
> (lambda () (exit 1))
> (lambda (key . args)
> (begin (format #t "in handler~%")
> (format #t "key:~a args:~a~%" key args)
> (cleanup))))
>
> (set! has-plan #t)
> (format #t "fiddle dee dee~%")
No, you would want to enclose the body of your code in `catch', i.e.,
(catch 'quit
(lambda ()
;; your code
...)
(lambda (key . args)
...))
Anyway, this isn't good, because if your code doesn't call `exit', then
the `quit' exception is never raised.
`dynamic-wind' provides a better solution, since the "handler" is called
whenever the dynamic extent of the body is left:
(dynamic-wind
(lambda ()
;; nothing to do here
#t)
(lambda ()
;; the code body
;; ...
)
(lambda ()
;; the "exit guard" or "handler", which gets called whenever the
;; body's extent is left, including via an `exit' call
))
If your code uses C code that may exit in other ways (e.g., C code that
calls `exit(3)' or similar), then you need an `atexit' similar to what
Neil described.
Thanks,
Ludovic.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Trigger action at exit?
2008-03-03 20:44 ` Neil Jerram
@ 2008-03-03 21:53 ` Ludovic Courtès
2008-03-03 22:17 ` Neil Jerram
0 siblings, 1 reply; 12+ messages in thread
From: Ludovic Courtès @ 2008-03-03 21:53 UTC (permalink / raw)
To: guile-user
Hi,
Neil Jerram <neil@ossau.uklinux.net> writes:
> In the light of this thread, and another one about atexit in AutoGen,
> perhaps we should look at providing a Scheme-level at-exit primitive?
> The main point, I think, would be to guarantee that anything
> registered by (at-exit ...) or scm_at_exit (...) would run _before_
> Guile starts doing its own cleanups.
Here, with only Scheme code, `dynamic-wind' should do the job.
In the AutoGen case, where C code is involved, that might be necessary.
OTOH, the AutoGen folks might as well be able to use `dynamic-wind', who
knows? :-)
(I tend to be reluctant to adding such a thing because it looks
fragile.)
Thanks,
Ludo'.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Trigger action at exit?
2008-03-03 21:53 ` Ludovic Courtès
@ 2008-03-03 22:17 ` Neil Jerram
2008-03-04 10:25 ` Ludovic Courtès
0 siblings, 1 reply; 12+ messages in thread
From: Neil Jerram @ 2008-03-03 22:17 UTC (permalink / raw)
To: Ludovic Courtès; +Cc: guile-user
ludo@gnu.org (Ludovic Courtès) writes:
> Hi,
>
> Neil Jerram <neil@ossau.uklinux.net> writes:
>
>> In the light of this thread, and another one about atexit in AutoGen,
>> perhaps we should look at providing a Scheme-level at-exit primitive?
>> The main point, I think, would be to guarantee that anything
>> registered by (at-exit ...) or scm_at_exit (...) would run _before_
>> Guile starts doing its own cleanups.
>
> Here, with only Scheme code, `dynamic-wind' should do the job.
>
> In the AutoGen case, where C code is involved, that might be necessary.
> OTOH, the AutoGen folks might as well be able to use `dynamic-wind', who
> knows? :-)
I agree that any main program should be able to handle its own cleanup
using dynamic-wind. What about a library, though?
> (I tend to be reluctant to adding such a thing because it looks
> fragile.)
In what sense fragile?
> Thanks,
> Ludo'.
Regards,
Neil
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Trigger action at exit?
2008-03-03 21:40 ` Ludovic Courtès
@ 2008-03-03 22:27 ` John Trammell
2008-03-05 21:27 ` Neil Jerram
0 siblings, 1 reply; 12+ messages in thread
From: John Trammell @ 2008-03-03 22:27 UTC (permalink / raw)
To: Ludovic Courtès; +Cc: guile-user
On Mon, Mar 3, 2008 at 3:40 PM, Ludovic Courtès <ludo@gnu.org> wrote:
> No, you would want to enclose the body of your code in `catch', i.e.,
>
> (catch 'quit
> (lambda ()
> ;; your code
> ...)
> (lambda (key . args)
> ...))
>
> Anyway, this isn't good, because if your code doesn't call `exit', then
> the `quit' exception is never raised.
>
> `dynamic-wind' provides a better solution, since the "handler" is called
> whenever the dynamic extent of the body is left:
>
> (dynamic-wind
> (lambda ()
> ;; nothing to do here
> #t)
> (lambda ()
> ;; the code body
> ;; ...
> )
> (lambda ()
> ;; the "exit guard" or "handler", which gets called whenever the
> ;; body's extent is left, including via an `exit' call
> ))
>
> If your code uses C code that may exit in other ways (e.g., C code that
> calls `exit(3)' or similar), then you need an `atexit' similar to what
> Neil described.
Thanks, this conversation is really clarifying things for me.
Unfortunately as written neither solution is really what I'm looking
for; since I haven't been too clear I'll try to explain.
Perl uses a unit testing framework written around the "Test Anything
Protocol" aka. "TAP" (see www.testanything.org). At its simplest, TAP
consists of a header describing the number of tests to run (the
"plan"), then the results of the tests as they are run. One option is
to tell the test framework you don't know how many tests you'll be
running, and have it display the number at the end of the run ("no
plan"). Here's some working code that generates TAP output:
(use-modules (test TAP estry))
(load-from-path "atom_p.scm")
(plan 4)
(ok (atom? 'abc))
(is (atom? 'abc) #t)
(isnt (atom? '()) #t)
(isnt (atom? '(foo)) #t)
The contents of test/TAP/estry.scm are about what you'd expect--some
local state, plus exporting the helper functions (plan, ok, is, isnt,
...) to the caller.
I'd like to be able to replace "(plan 4)" with "(no-plan)" and have
the module register some handler to be run at script exit (or object
destruction, or something else) that would check state, see we need to
output a terminal plan, and do so. I'm not too keen on cluttering up
the script with a lot of extra code.
Alternatively, we could go OO if I can hook in a handler to the guile
object destruction process. Is that an option?
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Trigger action at exit?
2008-03-03 22:17 ` Neil Jerram
@ 2008-03-04 10:25 ` Ludovic Courtès
2008-03-04 17:03 ` John Trammell
2008-03-05 21:14 ` Neil Jerram
0 siblings, 2 replies; 12+ messages in thread
From: Ludovic Courtès @ 2008-03-04 10:25 UTC (permalink / raw)
To: guile-user
Hi,
Neil Jerram <neil@ossau.uklinux.net> writes:
> I agree that any main program should be able to handle its own cleanup
> using dynamic-wind. What about a library, though?
Yes, you're right, and that's exactly what happens with John's "TAP"
modules.
At the same time, registering an `atexit' function from within the TAP
module seems inelegant: it assumes that the TAP module is used by
standalone programs only, and that exactly one Guile process is used for
each test that uses the module. If you decide to use a single process
to evaluate all the tests, the `atexit' trick no longer works.
To me, it would look better if each test case had to insert, say, a
`(finish-test)' call at its end, even if it adds more lines. That's
roughly what happens with SRFI-64: `test-end' must be invoked and in
addition, you may want to finish your standalone scripts with something
like `(exit (= (test-runner-fail-count (test-runner-current)) 0))'.
> In what sense fragile?
As in the example above, for instance.
More generally, `atexit' hooks are used only for their side effects, so
the order in which they are invoked is crucial. However, it may often
be hard to know exactly in what order or when a given hook will be
called, because you don't necessarily know what hooks have been
registered.
Not to mention the interaction with the C `atexit', `on_exit', `exit',
`_exit', etc.
Now, I'd like to see why/how AutoGen uses it.
Thanks,
Ludovic.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Trigger action at exit?
2008-03-04 10:25 ` Ludovic Courtès
@ 2008-03-04 17:03 ` John Trammell
2008-03-05 21:14 ` Neil Jerram
1 sibling, 0 replies; 12+ messages in thread
From: John Trammell @ 2008-03-04 17:03 UTC (permalink / raw)
To: Ludovic Courtès; +Cc: guile-user
On Tue, Mar 4, 2008 at 4:25 AM, Ludovic Courtès <ludo@gnu.org> wrote:
[snip]
> ... it assumes that the TAP module is used by
> standalone programs only, and that exactly one Guile process is used for
> each test that uses the module. If you decide to use a single process
> to evaluate all the tests, the `atexit' trick no longer works.
That is precisely the environment in which the tests are required to
run. Thanks again for the enlightening and informative discussion.
JT
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Trigger action at exit?
2008-03-04 10:25 ` Ludovic Courtès
2008-03-04 17:03 ` John Trammell
@ 2008-03-05 21:14 ` Neil Jerram
1 sibling, 0 replies; 12+ messages in thread
From: Neil Jerram @ 2008-03-05 21:14 UTC (permalink / raw)
To: Ludovic Courtès; +Cc: guile-user
ludo@gnu.org (Ludovic Courtès) writes:
> At the same time, registering an `atexit' function from within the TAP
> module seems inelegant: it assumes that the TAP module is used by
> standalone programs only, and that exactly one Guile process is used for
> each test that uses the module. If you decide to use a single process
> to evaluate all the tests, the `atexit' trick no longer works.
Thanks for explaining. I see what you mean now.
> To me, it would look better if each test case had to insert, say, a
> `(finish-test)' call at its end, even if it adds more lines. That's
> roughly what happens with SRFI-64: `test-end' must be invoked and in
> addition, you may want to finish your standalone scripts with something
> like `(exit (= (test-runner-fail-count (test-runner-current)) 0))'.
I have another suggestion along these lines, but I'll make that in
response to the relevant email from John.
> More generally, `atexit' hooks are used only for their side effects, so
> the order in which they are invoked is crucial. However, it may often
> be hard to know exactly in what order or when a given hook will be
> called, because you don't necessarily know what hooks have been
> registered.
Agreed - although if a library is dealing only with its own resources,
it may not matter how its atexit is ordered with those of other
libraries.
Regards,
Neil
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Trigger action at exit?
2008-03-03 22:27 ` John Trammell
@ 2008-03-05 21:27 ` Neil Jerram
0 siblings, 0 replies; 12+ messages in thread
From: Neil Jerram @ 2008-03-05 21:27 UTC (permalink / raw)
To: John Trammell; +Cc: Ludovic Courtès, guile-user
"John Trammell" <johntrammell@gmail.com> writes:
> Perl uses a unit testing framework written around the "Test Anything
> Protocol" aka. "TAP" (see www.testanything.org). At its simplest, TAP
> consists of a header describing the number of tests to run (the
> "plan"), then the results of the tests as they are run. One option is
> to tell the test framework you don't know how many tests you'll be
> running, and have it display the number at the end of the run ("no
> plan"). Here's some working code that generates TAP output:
>
> (use-modules (test TAP estry))
> (load-from-path "atom_p.scm")
> (plan 4)
> (ok (atom? 'abc))
> (is (atom? 'abc) #t)
> (isnt (atom? '()) #t)
> (isnt (atom? '(foo)) #t)
Something somewhere must be deciding to load that file. If it's just
guile -s <test-file>
you could instead do
guile -l <test-file> -s tap-atexit.scm
with tap-atexit.scm containing your end of test code.
Note, though, that if there is an error in the test code, this
approach gives you no way of handling that error. So you might want
to consider writing a wrapper for the load operation, something like
this:
(define (run-test-file file-name)
(catch #t
(lambda ()
(load file-name))
(lambda (key . args)
;; do any appropriate reporting and cleanup here
)))
(run-test-file (cadr (command-line)))
If that code was in a file called tap-wrapper.scm, the Guile
invocation would then be
guile -s tap-wrapper.scm <test-file>
And once you have wrapper code like this, you could easily add the
dynamic-wind that Ludovis suggested. (Or just put the end of test
code after the catch.)
> Alternatively, we could go OO if I can hook in a handler to the guile
> object destruction process. Is that an option?
No, there isn't a "guile object" in this sense.
Regards,
Neil
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2008-03-05 21:27 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-02-29 22:30 Trigger action at exit? John Trammell
2008-03-02 17:25 ` Ludovic Courtès
2008-03-03 20:44 ` Neil Jerram
2008-03-03 21:53 ` Ludovic Courtès
2008-03-03 22:17 ` Neil Jerram
2008-03-04 10:25 ` Ludovic Courtès
2008-03-04 17:03 ` John Trammell
2008-03-05 21:14 ` Neil Jerram
2008-03-03 21:22 ` John Trammell
2008-03-03 21:40 ` Ludovic Courtès
2008-03-03 22:27 ` John Trammell
2008-03-05 21:27 ` Neil Jerram
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 read-only IMAP folder(s) and NNTP newsgroup(s).