* 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-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 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-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 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: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).