Skip to content

Commit

Permalink
Add weakref support for pyjlwrap types
Browse files Browse the repository at this point in the history
I have added [weak reference support][1] for `pyjlwrap` types, following the
documentation for Python 3+. Adding weak reference support for Python 2.x seems
much the same as in 3.x, however 3.x does not use Py_TPFLAGS_HAVE_WEAKREFS,
which also does not seem necessary for 2.x. Simple testing suggests that this is
working as expected.

Closes JuliaPy#21, JuliaPy#158

[1]: https://docs.python.org/3/extending/newtypes.html#weak-reference-support
  • Loading branch information
galenlynch committed Oct 30, 2018
1 parent b9cca81 commit 5a5d90c
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 3 deletions.
3 changes: 2 additions & 1 deletion src/pyinit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const pyxrange = Ref{PyPtr}(0)
function pyjlwrap_init()
# PyMemberDef stores explicit pointers, hence must be initialized at runtime
push!(pyjlwrap_members, PyMemberDef(pyjlwrap_membername,
T_PYSSIZET, sizeof_PyObject_HEAD, READONLY,
T_PYSSIZET, sizeof_pyjlwrap_head, READONLY,
pyjlwrap_doc),
PyMemberDef(C_NULL,0,0,0,C_NULL))

Expand Down Expand Up @@ -57,6 +57,7 @@ function pyjlwrap_init()
t.tp_iter = pyjlwrap_getiter_ptr
t.tp_hash = sizeof(Py_hash_t) < sizeof(Int) ?
pyjlwrap_hash32_ptr : pyjlwrap_hash_ptr
t.tp_weaklistoffset = fieldoffset(Py_jlWrap, 3)
end
end

Expand Down
11 changes: 9 additions & 2 deletions src/pytype.jl
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ const Py_TPFLAGS_HAVE_STACKLESS_EXTENSION_ = (0x00000003<<15)
# -- most fields can default to 0 except where noted

const sizeof_PyObject_HEAD = sizeof(Int) + sizeof(PyPtr)
const sizeof_pyjlwrap_head = sizeof_PyObject_HEAD + sizeof(PyPtr)

mutable struct PyTypeObject
# PyObject_HEAD (for non-Py_TRACE_REFS build):
Expand Down Expand Up @@ -325,17 +326,22 @@ struct Py_jlWrap
ob_refcnt::Int
ob_type::PyPtr

ob_weakrefs::PyPtr
jl_value::Any
end

# destructor for jlwrap instance, assuming it was created with pyjlwrap_new
function pyjlwrap_dealloc(o::PyPtr)
p = convert(Ptr{PyPtr}, o)
if unsafe_load(p, 3) != PyPtr_NULL
ccall((@pysym :PyObject_ClearWeakRefs), Cvoid, (PyPtr,), o)
end
delete!(pycall_gc, o)
return nothing
end

unsafe_pyjlwrap_to_objref(o::PyPtr) =
unsafe_pointer_to_objref(unsafe_load(convert(Ptr{Ptr{Cvoid}}, o), 3))
unsafe_pointer_to_objref(unsafe_load(convert(Ptr{Ptr{Cvoid}}, o), 4))

function pyjlwrap_repr(o::PyPtr)
try
Expand Down Expand Up @@ -440,7 +446,8 @@ function pyjlwrap_new(pyT::PyTypeObject, value::Any)
pycall_gc[o.o] = value
ptr = pointer_from_objref(value)
end
unsafe_store!(p, ptr, 3)
unsafe_store!(p, C_NULL, 3)
unsafe_store!(p, ptr, 4)
return o
end

Expand Down
9 changes: 9 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,15 @@ const PyInt = pyversion < v"3" ? Int : Clonglong
@test get(weakdict(Dict(3=>weakdict)),3) == weakdict
end

# Weak ref support for pyjlwrap types
let weakref = pyimport("weakref")
bar = TestConstruct(1)
o = PyObject(bar)
@test PyCall.is_pyjlwrap(o)
r = weakref[:ref](o)
@test weakref[:getweakrefcount](o) == 1
end

# Expose python docs to Julia doc system
py"""
def foo():
Expand Down

0 comments on commit 5a5d90c

Please sign in to comment.