diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/zope/security/checker.py | 37 | ||||
-rw-r--r-- | src/zope/security/tests/test_checker.py | 38 |
2 files changed, 68 insertions, 7 deletions
diff --git a/src/zope/security/checker.py b/src/zope/security/checker.py index d5f261c..d75cfd5 100644 --- a/src/zope/security/checker.py +++ b/src/zope/security/checker.py @@ -770,6 +770,43 @@ if PYTHON2: _default_checkers[type({}.iterkeys())] = _iteratorChecker _default_checkers[type({}.itervalues())] = _iteratorChecker +try: + import BTrees +except ImportError: # pragma: no cover + pass +else: + # The C implementation of BTree.items() is its own iterator + # and doesn't need any special entries to enable iteration. + # But the Python implementation has to call __iter__ to be able + # to do iteration. Whitelist it so that they behave the same. + # In addition, Python 3 will attempt to call __len__ on iterators + # for a length hint, so the C implementations also need to be + # added to the _iteratorChecker. + # We do this here so that all users of zope.security can benefit + # without knowing implementation details. + # See https://github.com/zopefoundation/zope.security/issues/20 + + def _fixup_btrees(): + import BTrees._base + _default_checkers[BTrees._base._TreeItems] = _iteratorChecker + + for name in ('IF', 'II', 'IO', 'OI', 'OO'): + for family_name in ('family32', 'family64'): + family = getattr(BTrees, family_name) + btree = getattr(family, name).BTree() + + empty_type = type(btree.items()) + if empty_type not in _default_checkers: + _default_checkers[empty_type] = _iteratorChecker + + btree[1] = 1 + populated_type = type(btree.items()) + if populated_type not in _default_checkers: + _default_checkers[populated_type] = _iteratorChecker + + _fixup_btrees() + del _fixup_btrees + def _clear(): _checkers.clear() _checkers.update(_default_checkers) diff --git a/src/zope/security/tests/test_checker.py b/src/zope/security/tests/test_checker.py index c5c01e7..d1bad49 100644 --- a/src/zope/security/tests/test_checker.py +++ b/src/zope/security/tests/test_checker.py @@ -17,13 +17,15 @@ import unittest def _skip_if_not_Py2(testfunc): import sys - from functools import update_wrapper - if sys.version_info[0] >= 3: - def dummy(self): - pass - update_wrapper(dummy, testfunc) - return dummy - return testfunc + return unittest.skipIf(sys.version_info[0] >= 3, "Needs Python 2")(testfunc) + +def _skip_if_no_btrees(testfunc): + try: + import BTrees + except ImportError: + return unittest.skip("BTrees is not installed")(testfunc) + else: + return testfunc class Test_ProxyFactory(unittest.TestCase): @@ -389,6 +391,28 @@ class CheckerTestsBase(object): finally: _clear() + @_skip_if_no_btrees + def test_iteration_of_btree_items(self): + # iteration of BTree.items() is allowed by default. + from zope.security.proxy import Proxy + from zope.security.checker import Checker + from zope.security.checker import CheckerPublic + import BTrees + + checker = Checker({'items': CheckerPublic}) + + for name in ('IF', 'II', 'IO', 'OI', 'OO'): + for family_name in ('family32', 'family64'): + family = getattr(BTrees, family_name) + btree = getattr(family, name).BTree() + proxy = Proxy(btree, checker) + # empty + self.assertEqual([], list(proxy.items())) + + # With an object + btree[1] = 2 + self.assertEqual([(1, 2)], list(proxy.items())) + class CheckerPyTests(unittest.TestCase, CheckerTestsBase): |