-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Omit module prefix when printing types which are reachable from Main #23806
Conversation
I'm onboard with the intention here, but I don't think that making |
a61c91e
to
b8c5d19
Compare
b8c5d19
to
ded2da1
Compare
Good idea. I've updated the PR to add an
I've tried using that dummy module as a default, but that prints lots of warnings like "importing deprecated binding Base.airy into Empty" when calling
What do you mean by "context module"? In general it's hard to know what's the relevant module, as the string generated by |
test/repl.jl
Outdated
@@ -167,6 +167,21 @@ fake_repl() do stdin_write, stdout_read, repl | |||
close(t) | |||
readuntil(stdout_read, "\n\n") | |||
|
|||
# Test that module prefix is omitted when type is reachable from Main |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't find a simpler way to test display(::REPLDisplay, ...)
. If there's a cleaner solution, I'm all ears.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, as I said, you can
eval [...] in
Main
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But eval what? :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whatever you need to be run in Main
?
Instead of write(stdin_write, "module TestShowTypeREPL; export TypeA; struct TypeA end; end\n")
and write(stdin_write, "using .TestShowTypeREPL\n")
just do eval
instead?
@eval Main module TestShowTypeREPL; export TypeA; struct TypeA end; end
and @eval Main using .TestShowTypeREPL
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, that makes the code simpler. Though I haven't found a way to get the result without the whole readline
stuff.
Using
I just meant whatever the value of |
Cross-reffing JuliaDocs/Documenter.jl#574 (comment) since it is a bit related. |
Another cross ref: #24059. |
@@ -1992,7 +2007,7 @@ function showarray(io::IO, X::AbstractArray, repr::Bool = true; header = true) | |||
# override usual show method for Vector{Method}: don't abbreviate long lists | |||
io = IOContext(io, :limit => false) | |||
end | |||
(!repr && header) && print(io, summary(X)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change seems unrelated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but probably not worth a PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, and it's not completely unrelated since it's needed so that the module context is passed to summary
via io
. So it's useful on its own, but needed for this PR.
base/show.jl
Outdated
isdefined(from, sym) || return false | ||
getfield(from, sym) === getfield(parent, sym) | ||
end | ||
is_reachable(sym::Symbol, parent::Module, from::Void) = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as isdefined(nothing, :anysymbol) == false
, the 2 methods could be simplified to is_reachable(sym::Symbol, parent::Module, from::Union{Module,Void}) = isdefined(from, sym) && getfield(from, sym) === getfield(parent, sym)
. Not saying it's necessarily an improvement!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, at least the current definition is quite clear, if a bit verbose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find @rfourquet's formulation clearer, but people's brains are different, so YMMV. This isn't a public function so the name is not crucial, but this might be better called isvisible
; isreachable
sounds like there's some kind of recursive search going on, which there isn't – it's just an immediate visibility check.
Bump. I think this is good for a review. |
I will review soon! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally good, my major comment is about the criteria for when to print prefixes. I think it we should only consider visibility with respect to the given module. If no module is given, always print a fully qualified name.
base/repl/REPL.jl
Outdated
@@ -122,7 +122,7 @@ end | |||
function display(d::REPLDisplay, mime::MIME"text/plain", x) | |||
io = outstream(d.repl) | |||
Base.have_color && write(io, answer_color(d.repl)) | |||
show(IOContext(io, :limit => true), mime, x) | |||
show(IOContext(io, :limit => true, :module => Main), mime, x) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may want to allow giving the module via a keyword, but that can be added later.
base/show.jl
Outdated
isdefined(from, sym) || return false | ||
getfield(from, sym) === getfield(parent, sym) | ||
end | ||
is_reachable(sym::Symbol, parent::Module, from::Void) = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find @rfourquet's formulation clearer, but people's brains are different, so YMMV. This isn't a public function so the name is not crucial, but this might be better called isvisible
; isreachable
sounds like there's some kind of recursive search going on, which there isn't – it's just an immediate visibility check.
base/show.jl
Outdated
@@ -266,7 +274,14 @@ function show_type_name(io::IO, tn::TypeName) | |||
elseif globfunc | |||
print(io, "typeof(") | |||
end | |||
if isdefined(tn, :module) && !(is_exported_from_stdlib(sym, tn.module) || (tn.module === Main && !hidden)) | |||
# Print module prefix unless type is: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wasn't immediately clear to me without reading the code if this was a conjunction or disjunction of conditions, so maybe change the comment to this:
# Print module prefix unless type satisfies one of the following:
But see comment below which would change this to just check visibility.
base/show.jl
Outdated
if isdefined(tn, :module) && | ||
!(is_exported_from_stdlib(sym, tn.module) || | ||
is_reachable(sym, tn.module, get(io, :module, nothing)) || | ||
(tn.module === Main && !hidden)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like it might be better to only consider visibility. Doesn't that cover the other cases in the typical situations where we are displaying from Main
and the Base
has been imported? If the user asks to display something with respect to another module, wouldn't we want to only shorten names that are actually visible in that module? Why are Main
or stdlib relevant there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've just tried it, and it triggers a lot of test failures all over the place. That made me realize the IOContext
approach implies hardcoding :module => Main
in many places. For example, stringmime
calls _textreprmime(m::MIME, x) = sprint(verbose_show, m, x)
. To omit the module prefix, we need to create the IOBuffer
by hand to pass it an adequate IOContext
. There are many examples like that.
Do we really want to add this kind of thing all over the place? I'm asking before spending time to find them all. The problem is that in the end many functions do not take a IO
object, so they will hardcode Main
, which kind of defeats the use of IOContext
. Is that still worth it, rather than hardcoding Main
in a single central place?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, a good intermediate solution would be to default to Main
(i.e. use get(io, :module, Main)
instead of get(io, :module, nothing)
), but still allow passing a different :module
. That way by default we get reasonably short type names without hardcoding Main
in many places, but it can still be customized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds like a good approach: Main
as the default nothing
as a value that can be passed to have no context module. Another thing that occurs to me is allowing an array of modules to look in and having []
indicate not to look anywhere, defaulting to [Main]
. But it would also make sense to allow a single module as a shortcut in that case. Maybe too complicated as a starting point.
9ee1255
to
66a31de
Compare
66a31de
to
a0b430b
Compare
I've implemented the new approach which falls back to @StefanKarpinski You haven't commented on the fact that functions are always printed with the full prefix (i.e. not change). Does that sound reasonable to you? That's kind of inconsistent, but in practice I think you're generally interested in knowing where a function is define, notably to add methods to it. But I could be convinced otherwise. |
I do think that knowing where a function comes from is useful. |
base/show.jl
Outdated
if isdefined(tn, :module) && !(is_exported_from_stdlib(sym, tn.module) || (tn.module === Main && !hidden)) | ||
# Print module prefix unless type is visible from module passed to IOContext | ||
# (defaulting to Main) | ||
if isdefined(tn, :module) && !isvisible(sym, tn.module, get(io, :module, Main)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about pulling out get(io, :module, Main)
and checking if it's nothing
instead of adding the Void
method to isvisible
? That method seems a bit iffy to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Only add the module prefix when it is actually required to access the type from the module passed to IOContext. By default, take Main as a reference.
a0b430b
to
b211a8a
Compare
I agree with Alex here: #25798 (comment), I liked the idea at first, but after some experience with it I don't really like it anymore. What do other people think? I agree that printing julia> A'
2×2 LinearAlgebra.Adjoint{Float64,Array{Float64,2}}:
0.628495 0.0341545
0.693558 0.787898 and julia> A'
2×2 Adjoint{Float64,Array{Float64,2}}:
0.742562 0.773093
0.875984 0.206749 Here I think it is informative to see the (Unrelated, but |
Personally, I've really liked the new printing because it provides that extra piece of information of: what you see is what you can access. I.e. if it's prequalified w/ |
I've found this to be incredibly pleasant to use and consider it a big success at making the way we print things more concise and intuitive. Note that I've added |
This PR gives a way to force the printing of a module (by setting I would suggest instead using This came up for me in JuliaLang/IJulia.jl#632 where we need a very compact version of a type for display in a pop-op menu. Another possibility would be to support |
Using |
You could print Foo{...} to indicate that parameters are elided. |
Passing parentmodule is a pain when printing multiple types, and impossible for nested type parameters |
Only add the module prefix when it is actually required to access the type from
Main
.As discussed on Slack some time ago, this is an attempt at improving the printing of types defined outside Base. I'm really not familiar with this, so there might be better ways of doing this. Also, I'm not sure how to test this given that tests do not run in Main.
The improvement is the most visible for types defined in packages for which the package name is just the plural of the type name. Before:
After: