diff --git a/src/PyCall.jl b/src/PyCall.jl index bd4b8493..cd3a0383 100644 --- a/src/PyCall.jl +++ b/src/PyCall.jl @@ -643,14 +643,14 @@ for (mime, method) in ((MIME"text/html", "_repr_html_"), function writemime(io::IO, mime::$mime, o::PyObject) if o.o != C_NULL && haskey(o, $method) r = pycall(o[$method], PyObject) - r.o != pynothing && return write(io, convert($T, r)) + r.o != pynothing[] && return write(io, convert($T, r)) end throw(MethodError(writemime, (io, mime, o))) end mimewritable(::$mime, o::PyObject) = o.o != C_NULL && haskey(o, $method) && let meth = o[$method] - meth.o != pynothing && - pycall(meth, PyObject).o != pynothing + meth.o != pynothing[] && + pycall(meth, PyObject).o != pynothing[] end end end diff --git a/src/conversions.jl b/src/conversions.jl index 4cb3e0ce..7ea4e8fe 100644 --- a/src/conversions.jl +++ b/src/conversions.jl @@ -20,7 +20,7 @@ PyObject(c::Complex) = PyObject(@pycheckn ccall((@pysym :PyComplex_FromDoubles), PyPtr, (Cdouble,Cdouble), real(c), imag(c))) -PyObject(n::Void) = pyerr_check("PyObject(nothing)", pyincref(pynothing)) +PyObject(n::Void) = pyerr_check("PyObject(nothing)", pyincref(pynothing[])) # conversions to Julia types from PyObject @@ -110,7 +110,7 @@ end ######################################################################### # Pointer conversions, using ctypes or PyCapsule -PyObject(p::Ptr) = py_void_p(p) +PyObject(p::Ptr) = pycall(c_void_p_Type, PyObject, UInt(p)) function convert(::Type{Ptr{Void}}, po::PyObject) if pyisinstance(po, c_void_p_Type) @@ -567,7 +567,7 @@ end # Range: integer ranges are converted to xrange, # while other ranges (<: AbstractVector) are converted to lists -xrange(start, stop, step) = pycall(pyxrange, PyObject, +xrange(start, stop, step) = pycall(pyxrange[], PyObject, start, stop, step) function PyObject{T<:Integer}(r::Range{T}) @@ -703,7 +703,7 @@ pystring_query(o::PyObject) = pyisinstance(o, @pyglobalobj PyString_Type) ? Abst # we never automatically convert to Function. pyfunction_query(o::PyObject) = Union{} -pynothing_query(o::PyObject) = o.o == pynothing ? Void : Union{} +pynothing_query(o::PyObject) = o.o == pynothing[] ? Void : Union{} # we check for "items" attr since PyMapping_Check doesn't do this (it only # checks for __getitem__) and PyMapping_Check returns true for some @@ -720,7 +720,7 @@ function pysequence_query(o::PyObject) if pyisinstance(o, @pyglobalobj :PyTuple_Type) len = @pycheckz ccall((@pysym :PySequence_Size), Int, (PyPtr,), o) return typetuple([pytype_query(PyObject(ccall((@pysym :PySequence_GetItem), PyPtr, (PyPtr,Int), o,i-1)), PyAny) for i = 1:len]) - elseif pyisinstance(o, pyxrange) + elseif pyisinstance(o, pyxrange[]) return Range elseif ispybytearray(o) return Vector{UInt8} diff --git a/src/gc.jl b/src/gc.jl index e55c48f2..76f1bebd 100644 --- a/src/gc.jl +++ b/src/gc.jl @@ -7,7 +7,7 @@ # * When we wrap a Julia object jo inside a Python object po # (e.g a numpy array), we add jo to the pycall_gc dictionary, # keyed by a weak reference to po. The Python weak reference -# allows us to register a callback function that is called +# allows us to register a callback function that is called # when po is deallocated, and this callback function removes # jo from pycall_gc so that Julia can garbage-collect it. @@ -16,8 +16,8 @@ const pycall_gc = Dict{PyPtr,Any}() function weakref_callback(callback::PyPtr, wo::PyPtr) delete!(pycall_gc, wo) ccall((@pysym :Py_DecRef), Void, (PyPtr,), wo) - ccall((@pysym :Py_IncRef), Void, (PyPtr,), pynothing) - return pynothing + ccall((@pysym :Py_IncRef), Void, (PyPtr,), pynothing[]) + return pynothing[] end const weakref_callback_obj = PyNULL() # weakref_callback Python method @@ -34,8 +34,8 @@ function pyembed(po::PyObject, jo::Any) "weakref_callback", METH_O)).o end - wo = @pycheckn ccall((@pysym :PyWeakref_NewRef), PyPtr, (PyPtr,PyPtr), + wo = @pycheckn ccall((@pysym :PyWeakref_NewRef), PyPtr, (PyPtr,PyPtr), po, weakref_callback_obj) pycall_gc[wo] = jo return po -end \ No newline at end of file +end diff --git a/src/gui.jl b/src/gui.jl index d88ac5df..eea3f55b 100644 --- a/src/gui.jl +++ b/src/gui.jl @@ -16,7 +16,7 @@ pyexists(mod) = try # Tkinter was renamed to tkinter in Python 3 function tkinter_name() - return pyversion < v"3" ? "Tkinter" : "tkinter" + return pyversion_build.major < 3 ? "Tkinter" : "tkinter" end pygui_works(gui::Symbol) = gui == :default || @@ -109,7 +109,7 @@ function qt_eventloop(QtModule="PyQt4", sec::Real=50e-3) maxtime = PyObject(50) @doevent begin app = pycall(instance, PyObject) - if app.o != pynothing + if app.o != pynothing[] app["_in_event_loop"] = true pycall(processEvents, PyObject, AllEvents, maxtime) end @@ -125,7 +125,7 @@ function wx_eventloop(sec::Real=50e-3) EventLoopActivator = wx["EventLoopActivator"] @doevent begin app = pycall(GetApp, PyObject) - if app.o != pynothing + if app.o != pynothing[] app["_in_event_loop"] = true evtloop = pycall(EventLoop, PyObject) ea = pycall(EventLoopActivator, PyObject, evtloop) @@ -146,7 +146,7 @@ function Tk_eventloop(sec::Real=50e-3) Tk = pyimport(tkinter_name()) @doevent begin root = Tk["_default_root"] - if root.o != pynothing + if root.o != pynothing[] pycall(root["update"], PyObject) end end diff --git a/src/numpy.jl b/src/numpy.jl index 540a4c89..3db7edb3 100644 --- a/src/numpy.jl +++ b/src/numpy.jl @@ -48,7 +48,7 @@ function npyinitialize() catch e error("numpy.core.multiarray required for multidimensional Array conversions - ", e) end - if pyversion < v"3.0" + if pyversion_build.major < 3 PyArray_API = @pycheck ccall((@pysym :PyCObject_AsVoidPtr), Ptr{Ptr{Void}}, (PyPtr,), npy_multiarray["_ARRAY_API"]) diff --git a/src/pydates.jl b/src/pydates.jl index 4a535414..bd9b0bb5 100644 --- a/src/pydates.jl +++ b/src/pydates.jl @@ -48,34 +48,45 @@ end const PyDate_HEAD = sizeof(Int)+sizeof(PyPtr)+sizeof(Py_hash_t)+1 # called from __init__ +const DateType = Ref{PyPtr}() +const DateTimeType = Ref{PyPtr}() +const DeltaType = Ref{PyPtr}() +const Date_FromDate = Ref{Ptr{Void}}() +const DateTime_FromDateAndTime = Ref{Ptr{Void}}() +const Delta_FromDelta = Ref{Ptr{Void}}() function init_datetime() # emulate PyDateTime_IMPORT: - global const PyDateTimeAPI = - unsafe_load(@pycheckn ccall((@pysym :PyCapsule_Import), + PyDateTimeAPI = unsafe_load(@pycheckn ccall((@pysym :PyCapsule_Import), Ptr{PyDateTime_CAPI}, (Ptr{UInt8}, Cint), "datetime.datetime_CAPI", 0)) + DateType[] = PyDateTimeAPI.DateType + DateTimeType[] = PyDateTimeAPI.DateTimeType + DeltaType[] = PyDateTimeAPI.DeltaType + Date_FromDate[] = PyDateTimeAPI.Date_FromDate + DateTime_FromDateAndTime[] = PyDateTimeAPI.DateTime_FromDateAndTime + Delta_FromDelta[] = PyDateTimeAPI.Delta_FromDelta end PyObject(d::Dates.Date) = - PyObject(@pycheckn ccall(PyDateTimeAPI.Date_FromDate, PyPtr, + PyObject(@pycheckn ccall(Date_FromDate[], PyPtr, (Cint, Cint, Cint, PyPtr), Dates.year(d), Dates.month(d), Dates.day(d), - PyDateTimeAPI.DateType)) + DateType[])) PyObject(d::Dates.DateTime) = - PyObject(@pycheckn ccall(PyDateTimeAPI.DateTime_FromDateAndTime, PyPtr, + PyObject(@pycheckn ccall(DateTime_FromDateAndTime[], PyPtr, (Cint, Cint, Cint, Cint, Cint, Cint, Cint, PyPtr, PyPtr), Dates.year(d), Dates.month(d), Dates.day(d), Dates.hour(d), Dates.minute(d), Dates.second(d), Dates.millisecond(d) * 1000, - pynothing, PyDateTimeAPI.DateTimeType)) + pynothing[], DateTimeType[])) PyDelta_FromDSU(days, seconds, useconds) = - PyObject(@pycheckn ccall(PyDateTimeAPI.Delta_FromDelta, PyPtr, + PyObject(@pycheckn ccall(Delta_FromDelta[], PyPtr, (Cint, Cint, Cint, Cint, PyPtr), days, seconds, useconds, - 1, PyDateTimeAPI.DeltaType)) + 1, DeltaType[])) PyObject(p::Dates.Day) = PyDelta_FromDSU(Int(p), 0, 0) @@ -97,11 +108,9 @@ function PyObject(p::Dates.Millisecond) PyDelta_FromDSU(d, s, ms * 1000) end -for T in (:Date, :DateTime, :Delta) - f = Symbol(string("Py", T, "_Check")) - t = Expr(:., :PyDateTimeAPI, QuoteNode(Symbol(string(T, "Type")))) - @eval $f(o::PyObject) = pyisinstance(o, $t) -end +PyDate_Check(o::PyObject) = pyisinstance(o, DateType[]) +PyDateTime_Check(o::PyObject) = pyisinstance(o, DateTimeType[]) +PyDelta_Check(o::PyObject) = pyisinstance(o, DeltaType[]) function pydate_query(o::PyObject) if PyDate_Check(o) diff --git a/src/pyinit.jl b/src/pyinit.jl index 9bfad37c..c469eaf5 100644 --- a/src/pyinit.jl +++ b/src/pyinit.jl @@ -1,10 +1,10 @@ # Initializing Python (surprisingly complicated; see also deps/build.jl) ######################################################################### -# global constants, initialized to NULL and then overwritten in __init__ + +# global PyObject constants, initialized to NULL and then overwritten in __init__ # (eventually, the ability to define global const in __init__ may go away, # and in any case this is better for type inference during precompilation) - const inspect = PyNULL() const builtin = PyNULL() const BuiltinFunctionType = PyNULL() @@ -15,6 +15,13 @@ const ufuncType = PyNULL() const format_traceback = PyNULL() const pyproperty = PyNULL() const jlfun2pyfun = PyNULL() +const c_void_p_Type = PyNULL() + +# other global constants initialized at runtime are defined via Ref +# or are simply left as non-const values +pyversion = pyversion_build # not a Ref since pyversion is exported +const pynothing = Ref{PyPtr}() +const pyxrange = Ref{PyPtr}() ######################################################################### @@ -43,16 +50,15 @@ function __init__() end # cache the Python version as a Julia VersionNumber - global const pyversion = convert(VersionNumber, - split(bytestring(ccall(@pysym(:Py_GetVersion), - Ptr{UInt8}, ())))[1]) + global pyversion = convert(VersionNumber, split(bytestring(ccall(@pysym(:Py_GetVersion), + Ptr{UInt8}, ())))[1]) if pyversion_build.major != pyversion.major error("PyCall built with Python $pyversion_build, but now using Python $pyversion; ", "you need to relaunch Julia and re-run Pkg.build(\"PyCall\")") end copy!(inspect, pyimport("inspect")) - copy!(builtin, pyimport(pyversion.major < 3 ? "__builtin__" : "builtins")) + copy!(builtin, pyimport(pyversion_build.major < 3 ? "__builtin__" : "builtins")) copy!(pyproperty, pybuiltin(:property)) pyexc_initialize() # mappings from Julia Exception types to Python exceptions @@ -62,47 +68,20 @@ function __init__() # cache Python None -- PyPtr, not PyObject, to prevent it from # being finalized prematurely on exit - global const pynothing = @pyglobalobj(:_Py_NoneStruct) + pynothing[] = @pyglobalobj(:_Py_NoneStruct) # xrange type (or range in Python 3) - global const pyxrange = @pyglobalobj(:PyRange_Type) - - # cache ctypes.c_void_p type and function if available - vpt, pvp = try - (pyimport("ctypes")["c_void_p"], - p::Ptr -> pycall(c_void_p_Type, PyObject, UInt(p))) - catch # fallback to CObject - (@pyglobalobj(:PyCObject_FromVoidPtr), - p::Ptr -> PyObject(ccall(pycobject_new, PyPtr, (Ptr{Void}, Ptr{Void}), p, C_NULL))) - end - global const c_void_p_Type = vpt - global const py_void_p = pvp + pyxrange[] = @pyglobalobj(:PyRange_Type) + + # ctypes.c_void_p for Ptr types + copy!(c_void_p_Type, pyimport("ctypes")["c_void_p"]) # traceback.format_tb function, for show(PyError) copy!(format_traceback, pyimport("traceback")["format_tb"]) - # all cfunctions must be compiled at runtime - global const jl_Function_call_ptr = - cfunction(jl_Function_call, PyPtr, (PyPtr,PyPtr,PyPtr)) - global const pyjlwrap_dealloc_ptr = cfunction(pyjlwrap_dealloc, Void, (PyPtr,)) - global const pyjlwrap_repr_ptr = cfunction(pyjlwrap_repr, PyPtr, (PyPtr,)) - global const pyjlwrap_hash_ptr = cfunction(pyjlwrap_hash, UInt, (PyPtr,)) - global const pyjlwrap_hash32_ptr = cfunction(pyjlwrap_hash32, UInt32, (PyPtr,)) - - # PyMemberDef stores explicit pointers, hence must be initialized in __init__ - global const pyjlwrap_members = - PyMemberDef[ PyMemberDef(pyjlwrap_membername, - T_PYSSIZET, sizeof_PyObject_HEAD, READONLY, - pyjlwrap_doc), - PyMemberDef(C_NULL,0,0,0,C_NULL) ] - init_datetime() pyjlwrap_init() - global const jl_FunctionType = pyjlwrap_type("PyCall.jl_Function", - t -> t.tp_call = - jl_Function_call_ptr) - # jl_FunctionType is a class, and when assigning it to an object # obj[:foo] = some_julia_function # it won't behave like a regular Python method because it's not a Python @@ -114,7 +93,7 @@ function __init__() if !already_inited # some modules (e.g. IPython) expect sys.argv to be set - if pyversion.major < 3 + if pyversion_build.major < 3 argv_s = bytestring("") argv = unsafe_convert(Ptr{UInt8}, argv_s) ccall(@pysym(:PySys_SetArgvEx), Void, (Cint,Ptr{Ptr{UInt8}},Cint), 1, &argv, 0) diff --git a/src/pytype.jl b/src/pytype.jl index 02314610..9e6e13c1 100644 --- a/src/pytype.jl +++ b/src/pytype.jl @@ -263,57 +263,6 @@ type PyTypeObject # save the tp_name Julia string so that it is not garbage-collected tp_name_save # This is a gc slot that is never read from - function PyTypeObject(name::AbstractString, basicsize::Integer, init::Function) - # figure out Py_TPFLAGS_DEFAULT, depending on Python version - Py_TPFLAGS_HAVE_STACKLESS_EXTENSION = try pyimport("stackless") - Py_TPFLAGS_HAVE_STACKLESS_EXTENSION_; catch; 0; end - Py_TPFLAGS_DEFAULT = - pyversion >= v"3.0" ? - (Py_TPFLAGS_HAVE_STACKLESS_EXTENSION | - Py_TPFLAGS_HAVE_VERSION_TAG) : - (Py_TPFLAGS_HAVE_GETCHARBUFFER | - Py_TPFLAGS_HAVE_SEQUENCE_IN | - Py_TPFLAGS_HAVE_INPLACEOPS | - Py_TPFLAGS_HAVE_RICHCOMPARE | - Py_TPFLAGS_HAVE_WEAKREFS | - Py_TPFLAGS_HAVE_ITER | - Py_TPFLAGS_HAVE_CLASS | - Py_TPFLAGS_HAVE_STACKLESS_EXTENSION | - Py_TPFLAGS_HAVE_INDEX) - # This and the `unsafe_convert` below emulate a `ccall` - name_save = Base.cconvert(Ptr{UInt8}, name) - t = new(0,C_NULL,0, - unsafe_convert(Ptr{UInt8}, name_save), - convert(Int, basicsize), 0, - C_NULL,C_NULL,C_NULL,C_NULL,C_NULL,C_NULL, # tp_dealloc ... - C_NULL,C_NULL,C_NULL, # tp_as_number... - C_NULL,C_NULL,C_NULL,C_NULL,C_NULL, # tp_hash ... - C_NULL, # tp_as_buffer - Py_TPFLAGS_DEFAULT, - C_NULL, # tp_doc - C_NULL, # tp_traverse, - C_NULL, # tp_clear - C_NULL, # tp_richcompare - 0, # tp_weaklistoffset - C_NULL,C_NULL, # tp_iter, tp_iternext - convert(Ptr{PyMethodDef}, C_NULL), # tp_methods - convert(Ptr{PyMemberDef}, C_NULL), # tp_members - convert(Ptr{PyGetSetDef}, C_NULL), # tp_getset - C_NULL, # tp_base - C_NULL,C_NULL,C_NULL,0, # tp_dict... - C_NULL,C_NULL,C_NULL,C_NULL,C_NULL, # tp_init ... - C_NULL,C_NULL,C_NULL,C_NULL,C_NULL,C_NULL, # tp_bases... - 0, # tp_version_tag - 0,0,0,C_NULL,C_NULL, # tp_allocs... - name_save) - init(t) # initialize any other fields as needed - if t.tp_new == C_NULL - t.tp_new = @pyglobal :PyType_GenericNew - end - @pycheckz ccall((@pysym :PyType_Ready), Cint, (Ptr{PyTypeObject},), &t) - ccall((@pysym :Py_IncRef), Void, (Ptr{PyTypeObject},), &t) - return t - end function PyTypeObject() new(0,C_NULL,0, C_NULL, @@ -337,6 +286,42 @@ type PyTypeObject 0,0,0,C_NULL,C_NULL, # tp_allocs... "") end + PyTypeObject(name::AbstractString, basicsize::Integer, init::Function) = + PyTypeObject!(PyTypeObject(), name, basicsize, init) +end + +# Often, PyTypeObject instances are global constants, which we initialize +# to 0 via PyTypeObject() and then initialize at runtime via PyTypeObject! +function PyTypeObject!(t::PyTypeObject, name::AbstractString, basicsize::Integer, init::Function) + t.tp_basicsize = convert(Int, basicsize) + + # figure out Py_TPFLAGS_DEFAULT, depending on Python version + t.tp_flags = # Py_TPFLAGS_DEFAULT = + pyversion_build.major >= 3 ? + (Py_TPFLAGS_HAVE_STACKLESS_EXTENSION[] | + Py_TPFLAGS_HAVE_VERSION_TAG) : + (Py_TPFLAGS_HAVE_GETCHARBUFFER | + Py_TPFLAGS_HAVE_SEQUENCE_IN | + Py_TPFLAGS_HAVE_INPLACEOPS | + Py_TPFLAGS_HAVE_RICHCOMPARE | + Py_TPFLAGS_HAVE_WEAKREFS | + Py_TPFLAGS_HAVE_ITER | + Py_TPFLAGS_HAVE_CLASS | + Py_TPFLAGS_HAVE_STACKLESS_EXTENSION[] | + Py_TPFLAGS_HAVE_INDEX) + + # Emulate the rooting behavior of a ccall: + name_save = Base.cconvert(Ptr{UInt8}, name) + t.tp_name_save = name_save + t.tp_name = unsafe_convert(Ptr{UInt8}, name_save) + + init(t) # initialize any other fields as needed + if t.tp_new == C_NULL + t.tp_new = @pyglobal :PyType_GenericNew + end + @pycheckz ccall((@pysym :PyType_Ready), Cint, (Ptr{PyTypeObject},), &t) + ccall((@pysym :Py_IncRef), Void, (Ptr{PyTypeObject},), &t) + return t end ################################################################ @@ -381,33 +366,62 @@ end # constant strings (must not be gc'ed) for pyjlwrap_members const pyjlwrap_membername = "jl_value" const pyjlwrap_doc = "Julia jl_value_t* (Any object)" +# other pointer-containing constants that need to be initialized at runtime +const pyjlwrap_members = PyMemberDef[] +const jlWrapType = PyTypeObject() +const jl_FunctionType = PyTypeObject() +const Py_TPFLAGS_HAVE_STACKLESS_EXTENSION = Ref(0x00000000) # called in __init__ function pyjlwrap_init() - global const jlWrapType = - PyTypeObject("PyCall.jlwrap", sizeof(Py_jlWrap), - t::PyTypeObject -> begin - t.tp_flags |= Py_TPFLAGS_BASETYPE - t.tp_members = pointer(pyjlwrap_members); - t.tp_dealloc = pyjlwrap_dealloc_ptr - t.tp_repr = pyjlwrap_repr_ptr - t.tp_hash = sizeof(Py_hash_t) < sizeof(Int) ? - pyjlwrap_hash32_ptr : pyjlwrap_hash_ptr - end) + # PyMemberDef stores explicit pointers, hence must be initialized at runtime + push!(pyjlwrap_members, PyMemberDef(pyjlwrap_membername, + T_PYSSIZET, sizeof_PyObject_HEAD, READONLY, + pyjlwrap_doc), + PyMemberDef(C_NULL,0,0,0,C_NULL)) + + # all cfunctions must be compiled at runtime + pyjlwrap_dealloc_ptr = cfunction(pyjlwrap_dealloc, Void, (PyPtr,)) + pyjlwrap_repr_ptr = cfunction(pyjlwrap_repr, PyPtr, (PyPtr,)) + pyjlwrap_hash_ptr = cfunction(pyjlwrap_hash, UInt, (PyPtr,)) + pyjlwrap_hash32_ptr = cfunction(pyjlwrap_hash32, UInt32, (PyPtr,)) + jl_Function_call_ptr = cfunction(jl_Function_call, PyPtr, (PyPtr,PyPtr,PyPtr)) + + # detect at runtime whether we are using Stackless Python + try + pyimport("stackless") + Py_TPFLAGS_HAVE_STACKLESS_EXTENSION[] = Py_TPFLAGS_HAVE_STACKLESS_EXTENSION_ + end + + PyTypeObject!(jlWrapType, "PyCall.jlwrap", sizeof(Py_jlWrap), + t::PyTypeObject -> begin + t.tp_flags |= Py_TPFLAGS_BASETYPE + t.tp_members = pointer(pyjlwrap_members); + t.tp_dealloc = pyjlwrap_dealloc_ptr + t.tp_repr = pyjlwrap_repr_ptr + t.tp_hash = sizeof(Py_hash_t) < sizeof(Int) ? + pyjlwrap_hash32_ptr : pyjlwrap_hash_ptr + end) + + pyjlwrap_type!(jl_FunctionType, "PyCall.jl_Function", + t -> t.tp_call = jl_Function_call_ptr) end # use this to create a new jlwrap type, with init to set up custom members -function pyjlwrap_type(name::AbstractString, init::Function) - PyTypeObject(name, - sizeof(Py_jlWrap) + sizeof(PyPtr), # must be > base type - t::PyTypeObject -> begin +function pyjlwrap_type!(to::PyTypeObject, name::AbstractString, init::Function) + PyTypeObject!(to, name, sizeof(Py_jlWrap) + sizeof(PyPtr), # must be > base type + t::PyTypeObject -> begin t.tp_base = ccall(:jl_value_ptr, Ptr{Void}, (Ptr{PyTypeObject},), &jlWrapType) + ccall((@pysym :Py_IncRef), Void, (Ptr{PyTypeObject},), &jlWrapType) init(t) - end) + end) end +pyjlwrap_type(name::AbstractString, init::Function) = + pyjlwrap_type!(PyTypeObject(), name, init) + # Given a jlwrap type, create a new instance (and save value for gc) # (Careful: not sure if this works if value is an isbits type, # since, the jl_value_t* may be to a temporary copy. But don't need @@ -425,7 +439,7 @@ function pyjlwrap_new(x::Any) pyjlwrap_new(jlWrapType, x) end -is_pyjlwrap(o::PyObject) = ccall((@pysym :PyObject_IsInstance), Cint, (PyPtr,Ptr{PyTypeObject}), o, &jlWrapType) == 1 +is_pyjlwrap(o::PyObject) = jlWrapType.tp_new != C_NULL && ccall((@pysym :PyObject_IsInstance), Cint, (PyPtr,Ptr{PyTypeObject}), o, &jlWrapType) == 1 ################################################################ # Fallback conversion: if we don't have a better conversion function,