Commit b86f36bf authored by Marius Gedminas's avatar Marius Gedminas

Fix occasional RuntimeErrors in transaction.weakset on Python 3

One way to reproduce these in the wild was to run the ZODB test suite on
Python 3.3 in the py3 branch.  Example failure:

Error in test checkPackWhileWriting (ZODB.tests.testMVCCMappingStorage.MVCCMappingStorageTests)
Traceback (most recent call last):
  File "/usr/lib/python3.3/unittest/case.py", line 385, in _executeTestPart
    function()
  File "/home/mg/src/new-zope-order/ZODB/src/ZODB/tests/PackableStorage.py", line 281, in checkPackWhileWriting
    self._PackWhileWriting(pack_now=False)
  File "/home/mg/src/new-zope-order/ZODB/src/ZODB/tests/PackableStorage.py", line 208, in _PackWhileWriting
    transaction.commit()
  File "/home/mg/.buildout/eggs/transaction-1.4.0-py3.3.egg/transaction/_manager.py", line 111, in commit
    return self.get().commit()
  File "/home/mg/.buildout/eggs/transaction-1.4.0-py3.3.egg/transaction/_transaction.py", line 286, in commit
    self._synchronizers.map(lambda s: s.afterCompletion(self))
  File "/home/mg/.buildout/eggs/transaction-1.4.0-py3.3.egg/transaction/weakset.py", line 55, in map
    for wr in self.as_weakref_list():
RuntimeError: dictionary changed size during iteration
parent 377b589d
......@@ -7,6 +7,9 @@ Changes
- Document that values returned by ``sortKey`` must be strings, in order
to guarantee total ordering.
- Fix occasional RuntimeError: dictionary changed size during iteration errors
in transaction.weakset on Python 3.
1.4.0 (2013-01-03)
------------------
......
......@@ -58,7 +58,9 @@ class WeakSetTests(unittest.TestCase):
w.add(dummy3)
del dummy3
gc.collect()
L = [x() for x in w.as_weakref_list()]
refs = w.as_weakref_list()
self.assertTrue(isinstance(refs, list))
L = [x() for x in refs]
# L is a list, but it does not have a guaranteed order.
self.assertTrue(list, type(L))
self.assertEqual(set(L), set([dummy, dummy2]))
......
......@@ -73,9 +73,11 @@ class WeakSet(object):
# And so on. Stress tests showed that it was easy to get into a state
# where a WeakSet grows without bounds, despite that almost all its
# elements are actually trash. By returning a list of weakrefs instead,
# we avoid that, although the decision to use weakrefs is now# very
# we avoid that, although the decision to use weakrefs is now very
# visible to our clients.
def as_weakref_list(self):
# We're cheating by breaking into the internals of Python's
# WeakValueDictionary here (accessing its .data attribute).
return self.data.data.values()
# Python 3: be sure to freeze the list, to avoid RuntimeError:
# dictionary changed size during iteration.
return list(self.data.data.values())
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment