Skip to content

Commit 31296cd

Browse files
galenlynchstevengj
authored andcommitted
Add weakref support for pyjlwrap types (#606)
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 #21, #158 [1]: https://docs.python.org/3/extending/newtypes.html#weak-reference-support
1 parent ca792ed commit 31296cd

File tree

3 files changed

+20
-3
lines changed

3 files changed

+20
-3
lines changed

src/pyinit.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const pyxrange = Ref{PyPtr}(0)
2727
function pyjlwrap_init()
2828
# PyMemberDef stores explicit pointers, hence must be initialized at runtime
2929
push!(pyjlwrap_members, PyMemberDef(pyjlwrap_membername,
30-
T_PYSSIZET, sizeof_PyObject_HEAD, READONLY,
30+
T_PYSSIZET, sizeof_pyjlwrap_head, READONLY,
3131
pyjlwrap_doc),
3232
PyMemberDef(C_NULL,0,0,0,C_NULL))
3333

@@ -57,6 +57,7 @@ function pyjlwrap_init()
5757
t.tp_iter = pyjlwrap_getiter_ptr
5858
t.tp_hash = sizeof(Py_hash_t) < sizeof(Int) ?
5959
pyjlwrap_hash32_ptr : pyjlwrap_hash_ptr
60+
t.tp_weaklistoffset = fieldoffset(Py_jlWrap, 3)
6061
end
6162
end
6263

src/pytype.jl

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ const Py_TPFLAGS_HAVE_STACKLESS_EXTENSION_ = (0x00000003<<15)
168168
# -- most fields can default to 0 except where noted
169169

170170
const sizeof_PyObject_HEAD = sizeof(Int) + sizeof(PyPtr)
171+
const sizeof_pyjlwrap_head = sizeof_PyObject_HEAD + sizeof(PyPtr)
171172

172173
mutable struct PyTypeObject
173174
# PyObject_HEAD (for non-Py_TRACE_REFS build):
@@ -325,17 +326,22 @@ struct Py_jlWrap
325326
ob_refcnt::Int
326327
ob_type::PyPtr
327328

329+
ob_weakrefs::PyPtr
328330
jl_value::Any
329331
end
330332

331333
# destructor for jlwrap instance, assuming it was created with pyjlwrap_new
332334
function pyjlwrap_dealloc(o::PyPtr)
335+
p = convert(Ptr{PyPtr}, o)
336+
if unsafe_load(p, 3) != PyPtr_NULL
337+
ccall((@pysym :PyObject_ClearWeakRefs), Cvoid, (PyPtr,), o)
338+
end
333339
delete!(pycall_gc, o)
334340
return nothing
335341
end
336342

337343
unsafe_pyjlwrap_to_objref(o::PyPtr) =
338-
unsafe_pointer_to_objref(unsafe_load(convert(Ptr{Ptr{Cvoid}}, o), 3))
344+
unsafe_pointer_to_objref(unsafe_load(convert(Ptr{Ptr{Cvoid}}, o), 4))
339345

340346
function pyjlwrap_repr(o::PyPtr)
341347
try
@@ -440,7 +446,8 @@ function pyjlwrap_new(pyT::PyTypeObject, value::Any)
440446
pycall_gc[o.o] = value
441447
ptr = pointer_from_objref(value)
442448
end
443-
unsafe_store!(p, ptr, 3)
449+
unsafe_store!(p, C_NULL, 3)
450+
unsafe_store!(p, ptr, 4)
444451
return o
445452
end
446453

test/runtests.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,15 @@ const PyInt = pyversion < v"3" ? Int : Clonglong
393393
@test get(weakdict(Dict(3=>weakdict)),3) == weakdict
394394
end
395395

396+
# Weak ref support for pyjlwrap types
397+
let weakref = pyimport("weakref")
398+
bar = TestConstruct(1)
399+
o = PyObject(bar)
400+
@test PyCall.is_pyjlwrap(o)
401+
r = weakref[:ref](o)
402+
@test weakref[:getweakrefcount](o) == 1
403+
end
404+
396405
# Expose python docs to Julia doc system
397406
py"""
398407
def foo():

0 commit comments

Comments
 (0)