diff options
author | Jason Madden <jamadden@gmail.com> | 2017-05-17 07:10:48 -0500 |
---|---|---|
committer | Jason Madden <jamadden@gmail.com> | 2017-05-17 07:10:48 -0500 |
commit | cdb0161c7edf67857a7625ec666a80c9cd35c297 (patch) | |
tree | f1cde20a2bcc3f6dda7e89caf096534ca549e04f | |
parent | 2b82f83048802017a451d2abcdc2a2bae8ece182 (diff) | |
download | zope-security-cdb0161c7edf67857a7625ec666a80c9cd35c297.tar.gz |
OrderedDict, BTree and dict all iterate the same way.fix-ordered-dict-checker
Fixes #23. Also a further fix for #20 (you couldn't iterate a BTree
all by itself).
Refactor the test case for BTree to be a shared implementation and
confirm that it works as expected for dict, using the actual dict
checker. Then apply it to OrderedDict and BTree and fix the resulting
failures by refactoring the fixup in checker.py to a shared
implementation and applying it.
-rw-r--r-- | CHANGES.rst | 7 | ||||
-rw-r--r-- | src/zope/security/checker.py | 35 | ||||
-rw-r--r-- | src/zope/security/tests/test_checker.py | 52 |
3 files changed, 64 insertions, 30 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 0203e6a..aea7438 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,12 @@ Changes 4.1.1 (unreleased) ------------------ -- TBD +- Fix `issue 23 <https://github.com/zopefoundation/zope.security/issues/23>`_: + iteration of ``collections.OrderedDict`` and its various views is + now allowed by default on all versions of Python. + +- As a further fix for issue 20, iteration of BTree itself is now + allowed by default. 4.1.0 (2017-04-24) ------------------ diff --git a/src/zope/security/checker.py b/src/zope/security/checker.py index 3187afc..344428c 100644 --- a/src/zope/security/checker.py +++ b/src/zope/security/checker.py @@ -770,6 +770,27 @@ if PYTHON2: _default_checkers[type({}.iterkeys())] = _iteratorChecker _default_checkers[type({}.itervalues())] = _iteratorChecker +def _fixup_dictlike(dict_type): + empty_dict = dict_type() + populated_dict = dict_type({1: 2}) + for dictlike in (empty_dict, populated_dict): + for attr in ('__iter__', 'keys', 'items', 'values'): + obj = getattr(dictlike, attr)() + o_type = type(obj) + if o_type not in _default_checkers: + _default_checkers[o_type] = _iteratorChecker + +def _fixup_odict(): + # OrderedDicts have three different implementations: Python 2 (pure + # python, returns generators and lists), Python <=3.4 (pure Python, + # uses view classes) and CPython 3.5+ (implemented in C). These should + # all be iterable. + from collections import OrderedDict + _fixup_dictlike(OrderedDict) + +_fixup_odict() +del _fixup_odict + try: import BTrees except ImportError: # pragma: no cover @@ -794,20 +815,14 @@ else: 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 + btree = getattr(family, name).BTree + _fixup_dictlike(btree) _fixup_btrees() del _fixup_btrees +del _fixup_dictlike + 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 ceda502..03a8883 100644 --- a/src/zope/security/tests/test_checker.py +++ b/src/zope/security/tests/test_checker.py @@ -391,34 +391,48 @@ class CheckerTestsBase(object): finally: _clear() - @_skip_if_no_btrees - def test_iteration_of_btree_items(self): - # iteration of BTree.items() is allowed by default. + def _check_iteration_of_dict_like(self, dict_like): from zope.security.proxy import Proxy from zope.security.checker import Checker - from zope.security.checker import CheckerPublic - import BTrees + from zope.security.checker import _default_checkers + + checker = _default_checkers[dict] + + proxy = Proxy(dict_like, checker) + # empty + self.assertEqual([], list(proxy.items())) + self.assertEqual([], list(proxy.keys())) + self.assertEqual([], list(proxy.values())) + self.assertEqual([], list(proxy)) - checker = Checker({'items': CheckerPublic, - 'keys': CheckerPublic, - 'values': CheckerPublic}) + # With an object + dict_like[1] = 2 + self.assertEqual([(1, 2)], list(proxy.items())) + self.assertEqual([1], list(proxy.keys())) + self.assertEqual([1], list(proxy)) + self.assertEqual([2], list(proxy.values())) + + @_skip_if_no_btrees + def test_iteration_of_btree_items_keys_values(self): + # iteration of BTree.items() is allowed by default. + import BTrees 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())) - self.assertEqual([], list(proxy.keys())) - self.assertEqual([], list(proxy.values())) - - # With an object - btree[1] = 2 - self.assertEqual([(1, 2)], list(proxy.items())) - self.assertEqual([1], list(proxy.keys())) - self.assertEqual([2], list(proxy.values())) + self._check_iteration_of_dict_like(btree) + + def test_iteration_of_odict_items_keys_values(self): + # iteration of OrderedDict.items() is allowed by default. + from collections import OrderedDict + + odict = OrderedDict() + self._check_iteration_of_dict_like(odict) + def test_iteration_of_dict_items_keys_values(self): + # iteration of regular dict is allowed by default + self._check_iteration_of_dict_like(dict()) class CheckerPyTests(unittest.TestCase, CheckerTestsBase): |