Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add capture macro for both out and err. #36

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 54 additions & 1 deletion src/Suppressor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ __precompile__()
module Suppressor

export @suppress, @suppress_out, @suppress_err
export @capture_out, @capture_err
export @capture, @capture_out, @capture_err
export @color_output

"""
Expand Down Expand Up @@ -182,6 +182,59 @@ macro capture_err(block)
end
end

"""
@capture expr

Capture the `output` and `stderr` streams for the given expression,
return a tuple of stdout and stderr.
"""
macro capture(block)
quote
if ccall(:jl_generating_output, Cint, ()) == 0
original_stdout = stdout
out_rd, out_wr = redirect_stdout()
out_reader = @async read(out_rd, String)

original_stderr = stderr
err_rd, err_wr = redirect_stderr()
err_reader = @async read(err_rd, String)



# approach adapted from https://github.com/JuliaLang/IJulia.jl/pull/667/files
logstate = Base.CoreLogging._global_logstate
logger = logstate.logger
if logger.stream == original_stderr
new_logstate = Base.CoreLogging.LogState(typeof(logger)(err_wr, logger.min_level))
Core.eval(Base.CoreLogging, Expr(:(=), :(_global_logstate), new_logstate))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hack seems quite ugly and fragile. It's quite likely to break in future versions as it relies on a lot of implementation details.

It also basically assumes that logger is a Logging.ConsoleLogger because it assumes that logger.stream exists.

I'm not sure what the intent of checking logger.stream == original_stderr, but other than that it would be better to create a new ConsoleLogger, and use the public API Logging.with_logger to install it. Then you can avoid relying on a big pile of implementation detail.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is pre-Julia-1.0 code!

with_logger makes total sense, that's probably better than using global_logger(logger)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, kind of more understandable: JuliaPluto/PlutoUI.jl#31 (comment)
However not sure if I can ignore @async ...

end
end

try
$(esc(block))
finally
if ccall(:jl_generating_output, Cint, ()) == 0
redirect_stdout(original_stdout)
close(out_wr)

redirect_stderr(original_stderr)
close(err_wr)

if logger.stream == stderr
Core.eval(Base.CoreLogging, Expr(:(=), :(_global_logstate), logstate))
end
end
end

if ccall(:jl_generating_output, Cint, ()) == 0
(fetch(out_reader),fetch(err_reader))
else
("","")
end
end
end


"""
@color_output enabled::Bool expr

Expand Down