diff --git a/CHANGES.rst b/CHANGES.rst index a6864d7..0dd0e27 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,6 +7,7 @@ For changes before version 3.0, see ``HISTORY.rst``. ---------------- - Add support for Python 3.11 (as of 3.11.0a6). +- Support ``default`` argument in ``next`` built-in function. 5.3.1 (2022-03-29) diff --git a/src/AccessControl/ZopeGuards.py b/src/AccessControl/ZopeGuards.py index 16062e8..4b41a6e 100644 --- a/src/AccessControl/ZopeGuards.py +++ b/src/AccessControl/ZopeGuards.py @@ -254,8 +254,11 @@ def _check_list_access(name, value): # iterator that is known to be safe (as in guarded_enumerate). -def guarded_next(iterator): - ob = next(iterator) +def guarded_next(iterator, default=_marker): + if default is _marker: + ob = next(iterator) + else: + ob = next(iterator, default) if not isinstance(iterator, SafeIter): guard(ob, ob) return ob diff --git a/src/AccessControl/tests/testZopeGuards.py b/src/AccessControl/tests/testZopeGuards.py index 7d39b64..1ac61ce 100644 --- a/src/AccessControl/tests/testZopeGuards.py +++ b/src/AccessControl/tests/testZopeGuards.py @@ -878,6 +878,37 @@ def test_guarded_next__1(self): self.assertEqual(its_globals['result'], 2411) + def test_guarded_next_default(self): + """There is a `safe_builtin` named `next` with `default`.""" + SCRIPT = "result = next(iterator, 'default')" + + code, its_globals = self._compile_str(SCRIPT, 'ignored') + its_globals['iterator'] = iter([]) + + sm = SecurityManager() + old = self.setSecurityManager(sm) + try: + exec(code, its_globals) + finally: + self.setSecurityManager(old) + self.assertEqual(its_globals['result'], "default") + + def test_guarded_next_StopIteration(self): + """There is a `safe_builtin` named `next`, raising StopIteration + when iterator is exhausted.""" + SCRIPT = "result = next(iterator)" + + code, its_globals = self._compile_str(SCRIPT, 'ignored') + its_globals['iterator'] = iter([]) + + sm = SecurityManager() + old = self.setSecurityManager(sm) + with self.assertRaises(StopIteration): + try: + exec(code, its_globals) + finally: + self.setSecurityManager(old) + def test_guarded_next__2(self): """It guards the access during iteration.""" from AccessControl import Unauthorized