You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Difference in ressurrection behavior with __del__ in py2 vs. py3 #76558
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=<Date2017-12-19.19:00:56.948>created_at=<Date2017-12-19.16:29:59.034>labels= ['type-bug', '3.7', 'docs']
title='Difference in ressurrection behavior with __del__ in py2 vs. py3'updated_at=<Date2020-04-09.15:25:33.816>user='https://github.com/EricCousineau-TRI'
Due to how PyObject_CallFinalizer is written in python3, __del__ will only ever be called once.
In my use case, I am experimenting with a feature in pybind11 to prevent slicing with Python class instances that inherit from pybind11-C++ base classes, which involves detecting when an instance loses all reference in Python (Py_REFCNT(...) == 0) but still has reference in C++ (shared_ptr::count() > 0), and reviving the Python portion when this situation happens.
In python2, I could do this without a hitch, as a resurrected object could have its __del__ method called multiple times (through tp_dealloc I believe?). But in python3, the object is marked with _PyGC_SET_FINALIZED(...), thus preventing __del__ from being called again.
You're welcome, and thank you for the prompt response!
I will say that it feels a tad odd to only have tp_finalize be called once for the entire lifetime of the object, while still having the option of it being resurrected.
Is there any way to somehow "un-mark" the object to enable this workflow that I would like to have?
My current hack is to call _PyGC_SET_FINALIZED(self, 0) - may I ask if there is a simpler way to do this?
New changeset dc5770b by Antoine Pitrou (Miss Islington (bot)) in branch '3.6': bpo-32377: improve __del__ docs and fix mention about resurrection (GH-4927) (bpo-4929) dc5770b
Well... perhaps you could create another PyObject (it's just a wrapper,
right?) since the old one doesn't have any outside references to it
remaining.
In certain cases, yes, that would be the case. I do have unittests that
(hackily) check id(o) before and after resurrection by using a weakref,
so I could relax that contract.
However, in other cases, the "wrapper" object in Python is actually a C++
base class that has been derived from in Python. If ownership of the C++
instance (which should more-or-less own the Python portion) goes solely to C++,
then the Python portion could be garbage collected, and you get a sort-of
slicing effect: pybind/pybind11#1145
Granted, within this web of issues, one alternative is to always extend the
lifetime of the Python object by the C++ portion. However, that creates a
(opaque?) reference cycle between C++ and CPython, so I am a bit afraid of what
that would do. I would prefer to make memory management conservative if
possible.
Or rather: It's something cool to try out, but I have not yet had time to dig
in :(
EricCousineau-TRI commented on Apr 9, 2020
EricCousineau-TRImannequin
MannequinAuthor
More actions
See also bpo-40240: "Expose public spelling of _PyGC_FINALIZED and _PyGC_SET_FINALIZED?"
Activity
EricCousineau-TRI commentedon Dec 19, 2017
Due to how
PyObject_CallFinalizeris written in python3,__del__will only ever be called once.In my use case, I am experimenting with a feature in
pybind11to prevent slicing with Python class instances that inherit from pybind11-C++ base classes, which involves detecting when an instance loses all reference in Python (Py_REFCNT(...) == 0) but still has reference in C++ (shared_ptr::count() > 0), and reviving the Python portion when this situation happens.In python2, I could do this without a hitch, as a resurrected object could have its
__del__method called multiple times (throughtp_deallocI believe?). But in python3, the object is marked with_PyGC_SET_FINALIZED(...), thus preventing__del__from being called again.It'd be nice to either (a) somehow allow
__del__to be called naturally without too much fuss or, at the least, (b) have this reflected in the documentation:https://docs.python.org/3/reference/datamodel.html#object.__del__
See attached
revive_test. Example execution:pitrou commentedon Dec 19, 2017
Thanks for the report. Apparently I forgot to update that piece of documentation when PEP-442 was implemented.
EricCousineau-TRI commentedon Dec 19, 2017
You're welcome, and thank you for the prompt response!
I will say that it feels a tad odd to only have
tp_finalizebe called once for the entire lifetime of the object, while still having the option of it being resurrected.Is there any way to somehow "un-mark" the object to enable this workflow that I would like to have?
My current hack is to call
_PyGC_SET_FINALIZED(self, 0)- may I ask if there is a simpler way to do this?pitrou commentedon Dec 19, 2017
Le 19/12/2017 à 17:46, Eric Cousineau a écrit :
Well... perhaps you could create another PyObject (it's just a wrapper,
right?) since the old one doesn't have any outside references to it
remaining.
Note that calling __del__ only once is also how PyPy works:
http://doc.pypy.org/en/latest/cpython_differences.html#differences-related-to-garbage-collection-strategies
If there is some demand we could expose a higher-level spelling of
_PyGC_SET_FINALIZED(self, 0).pitrou commentedon Dec 19, 2017
I've proposed a documentation improvement in #4927 . Please chime in if you see have issues with it.
pitrou commentedon Dec 19, 2017
New changeset 4b96593 by Antoine Pitrou in branch 'master':
bpo-32377: improve __del__ docs and fix mention about resurrection (bpo-4927)
4b96593
pitrou commentedon Dec 19, 2017
New changeset dc5770b by Antoine Pitrou (Miss Islington (bot)) in branch '3.6':
bpo-32377: improve __del__ docs and fix mention about resurrection (GH-4927) (bpo-4929)
dc5770b
EricCousineau-TRI commentedon Apr 9, 2020
Super late response, but for this part:
In certain cases, yes, that would be the case. I do have unittests that
(hackily) check
id(o)before and after resurrection by using aweakref,so I could relax that contract.
However, in other cases, the "wrapper" object in Python is actually a C++
base class that has been derived from in Python. If ownership of the C++
instance (which should more-or-less own the Python portion) goes solely to C++,
then the Python portion could be garbage collected, and you get a sort-of
slicing effect:
pybind/pybind11#1145
Granted, within this web of issues, one alternative is to always extend the
lifetime of the Python object by the C++ portion. However, that creates a
(opaque?) reference cycle between C++ and CPython, so I am a bit afraid of what
that would do. I would prefer to make memory management conservative if
possible.
Or rather: It's something cool to try out, but I have not yet had time to dig
in :(
EricCousineau-TRI commentedon Apr 9, 2020
See also bpo-40240: "Expose public spelling of _PyGC_FINALIZED and _PyGC_SET_FINALIZED?"