Skip to content

More convenient redirect_* methods #28679

@ssfrr

Description

@ssfrr

The usability of the redirect_stdout etc. functions got a big boost with the do versions, but there are still some gotchas that make them often inconvenient, and I think they can be improved without any breaking changes:

Sometimes you just want to throw away output

This could be written as:

redirect_stdout(devnull) do
    # noisy code here
end

This was proposed in #22879, and seems like it would need a cross-platform way to open a null file. It looks like on windows you can open("nul", "w"). So this could be implemented as something like:

function Base.redirect_stdout(dofunc::Function, ::Base.DevNullStream)
    nullfile = @static Sys.iswindows() ? "nul" : "/dev/null"
    open(nullfile, "w") do io
        redirect_stdout(dofunc, io)
    end
end

It's come up before that the redirect_ functions don't support more generic IO types

This was mentioned in the above issue, and also in #12711. It would look like:

io = IOBuffer()
redirect_stdout(io) do
    # code
end
seekstart(io)
read(io) # get the info

Capturing to a string is a multi-step process

It's common in testing to check that your code is outputting what you expect, so an easy way to capture to a string would be useful. This could be:

output = redirect_stdout() do
    # test code here
end

This implementation for both these last two could look very similar to the @capture_out macro from Suppressor.jl except that there's no need for it to be a macro (originally those were macros so that the macro didn't affect the expression's scope, but that got blown out of the water when we added the try...finally block).:

function Base.redirect_stdout(dofunc::Function, io::Union{IO,Nothing}=nothing)
    if ccall(:jl_generating_output, Cint, ()) == 0
        original_stdout = stdout
        out_rd, out_wr = redirect_stdout()
        out_reader = @async if io === nothing
            read(out_rd, String)
        else
            read(out_rd)
        end
    end

    out = io === nothing ? "" : UInt8[]

    try
        dofunc()
    finally
        if ccall(:jl_generating_output, Cint, ()) == 0
            redirect_stdout(original_stdout)
            close(out_wr)
            out = fetch(out_reader)
        end
    end

    if io === nothing
        out
    else
        write(io, out)
        nothing
    end
end

TBH I'm not 100% sure what the if ccall(:jl_generating_output, Cint, ()) == 0 checks are for, I think they came from some of @keno's code

These methods are all currently errors, so it seems like they could be candidates for a 1.x release if they seem like OK ideas.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions