Philopolis
Philopolis

Reputation: 555

Redirecting all output to file in Julia

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

Answers (1)

Bogumił Kamiński
Bogumił Kamiński

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 (unlike print(x) or show(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, use show(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 Symbols or strings specifying column names, or the symbol :auto to generate column names x1, x2, ... automatically.

EDIT

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 printed (or shown - 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

Related Questions