Reputation: 555
I would like to know how to redirect any output of a given call to a file, so that this file contains exactly what would have normally been displayed into the REPL.
I know that the package Suppressor.jl is an interesting entry point and does almost what I need, but for instance:
using DataFrames
using Suppressor
output = @capture_out begin
dtf = DataFrame(
[8.04 9.14 7.46 6.58;
6.95 8.14 6.77 5.76;
8.33 9.26 7.81 8.47])
dtf
end
captures nothing (or more precisely, it captures ""
) in output. To be able to capture the output, we have to write print(dtf)
instead of dtf
in this code block (while the simple dtf
instruction would actually produce an output in the REPL). So it seems that any output cannot be captured by this macro @capture_out
.
Actually, when I send a given code block to Julia, I would need to redirect to a given file all that would be displayed in the console: either normal output or error messages.
What would be the best / easiest way to achieve that? And, if possible, with minimal dependencies on packages?
Upvotes: 5
Views: 2370
Reputation: 69839
The problem you are facing is described in the docstring of display
:
In general, you cannot assume that display output goes to
stdout
(unlikeprint(x)
orshow(x)
). For example, display(x) may open up a separate window with an image.display(x)
means "show x in the best way you can for the current output device(s)." If you want REPL-like text output that is guaranteed to go to stdout, useshow(stdout, "text/plain", x)
instead.
And the problem is that if you just write dtf
the output is displayed using display
.
Unfortunately I cannot recommend a simple work-around of this behavior (you could e.g. modify the destination where the display
sends its output but then you will get also e.g. terminal escape sequences which I assume you do not want).
For instance Documenter.jl needs to intercept the output to test the REPL results and what it does is quite complex unfortunately, as you can check here.
Maybe there are better solutions, but the simplest one is to do what display
documentation recommends, i.e. use show(stdout, "text/plain", x)
instead (and just replace stdout
with a custom stream).
For instance here you can check how we test output in DataFrames.jl.
PS. If you want to pass a Matrix
to DataFrame
constructor and want to generate column names automatically add :auto
argument for column names, as DataFrame
docsting explains:
It is also allowed to pass a vector of vectors or a matrix as as the first argument. In this case the second argument must be a vector of
Symbol
s or strings specifying column names, or the symbol:auto
to generate column namesx1
,x2
, ... automatically.
If you need to run external code blocks and fetch their output then the task is simpler. The only assumption is that these code blocks do not use display
explicitly (which is unlikely). In this case just use e.g. Suppresor.jl and wrap whole code block in print
like this:
julia> output = @capture_out print(begin
println("Hello")
dtf = DataFrame(
[8.04 9.14 7.46 6.58;
6.95 8.14 6.77 5.76;
8.33 9.26 7.81 8.47], :auto)
dtf
end)
"Hello\n3×4 DataFrame\n Row │ x1 x2 x3 x4\n │ Float64 Float64 Float64 Float64\n─────┼────────────────────────────────────\n 1 │ 8.04 9.14 7.46 6.58\n 2 │ 6.95 8.14 6.77 5.76\n 3 │ 8.33 9.26 7.81 8.47"
julia> println(output)
Hello
3×4 DataFrame
Row │ x1 x2 x3 x4
│ Float64 Float64 Float64 Float64
─────┼────────────────────────────────────
1 │ 8.04 9.14 7.46 6.58
2 │ 6.95 8.14 6.77 5.76
3 │ 8.33 9.26 7.81 8.47
in this way you fetch both all what is printed to stdout
within the block and the value of last expression gets print
ed (or show
n - whatever you would prefer).
As commented this will work correctly unless display
is called by the user within the block but it is unlikely as display
is normally not used explicitly.
Upvotes: 5