summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Oudkerk <shibturn@gmail.com>2013-05-05 21:12:34 (GMT)
committerRichard Oudkerk <shibturn@gmail.com>2013-05-05 21:12:34 (GMT)
commit39b17c513ae7b9baecdc8292876683647186fee4 (patch)
tree62d11ed77e5a5b70b729d56627adcfed01fabeba
parentf3593026ded99404c2c8f6c6fbf4c20197c9830a (diff)
downloadcpython-39b17c513ae7b9baecdc8292876683647186fee4.zip
cpython-39b17c513ae7b9baecdc8292876683647186fee4.tar.gz
cpython-39b17c513ae7b9baecdc8292876683647186fee4.tar.bz2
Backout 2e446e87ac5b; it breaks the unix buildbots.
-rw-r--r--Doc/library/weakref.rst215
-rw-r--r--Lib/test/test_weakref.py152
-rw-r--r--Lib/weakref.py137
-rw-r--r--Misc/NEWS3
4 files changed, 7 insertions, 500 deletions
diff --git a/Doc/library/weakref.rst b/Doc/library/weakref.rst
index 5b5e460..ec50107 100644
--- a/Doc/library/weakref.rst
+++ b/Doc/library/weakref.rst
@@ -51,15 +51,10 @@ garbage collection. :class:`WeakSet` implements the :class:`set` interface,
but keeps weak references to its elements, just like a
:class:`WeakKeyDictionary` does.
-:class:`finalize` provides a straight forward way to register a
-cleanup function to be called when an object is garbage collected.
-This is simpler to use than setting up a callback function on a raw
-weak reference.
-
-Most programs should find that using one of these weak container types
-or :class:`finalize` is all they need -- it's not usually necessary to
-create your own weak references directly. The low-level machinery is
-exposed by the :mod:`weakref` module for the benefit of advanced uses.
+Most programs should find that using one of these weak container types is all
+they need -- it's not usually necessary to create your own weak references
+directly. The low-level machinery used by the weak dictionary implementations
+is exposed by the :mod:`weakref` module for the benefit of advanced uses.
Not all objects can be weakly referenced; those objects which can include class
instances, functions written in Python (but not in C), instance methods, sets,
@@ -122,16 +117,7 @@ Extension types can easily be made to support weak references; see
weakref. If there is no callback or if the referent of the weakref is
no longer alive then this attribute will have value ``None``.
- .. note::
-
- Like :meth:`__del__` methods, weak reference callbacks can be
- called during interpreter shutdown when module globals have been
- overwritten with :const:`None`. This can make writing robust
- weak reference callbacks a challenge. Callbacks registered
- using :class:`finalize` do not have to worry about this issue
- because they will not be run after module teardown has begun.
-
- .. versionchanged:: 3.4
+ .. versionadded:: 3.4
Added the :attr:`__callback__` attribute.
@@ -243,66 +229,6 @@ These method have the same issues as the and :meth:`keyrefs` method of
.. versionadded:: 3.4
-.. class:: finalize(obj, func, *args, **kwargs)
-
- Return a callable finalizer object which will be called when *obj*
- is garbage collected. A finalizer is *alive* until it is called
- (either explicitly or at garbage collection), and after that it is
- *dead*. Calling a live finalizer returns the result of evaluating
- ``func(*arg, **kwargs)``, whereas calling a dead finalizer returns
- :const:`None`.
-
- Exceptions raised by finalizer callbacks during garbage collection
- will be shown on the standard error output, but cannot be
- propagated. They are handled in the same way as exceptions raised
- from an object's :meth:`__del__` method or a weak reference's
- callback.
-
- When the program exits, each remaining live finalizer is called
- unless its :attr:`atexit` attribute has been set to false. They
- are called in reverse order of creation.
-
- A finalizer will never invoke its callback during the later part of
- the interpreter shutdown when module globals are liable to have
- been replaced by :const:`None`.
-
- .. method:: __call__()
-
- If *self* is alive then mark it as dead and return the result of
- calling ``func(*args, **kwargs)``. If *self* is dead then return
- :const:`None`.
-
- .. method:: detach()
-
- If *self* is alive then mark it as dead and return the tuple
- ``(obj, func, args, kwargs)``. If *self* is dead then return
- :const:`None`.
-
- .. method:: peek()
-
- If *self* is alive then return the tuple ``(obj, func, args,
- kwargs)``. If *self* is dead then return :const:`None`.
-
- .. attribute:: alive
-
- Property which is true if the finalizer is alive, false otherwise.
-
- .. attribute:: atexit
-
- A writable boolean property which by default is true. When the
- program exits, it calls all remaining live finalizers for which
- :attr:`.atexit` is true. They are called in reverse order of
- creation.
-
- .. note::
-
- It is important to ensure that *func*, *args* and *kwargs* do
- not own any references to *obj*, either directly or indirectly,
- since otherwise *obj* will never be garbage collected. In
- particular, *func* should not be a bound method of *obj*.
-
- .. versionadded:: 3.4
-
.. data:: ReferenceType
@@ -439,134 +365,3 @@ objects can still be retrieved by ID if they do.
def id2obj(oid):
return _id2obj_dict[oid]
-
-.. _finalize-examples:
-
-Finalizer Objects
------------------
-
-Often one uses :class:`finalize` to register a callback without
-bothering to keep the returned finalizer object. For instance
-
- >>> import weakref
- >>> class Object:
- ... pass
- ...
- >>> kenny = Object()
- >>> weakref.finalize(kenny, print, "You killed Kenny!") #doctest:+ELLIPSIS
- <finalize object at ...; for 'Object' at ...>
- >>> del kenny
- You killed Kenny!
-
-The finalizer can be called directly as well. However the finalizer
-will invoke the callback at most once.
-
- >>> def callback(x, y, z):
- ... print("CALLBACK")
- ... return x + y + z
- ...
- >>> obj = Object()
- >>> f = weakref.finalize(obj, callback, 1, 2, z=3)
- >>> assert f.alive
- >>> assert f() == 6
- CALLBACK
- >>> assert not f.alive
- >>> f() # callback not called because finalizer dead
- >>> del obj # callback not called because finalizer dead
-
-You can unregister a finalizer using its :meth:`~finalize.detach`
-method. This kills the finalizer and returns the arguments passed to
-the constructor when it was created.
-
- >>> obj = Object()
- >>> f = weakref.finalize(obj, callback, 1, 2, z=3)
- >>> f.detach() #doctest:+ELLIPSIS
- (<__main__.Object object ...>, <function callback ...>, (1, 2), {'z': 3})
- >>> newobj, func, args, kwargs = _
- >>> assert not f.alive
- >>> assert newobj is obj
- >>> assert func(*args, **kwargs) == 6
- CALLBACK
-
-Unless you set the :attr:`~finalize.atexit` attribute to
-:const:`False`, a finalizer will be called when the program exit if it
-is still alive. For instance
-
- >>> obj = Object()
- >>> weakref.finalize(obj, print, "obj dead or exiting") #doctest:+ELLIPSIS
- <finalize object at ...; for 'Object' at ...>
- >>> exit() #doctest:+SKIP
- obj dead or exiting
-
-
-Comparing finalizers with :meth:`__del__` methods
--------------------------------------------------
-
-Suppose we want to create a class whose instances represent temporary
-directories. The directories should be deleted with their contents
-when the first of the following events occurs:
-
-* the object is garbage collected,
-* the object's :meth:`remove` method is called, or
-* the program exits.
-
-We might try to implement the class using a :meth:`__del__` method as
-follows::
-
- class TempDir:
- def __init__(self):
- self.name = tempfile.mkdtemp()
-
- def remove(self):
- if self.name is not None:
- shutil.rmtree(self.name)
- self.name = None
-
- @property
- def removed(self):
- return self.name is None
-
- def __del__(self):
- self.remove()
-
-This solution has a couple of serious problems:
-
-* There is no guarantee that the object will be garbage collected
- before the program exists, so the directory might be left. This is
- because reference cycles containing an object with a :meth:`__del__`
- method can never be collected. And even if the :class:`TempDir`
- object is not itself part of a reference cycle, it may still be kept
- alive by some unkown uncollectable reference cycle.
-
-* The :meth:`__del__` method may be called at shutdown after the
- :mod:`shutil` module has been cleaned up, in which case
- :attr:`shutil.rmtree` will have been replaced by :const:`None`.
- This will cause the :meth:`__del__` method to fail and the directory
- will not be removed.
-
-Using finalizers we can avoid these problems::
-
- class TempDir:
- def __init__(self):
- self.name = tempfile.mkdtemp()
- self._finalizer = weakref.finalize(self, shutil.rmtree, self.name)
-
- def remove(self):
- self._finalizer()
-
- @property
- def removed(self):
- return not self._finalizer.alive
-
-Defined like this, even if a :class:`TempDir` object is part of a
-reference cycle, that reference cycle can still be garbage collected.
-If the object never gets garbage collected the finalizer will still be
-called at exit.
-
-.. note::
-
- If you create a finalizer object in a daemonic thread just as the
- the program exits then there is the possibility that the finalizer
- does not get called at exit. However, in a daemonic thread
- :func:`atexit.register`, ``try: ... finally: ...`` and ``with: ...``
- do not guarantee that cleanup occurs either.
diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py
index 551d95c..e32e248 100644
--- a/Lib/test/test_weakref.py
+++ b/Lib/test/test_weakref.py
@@ -7,15 +7,11 @@ import operator
import contextlib
import copy
-from test import support, script_helper
+from test import support
# Used in ReferencesTestCase.test_ref_created_during_del() .
ref_from_del = None
-# Used by FinalizeTestCase as a global that may be replaced by None
-# when the interpreter shuts down.
-_global_var = 'foobar'
-
class C:
def method(self):
pass
@@ -1555,151 +1551,6 @@ class WeakKeyDictionaryTestCase(mapping_tests.BasicTestMappingProtocol):
def _reference(self):
return self.__ref.copy()
-
-class FinalizeTestCase(unittest.TestCase):
-
- class A:
- pass
-
- def _collect_if_necessary(self):
- # we create no ref-cycles so in CPython no gc should be needed
- if sys.implementation.name != 'cpython':
- support.gc_collect()
-
- def test_finalize(self):
- def add(x,y,z):
- res.append(x + y + z)
- return x + y + z
-
- a = self.A()
-
- res = []
- f = weakref.finalize(a, add, 67, 43, z=89)
- self.assertEqual(f.alive, True)
- self.assertEqual(f.peek(), (a, add, (67,43), {'z':89}))
- self.assertEqual(f(), 199)
- self.assertEqual(f(), None)
- self.assertEqual(f(), None)
- self.assertEqual(f.peek(), None)
- self.assertEqual(f.detach(), None)
- self.assertEqual(f.alive, False)
- self.assertEqual(res, [199])
-
- res = []
- f = weakref.finalize(a, add, 67, 43, 89)
- self.assertEqual(f.peek(), (a, add, (67,43,89), {}))
- self.assertEqual(f.detach(), (a, add, (67,43,89), {}))
- self.assertEqual(f(), None)
- self.assertEqual(f(), None)
- self.assertEqual(f.peek(), None)
- self.assertEqual(f.detach(), None)
- self.assertEqual(f.alive, False)
- self.assertEqual(res, [])
-
- res = []
- f = weakref.finalize(a, add, x=67, y=43, z=89)
- del a
- self._collect_if_necessary()
- self.assertEqual(f(), None)
- self.assertEqual(f(), None)
- self.assertEqual(f.peek(), None)
- self.assertEqual(f.detach(), None)
- self.assertEqual(f.alive, False)
- self.assertEqual(res, [199])
-
- def test_order(self):
- a = self.A()
- res = []
-
- f1 = weakref.finalize(a, res.append, 'f1')
- f2 = weakref.finalize(a, res.append, 'f2')
- f3 = weakref.finalize(a, res.append, 'f3')
- f4 = weakref.finalize(a, res.append, 'f4')
- f5 = weakref.finalize(a, res.append, 'f5')
-
- # make sure finalizers can keep themselves alive
- del f1, f4
-
- self.assertTrue(f2.alive)
- self.assertTrue(f3.alive)
- self.assertTrue(f5.alive)
-
- self.assertTrue(f5.detach())
- self.assertFalse(f5.alive)
-
- f5() # nothing because previously unregistered
- res.append('A')
- f3() # => res.append('f3')
- self.assertFalse(f3.alive)
- res.append('B')
- f3() # nothing because previously called
- res.append('C')
- del a
- self._collect_if_necessary()
- # => res.append('f4')
- # => res.append('f2')
- # => res.append('f1')
- self.assertFalse(f2.alive)
- res.append('D')
- f2() # nothing because previously called by gc
-
- expected = ['A', 'f3', 'B', 'C', 'f4', 'f2', 'f1', 'D']
- self.assertEqual(res, expected)
-
- def test_all_freed(self):
- # we want a weakrefable subclass of weakref.finalize
- class MyFinalizer(weakref.finalize):
- pass
-
- a = self.A()
- res = []
- def callback():
- res.append(123)
- f = MyFinalizer(a, callback)
-
- wr_callback = weakref.ref(callback)
- wr_f = weakref.ref(f)
- del callback, f
-
- self.assertIsNotNone(wr_callback())
- self.assertIsNotNone(wr_f())
-
- del a
- self._collect_if_necessary()
-
- self.assertIsNone(wr_callback())
- self.assertIsNone(wr_f())
- self.assertEqual(res, [123])
-
- @classmethod
- def run_in_child(cls):
- def error():
- # Create an atexit finalizer from inside a finalizer called
- # at exit. This should be the next to be run.
- g1 = weakref.finalize(cls, print, 'g1')
- print('f3 error')
- 1/0
-
- # cls should stay alive till atexit callbacks run
- f1 = weakref.finalize(cls, print, 'f1', _global_var)
- f2 = weakref.finalize(cls, print, 'f2', _global_var)
- f3 = weakref.finalize(cls, error)
- f4 = weakref.finalize(cls, print, 'f4', _global_var)
-
- assert f1.atexit == True
- f2.atexit = False
- assert f3.atexit == True
- assert f4.atexit == True
-
- def test_atexit(self):
- prog = ('from test.test_weakref import FinalizeTestCase;'+
- 'FinalizeTestCase.run_in_child()')
- rc, out, err = script_helper.assert_python_ok('-c', prog)
- out = out.decode('ascii').splitlines()
- self.assertEqual(out, ['f4 foobar', 'f3 error', 'g1', 'f1 foobar'])
- self.assertTrue(b'ZeroDivisionError' in err)
-
-
libreftest = """ Doctest for examples in the library reference: weakref.rst
>>> import weakref
@@ -1793,7 +1644,6 @@ def test_main():
WeakValueDictionaryTestCase,
WeakKeyDictionaryTestCase,
SubclassableWeakrefTestCase,
- FinalizeTestCase,
)
support.run_doctest(sys.modules[__name__])
diff --git a/Lib/weakref.py b/Lib/weakref.py
index 7a17d17..8f9c107 100644
--- a/Lib/weakref.py
+++ b/Lib/weakref.py
@@ -21,16 +21,13 @@ from _weakref import (
from _weakrefset import WeakSet, _IterationGuard
import collections # Import after _weakref to avoid circular import.
-import sys
-import atexit
-import itertools
ProxyTypes = (ProxyType, CallableProxyType)
__all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs",
"WeakKeyDictionary", "ReferenceType", "ProxyType",
"CallableProxyType", "ProxyTypes", "WeakValueDictionary",
- "WeakSet", "WeakMethod", "finalize"]
+ "WeakSet", "WeakMethod"]
class WeakMethod(ref):
@@ -439,135 +436,3 @@ class WeakKeyDictionary(collections.MutableMapping):
d[ref(key, self._remove)] = value
if len(kwargs):
self.update(kwargs)
-
-
-class finalize:
- """Class for finalization of weakrefable objects
-
- finalize(obj, func, *args, **kwargs) returns a callable finalizer
- object which will be called when obj is garbage collected. The
- first time the finalizer is called it evaluates func(*arg, **kwargs)
- and returns the result. After this the finalizer is dead, and
- calling it just returns None.
-
- When the program exits any remaining finalizers for which the
- atexit attribute is true will be run in reverse order of creation.
- By default atexit is true.
- """
-
- # Finalizer objects don't have any state of their own. They are
- # just used as keys to lookup _Info objects in the registry. This
- # ensures that they cannot be part of a ref-cycle.
-
- __slots__ = ()
- _registry = {}
- _shutdown = False
- _index_iter = itertools.count()
- _dirty = False
-
- class _Info:
- __slots__ = ("weakref", "func", "args", "kwargs", "atexit", "index")
-
- def __init__(self, obj, func, *args, **kwargs):
- info = self._Info()
- info.weakref = ref(obj, self)
- info.func = func
- info.args = args
- info.kwargs = kwargs or None
- info.atexit = True
- info.index = next(self._index_iter)
- self._registry[self] = info
- finalize._dirty = True
-
- def __call__(self, _=None):
- """If alive then mark as dead and return func(*args, **kwargs);
- otherwise return None"""
- info = self._registry.pop(self, None)
- if info and not self._shutdown:
- return info.func(*info.args, **(info.kwargs or {}))
-
- def detach(self):
- """If alive then mark as dead and return (obj, func, args, kwargs);
- otherwise return None"""
- info = self._registry.get(self)
- obj = info and info.weakref()
- if obj is not None and self._registry.pop(self, None):
- return (obj, info.func, info.args, info.kwargs or {})
-
- def peek(self):
- """If alive then return (obj, func, args, kwargs);
- otherwise return None"""
- info = self._registry.get(self)
- obj = info and info.weakref()
- if obj is not None:
- return (obj, info.func, info.args, info.kwargs or {})
-
- @property
- def alive(self):
- """Whether finalizer is alive"""
- return self in self._registry
-
- @property
- def atexit(self):
- """Whether finalizer should be called at exit"""
- info = self._registry.get(self)
- return bool(info) and info.atexit
-
- @atexit.setter
- def atexit(self, value):
- info = self._registry.get(self)
- if info:
- info.atexit = bool(value)
-
- def __repr__(self):
- info = self._registry.get(self)
- obj = info and info.weakref()
- if obj is None:
- return '<%s object at %#x; dead>' % (type(self).__name__, id(self))
- else:
- return '<%s object at %#x; for %r at %#x>' % \
- (type(self).__name__, id(self), type(obj).__name__, id(obj))
-
- @classmethod
- def _select_for_exit(cls):
- # Return live finalizers marked for exit, oldest first
- L = [(f,i) for (f,i) in cls._registry.items() if i.atexit]
- L.sort(key=lambda item:item[1].index)
- return [f for (f,i) in L]
-
- @classmethod
- def _exitfunc(cls):
- # At shutdown invoke finalizers for which atexit is true.
- # This is called once all other non-daemonic threads have been
- # joined.
- reenable_gc = False
- try:
- if cls._registry:
- import gc
- if gc.isenabled():
- reenable_gc = True
- gc.disable()
- pending = None
- while True:
- if pending is None or finalize._dirty:
- pending = cls._select_for_exit()
- finalize._dirty = False
- if not pending:
- break
- f = pending.pop()
- try:
- # gc is disabled, so (assuming no daemonic
- # threads) the following is the only line in
- # this function which might trigger creation
- # of a new finalizer
- f()
- except Exception:
- sys.excepthook(*sys.exc_info())
- assert f not in cls._registry
- finally:
- # prevent any more finalizers from executing during shutdown
- finalize._shutdown = True
- if reenable_gc:
- gc.enable()
-
-atexit.register(finalize._exitfunc)
diff --git a/Misc/NEWS b/Misc/NEWS
index 42bd382..70e8f21 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -69,9 +69,6 @@ Core and Builtins
Library
-------
-- Issue #15528: Add weakref.finalize to support finalization using
- weakref callbacks.
-
- Issue #14173: Avoid crashing when reading a signal handler during
interpreter shutdown.