summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Madden <jason+github@nextthought.com>2017-05-17 09:49:03 -0500
committerGitHub <noreply@github.com>2017-05-17 09:49:03 -0500
commit7c93ce533da0f56d707200f66475e993541b3604 (patch)
treef1cde20a2bcc3f6dda7e89caf096534ca549e04f
parent2b82f83048802017a451d2abcdc2a2bae8ece182 (diff)
parentcdb0161c7edf67857a7625ec666a80c9cd35c297 (diff)
downloadzope-security-7c93ce533da0f56d707200f66475e993541b3604.tar.gz
Merge pull request #24 from zopefoundation/fix-ordered-dict-checker
OrderedDict, BTree and dict all iterate the same way.
-rw-r--r--CHANGES.rst7
-rw-r--r--src/zope/security/checker.py35
-rw-r--r--src/zope/security/tests/test_checker.py52
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):