Skip to content

Instantly share code, notes, and snippets.

@mortenpi
Last active November 22, 2020 10:59
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mortenpi/554d074a582748cf3af48dd25eccfcc7 to your computer and use it in GitHub Desktop.
Save mortenpi/554d074a582748cf3af48dd25eccfcc7 to your computer and use it in GitHub Desktop.
Example code to capture STDOUT and STDERR in Julia.
# A wrapper function to capture STDOUT and STDERR into strings.
#
# Heavily inspired by
# https://github.com/JuliaStats/RCall.jl/blob/c1ff136864964cf2ac04b679f0c1b3b243df7e37/src/iface.jl#L35-L46
# referred to by the issue comment
# https://github.com/JuliaLang/julia/issues/12711#issuecomment-133045787
#
# The Base.start_reading(stream, cb) requires fixes in Base.
#
function capture_streams(f)
_stdout, _stderr = STDOUT, STDERR
stdout_rd, stdout_wr = redirect_stdout()
stderr_rd, stderr_wr = redirect_stderr()
# buf combines the stdout and stderr
buf, buf_stdout, buf_stderr = IOBuffer(), IOBuffer(), IOBuffer()
# the signature of the callback is cb(stream, n)
function cb_stdout(s,n)
bytes = read(s,n)
write(buf_stdout, bytes)
write(buf, bytes)
false
end
Base.start_reading(stdout_rd, cb_stdout)
function cb_stderr(s,n)
bytes = read(s,n)
write(buf_stderr, bytes)
write(buf, bytes)
false
end
Base.start_reading(stderr_rd, cb_stderr)
ret = try
f()
catch e
println("ERROR in capture_streams(): $e")
finally
# read and restore
redirect_stdout(_stdout)
redirect_stderr(_stderr)
close(stdout_wr)
close(stderr_wr)
#stdout_buf = readstring(stdout_rd)
#stderr_buf = readstring(stderr_rd)
close(stdout_rd)
close(stderr_rd)
end
ret, takebuf_string(buf), takebuf_string(buf_stdout), takebuf_string(buf_stderr)
end
# Test / examples
function foo(x)
for n = 1:x
info("$n/$x \t[to STDERR]")
println("$n/$x \t[to STDOUT]")
end
x
end
function test_capture(f)
ret, all, out, err = capture_streams(f)
println("Return value: $(ret)")
println("------ STDOUT+STDERR -------")
println(all)
println("---------- STDOUT ----------")
println(out)
println("---------- STDERR ----------")
println(err)
println("----------------------------")
end
info("Capturing normally:")
test_capture() do
info("Calling foo()")
foo(5)
end
println(); println()
info("Capturing when error is thrown:")
try
test_capture() do
f(200) # error -- function does not exist
end
catch e
println("ERROR: $e")
end
info("Streams restored.")
Return value: 5
------ STDOUT+STDERR -------
INFO: Calling foo()
INFO: 1/5 [to STDERR]
1/5 [to STDOUT]
INFO: 2/5 [to STDERR]
2/5 [to STDOUT]
INFO: 3/5 [to STDERR]
3/5 [to STDOUT]
INFO: 4/5 [to STDERR]
4/5 [to STDOUT]
INFO: 5/5 [to STDERR]
5/5 [to STDOUT]
---------- STDOUT ----------
1/5 [to STDOUT]
2/5 [to STDOUT]
3/5 [to STDOUT]
4/5 [to STDOUT]
5/5 [to STDOUT]
---------- STDERR ----------
INFO: Calling foo()
INFO: 1/5 [to STDERR]
INFO: 2/5 [to STDERR]
INFO: 3/5 [to STDERR]
INFO: 4/5 [to STDERR]
INFO: 5/5 [to STDERR]
----------------------------
Return value: nothing
------ STDOUT+STDERR -------
ERROR in capture_streams(): UndefVarError(:f)
---------- STDOUT ----------
ERROR in capture_streams(): UndefVarError(:f)
---------- STDERR ----------
----------------------------
INFO: Capturing normally:
INFO: Capturing when error is thrown:
INFO: Streams restored.
diff --git a/base/stream.jl b/base/stream.jl
index d649b88..963b785 100644
--- a/base/stream.jl
+++ b/base/stream.jl
@@ -445,7 +445,7 @@ function notify_filled(stream::LibuvStream, nread::Int)
more = true
while more
if isa(stream.readcb,Function)
- nreadable = (stream.line_buffered ? Int(search(stream.buffer, '\n')) : nb_available(stream.buffer))
+ nreadable = (stream.line_buffered ? Int(search(stream.buffer, UInt8('\n'))) : nb_available(stream.buffer))
if nreadable > 0
more = stream.readcb(stream, nreadable)
else
@@ -674,7 +674,7 @@ function start_reading(stream::LibuvStream)
end
function start_reading(stream::LibuvStream, cb::Function)
- failure = start_reading(stream)
+ failure_code = start_reading(stream)
stream.readcb = cb
nread = nb_available(stream.buffer)
if nread > 0
@rikhuijzer
Copy link

Aha, now I get it. Thanks for the clarification 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment