Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

error-output cannot be retrieved from uiop's condition object #20

Open
mfiano opened this issue May 21, 2022 · 5 comments
Open

error-output cannot be retrieved from uiop's condition object #20

mfiano opened this issue May 21, 2022 · 5 comments

Comments

@mfiano
Copy link

mfiano commented May 21, 2022

I'm not sure if this is something that can be worked around in cmd, but it is not possible to get at the process-info of a condition being signalled with the synchronous uiop:run-program. My idea was that I could pass an :error-output string/stream, and get at it when handling a condition, but by that point the process has already exited, and uiop has cleaned up its state.

I guess uiop does something similar to (run-program) (when error (cleanup) (signal))), so when the subprocess-error condition is signalled, its process slot is already nil.

Is there any way to work around this? I really would like to get the stderr output in a condition handler.

@mfiano
Copy link
Author

mfiano commented May 21, 2022

Simple example:

(let ((s (make-string-output-stream)))
  (handler-case (uiop:run-program "exit 1" :error-output s)
    (uiop/run-program:subprocess-error (c) c)))
    
(inspect *)

@ruricolist
Copy link
Owner

This works for me:

(let ((s (make-string-output-stream)))
  (handler-case (uiop:run-program "echo something >&2; exit 1" :error-output s)
    (uiop/run-program:subprocess-error (c)
      (print (get-output-stream-string s)))))

@mfiano
Copy link
Author

mfiano commented May 21, 2022

Of course. s is in the lexical environment of that condition handler, but in the case that it's not, when the condition is being handled around a caller of the function calling uiop:run-program, this is where the issue is most problematic.

(in-package :supplier-package)

(defun foo (string)
  (let ((s (make-string-output-stream)))
    (cmd:$cmd "echo" string :error-output s)))

(in-package :client-package)

(defun some-user-function ()
  (handler-case (supplier-package:foo "hello")
    (uiop/run-program:subprocess-error (c)
      ;; print or resignal an error using stderr output here
    )))

@ruricolist
Copy link
Owner

I've pushed up a feature to allow overriding the null device for output and error output. E.g.:

(defun foo (string)
  (cmd:$cmd "bash -c 'echo $0; echo busted >&2; exit 1'" string))

(defun some-user-function ()
  (let ((cmd:*null-error-output* (make-string-output-stream)))
    (handler-case (foo "hello")
      (uiop/run-program:subprocess-error ()
        (princ (get-output-stream-string cmd::*null-error-output*))
        ;; print or resignal an error using stderr output here
        ))))

I agree it would be better if when there was an error we got back an error object containing the stderr. I have to think more about how to do this in a way that (1) doesn't store unbounded amounts of error output, (2) works with launch-program's handling of error output (e.g. :if-error-output-does-not exist) and (3) works even if the user has explicitly redirected stderr.

@ruricolist
Copy link
Owner

I have a branch up (https://github.com/ruricolist/cmd/tree/stderr-temporary) that partially handles this by storing error output in a temporary file if no stderr is specified. It doesn't "tee" the output yet though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants