diff --git a/BTrees/_base.py b/BTrees/_base.py index b42ee83..e7dd815 100644 --- a/BTrees/_base.py +++ b/BTrees/_base.py @@ -1050,6 +1050,7 @@ def _del(self, key): self._firstbucket = child._next removed_first_bucket = True del data[index] + self._p_changed = True return removed_first_bucket, value diff --git a/BTrees/tests/common.py b/BTrees/tests/common.py index 1267704..1f0e3ad 100644 --- a/BTrees/tests/common.py +++ b/BTrees/tests/common.py @@ -91,24 +91,14 @@ class SignedMixin(object): KEY_RANDRANGE_ARGS = (-2000, 2001) -class Base(SignedMixin): - # Tests common to all types: sets, buckets, and BTrees +class ZODBAccess(object): db = None - def _getTargetClass(self): - raise NotImplementedError("subclass should return the target type") - - def _makeOne(self): - return self._getTargetClass()() - - def setUp(self): - super(Base, self).setUp() - _skip_if_pure_py_and_py_test(self) - def tearDown(self): if self.db is not None: self.db.close() + del self.db def _getRoot(self): from ZODB import DB @@ -131,6 +121,21 @@ def _closeRoot(self, root): transaction.abort() root._p_jar.close() + +class Base(ZODBAccess, SignedMixin): + # Tests common to all types: sets, buckets, and BTrees + + def _getTargetClass(self): + raise NotImplementedError("subclass should return the target type") + + def _makeOne(self): + return self._getTargetClass()() + + def setUp(self): + super(Base, self).setUp() + _skip_if_pure_py_and_py_test(self) + + def testPersistentSubclass(self): # Can we subclass this and Persistent? # https://github.com/zopefoundation/BTrees/issues/78 diff --git a/BTrees/tests/testPersistency.py b/BTrees/tests/testPersistency.py new file mode 100644 index 0000000..4b36554 --- /dev/null +++ b/BTrees/tests/testPersistency.py @@ -0,0 +1,47 @@ +############################################################################## +# +# Copyright (c) 2020 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE +# +############################################################################## + +from unittest import TestCase + +from ..OOBTree import OOBTree, OOBTreePy +from .common import _skip_wo_ZODB, ZODBAccess + + +BUCKET_SIZE = OOBTreePy.max_leaf_size + + +class TestPersistency(ZODBAccess, TestCase): + @_skip_wo_ZODB + def test_empty_bucket_persistency(self): + from transaction import commit + root = self._getRoot() + try: + # tree with 3 buckets (internal implementation details) + tree = OOBTree( + dict((i, i) for i in range(3 * BUCKET_SIZE // 2 + 2))) + root["tree"] = tree + commit() + # almost clear the second bucket keeping the last element + for i in range(BUCKET_SIZE // 2, BUCKET_SIZE - 1): + del tree[i] + commit() + del tree[BUCKET_SIZE - 1] # remove the last element + commit() + tree._check() + tree._p_deactivate() + tree._check() # fails in case of bad persistency + finally: + self._closeRoot(root) + + diff --git a/CHANGES.rst b/CHANGES.rst index b2d1d31..c881835 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ 4.7.3 (unreleased) ================== +- Fix persistency bug in the Python version + (`#118 `_). + - Fix ``Tree.__setstate__`` to no longer accept children besides tree or bucket types to prevent crashes. See `PR 143 `_ for details. diff --git a/tox.ini b/tox.ini index e4cf67a..72890d2 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,7 @@ envlist = # Jython support pending 2.7 support, due 2012-07-15 or so. See: # http://fwierzbicki.blogspot.com/2012/03/adconion-to-fund-jython-27.html # py27,jython,pypy,coverage,docs - py27,py27-pure,py35,py35-pure,py36,py37,py38,pypy,pypy3,w_zodb,coverage,docs + py27,py27-pure,py35,py35-pure,py36,py37,py38,pypy,pypy3,w_zodb,w_zodb-pure,coverage,docs [testenv] usedevelop = true @@ -29,6 +29,12 @@ basepython = deps = ZODB +[testenv:w_zodb-pure] +basepython = + python2.7 +deps = + ZODB + [testenv:coverage] basepython = python3.6