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

Confusing MethodError when display fails due to an ambiguous method call to show #18221

Open
davidagold opened this issue Aug 24, 2016 · 4 comments
Labels
domain:display and printing Aesthetics and correctness of printed representations of objects. domain:error handling Handling of exceptions by Julia or the user

Comments

@davidagold
Copy link
Contributor

I understand that in order to extend Base.show one needs to qualify the type of io in the argument signature (I encountered the following behavior while trying without the qualification...), so this shouldn't work... But the backtrace is really quite confusing, since it seems impossible that there should be a MethodError for a particular argument signature for display and yet (i) display has a method display(x) and (ii) the error occurs inside a call to display with precisely the same signature as is being complained about.

               _
   _       _ _(_)_     |  A fresh approach to technical computing
  (_)     | (_) (_)    |  Documentation: http://docs.julialang.org
   _ _   _| |_  __ _   |  Type "?help" for help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 0.5.0-rc2+0 (2016-08-12 11:25 UTC)
 _/ |\__'_|_|_|\__'_|  |  
|__/                   |  x86_64-apple-darwin15.5.0

julia> type A end

julia> Base.show(io, ::A) = print("hi")

julia> A()
Error showing value of type A:
ERROR: MethodError: no method matching display(::A)
Closest candidates are:
  display(::Any) at multimedia.jl:154
  display(::Display, ::AbstractString, ::Any) at multimedia.jl:102
  display(::AbstractString, ::Any) at multimedia.jl:103
  ...
 in display(::A) at ./multimedia.jl:158
 in print_response(::Base.Terminals.TTYTerminal, ::Any, ::Void, ::Bool, ::Bool, ::Void) at ./REPL.jl:154
 in print_response(::Base.REPL.LineEditREPL, ::Any, ::Void, ::Bool, ::Bool) at ./REPL.jl:139
 in (::Base.REPL.##22#23{Bool,Base.REPL.##33#42{Base.REPL.LineEditREPL,Base.REPL.REPLHistoryProvider},Base.REPL.LineEditREPL,Base.LineEdit.Prompt})(::Base.LineEdit.MIState, ::Base.AbstractIOBuffer{Array{UInt8,1}}, ::Bool) at ./REPL.jl:652
 in run_interface(::Base.Terminals.TTYTerminal, ::Base.LineEdit.ModalInterface) at ./LineEdit.jl:1579
 in run_frontend(::Base.REPL.LineEditREPL, ::Base.REPL.REPLBackendRef) at ./REPL.jl:903
 in run_repl(::Base.REPL.LineEditREPL, ::Base.##850#851) at ./REPL.jl:188
 in _start() at ./client.jl:360
@vtjnash
Copy link
Sponsor Member

vtjnash commented Aug 24, 2016

the confusing part is probably that the MethodError was manually thrown:

153 function display(x)
154     for i = length(displays):-1:1
155         xdisplayable(displays[i], x) &&
156             @try_display return display(displays[i], x)
157     end
158     throw(MethodError(display, (x,)))
159 end

this hides the actual error:

julia> show(A())
ERROR: MethodError: show(::Base.TTY, ::A) is ambiguous. Candidates:
  show(io, ::A) at REPL[2]:1
  show(io::IO, x::ANY<:Any) at show.jl:116
 in show(::A) at coreio.jl:3
 in eval(::Module, ::Any) at ./boot.jl:234
 in eval(::Module, ::Any) at /Users/jameson/julia/usr/lib/julia/sys-debug.dylib:?
 in eval_user_input(::Any, ::Base.REPL.REPLBackend) at REPL.jl:66
 in macro expansion; at REPL.jl:97 [inlined]
 in (::Base.REPL.##3#4{Base.REPL.REPLBackend})() at event.jl:68

@vtjnash vtjnash changed the title Confusing backtrace Confusing MethodError when display fails due to an ambiguous method call to show Aug 24, 2016
@kshyatt kshyatt added domain:io Involving the I/O subsystem: libuv, read, write, etc. domain:error handling Handling of exceptions by Julia or the user labels Aug 24, 2016
@tkelman
Copy link
Contributor

tkelman commented Aug 25, 2016

x-ref #18018

@JeffBezanson JeffBezanson added domain:display and printing Aesthetics and correctness of printed representations of objects. and removed domain:io Involving the I/O subsystem: libuv, read, write, etc. labels Jan 3, 2017
@clarkevans
Copy link
Member

clarkevans commented Jun 20, 2021

I've hit this issue. I was trying to create a dispatch for showing JavaScript values within HypertextLiteral.

julia> versioninfo()
Julia Version 1.6.1
Commit 6aaedecc44 (2021-04-23 05:59 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Celeron(R) CPU J3455 @ 1.50GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-11.0.1 (ORCJIT, goldmont)

julia> struct Wrapper content; end

julia> function Base.show(io::IO, w::Wrapper)
           print(io, "!")
           show(io, MIME"text/javascript"(), w.content)
       end

julia> Wrapper(1)
!!Error showing value of type Wrapper:
ERROR: MethodError: no method matching display(::Wrapper)
Closest candidates are:
  display(::Any) at multimedia.jl:324
  display(::AbstractDisplay, ::AbstractString, ::Any) at multimedia.jl:216
  display(::AbstractString, ::Any) at multimedia.jl:217
  ...
Stacktrace:
  [1] display(x::Any)
    @ Base.Multimedia ./multimedia.jl:335
  [2] #invokelatest#2
    @ ./essentials.jl:708 [inlined]
  [3] invokelatest
    @ ./essentials.jl:706 [inlined]
  [4] print_response(errio::IO, response::Any, show_value::Bool, have_color::Bool, specialdisplay::Union{Nothing, AbstractDisplay})
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:247
  [5] (::REPL.var"#40#41"{REPL.LineEditREPL, Pair{Any, Bool}, Bool, Bool})(io::Any)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:231
  [6] with_repl_linfo(f::Any, repl::REPL.LineEditREPL)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:462
  [7] print_response(repl::REPL.AbstractREPL, response::Any, show_value::Bool, have_color::Bool)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:229
  [8] (::REPL.var"#do_respond#61"{Bool, Bool, REPL.var"#72#82"{REPL.LineEditREPL, REPL.REPLHistoryProvider}, REPL.LineEditREPL, REPL.LineEdit.Prompt})(s::REPL.LineEdit.MIState, buf::Any, ok::Bool)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:798
  [9] #invokelatest#2
    @ ./essentials.jl:708 [inlined]
 [10] invokelatest
    @ ./essentials.jl:706 [inlined]
 [11] run_interface(terminal::REPL.Terminals.TextTerminal, m::REPL.LineEdit.ModalInterface, s::REPL.LineEdit.MIState)
    @ REPL.LineEdit /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/LineEdit.jl:2441
 [12] run_frontend(repl::REPL.LineEditREPL, backend::REPL.REPLBackendRef)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:1126
 [13] (::REPL.var"#44#49"{REPL.LineEditREPL, REPL.REPLBackendRef})()
    @ REPL ./task.jl:411

julia> print(v)
!ERROR: MethodError: no method matching show(::Base.TTY, ::MIME{Symbol("text/javascript")}, ::Int64)
Closest candidates are:
  show(::IO, ::AbstractString, ::Any) at multimedia.jl:111
  show(::IO, ::MIME{Symbol("text/csv")}, ::Any) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/DelimitedFiles/src/DelimitedFiles.jl:828
  show(::IO, ::MIME{Symbol("text/tab-separated-values")}, ::Any) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/DelimitedFiles/src/DelimitedFiles.jl:829
  ...
Stacktrace:
 [1] show(io::Base.TTY, w::Wrapper)
   @ Main ./REPL[3]:1
 [2] print(io::Base.TTY, x::Wrapper)
   @ Base ./strings/io.jl:35
 [3] print(xs::Wrapper)
   @ Base ./coreio.jl:3
 [4] top-level scope
   @ REPL[6]:1

I would expect the latter error. The former error is not easily discoverable.

I believe there is a failed attempt to "look before you leap".

function display(@nospecialize x)
    for i = length(displays):-1:1
        if xdisplayable(displays[i], x)
            try
                return display(displays[i], x)
            catch e
                isa(e, MethodError) && (e.f === display || e.f === show) ||
                    rethrow()
            end
        end
    end
    throw(MethodError(display, (x,)))
end

I think I see two issues. First, xdisplayable, which delegates to applicable, isn't actually testing to see if the object is displayable. Second, there is an assumption that display can be restarted upon failure, while this is probably not true in the general case. If you look at the test case above, you'll see I'm printing "!" to the output stream before the underlying MethodError occurs. You can see immediately before the trackback that ! is written twice. Hence, even if the recovery were possible, this approach could still produce invalid results.

This isn't high priority for me. I can use a generated function to work around the problem.

@generated function print_script(io::IO, value)
     # workaround for Julia#18221
     if hasmethod(show, Tuple{IO, MIME{Symbol("text/javascript")}, value})
         return :(show(io, MIME"text/javascript"(), value))
     end
     throw("$(value) is not showable as text/javascript")
end

This essentially bypasses the catch of MethodError above, permitting users to know that they must implement show for the given datatype.

@vtjnash
Copy link
Sponsor Member

vtjnash commented Jan 31, 2024

This is still quite terrible actually

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
domain:display and printing Aesthetics and correctness of printed representations of objects. domain:error handling Handling of exceptions by Julia or the user
Projects
None yet
Development

No branches or pull requests

6 participants