diff options
author | Jason Madden <jamadden@gmail.com> | 2017-04-24 06:54:36 -0500 |
---|---|---|
committer | Jason Madden <jamadden@gmail.com> | 2017-04-24 08:00:59 -0500 |
commit | 9aaf59b541bc3e0616f9f9f95b6345d1a4400e68 (patch) | |
tree | a73601e4bf67de1d67fc67eb43065691c8648130 /src | |
parent | 6292afe78a5eee46405d982405fcc63603478504 (diff) | |
download | zope-security-9aaf59b541bc3e0616f9f9f95b6345d1a4400e68.tar.gz |
Fix iteration of BTrees.items() in pure-python; and 3.6 support
Also fix ``list(proxy_btree.items())`` (or a list comprehension of the
same) in Python 3, which wants the ``__len__`` for a hint.
This is a central place to make sure these all behave consistently.
Fixes #20
Also drop pypy3
As a 3.2 implementation, it's not supported by pip anymore. There is a
much more recent version, 3.5-beta, but it's not on Travis yet. The
3.3-alpha which is on Travis is a dead end.
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): |