Capturing available data written to redirected STDOUT and/or STDERR

I’ve tried to write a set of functions that redirect both STDOUT and/or STDERR (capture_stderr, capture_stdout, capture_console), and return with the data as a string (we use this for testing purposes).

I had originally used readavailable, which seemed to work, except that it blocks if there is nothing available.
Then I tried to modify it to check nb_available, to see if it would block, however, even if readavailable has something to return, it is always returning 0.

Has anybody else run into this problem? (bug maybe?)
I would have thought that nb_availabile would return non-zero, if readavailable had something to return without waiting, but that is not the case.

https://github.com/JuliaLang/julia/issues/23591
https://github.com/JuliaLang/julia/issues/8762
https://github.com/JuliaLang/julia/issues/14624

Basically you put readavailable in a Task so the blocking isn’t an issue:

https://github.com/JuliaLang/IJulia.jl/blob/382c6657732435f3ef96c3113d3fc9a4339ba58e/src/stdio.jl#L46

I have a minimal example here:

https://github.com/JuliaGraphics/Gtk.jl/issues/181

Also, for the benefit of future googlers (we already chatted on gitter) - check out Suppressor.jl, which provides nice macros for suppressing and/or capturing stdout and stderr.

-s

1 Like

2022 update!

Now there’s Base.redirect_stdio, which, according to the name, should be able to redirect the input/output streams to… other streams, I guess. Since IOBuffer is “an in-memory I/O stream” (so basically an IOStream, but in-memory), I assumed it was possible to redirect output to an IOBuffer:

buf_stdout, buf_stderr = IOBuffer(), IOBuffer()
redirect_stdio(stdout=buf_stdout, stderr=buf_stderr, stdin=devnull) do
    @info "Hello!"
end

However, this doesn’t work:

MethodError: no method matching (::Base.RedirectStdStream)(::IOBuffer)
  Closest candidates are:
    (::Base.RedirectStdStream)() at stream.jl:1254
    (::Base.RedirectStdStream)(::Union{IOStream, Base.LibuvStream}) at stream.jl:1222
    (::Base.RedirectStdStream)(::Function, ::Any) at stream.jl:1417
    ...
  Stacktrace:
    [1] redirect_stdio(; stdin::Base.DevNull, stderr::IOBuffer, stdout::IOBuffer)
      @ Base ./stream.jl:1315
    [2] redirect_stdio(f::var"#9#11"{Symbol, Symbol, Matrix{Float64}}; stdin::Base.DevNull, stderr::IOBuffer, stdout::IOBuffer)
      @ Base ./stream.jl:1405
    ...

So apparently, IOBuffer, being “an in-memory I/O stream”, is actually not a proper IOStream, since it’s not possible to redirect_stdio to it.

There’s an issue about this, but it’s been open since 2015, and it doesn’t look like there’s a generally accepted solution…