@@ -29,7 +29,23 @@ function _pycall!(ret::PyObject, o::Union{PyObject,PyPtr}, args,
2929 end
3030 check_pyargsptr (nargs)
3131 pyargsptr = pyarg_tuples[nargs+ 1 ]
32- return _pycall! (ret, pyargsptr, o, args, nargs, kw) # ::PyObject
32+ # We temporarily set pyarg_tuples[nargs+1] to NULL to ensure that nested
33+ # calls to pycall don't try to use the same tuple object (issue #533).
34+ pyarg_tuples[nargs+ 1 ] = C_NULL
35+ try
36+ pysetargs! (pyargsptr, args, nargs)
37+ return __pycall! (ret, pyargsptr, o, kw) # ::PyObject
38+ finally
39+ if nargs > 0 && unsafe_load (pyargsptr). ob_refcnt > 1
40+ # we are no longer the only reference to the tuple,
41+ # so PyTuple_SetItem will fail (see check_pyargsptr)
42+ pydecref_ (pyargsptr)
43+ else
44+ pyunsetargs! (pyargsptr, nargs) # garbage-collect args
45+ pydecref_ (pyarg_tuples[nargs+ 1 ]) # no-op if it's still NULL
46+ pyarg_tuples[nargs+ 1 ] = pyargsptr # restore tuple cache
47+ end
48+ end
3349end
3450
3551"""
@@ -43,25 +59,16 @@ OTOH this py"def foo(*args): global z; z=args" doesn't trigger this.
4359Fortunately, this check for ob_refcnt is fast - only a few cpu clock cycles.
4460"""
4561function check_pyargsptr (nargs:: Int )
46- if nargs > 0 && unsafe_load (pyarg_tuples[nargs+ 1 ]). ob_refcnt > 1
47- pydecref_ (pyarg_tuples[nargs+ 1 ])
48- pyarg_tuples[nargs+ 1 ] =
49- @pycheckn ccall ((@pysym :PyTuple_New ), PyPtr, (Int,), nargs)
62+ if nargs > 0
63+ p = pyarg_tuples[nargs+ 1 ]
64+ if p == C_NULL || unsafe_load (p). ob_refcnt > 1
65+ pydecref_ (p) # no-op for NULL p
66+ pyarg_tuples[nargs+ 1 ] =
67+ @pycheckn ccall ((@pysym :PyTuple_New ), PyPtr, (Int,), nargs)
68+ end
5069 end
5170end
5271
53- """
54- Low-level version of `pycall!(ret, o, ...)` for when `kw` is already in python
55- friendly format and you have the python tuple to hold the arguments (`pyargsptr`).
56- Sets the tuple's values to the python version of your arguments, and calls the
57- function. Sets `ret.o` to the result of the call, and returns `ret::PyObject`.
58- """
59- function _pycall! (ret:: PyObject , pyargsptr:: PyPtr , o:: Union{PyObject,PyPtr} ,
60- args, nargs:: Int = length (args), kw:: Union{Ptr{Cvoid}, PyObject} = C_NULL )
61- pysetargs! (pyargsptr, args, nargs)
62- return __pycall! (ret, pyargsptr, o, kw) # ::PyObject
63- end
64-
6572"""
6673```
6774pysetargs!(pyargsptr::PyPtr, args...)
@@ -75,6 +82,16 @@ function pysetargs!(pyargsptr::PyPtr, args, N::Int)
7582 end
7683end
7784
85+ """
86+ Decref the `N`` elements of the Python tuple `pyargsptr` by setting the
87+ elements to NULL, to ensure that they are garbage-collected.
88+ """
89+ function pyunsetargs! (pyargsptr:: PyPtr , N:: Int )
90+ for i = 1 : N
91+ @pycheckz ccall ((@pysym :PyTuple_SetItem ), Cint, (PyPtr,Int,PyPtr), pyargsptr, i- 1 , C_NULL )
92+ end
93+ end
94+
7895"""
7996```
8097pysetarg!(pyargsptr::PyPtr, arg, i::Integer=1)
@@ -88,7 +105,6 @@ function pysetarg!(pyargsptr::PyPtr, arg, i::Integer=1)
88105 @pycheckz ccall ((@pysym :PyTuple_SetItem ), Cint,
89106 (PyPtr,Int,PyPtr), pyargsptr, i- 1 , pyarg)
90107end
91-
92108"""
93109Lowest level version of `pycall!(ret, o, ...)`, assumes `pyargsptr` and `kw`
94110have all their args set to Python values, so we can just call the function `o`.
0 commit comments