Skip to content

Commit 3e1c716

Browse files
bpo-43693: Un-revert commit f3fa63e. (#26609)
This was reverted in GH-26596 (commit 6d518bb) due to some bad memory accesses. * Add the MAKE_CELL opcode. (gh-26396) The memory accesses have been fixed. https://bugs.python.org/issue43693
1 parent ab36b9f commit 3e1c716

16 files changed

+4467
-4231
lines changed

Doc/library/dis.rst

+8
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,14 @@ All of the following opcodes use their arguments.
10561056
Deletes local ``co_varnames[var_num]``.
10571057

10581058

1059+
.. opcode:: MAKE_CELL (i)
1060+
1061+
Creates a new cell in slot ``i``. If that slot is empty then
1062+
that value is stored into the new cell.
1063+
1064+
.. versionadded:: 3.11
1065+
1066+
10591067
.. opcode:: LOAD_CLOSURE (i)
10601068

10611069
Pushes a reference to the cell contained in slot ``i`` of the "fast locals"

Include/cpython/code.h

+9-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,16 @@
22
# error "this header file must not be included directly"
33
#endif
44

5+
/* Each instruction in a code object is a fixed-width value,
6+
* currently 2 bytes: 1-byte opcode + 1-byte oparg. The EXTENDED_ARG
7+
* opcode allows for larger values but the current limit is 3 uses
8+
* of EXTENDED_ARG (see Python/wordcode_helpers.h), for a maximum
9+
* 32-bit value. This aligns with the note in Python/compile.c
10+
* (compiler_addop_i_line) indicating that the max oparg value is
11+
* 2**32 - 1, rather than INT_MAX.
12+
*/
13+
514
typedef uint16_t _Py_CODEUNIT;
6-
// Each oparg must fit in the second half of _Py_CODEUNIT, hence 8 bits.
7-
#define _Py_MAX_OPARG 255
815

916
#ifdef WORDS_BIGENDIAN
1017
# define _Py_OPCODE(word) ((word) >> 8)

Include/internal/pycore_frame.h

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ _PyFrame_GetBuiltins(PyFrameObject *f)
3232

3333
int _PyFrame_TakeLocals(PyFrameObject *f);
3434

35+
PyAPI_FUNC(int) _PyFrame_OpAlreadyRan(PyFrameObject *f, int opcode, int oparg);
36+
3537
#ifdef __cplusplus
3638
}
3739
#endif

Include/opcode.h

+5-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/importlib/_bootstrap_external.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ def _write_atomic(path, data, mode=0o666):
357357
# Python 3.11a1 3452 (drop nlocals from marshaled code objects)
358358
# Python 3.11a1 3453 (add co_fastlocalnames and co_fastlocalkinds)
359359
# Python 3.11a1 3454 (compute cell offsets relative to locals bpo-43693)
360+
# Python 3.11a1 3455 (add MAKE_CELL bpo-43693)
360361

361362
#
362363
# MAGIC must change whenever the bytecode emitted by the compiler may no
@@ -366,7 +367,7 @@ def _write_atomic(path, data, mode=0o666):
366367
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
367368
# in PC/launcher.c must also be updated.
368369

369-
MAGIC_NUMBER = (3454).to_bytes(2, 'little') + b'\r\n'
370+
MAGIC_NUMBER = (3455).to_bytes(2, 'little') + b'\r\n'
370371
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
371372

372373
_PYCACHE = '__pycache__'

Lib/opcode.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -181,14 +181,16 @@ def jabs_op(name, op):
181181
def_op('MAKE_FUNCTION', 132) # Flags
182182
def_op('BUILD_SLICE', 133) # Number of items
183183

184-
def_op('LOAD_CLOSURE', 135)
184+
def_op('MAKE_CELL', 135)
185185
hasfree.append(135)
186-
def_op('LOAD_DEREF', 136)
186+
def_op('LOAD_CLOSURE', 136)
187187
hasfree.append(136)
188-
def_op('STORE_DEREF', 137)
188+
def_op('LOAD_DEREF', 137)
189189
hasfree.append(137)
190-
def_op('DELETE_DEREF', 138)
190+
def_op('STORE_DEREF', 138)
191191
hasfree.append(138)
192+
def_op('DELETE_DEREF', 139)
193+
hasfree.append(139)
192194

193195
def_op('CALL_FUNCTION_KW', 141) # #args + #kwargs
194196
def_op('CALL_FUNCTION_EX', 142) # Flags

Lib/test/test_dis.py

+70-61
Original file line numberDiff line numberDiff line change
@@ -427,15 +427,17 @@ def foo(x):
427427
return foo
428428

429429
dis_nested_0 = """\
430-
%3d 0 LOAD_CLOSURE 2 (y)
431-
2 BUILD_TUPLE 1
432-
4 LOAD_CONST 1 (<code object foo at 0x..., file "%s", line %d>)
433-
6 LOAD_CONST 2 ('_h.<locals>.foo')
434-
8 MAKE_FUNCTION 8 (closure)
435-
10 STORE_FAST 1 (foo)
436-
437-
%3d 12 LOAD_FAST 1 (foo)
438-
14 RETURN_VALUE
430+
0 MAKE_CELL 2 (y)
431+
432+
%3d 2 LOAD_CLOSURE 2 (y)
433+
4 BUILD_TUPLE 1
434+
6 LOAD_CONST 1 (<code object foo at 0x..., file "%s", line %d>)
435+
8 LOAD_CONST 2 ('_h.<locals>.foo')
436+
10 MAKE_FUNCTION 8 (closure)
437+
12 STORE_FAST 1 (foo)
438+
439+
%3d 14 LOAD_FAST 1 (foo)
440+
16 RETURN_VALUE
439441
""" % (_h.__code__.co_firstlineno + 1,
440442
__file__,
441443
_h.__code__.co_firstlineno + 1,
@@ -444,15 +446,17 @@ def foo(x):
444446

445447
dis_nested_1 = """%s
446448
Disassembly of <code object foo at 0x..., file "%s", line %d>:
447-
%3d 0 LOAD_CLOSURE 1 (x)
448-
2 BUILD_TUPLE 1
449-
4 LOAD_CONST 1 (<code object <listcomp> at 0x..., file "%s", line %d>)
450-
6 LOAD_CONST 2 ('_h.<locals>.foo.<locals>.<listcomp>')
451-
8 MAKE_FUNCTION 8 (closure)
452-
10 LOAD_DEREF 2 (y)
453-
12 GET_ITER
454-
14 CALL_FUNCTION 1
455-
16 RETURN_VALUE
449+
0 MAKE_CELL 1 (x)
450+
451+
%3d 2 LOAD_CLOSURE 1 (x)
452+
4 BUILD_TUPLE 1
453+
6 LOAD_CONST 1 (<code object <listcomp> at 0x..., file "%s", line %d>)
454+
8 LOAD_CONST 2 ('_h.<locals>.foo.<locals>.<listcomp>')
455+
10 MAKE_FUNCTION 8 (closure)
456+
12 LOAD_DEREF 2 (y)
457+
14 GET_ITER
458+
16 CALL_FUNCTION 1
459+
18 RETURN_VALUE
456460
""" % (dis_nested_0,
457461
__file__,
458462
_h.__code__.co_firstlineno + 1,
@@ -958,59 +962,64 @@ def jumpy():
958962
#print('expected_opinfo_jumpy = [\n ',
959963
#',\n '.join(map(str, _instructions)), ',\n]', sep='')
960964

965+
#dis.dis(outer)
961966

962967
Instruction = dis.Instruction
963968
expected_opinfo_outer = [
964-
Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval=(3, 4), argrepr='(3, 4)', offset=0, starts_line=2, is_jump_target=False),
965-
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=3, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False),
966-
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=4, argval='b', argrepr='b', offset=4, starts_line=None, is_jump_target=False),
967-
Instruction(opname='BUILD_TUPLE', opcode=102, arg=2, argval=2, argrepr='', offset=6, starts_line=None, is_jump_target=False),
968-
Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_f, argrepr=repr(code_object_f), offset=8, starts_line=None, is_jump_target=False),
969-
Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer.<locals>.f', argrepr="'outer.<locals>.f'", offset=10, starts_line=None, is_jump_target=False),
970-
Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='defaults, closure', offset=12, starts_line=None, is_jump_target=False),
971-
Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='f', argrepr='f', offset=14, starts_line=None, is_jump_target=False),
972-
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=16, starts_line=7, is_jump_target=False),
973-
Instruction(opname='LOAD_DEREF', opcode=136, arg=3, argval='a', argrepr='a', offset=18, starts_line=None, is_jump_target=False),
974-
Instruction(opname='LOAD_DEREF', opcode=136, arg=4, argval='b', argrepr='b', offset=20, starts_line=None, is_jump_target=False),
975-
Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval='', argrepr="''", offset=22, starts_line=None, is_jump_target=False),
976-
Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval=1, argrepr='1', offset=24, starts_line=None, is_jump_target=False),
977-
Instruction(opname='BUILD_LIST', opcode=103, arg=0, argval=0, argrepr='', offset=26, starts_line=None, is_jump_target=False),
978-
Instruction(opname='BUILD_MAP', opcode=105, arg=0, argval=0, argrepr='', offset=28, starts_line=None, is_jump_target=False),
979-
Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval='Hello world!', argrepr="'Hello world!'", offset=30, starts_line=None, is_jump_target=False),
980-
Instruction(opname='CALL_FUNCTION', opcode=131, arg=7, argval=7, argrepr='', offset=32, starts_line=None, is_jump_target=False),
981-
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=34, starts_line=None, is_jump_target=False),
982-
Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='f', argrepr='f', offset=36, starts_line=8, is_jump_target=False),
983-
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=38, starts_line=None, is_jump_target=False),
969+
Instruction(opname='MAKE_CELL', opcode=135, arg=3, argval='a', argrepr='a', offset=0, starts_line=None, is_jump_target=False),
970+
Instruction(opname='MAKE_CELL', opcode=135, arg=4, argval='b', argrepr='b', offset=2, starts_line=None, is_jump_target=False),
971+
Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval=(3, 4), argrepr='(3, 4)', offset=4, starts_line=2, is_jump_target=False),
972+
Instruction(opname='LOAD_CLOSURE', opcode=136, arg=3, argval='a', argrepr='a', offset=6, starts_line=None, is_jump_target=False),
973+
Instruction(opname='LOAD_CLOSURE', opcode=136, arg=4, argval='b', argrepr='b', offset=8, starts_line=None, is_jump_target=False),
974+
Instruction(opname='BUILD_TUPLE', opcode=102, arg=2, argval=2, argrepr='', offset=10, starts_line=None, is_jump_target=False),
975+
Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_f, argrepr=repr(code_object_f), offset=12, starts_line=None, is_jump_target=False),
976+
Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer.<locals>.f', argrepr="'outer.<locals>.f'", offset=14, starts_line=None, is_jump_target=False),
977+
Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='defaults, closure', offset=16, starts_line=None, is_jump_target=False),
978+
Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='f', argrepr='f', offset=18, starts_line=None, is_jump_target=False),
979+
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=20, starts_line=7, is_jump_target=False),
980+
Instruction(opname='LOAD_DEREF', opcode=137, arg=3, argval='a', argrepr='a', offset=22, starts_line=None, is_jump_target=False),
981+
Instruction(opname='LOAD_DEREF', opcode=137, arg=4, argval='b', argrepr='b', offset=24, starts_line=None, is_jump_target=False),
982+
Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval='', argrepr="''", offset=26, starts_line=None, is_jump_target=False),
983+
Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval=1, argrepr='1', offset=28, starts_line=None, is_jump_target=False),
984+
Instruction(opname='BUILD_LIST', opcode=103, arg=0, argval=0, argrepr='', offset=30, starts_line=None, is_jump_target=False),
985+
Instruction(opname='BUILD_MAP', opcode=105, arg=0, argval=0, argrepr='', offset=32, starts_line=None, is_jump_target=False),
986+
Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval='Hello world!', argrepr="'Hello world!'", offset=34, starts_line=None, is_jump_target=False),
987+
Instruction(opname='CALL_FUNCTION', opcode=131, arg=7, argval=7, argrepr='', offset=36, starts_line=None, is_jump_target=False),
988+
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=38, starts_line=None, is_jump_target=False),
989+
Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='f', argrepr='f', offset=40, starts_line=8, is_jump_target=False),
990+
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=42, starts_line=None, is_jump_target=False),
984991
]
985992

986993
expected_opinfo_f = [
987-
Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=(5, 6), argrepr='(5, 6)', offset=0, starts_line=3, is_jump_target=False),
988-
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=5, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False),
989-
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=6, argval='b', argrepr='b', offset=4, starts_line=None, is_jump_target=False),
990-
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=3, argval='c', argrepr='c', offset=6, starts_line=None, is_jump_target=False),
991-
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=4, argval='d', argrepr='d', offset=8, starts_line=None, is_jump_target=False),
992-
Instruction(opname='BUILD_TUPLE', opcode=102, arg=4, argval=4, argrepr='', offset=10, starts_line=None, is_jump_target=False),
993-
Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_inner, argrepr=repr(code_object_inner), offset=12, starts_line=None, is_jump_target=False),
994-
Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer.<locals>.f.<locals>.inner', argrepr="'outer.<locals>.f.<locals>.inner'", offset=14, starts_line=None, is_jump_target=False),
995-
Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='defaults, closure', offset=16, starts_line=None, is_jump_target=False),
996-
Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='inner', argrepr='inner', offset=18, starts_line=None, is_jump_target=False),
997-
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=20, starts_line=5, is_jump_target=False),
998-
Instruction(opname='LOAD_DEREF', opcode=136, arg=5, argval='a', argrepr='a', offset=22, starts_line=None, is_jump_target=False),
999-
Instruction(opname='LOAD_DEREF', opcode=136, arg=6, argval='b', argrepr='b', offset=24, starts_line=None, is_jump_target=False),
1000-
Instruction(opname='LOAD_DEREF', opcode=136, arg=3, argval='c', argrepr='c', offset=26, starts_line=None, is_jump_target=False),
1001-
Instruction(opname='LOAD_DEREF', opcode=136, arg=4, argval='d', argrepr='d', offset=28, starts_line=None, is_jump_target=False),
1002-
Instruction(opname='CALL_FUNCTION', opcode=131, arg=4, argval=4, argrepr='', offset=30, starts_line=None, is_jump_target=False),
1003-
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=32, starts_line=None, is_jump_target=False),
1004-
Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='inner', argrepr='inner', offset=34, starts_line=6, is_jump_target=False),
1005-
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=36, starts_line=None, is_jump_target=False),
994+
Instruction(opname='MAKE_CELL', opcode=135, arg=3, argval='c', argrepr='c', offset=0, starts_line=None, is_jump_target=False),
995+
Instruction(opname='MAKE_CELL', opcode=135, arg=4, argval='d', argrepr='d', offset=2, starts_line=None, is_jump_target=False),
996+
Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=(5, 6), argrepr='(5, 6)', offset=4, starts_line=3, is_jump_target=False),
997+
Instruction(opname='LOAD_CLOSURE', opcode=136, arg=5, argval='a', argrepr='a', offset=6, starts_line=None, is_jump_target=False),
998+
Instruction(opname='LOAD_CLOSURE', opcode=136, arg=6, argval='b', argrepr='b', offset=8, starts_line=None, is_jump_target=False),
999+
Instruction(opname='LOAD_CLOSURE', opcode=136, arg=3, argval='c', argrepr='c', offset=10, starts_line=None, is_jump_target=False),
1000+
Instruction(opname='LOAD_CLOSURE', opcode=136, arg=4, argval='d', argrepr='d', offset=12, starts_line=None, is_jump_target=False),
1001+
Instruction(opname='BUILD_TUPLE', opcode=102, arg=4, argval=4, argrepr='', offset=14, starts_line=None, is_jump_target=False),
1002+
Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_inner, argrepr=repr(code_object_inner), offset=16, starts_line=None, is_jump_target=False),
1003+
Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer.<locals>.f.<locals>.inner', argrepr="'outer.<locals>.f.<locals>.inner'", offset=18, starts_line=None, is_jump_target=False),
1004+
Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='defaults, closure', offset=20, starts_line=None, is_jump_target=False),
1005+
Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='inner', argrepr='inner', offset=22, starts_line=None, is_jump_target=False),
1006+
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=24, starts_line=5, is_jump_target=False),
1007+
Instruction(opname='LOAD_DEREF', opcode=137, arg=5, argval='a', argrepr='a', offset=26, starts_line=None, is_jump_target=False),
1008+
Instruction(opname='LOAD_DEREF', opcode=137, arg=6, argval='b', argrepr='b', offset=28, starts_line=None, is_jump_target=False),
1009+
Instruction(opname='LOAD_DEREF', opcode=137, arg=3, argval='c', argrepr='c', offset=30, starts_line=None, is_jump_target=False),
1010+
Instruction(opname='LOAD_DEREF', opcode=137, arg=4, argval='d', argrepr='d', offset=32, starts_line=None, is_jump_target=False),
1011+
Instruction(opname='CALL_FUNCTION', opcode=131, arg=4, argval=4, argrepr='', offset=34, starts_line=None, is_jump_target=False),
1012+
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=36, starts_line=None, is_jump_target=False),
1013+
Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='inner', argrepr='inner', offset=38, starts_line=6, is_jump_target=False),
1014+
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=40, starts_line=None, is_jump_target=False),
10061015
]
10071016

10081017
expected_opinfo_inner = [
10091018
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=0, starts_line=4, is_jump_target=False),
1010-
Instruction(opname='LOAD_DEREF', opcode=136, arg=2, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False),
1011-
Instruction(opname='LOAD_DEREF', opcode=136, arg=3, argval='b', argrepr='b', offset=4, starts_line=None, is_jump_target=False),
1012-
Instruction(opname='LOAD_DEREF', opcode=136, arg=4, argval='c', argrepr='c', offset=6, starts_line=None, is_jump_target=False),
1013-
Instruction(opname='LOAD_DEREF', opcode=136, arg=5, argval='d', argrepr='d', offset=8, starts_line=None, is_jump_target=False),
1019+
Instruction(opname='LOAD_DEREF', opcode=137, arg=2, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False),
1020+
Instruction(opname='LOAD_DEREF', opcode=137, arg=3, argval='b', argrepr='b', offset=4, starts_line=None, is_jump_target=False),
1021+
Instruction(opname='LOAD_DEREF', opcode=137, arg=4, argval='c', argrepr='c', offset=6, starts_line=None, is_jump_target=False),
1022+
Instruction(opname='LOAD_DEREF', opcode=137, arg=5, argval='d', argrepr='d', offset=8, starts_line=None, is_jump_target=False),
10141023
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='e', argrepr='e', offset=10, starts_line=None, is_jump_target=False),
10151024
Instruction(opname='LOAD_FAST', opcode=124, arg=1, argval='f', argrepr='f', offset=12, starts_line=None, is_jump_target=False),
10161025
Instruction(opname='CALL_FUNCTION', opcode=131, arg=6, argval=6, argrepr='', offset=14, starts_line=None, is_jump_target=False),

Lib/test/test_scope.py

+51
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,57 @@ def bar():
176176
self.assertEqual(foo(a=42), 50)
177177
self.assertEqual(foo(), 25)
178178

179+
def testCellIsArgAndEscapes(self):
180+
# We need to be sure that a cell passed in as an arg still
181+
# gets wrapped in a new cell if the arg escapes into an
182+
# inner function (closure).
183+
184+
def external():
185+
value = 42
186+
def inner():
187+
return value
188+
cell, = inner.__closure__
189+
return cell
190+
cell_ext = external()
191+
192+
def spam(arg):
193+
def eggs():
194+
return arg
195+
return eggs
196+
197+
eggs = spam(cell_ext)
198+
cell_closure, = eggs.__closure__
199+
cell_eggs = eggs()
200+
201+
self.assertIs(cell_eggs, cell_ext)
202+
self.assertIsNot(cell_eggs, cell_closure)
203+
204+
def testCellIsLocalAndEscapes(self):
205+
# We need to be sure that a cell bound to a local still
206+
# gets wrapped in a new cell if the local escapes into an
207+
# inner function (closure).
208+
209+
def external():
210+
value = 42
211+
def inner():
212+
return value
213+
cell, = inner.__closure__
214+
return cell
215+
cell_ext = external()
216+
217+
def spam(arg):
218+
cell = arg
219+
def eggs():
220+
return cell
221+
return eggs
222+
223+
eggs = spam(cell_ext)
224+
cell_closure, = eggs.__closure__
225+
cell_eggs = eggs()
226+
227+
self.assertIs(cell_eggs, cell_ext)
228+
self.assertIsNot(cell_eggs, cell_closure)
229+
179230
def testRecursion(self):
180231

181232
def f(x):

0 commit comments

Comments
 (0)