JuliaPy / PythonCall.jl

Python and Julia in harmony.
https://juliapy.github.io/PythonCall.jl/stable/
MIT License
715 stars 61 forks source link

`display` broken for parametric structs with `show` referencing the struct parameters #487

Closed jlbosse closed 2 months ago

jlbosse commented 2 months ago

Affects: JuliaCall

Describe the bug display is broken for parametric structs whose Base.show references the type parameter. See the following three examples:

In a ipython REPL (or jupyter notebook) with juliacall installed do the following:

from juliacall import Main as jl

code = """
struct Struct1{T}
    x::T
end

function Base.show(io::IO, ::MIME"text/plain", t::Struct1{T}) where T
    print(io, t.x)
end

struct Struct2{T<:Number}
    x::T
end

function Base.show(io::IO, ::MIME"text/plain", t::Struct2)
    print(io, t.x)
end

struct Struct3{T<:Number}
    x::T
end

function Base.show(io::IO, ::MIME"text/plain", t::Struct3{T}) where T
    print(io, t.x)
end
"""

jl.seval(code)

a = jl.Struct1(5)
display(a) # works fine and prints '5'

a = jl.Struct2(5)
display(a) # works fine and prints '5'

a = jl.Struct3(5)
display(a) # throws a JuliaError
---------------------------------------------------------------------------
JuliaError                                Traceback (most recent call last)
File ~/PhD/Work/PauliCraftPython/pyenv/lib/python3.8/site-packages/IPython/core/formatters.py:974, in MimeBundleFormatter.__call__(self, obj, include, exclude)
    971     method = get_real_method(obj, self.print_method)
    973     if method is not None:
--> 974         return method(include=include, exclude=exclude)
    975     return None
    976 else:

File ~/.julia/packages/PythonCall/bb3ax/src/JlWrap/any.jl:326, in _repr_mimebundle_(self, include, exclude)
    324         return self._jl_callmethod($(pyjl_methodnum(pyjlany_help)), mime)
    325     def _repr_mimebundle_(self, include=None, exclude=None):
--> 326         return self._jl_callmethod($(pyjl_methodnum(pyjlany_mimebundle)), include, exclude)
    327 """, @__FILE__(), "exec"), jl.__dict__)
    328 pycopy!(pyjlanytype, jl.AnyValue)

JuliaError: TypeError: in TestStruct, in T, expected T<:Number, got Type{Any}
Stacktrace:
 [1] #s2#4
   @ ~/.julia/packages/PythonCall/bb3ax/src/Utils/Utils.jl:106 [inlined]
 [2] var"#s2#4"(T::Any, ::Any, ::Any)
   @ PythonCall.Utils ./none:0
 [3] (::Core.GeneratedFunctionStub)(::UInt64, ::LineNumberNode, ::Any, ::Vararg{Any})
   @ Core ./boot.jl:602
 [4] mimes_for(x::Any)
   @ PythonCall.Utils ~/.julia/packages/PythonCall/bb3ax/src/Utils/Utils.jl:93
 [5] pyjlany_mimebundle(self::TestStruct{Int64}, include::Py, exclude::Py)
   @ PythonCall.JlWrap ~/.julia/packages/PythonCall/bb3ax/src/JlWrap/any.jl:173
 [6] _pyjl_callmethod(f::Any, self_::Ptr{PythonCall.C.PyObject}, args_::Ptr{PythonCall.C.PyObject}, nargs::Int64)
   @ PythonCall.JlWrap ~/.julia/packages/PythonCall/bb3ax/src/JlWrap/base.jl:72
 [7] _pyjl_callmethod(o::Ptr{PythonCall.C.PyObject}, args::Ptr{PythonCall.C.PyObject})
   @ PythonCall.JlWrap.Cjl ~/.julia/packages/PythonCall/bb3ax/src/JlWrap/C.jl:63
Julia: 5

Your system Please provide detailed information about your system:

pip list
Package            Version
------------------ -----------
asttokens          2.4.1
backcall           0.2.0
comm               0.2.2
debugpy            1.8.1
decorator          5.1.1
executing          2.0.1
importlib-metadata 7.1.0
ipykernel          6.29.4
ipython            8.12.3
jedi               0.19.1
juliacall          0.9.19
juliapkg           0.1.11
jupyter-client     8.6.1
jupyter-core       5.7.2
matplotlib-inline  0.1.6
nest-asyncio       1.6.0
numpy              1.24.4
packaging          24.0
parso              0.8.3
pexpect            4.9.0
pickleshare        0.7.5
pip                20.0.2
pkg-resources      0.0.0
platformdirs       4.2.0
prompt-toolkit     3.0.43
psutil             5.9.8
ptyprocess         0.7.0
pure-eval          0.2.2
pygments           2.17.2
python-dateutil    2.9.0.post0
pyzmq              25.1.2
semantic-version   2.10.0
setuptools         44.0.0
six                1.16.0
stack-data         0.6.3
tornado            6.4
traitlets          5.14.2
typing-extensions  4.10.0
wcwidth            0.2.13
zipp               3.18.1
juliapkg.status()

JuliaPkg Status
/home/yc20910/PhD/Work/PauliCraftPython/pyenv/julia_env/pyjuliapkg/juliapkg.json (empty project)
Julia 1.10.0 @ /home/yc20910/.julia/juliaup/julia-1.10.0+0.x64.linux.gnu/bin/julia
cjdoris commented 2 months ago

Thanks, here's a reproducer for the underlying issue

julia> using PythonCall

julia> struct Struct3{T<:Number}
           x::T
       end

julia> function Base.show(io::IO, ::MIME"text/plain", t::Struct3{T}) where T
           print(io, t.x)
       end

julia> PythonCall.Utils.mimes_for(Struct3(5))
ERROR: TypeError: in Struct3, in T, expected T<:Number, got Type{Any}
Stacktrace:
 [1] #s2#4
   @ C:\Users\chris\.julia\dev\PythonCall\src\Utils\Utils.jl:106 [inlined]
 [2] var"#s2#4"(T::Any, ::Any, ::Any)
   @ PythonCall.Utils .\none:0
 [3] (::Core.GeneratedFunctionStub)(::UInt64, ::LineNumberNode, ::Any, ::Vararg{Any})
   @ Core .\boot.jl:602
 [4] mimes_for(x::Any)
   @ PythonCall.Utils C:\Users\chris\.julia\dev\PythonCall\src\Utils\Utils.jl:93
 [5] top-level scope
   @ REPL[3]:1