|
311 | 311 | getproperty(o::PyObject, s::AbstractString) = __getproperty(o, s)
|
312 | 312 | getproperty(o::PyObject, s::Symbol) = convert(PyAny, __getproperty(o, s))
|
313 | 313 |
|
| 314 | +function trygetproperty(o::PyObject, s::AbstractString, d) |
| 315 | + p = _getproperty(o, s) |
| 316 | + return p == C_NULL ? d : PyObject(p) |
| 317 | +end |
| 318 | + |
314 | 319 | propertynames(o::PyObject) = ispynull(o) ? Symbol[] : map(x->Symbol(first(x)), PyIterator{PyObject}(pycall(inspect."getmembers", PyObject, o)))
|
315 | 320 |
|
316 | 321 | # avoiding method ambiguity
|
@@ -890,20 +895,64 @@ for (mime, method) in ((MIME"text/html", "_repr_html_"),
|
890 | 895 | T = istextmime(mime()) ? AbstractString : Vector{UInt8}
|
891 | 896 | @eval begin
|
892 | 897 | function show(io::IO, mime::$mime, o::PyObject)
|
893 |
| - if !ispynull(o) && hasproperty(o, $method) |
894 |
| - r = pycall(o.$method, PyObject) |
| 898 | + m = get_real_method(o, $method) |
| 899 | + if m !== nothing |
| 900 | + r = pycall(m, PyObject) |
895 | 901 | !(r ≛ pynothing[]) && return write(io, convert($T, r))
|
896 | 902 | end
|
897 | 903 | throw(MethodError(show, (io, mime, o)))
|
898 | 904 | end
|
899 |
| - Base.showable(::$mime, o::PyObject) = |
900 |
| - !ispynull(o) && hasproperty(o, $method) && let meth = o.$method |
901 |
| - !(meth ≛ pynothing[]) && |
902 |
| - !(pycall(meth, PyObject) ≛ pynothing[]) |
903 |
| - end |
| 905 | + function Base.showable(::$mime, o::PyObject) |
| 906 | + m = get_real_method(o, $method) |
| 907 | + m === nothing && return false |
| 908 | + return !(pycall(m, PyObject) ≛ pynothing[]) |
| 909 | + end |
904 | 910 | end
|
905 | 911 | end
|
906 | 912 |
|
| 913 | +""" |
| 914 | + get_real_method(obj::PyObject, name::String) -> method::Union{PyObject,Nothing} |
| 915 | +
|
| 916 | +This is a port of `IPython.utils.dir2.get_real_method`: |
| 917 | +https://github.com/ipython/ipython/blob/7.9.0/IPython/utils/dir2.py |
| 918 | +
|
| 919 | +For Python 2-era |
| 920 | +https://github.com/ipython/ipython/blob/5.9.0/IPython/utils/dir2.py |
| 921 | +""" |
| 922 | +function get_real_method(obj, name) |
| 923 | + ispynull(obj) && return nothing |
| 924 | + @static if pyversion_build < v"3" |
| 925 | + pyisinstance(obj, @pyglobalobj :PyType_Type) && return nothing |
| 926 | + end |
| 927 | + |
| 928 | + canary = try |
| 929 | + trygetproperty(obj, "_ipython_canary_method_should_not_exist_", nothing) |
| 930 | + catch |
| 931 | + nothing |
| 932 | + end |
| 933 | + |
| 934 | + # It claimed to have an attribute it should never have |
| 935 | + canary === nothing || return nothing |
| 936 | + |
| 937 | + m = try |
| 938 | + trygetproperty(obj, name, nothing) |
| 939 | + catch |
| 940 | + nothing |
| 941 | + end |
| 942 | + m === nothing && return nothing |
| 943 | + |
| 944 | + if ( |
| 945 | + pyisinstance(obj, @pyglobalobj :PyType_Type) && |
| 946 | + !pyisinstance(m, @pyglobalobj :PyMethod_Type) |
| 947 | + ) |
| 948 | + return nothing |
| 949 | + end |
| 950 | + |
| 951 | + ccall((@pysym :PyCallable_Check), Cint, (PyPtr,), m) == 1 && return m |
| 952 | + |
| 953 | + return nothing |
| 954 | +end |
| 955 | + |
907 | 956 | #########################################################################
|
908 | 957 | # Expose Python docstrings to the Julia doc system
|
909 | 958 |
|
|
0 commit comments