ronisbr / PrettyTables.jl

Print data in formatted tables.
MIT License
391 stars 38 forks source link

The HTML backend can fail to print to `stdout` #235

Open jakobjpeters opened 6 months ago

jakobjpeters commented 6 months ago

I found that writing a doctest using the HTML backend can fail. It was printing to the terminal, rather than whichever IO that Documenter.jl was redirecting to. I think that the issue is caused by this line. Note that the docstring for display says that "In general, you cannot assume that display output goes to stdout".

In this MWE, the output of pretty_table should get written to the pipe. Instead, it is printed on the terminal.

julia> using PrettyTables

julia> (_read, _write) = pipe = Pipe()
Pipe(RawFD(4294967295) init => RawFD(4294967295) init, 0 bytes waiting)

# note the semicolon, the table is getting printed on the terminal instead of written to `stdout`
julia> redirect_stdout(() -> pretty_table(stdout, [1,2,3,4]; backend = Val(:html)), pipe);
<table>
  <thead>
    <tr class = "header headerLastRow">
      <th style = "text-align: right;">Col. 1</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style = "text-align: right;">1</td>
    </tr>
    <tr>
      <td style = "text-align: right;">2</td>
    </tr>
    <tr>
      <td style = "text-align: right;">3</td>
    </tr>
    <tr>
      <td style = "text-align: right;">4</td>
    </tr>
  </tbody>
</table>

julia> close(_write)

julia> read(_read, String)
""

For comparison, here is the redirection working correctly:

julia> using PrettyTables

julia> (_read, _write) = pipe = Pipe()
Pipe(RawFD(4294967295) init => RawFD(4294967295) init, 0 bytes waiting)

julia> redirect_stdout(() -> pretty_table(stdout, [1,2,3,4]), pipe);

julia> close(_write)

julia> read(_read, String)
"┌────────┐\n│ Col. 1 │\n├────────┤\n│      1 │\n│      2 │\n│      3 │\n│      4 │\n└────────┘\n"
ronisbr commented 6 months ago

Hi @jakobjpeters !

Yes, I was aware of this problem. The issue is that we need that logic to make environments like Jupyter or Pluto to render the HTML content properly. I have no idea if we can properly fix this issue without compromising how PrettyTables renders in Jupyter, for example.

Besides doctests, are there any scenarios that this issue is causing problems?

jakobjpeters commented 6 months ago

Hi, thanks for the reply :)

I'm not aware of any other specific scenarios. It seems like this behavior deviates from the expected behavior of writing to the given IO, but I understand the use-case you point out. Is this documented?

This may or may not be a different issue, but I anticipated (EDIT: based on your rationale for using display when io = stdout) that the 2nd call in the notebook would also render the table as HTML. Am I using it incorrectly here? pluto_pretty_table

I would argue for the idiomatic way to print a table in a notebook would be to explicitly use the HTML as the io argument. However, I am not a notebook user so I don't know about their behaviors and have that bias.

ronisbr commented 6 months ago

Hum, I have no idea. Everything is working in Jupyter (I really do not use Pluto.jl). Maybe Pluto.jl does not use stdout to render HTML content? Anyway, we can definitely improve our support for Pluto.jl here.

One thing that we should reach is to support the front ends so that pretty_table(...; backend = Val(:html)) renders the HTML when it is supported.

ronisbr commented 6 months ago

Oh! I think I remember this case! Pluto expects an object, that's why we need to pass HTML to pretty_table. Jupyter, on the other hand, renders the output to stdout.

jakobjpeters commented 6 months ago

I setup Jupyter and it works as you described. Currently, pretty_table(HTML, ...) renders in both Pluto and Jupyter while pretty_table(...; backend = Val(:html)) only renders in Jupyter. I support some way to disentangle these behaviors so that the notebook rendering is consistent and printing HTML to stdout works correctly. A couple of methods I can think of that may accomplish this are:

Do you have any other ideas? Do you want the behavior to remain as is or would you prefer to change it (such as implementing one of these methods)?

ronisbr commented 6 months ago

Only render when using the form pretty_table(HTML, ...)

I think the user would expect that that system renders the HTML table is backend = Val(:html) is selected.

A new function for rendering in notebooks

Wouldn't this be way too confusing?

A keyword argument to specify whether or not to use display

It can be an alternative, do you have a suggestion?

Wrapping tables in a struct (PrettyTable) and using show methods (?)

I think this is not a correct move. PrettyTables is a package to render tables. It should not have a specific type to "describe" a table.

Do you have any other ideas? Do you want the behavior to remain as is or would you prefer to change it (such as implementing one of these methods)?

No, I do not! But I am 100% open to improve the user experience :)

jakobjpeters commented 6 months ago

Thanks for the feedback! I have another idea that I think satisfies both desires.

What do you think?

jakobjpeters commented 6 months ago

Another option: check to see if the current environment is IJulia or is Pluto and respond accordingly. This can also be used to enable rendering for the case that Pluto is missing.