summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2017-04-24 06:54:36 -0500
committerJason Madden <jamadden@gmail.com>2017-04-24 08:00:59 -0500
commit9aaf59b541bc3e0616f9f9f95b6345d1a4400e68 (patch)
treea73601e4bf67de1d67fc67eb43065691c8648130 /src
parent6292afe78a5eee46405d982405fcc63603478504 (diff)
downloadzope-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.py37
-rw-r--r--src/zope/security/tests/test_checker.py38
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):