im trying the following on a datomic cloud ion server, but not getting any output:
(do (alter-var-root #'*out* (constantly *out*))
(-> (clojure.java.process/start {:out :inherit} "ls" "-lah" "/")
process/exit-ref deref))
same command works on a locally started clojure process, even without the alter-var-root hack.
this works in both contexts, but i would like to have realtime feedback on what's happening, because im debugging a process, which might be prompting for input:
(gini.clojure.java.process/exec "ls" "-lah" "/")
(`gini.clojure.java.process` is just a copy of clojure.java.process, because datomic cloud still runs on clojure 1.11.x.)
what could be the problem?i do have :handler cider.nrepl/cider-nrepl-handler passed to the nrepl.server/start-server and im on nrepl.core/version-string => "1.3.1"
my nrepl client is cursive, which might be still on nrepl v0.8.3, but im not sure how to check that.
i've also noticed there is a nrepl.middleware.print (https://nrepl.org/nrepl/usage/misc.html#pretty-printing) which i guess is present because i see a key from that ns on my repl messages:
(-> nrepl.middleware.interruptible-eval/*msg* keys)
=>
(:code :id :op :session :transport
:nrepl.middleware.print/print-fn
:nrepl.middleware.caught/caught-fn)Because the inherit option is a low level option to starting processes from the jvm which tells it to output to the processes stdout
Copy the output from the launched process to the jvms stdout
it does work as expected in the cursive local nrepl client window, though
How is the clojure process that the cursive nrepl connects to started?
i will make some slimmed down examples to reproduce the issue and based on the results, i will post it more appropriate channels. i just tried it over a socket repl against the same datomic cloud ion server process and that has the same problem, so it's not nrepl related probably.
If it is started by cursive then cursive is just showing you the process stdout at the same place it is showing printed out repl output
@hiredman i think i understand what you mean.
i don't even know how anything written to *out* is communicated via the nrepl protocol, so im pretty far from being able to solve this myself 😕
but if i write something into *out* it does appear in the cursive nrepl client window, so i thought i could just do (process/exec {:out *out*} ...), but :out is expected to be a java.lang.Process$Redirect instance, which only has .to(File) or .appendTo(File) constructors, but *out* is a java.io.PrintWriter, so im not sure how to wire them together.
A java.langProcess has methods for getting steams for the processes stdin and stdout, get the in stream from the processes stdout and copy it to *out*
for the record, something like this seems to work in local cursive repl, socket repl and remote datomic cloud nrepl connections:
(defn copy-process-output
"Copies process output to a writer using Clojure's IO functions"
[^Process process writer]
(future
(with-open [reader (io/reader (process/stdout process))]
(doseq [line (line-seq reader)]
(.println writer (str "> " line))
(.flush writer)))))
(comment
(-> (process/start "bash" "-c" "for i in $(seq 20); do echo $i; sleep 0.1; done")
(doto (copy-process-output *out*)
(-> process/exit-ref deref)))
:-)
i tried a simpler implementation - as recommended by claude opus 4.1 -, but that seems to be doing some buffering, so the output only appears at the end of the process:
(defn copy-process-output
[^Process process writer]
(future
(-> process process/stdout (io/copy writer))))The stream redirection via :inerit is implemented on the Java side, in the ProcessBuilder class. Under the hood, it writes into System/out stream, not into Clojure's *out* . CIDER middleware tries to capture System/out output too and forward it to the client, but that is achieved by substituting the current System/out with another wrapped one. I believe that ProcessHandler machinery captures the old System/out early and writes there, not into our wrapped variant. That is why you see the output in the local window (I assume that is the output of the nREPL process) and not in the "REPL" (the client).
The http://java.net package also have some CookieJar class implementation, which is hardwired to the System/getCurrentMillis so it can't be controlled in integration tests to keep it in sync with other components' time, like Datomic's :db/txInstant values...
Sounds like this System/out dependence is the same kind of silliness.