@@ -29,7 +29,23 @@ function _pycall!(ret::PyObject, o::Union{PyObject,PyPtr}, args,
29
29
end
30
30
check_pyargsptr (nargs)
31
31
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
33
49
end
34
50
35
51
"""
@@ -43,25 +59,16 @@ OTOH this py"def foo(*args): global z; z=args" doesn't trigger this.
43
59
Fortunately, this check for ob_refcnt is fast - only a few cpu clock cycles.
44
60
"""
45
61
function 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
50
69
end
51
70
end
52
71
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
-
65
72
"""
66
73
```
67
74
pysetargs!(pyargsptr::PyPtr, args...)
@@ -75,6 +82,16 @@ function pysetargs!(pyargsptr::PyPtr, args, N::Int)
75
82
end
76
83
end
77
84
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
+
78
95
"""
79
96
```
80
97
pysetarg!(pyargsptr::PyPtr, arg, i::Integer=1)
@@ -88,7 +105,6 @@ function pysetarg!(pyargsptr::PyPtr, arg, i::Integer=1)
88
105
@pycheckz ccall ((@pysym :PyTuple_SetItem ), Cint,
89
106
(PyPtr,Int,PyPtr), pyargsptr, i- 1 , pyarg)
90
107
end
91
-
92
108
"""
93
109
Lowest level version of `pycall!(ret, o, ...)`, assumes `pyargsptr` and `kw`
94
110
have all their args set to Python values, so we can just call the function `o`.
0 commit comments