@@ -47,12 +47,6 @@ It must return one of:
4747The target type `S` is never a union or the empty type, i.e. it is always a data type or
4848union-all.
4949
50- **Important.** To enable an optimization, the *Julia* object `x` may be invalidated after
51- the call to `func`, which means that the returned `ans` must not contain a reference to `x`.
52- Therefore if `ans` is a wrapper type (such as `PyList`) you may need to create a new
53- Julia object around the same Python object by calling `Py(x)` and wrap that. It is
54- recommended that wrapper types do this automatically in their inner constructor.
55-
5650### Priority
5751
5852Most rules should have priority `PYCONVERT_PRIORITY_NORMAL` (the default) which is for any
@@ -66,6 +60,17 @@ and not to a `Vector`. There should not be more than one canonical conversion ru
6660given Python type.
6761
6862Other priorities are reserved for internal use.
63+
64+ ### Optional optimization (for experts)
65+
66+ If `func(S, x::Py)` makes a successful conversion, it may call `pydel!(x)` if safe to do so.
67+ This means that the returned answer must not reference `x` in any way.
68+
69+ If you are returning a wrapper type, such as `PyList(x)`, then the object `x` should be
70+ duplicated first, as in `Py(x)`. It is recommended to force this in the inner constructor
71+ of the wrapper type.
72+
73+ If the conversion is not successful, it **must not** call `pydel!(x)`.
6974"""
7075function pyconvert_add_rule (pytypename:: String , type:: Type , func:: Function , priority:: PyConvertPriority = PYCONVERT_PRIORITY_NORMAL)
7176 @nospecialize type func
@@ -278,14 +283,16 @@ function pyconvert_rule_fast(::Type{T}, x::Py) where {T}
278283 pyconvert_unconverted ()
279284end
280285
281- pytryconvert (:: Type{T} , x) where {T} = @autopy x begin
286+ function pytryconvert (:: Type{T} , x_) where {T}
287+ # Copy the input
288+ x = Py (x_)
282289 # We can optimize the conversion for some types by overloading pytryconvert_fast.
283290 # It MUST give the same results as via the slower route using rules.
284- ans = pyconvert_rule_fast (T, getpy (x_) ) :: pyconvert_returntype (T)
291+ ans = pyconvert_rule_fast (T, x ) :: pyconvert_returntype (T)
285292 pyconvert_isunconverted (ans) || return ans
286293 # get rules from the cache
287294 # TODO : we should hold weak references and clear the cache if types get deleted
288- tptr = C. Py_Type (getptr (x_ ))
295+ tptr = C. Py_Type (getptr (x ))
289296 trules = get! (Dict{Type, Vector{PyConvertRule}}, PYCONVERT_RULES_CACHE, tptr)
290297 if ! haskey (trules, T)
291298 t = pynew (incref (tptr))
@@ -295,9 +302,11 @@ pytryconvert(::Type{T}, x) where {T} = @autopy x begin
295302 rules = trules[T]
296303 # apply the rules
297304 for rule in rules
298- ans = rule (getpy (x_) ) :: pyconvert_returntype (T)
305+ ans = rule (x ) :: pyconvert_returntype (T)
299306 pyconvert_isunconverted (ans) || return ans
300307 end
308+ # if no rule succeeded, assume nothing references x
309+ pydel! (x)
301310 return pyconvert_unconverted ()
302311end
303312
@@ -310,11 +319,13 @@ On failure, evaluates to `onfail`, which defaults to `return pyconvert_unconvert
310319"""
311320macro pyconvert (T, x, onfail= :(return $ pyconvert_unconverted ()))
312321 quote
313- ans = pytryconvert ($ (esc (T)), $ (esc (x)))
322+ T = $ (esc (T))
323+ x = $ (esc (x))
324+ ans = pytryconvert (T, x)
314325 if pyconvert_isunconverted (ans)
315326 $ (esc (onfail))
316327 else
317- pyconvert_result ($ ( esc (T)) , ans)
328+ pyconvert_result (T , ans)
318329 end
319330 end
320331end
0 commit comments