When any of the babashka.process/process :in, :out and :err OPTS is set to :inherit, their stream is inherited from the parent process' corresponding stream, which, for clarification, is not the same as the parent process' Clojure *in*, *out* and *err* "streams".
This at first sight might cause confusion to clojurians, because they are accustomed to think at the clojure context and thus their first expectation is for the clojure streams to be inherited. It can also cause some confusing at first puzzling effects, such as the error output of a failing babashka.process/shell call during an nREPL session to be printed by the nREPL server than send over to the REPL client (since the parent java process in this case is the nREPL server), i.e. the error message is likely to be "hidden" from the user.
Was this redirection at the java level a design decision or rather happened for convenience, or maybe both? I'm trying to ascertain whether it would be a more convenient option to inherit from the clojure streams by default or add an additional option for this or just update the documentation to elaborate just a bit more on the inheritance so that clojurians are less likely to be confused by the side effects. What do you think? :)
Looking at the code, the redirection to the java process happens in the build fn which works at the java ProcessBuilder level:
(defn- build
(^java.lang.ProcessBuilder [cmd] (build cmd nil))
(^java.lang.ProcessBuilder [^java.util.List cmd opts]
(let [
;; ...
pb (cond-> (java.lang.ProcessBuilder. ^java.util.List cmd)
dir (.directory (io/file dir))
env (set-env env)
extra-env (add-env extra-env))]
(case out
:inherit (.redirectOutput pb ProcessBuilder$Redirect/INHERIT)
:write (.redirectOutput pb (ProcessBuilder$Redirect/to (io/file out-file)))
:append (.redirectOutput pb (ProcessBuilder$Redirect/appendTo (io/file out-file)))
nil)
(case err
:inherit (.redirectError pb ProcessBuilder$Redirect/INHERIT)
:write (.redirectError pb (ProcessBuilder$Redirect/to (io/file err-file)))
:append (.redirectError pb (ProcessBuilder$Redirect/appendTo (io/file err-file)))
nil)
(case in
:inherit (.redirectInput pb ProcessBuilder$Redirect/INHERIT)
nil)
pb)))
To reproduce the Hudini effect with babashka.process/shell and Emacs:
- Jack-in to
babashka with Emacs Cider
- In the repl run
(babashka.process/shell "ls" "-----"), an error is thrown but there is no indication what has gone wrong, just that the process exited with error code 2:
clojure.lang.ExceptionInfo:
{:type :sci/error, :line 1, :column 1, :message "",
#...
}
at sci.impl.utils$rethrow_with_location_of_node.invokeStatic (utils.cljc:128)
#...
Caused by: clojure.lang.ExceptionInfo:
{:proc #object[java.lang.ProcessImpl 0x1d310663 "Process[pid=28332, exitValue=2]"], :exit 2
# ...
- The error is hidden instead in the
*nrepl-server ...* buffer:
Started nREPL server at 127.0.0.1:1667
For more info visit: https://book.babashka.org/#_nrepl
ls: unknown option -- ---
Try '/usr/bin/ls --help' for more information.
- While if we nilify the
:err OPTS we get some sense out of babashka.process/check with (babashka.process/shell {:err nil } "ls" "-----"):
clojure.lang.ExceptionInfo: ls: unknown option -- ---
Try '/usr/bin/ls --help' for more information.
;; ...
{:type :sci/error, :line 1, :column 1,
Caused by: clojure.lang.ExceptionInfo: ls: unknown option -- ---
Try '/usr/bin/ls --help' for more information.
{:proc #object[java.lang.ProcessImpl 0x3b4ce6c0 "Process[pid=22280, exitValue=2]"],
;; ...
Thanks
When any of the
babashka.process/process:in,:outand:errOPTSis set to:inherit, their stream is inherited from the parent process' corresponding stream, which, for clarification, is not the same as the parent process' Clojure*in*,*out*and*err*"streams".This at first sight might cause confusion to clojurians, because they are accustomed to think at the clojure context and thus their first expectation is for the clojure streams to be inherited. It can also cause some confusing at first puzzling effects, such as the error output of a failing
babashka.process/shellcall during an nREPL session to be printed by the nREPL server than send over to the REPL client (since the parent java process in this case is the nREPL server), i.e. the error message is likely to be "hidden" from the user.Was this redirection at the java level a design decision or rather happened for convenience, or maybe both? I'm trying to ascertain whether it would be a more convenient option to inherit from the clojure streams by default or add an additional option for this or just update the documentation to elaborate just a bit more on the inheritance so that clojurians are less likely to be confused by the side effects. What do you think? :)
Looking at the code, the redirection to the java process happens in the
buildfn which works at the java ProcessBuilder level:process/src/babashka/process.cljc
Line 221 in bd203b7
To reproduce the Hudini effect with
babashka.process/shelland Emacs:babashkawith Emacs Cider(babashka.process/shell "ls" "-----"), an error is thrown but there is no indication what has gone wrong, just that the process exited with error code 2:clojure.lang.ExceptionInfo: {:type :sci/error, :line 1, :column 1, :message "", #... } at sci.impl.utils$rethrow_with_location_of_node.invokeStatic (utils.cljc:128) #... Caused by: clojure.lang.ExceptionInfo: {:proc #object[java.lang.ProcessImpl 0x1d310663 "Process[pid=28332, exitValue=2]"], :exit 2 # ...*nrepl-server ...*buffer::errOPTS we get some sense out ofbabashka.process/checkwith(babashka.process/shell {:err nil } "ls" "-----"):Thanks