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

Provide a simple way to print all entries in a collection #29223

Open
nalimilan opened this issue Sep 17, 2018 · 27 comments
Open

Provide a simple way to print all entries in a collection #29223

nalimilan opened this issue Sep 17, 2018 · 27 comments
Labels
display and printing Aesthetics and correctness of printed representations of objects.

Comments

@nalimilan
Copy link
Member

There is currently no simple way to print all entries in a collection (in particular in a vector) in a readable way. For example, if one wants to get all the column names in a table/data frame. show(x) prints a vector in full, but the entries are not aligned, making it quite painful to read on most terminals:

julia> show(string.("col", 1:50))
["col1", "col2", "col3", "col4", "col5", "col6", "col7", "col8", "col9", "col10", "
col11", "col12", "col13", "col14", "col15", "col16", "col17", "col18", "col19", "co
l20", "col21", "col22", "col23", "col24", "col25", "col26", "col27", "col28", "col2
9", "col30", "col31", "col32", "col33", "col34", "col35", "col36", "col37", "col38"
, "col39", "col40", "col41", "col42", "col43", "col44", "col45", "col46", "col47", 
"col48", "col49", "col50"]

Currently the only way to print all entries in a vertical layout is show(stdout, "text/plain", x). It works well, but telling newcomers to write this just to see the list of columns in their table isn't terribly convincing, and it's very hard to discover.

Can we do something about this? Intuitively I would have expected show(x) to be equivalent to show(stdout, "text/plain", x) (and have print(x) do what show(x) currently does), but it's kind of late to change this. Should we introduce a new convenience function, or a standardized keyword argument?

FWIW, this issue also exists e.g. with DataFrames, where we support a few keyword arguments to easily decide whether to print all rows/columns (JuliaData/DataFrames.jl#1506).

@nalimilan nalimilan added the display and printing Aesthetics and correctness of printed representations of objects. label Sep 17, 2018
@JeffBezanson
Copy link
Member

showall ? :trollface:

Maybe we should try to add some simple line break logic?

@nalimilan
Copy link
Member Author

showall ? :trollface:

Yeah, that would be ironic, but why not. #22847 said "This function has become pretty useless, since showing everything is basically the default." That's right, except for interactive use at the REPL. It wouldn't be totally absurd to me to have the convenience function showall(io, x) = show(IOContext(io, :limit => false), x). Or we could add varargs to show, to allow passing context properties in a concise way.

Maybe we should try to add some simple line break logic?

What do you mean?

BTW, another context where one often needs to list names or entries is when looking at BenchmarkTools results (which is currently buggy, see JuliaCI/BenchmarkTools.jl#96).

@nalimilan
Copy link
Member Author

Actually, I realize the problem with the old showall is that it didn't allow reproducing vertical layout of the REPL output, just adding :limit=>false. What's needed for that is show(IOContext(io, :limit=>false), "text/plain", x). So it was indeed essentially equivalent to show, print and repr.

@JeffBezanson
Copy link
Member

Maybe we should try to add some simple line break logic?

What do you mean?

Making the entries line up better.

@nalimilan
Copy link
Member Author

So that means making show(io, x::AbstractArray) significantly different from print(io, x)? That wouldn't be absurd, but currently the manual says that show(io, x) is for single-line representation.

@stevengj
Copy link
Member

stevengj commented Sep 20, 2018

Currently the only way to print all entries in a vertical layout is show(stdout, "text/plain", x)

You can also do println.(x);

@scheidan
Copy link
Contributor

scheidan commented Dec 5, 2018

I'd like to emphasis that the current solution (show(stdout, "text/plain", x)) is very hard to discover in the manual.

A typical reading flow trough the manual may look like this:

  1. You read about pretty printing of types and learn how show works (https://docs.julialang.org/en/v1/manual/types/#man-custom-pretty-printing-1)
  2. you jump to Multimedia IO and read more about show (https://docs.julialang.org/en/v1/base/io-network/#Base.show-Tuple{Any,Any,Any}). You learn that
    "The default MIME type is MIME"text/plain". There is a fallback definition for text/plain output that calls show with 2 arguments. Therefore, this case should be handled by defining a 2-argument show(io::IO, x::MyType) method."
    This is very confusion because it sounds as if show(io::IO, x::MyType) and show(stdout, "text/plain", x) are the same function.
  3. You learn about IOContext (https://docs.julialang.org/en/v1/base/io-network/#Base.IOContext-Tuple{IO,Pair}) and try show(IOContext(stdout, :limit => false), x) which is close but still not the solution
  4. you give up or google to find this issue :)

@RodrigoZepeda
Copy link

Currently the only way to print all entries in a vertical layout is show(stdout, "text/plain", x)

You can also do println.(x);

println.(x); doesn't work well with NamedArray effectively deleting the names. show(stdout, "text/plain", x) doesn't work well with arrays of length 20 either. The only solution for those cases is show(IOContext(stdout, :limit => false), x)

You can play with this example:

#Create named array
x = NamedArray([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],
                            (["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t"],))

println.(x)  #works but deletes names
show(stdout, "text/plain", x)     #jumps from 7 to 14 missing the middle
show(IOContext(stdout, :limit => false), x) #works showing everything

@FerreolS
Copy link

FerreolS commented Apr 10, 2021

I'd like to emphasis that the current solution (show(stdout, "text/plain", x)) is very hard to discover in the manual.

A typical reading flow trough the manual may look like this:

1. You read about pretty printing of types and learn how show works (https://docs.julialang.org/en/v1/manual/types/#man-custom-pretty-printing-1)

2. you jump to Multimedia IO and read more about `show` ([https://docs.julialang.org/en/v1/base/io-network/#Base.show-Tuple{Any,Any,Any}](https://docs.julialang.org/en/v1/base/io-network/#Base.show-Tuple%7BAny,Any,Any%7D)). You learn that
   "The default MIME type is `MIME"text/plain"`. There is a fallback definition for `text/plain` output that calls show with 2 arguments. Therefore, this case should be handled by defining a 2-argument `show(io::IO, x::MyType)` method."
   This is very confusion because it sounds as if `show(io::IO, x::MyType)` and `show(stdout, "text/plain", x)` are the same function.

3. You learn about IOContext ([https://docs.julialang.org/en/v1/base/io-network/#Base.IOContext-Tuple{IO,Pair}](https://docs.julialang.org/en/v1/base/io-network/#Base.IOContext-Tuple%7BIO,Pair%7D)) and try `show(IOContext(stdout, :limit => false), x)` which is close but still not the solution

4. you give up or google to find this issue :)

I completely agree, I have followed the same path.
I'm happy with the default but in some case in need to have a look in details to some arrays or dict.
Why not proposing a macro @showall as (excuse my partial knowledge of macro coding):

macro showall(expr) 
    quote 
        show(stdout, IOContext(stdout, :limit => false), "text/plain",$(esc(expr)))
    end
end 

giving :

julia> @showall string.("col", 1:15)
15-element Vector{String}:
 "col1"
 "col2"
 "col3"
 "col4"
 "col5"
 "col6"
 "col7"
 "col8"
 "col9"
 "col10"
 "col11"
 "col12"
 "col13"
 "col14"
 "col15"

julia> @showall x = NamedArray([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],
           (["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t"],))
20-element Named Vector{Int64}
A  │ 
───┼───
a  │  1
b  │  2
c  │  3
d  │  4
e  │  5
f  │  6
g  │  7
h  │  8
i  │  9
j  │ 10
k  │ 11
l  │ 12
m  │ 13
n  │ 14
o  │ 15
p  │ 16
q  │ 17
r  │ 18
s  │ 19
t  │ 20

instead of

julia> x = NamedArray([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],
           (["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t"],))
20-element Named Vector{Int64}
A  │ 
───┼───
a  │  1
b  │  2
c  │  3
⋮     ⋮
r  │ 18
s  │ 19
t  │ 20

julia> string.("col", 1:15)
15-element Vector{String}:
 "col1"
 "col2"
 "col3"
 ⋮
 "col13"
 "col14"
 "col15"

@singularitti
Copy link
Contributor

Is it a typo @FerreolS? Should it be

macro showall(expr) 
    quote 
        show(IOContext(stdout, :limit => false), "text/plain",$(esc(expr)))
    end
end 

@FerreolS
Copy link

FerreolS commented Oct 5, 2021

@singularitti Yes! you are right! something went wrong when I copy pasted my own startup.jl
Thanks

@LilithHafner
Copy link
Member

LilithHafner commented Jun 12, 2022

The NammedArrays example is now fixed. display(x) skips the middle and show(x) and show(stdout, "text/plain", x) show everything.

@IanButterworth
Copy link
Member

Is there a reason we've not just added @FerreolS 's #29223 (comment)

macro showall(expr) 
    quote 
        show(IOContext(stdout, :limit => false), "text/plain",$(esc(expr)))
    end
end 

It does what I think it should and I'd appreciate having it.

@stevengj
Copy link
Member

stevengj commented May 7, 2024

Why would you need a macro for this? Just showall(x) = show(stdout, "text/plain", x) … but the latter is so short that I don't quite get the need for showall.

@jariji
Copy link
Contributor

jariji commented May 7, 2024

the latter is so short that I don't quite get the need for showall.

It's more about remembering what to type than the typing itself - nothing in the text of show(stdout, "text/plain", x) makes it immediately the thing I think of when I want "show the whole thing".

Tho it is also long to type if I have to do it several times in a session.

@LilithHafner
Copy link
Member

@tecosaur, would this be at home in About.jl?

@singularitti
Copy link
Contributor

@jariji How about

showall(x) = show(IOContext(stdout, :compact => false, :limit => false), "text/plain", x)

It's the same thing.

@IanButterworth
Copy link
Member

What I really want is a way to do

julia> x
... truncated

# oh I want to see all of x in the same print style

julia> something_easy_to_remember x
... all

Nothing currently available is both easy to remember and guaranteed to show in the same format as the default show method AFAIK.

@stevengj
Copy link
Member

stevengj commented May 8, 2024

:limit => false is normally the default, so you shouldn't need to specify it explicitly, though of course it doesn't hurt.

@stevengj
Copy link
Member

stevengj commented May 8, 2024

# oh I want to see all of x in the same print style

Then using show could be problematic because you may be in an environment like IJulia with an object that displayed as text/markdown or something else.

@IanButterworth
Copy link
Member

IanButterworth commented May 8, 2024

The way I was implementing this in #47520 was setting and unsetting Base.active_repl.options.iocontext[:displaysize] = (1000, displaysize(io)[2]) as part of REPL.print_response.

The mechanism of a trailing character(s) to activate it wasn't popular, but perhaps that approach of wrapping the change in print_response could be used, given this is probably best suited as specifically a REPL show modifier, not a generic show modifier.

@tecosaur
Copy link
Contributor

tecosaur commented May 8, 2024

@tecosaur would this be at home in About.jl?

@LilithHafner I don't really think so, not without making it more complicated to use, and I'm trying to prioritise simplicity a bit.

@stevengj
Copy link
Member

stevengj commented May 8, 2024

The way I was implementing this in #47520 was setting and unsetting Base.active_repl.options.iocontext[:displaysize] = (1000, displaysize(io)[2])

That will break with non-REPL displays, and it also seems problematic to mutate global state here (e.g. potentially creating a race condition in multithreaded code).

One simple option would be to wrap the object to be displayed in an InteractiveUtils.ShowAll(x) object, with definitions like:

struct ShowAll
    x
end
Base.show(io::IO, x::ShowAll) = show(IOContext(io, :limit=>false), x.x)
Base.show(io::IO, mime::MIME, x::ShowAll) = show(IOContext(io, :limit=>false), mime, x.x)
Base.show(io::IO, mime::MIME"text/plain", x::ShowAll) = show(IOContext(io, :limit=>false), mime, x.x) # fix method ambiguity
Base.showable(mime::MIME, x::ShowAll) = showable(mime, x.x)
Base.print(io::IO, x::ShowAll) = print(IOContext(io, :limit=>false), x.x)

This works not only with the REPL but also with IJulia and other displays, and with other MIME types as long as they use the :limit mechanism of IOContext.

(InteractiveUtils seems like a good place for this. REPL sessions, IJulia sessions, and Pluto sessions all do using InteractiveUtils automatically.)

@IanButterworth
Copy link
Member

Re. the point about global state and multithreaded code, I was thinking of this as a REPL output modifier. Something that says do something different for this REPL evaluation output, so strictly serial.

ShowAll seems reasonable, but feels odd having to change your object into something else to see all its contents. It makes sense from a mechanics perspective, just might not be new user-friendly?

julia> ShowAll(x)

An alternative is to make an options REPL mode, which was discussed in the other PR and triage at that time.

julia> x
... # truncated 

julia> }
options> showall true

julia> x
... # all

@IanButterworth
Copy link
Member

An options mode would also have the benefit of being able to guide the user, for discoverability

options> show[all] # tab complete hinting etc.

options> ?
showall true/false - Show all rows in REPL output. Default false limits output based on terminal height
...

@tecosaur
Copy link
Contributor

tecosaur commented May 8, 2024

Alternatively, could it be viable to use Preferences.jl and make a nicer UI for interactively setting preferences?

@stevengj
Copy link
Member

stevengj commented May 8, 2024

ShowAll seems reasonable, but feels odd having to change your object into something else to see all its contents.

You could always hide the mechanics of this with another UI, e.g.

displayall(x) = display(ShowAll(x))

An alternative is to make an options REPL mode

This could be a UI for ShowAll in the REPL, I agree, but I would still want a mechanism like ShowAll under the hood that works for other Julia interfaces.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
display and printing Aesthetics and correctness of printed representations of objects.
Projects
None yet
Development

No branches or pull requests