diff options
author | Jason Madden <jamadden@gmail.com> | 2017-08-30 08:50:21 -0500 |
---|---|---|
committer | Jason Madden <jamadden@gmail.com> | 2017-08-30 09:05:37 -0500 |
commit | 6f8de039836b62cf8c24c5ea23dfa0e6f0e629b6 (patch) | |
tree | d33ba4f9db75c32c75e9a1ee01c196e6d0572925 /src/zope/security/tests | |
parent | 4e08d85f4ad2d9fb1f1acbe1fad004c81b6d33c0 (diff) | |
download | zope-security-6f8de039836b62cf8c24c5ea23dfa0e6f0e629b6.tar.gz |
Fix proxying of providedBy on Python 3 and fix __length_hint__
Fixes #27.
Add special cases to defaultCheckers for the two types of objects that
can be returned from zope.interface.providedBy. On Python 2, these
were never proxied, but on Python 3 they were. Now it's
consistent (they're never proxied). (Using an _iteratorChecker for
them would be a breaking change because the results of iterating them
would be security proxied interface objects that don't compare
equally.)
Also fix `__length_hint__` while we're at it. Previously it was
ignored because it is looked up on the type of the object, and proxy
didn't implement that. So implement it, and add it to the list of
names allowed for iterators.
Diffstat (limited to 'src/zope/security/tests')
-rw-r--r-- | src/zope/security/tests/test_checker.py | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/src/zope/security/tests/test_checker.py b/src/zope/security/tests/test_checker.py index 03a8883..b82799f 100644 --- a/src/zope/security/tests/test_checker.py +++ b/src/zope/security/tests/test_checker.py @@ -434,6 +434,125 @@ class CheckerTestsBase(object): # iteration of regular dict is allowed by default self._check_iteration_of_dict_like(dict()) + def test_iteration_of_interface_implementedBy(self): + # Iteration of implementedBy is allowed by default + # See https://github.com/zopefoundation/zope.security/issues/27 + from zope.security.proxy import Proxy + from zope.security.checker import Checker + + from zope.interface import providedBy + from zope.interface import implementer + from zope.interface import Interface + + class I1(Interface): + pass + + @implementer(I1) + class O(object): + pass + + o = O() + + checker = Checker({}) + + proxy = Proxy(o, checker) + + # Since the object itself doesn't have any interfaces, + # the providedBy will return the implementedBy of the class + l = list(providedBy(proxy)) + + self.assertEqual(l, [I1]) + + def test_iteration_of_interface_providesBy(self): + # Iteration of zope.interface.Provides is allowed by default + # See https://github.com/zopefoundation/zope.security/issues/27 + from zope.security.proxy import Proxy + from zope.security.checker import Checker + + from zope.interface import providedBy + from zope.interface import alsoProvides + from zope.interface import implementer + from zope.interface import Interface + + class I1(Interface): + pass + + class I2(Interface): + pass + + @implementer(I1) + class O(object): + pass + + o = O() + alsoProvides(o, I2) + + checker = Checker({}) + + proxy = Proxy(o, checker) + + # Since the object has its own interfaces, provided + # by will return a zope.interface.Provides object + l = list(providedBy(proxy)) + + self.assertEqual(l, [I2, I1]) + + def test_iteration_with_length_hint(self): + # PEP 424 implemented officially in Python 3.4 and + # unofficially before in cPython and PyPy that allows for a + # __length_hint__ method to be defined on iterators. It should + # be allowed by default. See + # https://github.com/zopefoundation/zope.security/issues/27 + from zope.security.proxy import Proxy + from zope.security.checker import _iteratorChecker + from zope.security.checker import Checker + + class Iter(object): + __Security_checker__ = _iteratorChecker + + items = (0, 1, 2) + index = 0 + hint = len(items) + hint_called = False + + def __iter__(self): + return self + + def __next__(self): + try: + return self.items[self.index] + except IndexError: + raise StopIteration() + finally: + self.index += 1 + + next = __next__ + + def __length_hint__(self): + self.hint_called = True + return self.hint + + # The hint is called on raw objects + i = Iter() + list(i) + self.assertTrue(i.hint_called, "__length_hint__ should be called") + + # The hint is called when we proxy the root object + i = Iter() + proxy = Proxy(i, _iteratorChecker) + l = list(proxy) + self.assertEqual(l, [0, 1, 2]) + self.assertTrue(i.hint_called, "__length_hint__ should be called") + + # The hint is called when we proxy its iterator + i = Iter() + it = iter(i) + proxy = Proxy(it, _iteratorChecker) + l = list(proxy) + self.assertEqual(l, [0, 1, 2]) + self.assertTrue(i.hint_called, "__length_hint__ should be called") + + class CheckerPyTests(unittest.TestCase, CheckerTestsBase): def _getTargetClass(self): |