From cdb0161c7edf67857a7625ec666a80c9cd35c297 Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Wed, 17 May 2017 07:10:48 -0500 Subject: OrderedDict, BTree and dict all iterate the same way. 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. --- src/zope/security/checker.py | 35 +++++++++++++++------- src/zope/security/tests/test_checker.py | 52 +++++++++++++++++++++------------ 2 files changed, 58 insertions(+), 29 deletions(-) (limited to 'src') 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): -- cgit v1.2.1