mirror of
https://github.com/python/cpython.git
synced 2025-11-25 12:44:13 +00:00
gh-92203: Add closure support to exec(). (#92204)
Add a closure keyword-only parameter to exec(). It can only be specified when exec-ing a code object that uses free variables. When specified, it must be a tuple, with exactly the number of cell variables referenced by the code object. closure has a default value of None, and it must be None if the code object doesn't refer to any free variables.
This commit is contained in:
parent
973a5203c1
commit
5021064390
5 changed files with 171 additions and 21 deletions
|
|
@ -24,7 +24,7 @@ from functools import partial
|
|||
from inspect import CO_COROUTINE
|
||||
from itertools import product
|
||||
from textwrap import dedent
|
||||
from types import AsyncGeneratorType, FunctionType
|
||||
from types import AsyncGeneratorType, FunctionType, CellType
|
||||
from operator import neg
|
||||
from test import support
|
||||
from test.support import (swap_attr, maybe_get_event_loop_policy)
|
||||
|
|
@ -772,6 +772,84 @@ class BuiltinTest(unittest.TestCase):
|
|||
finally:
|
||||
sys.stdout = savestdout
|
||||
|
||||
def test_exec_closure(self):
|
||||
def function_without_closures():
|
||||
return 3 * 5
|
||||
|
||||
result = 0
|
||||
def make_closure_functions():
|
||||
a = 2
|
||||
b = 3
|
||||
c = 5
|
||||
def three_freevars():
|
||||
nonlocal result
|
||||
nonlocal a
|
||||
nonlocal b
|
||||
result = a*b
|
||||
def four_freevars():
|
||||
nonlocal result
|
||||
nonlocal a
|
||||
nonlocal b
|
||||
nonlocal c
|
||||
result = a*b*c
|
||||
return three_freevars, four_freevars
|
||||
three_freevars, four_freevars = make_closure_functions()
|
||||
|
||||
# "smoke" test
|
||||
result = 0
|
||||
exec(three_freevars.__code__,
|
||||
three_freevars.__globals__,
|
||||
closure=three_freevars.__closure__)
|
||||
self.assertEqual(result, 6)
|
||||
|
||||
# should also work with a manually created closure
|
||||
result = 0
|
||||
my_closure = (CellType(35), CellType(72), three_freevars.__closure__[2])
|
||||
exec(three_freevars.__code__,
|
||||
three_freevars.__globals__,
|
||||
closure=my_closure)
|
||||
self.assertEqual(result, 2520)
|
||||
|
||||
# should fail: closure isn't allowed
|
||||
# for functions without free vars
|
||||
self.assertRaises(TypeError,
|
||||
exec,
|
||||
function_without_closures.__code__,
|
||||
function_without_closures.__globals__,
|
||||
closure=my_closure)
|
||||
|
||||
# should fail: closure required but wasn't specified
|
||||
self.assertRaises(TypeError,
|
||||
exec,
|
||||
three_freevars.__code__,
|
||||
three_freevars.__globals__,
|
||||
closure=None)
|
||||
|
||||
# should fail: closure of wrong length
|
||||
self.assertRaises(TypeError,
|
||||
exec,
|
||||
three_freevars.__code__,
|
||||
three_freevars.__globals__,
|
||||
closure=four_freevars.__closure__)
|
||||
|
||||
# should fail: closure using a list instead of a tuple
|
||||
my_closure = list(my_closure)
|
||||
self.assertRaises(TypeError,
|
||||
exec,
|
||||
three_freevars.__code__,
|
||||
three_freevars.__globals__,
|
||||
closure=my_closure)
|
||||
|
||||
# should fail: closure tuple with one non-cell-var
|
||||
my_closure[0] = int
|
||||
my_closure = tuple(my_closure)
|
||||
self.assertRaises(TypeError,
|
||||
exec,
|
||||
three_freevars.__code__,
|
||||
three_freevars.__globals__,
|
||||
closure=my_closure)
|
||||
|
||||
|
||||
def test_filter(self):
|
||||
self.assertEqual(list(filter(lambda c: 'a' <= c <= 'z', 'Hello World')), list('elloorld'))
|
||||
self.assertEqual(list(filter(None, [1, 'hello', [], [3], '', None, 9, 0])), [1, 'hello', [3], 9])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue