Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e028ae9

Browse filesBrowse files
authored
bpo-45923: Handle call events in bytecode (GH-30364)
* Add a RESUME instruction to handle "call" events.
1 parent 3e43fac commit e028ae9
Copy full SHA for e028ae9

File tree

13 files changed

+678
-529
lines changed
Filter options

13 files changed

+678
-529
lines changed

‎Doc/library/dis.rst

Copy file name to clipboardExpand all lines: Doc/library/dis.rst
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,6 +1206,20 @@ All of the following opcodes use their arguments.
12061206
.. versionadded:: 3.11
12071207

12081208

1209+
.. opcode:: RESUME (where)
1210+
1211+
A no-op. Performs internal tracing, debugging and optimization checks.
1212+
1213+
The ``where`` operand marks where the ``RESUME`` occurs:
1214+
1215+
* ``0`` The start of a function
1216+
* ``1`` After a ``yield`` expression
1217+
* ``2`` After a ``yield from`` expression
1218+
* ``3`` After an ``await`` expression
1219+
1220+
.. versionadded:: 3.11
1221+
1222+
12091223
.. opcode:: HAVE_ARGUMENT
12101224

12111225
This is not really an opcode. It identifies the dividing line between

‎Include/opcode.h

Copy file name to clipboardExpand all lines: Include/opcode.h
+1Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Lib/importlib/_bootstrap_external.py

Copy file name to clipboardExpand all lines: Lib/importlib/_bootstrap_external.py
+6-1Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,16 +379,21 @@ def _write_atomic(path, data, mode=0o666):
379379
# Python 3.11a4 3471 (bpo-46202: remove pop POP_EXCEPT_AND_RERAISE)
380380
# Python 3.11a4 3472 (bpo-46009: replace GEN_START with POP_TOP)
381381
# Python 3.11a4 3473 (Add POP_JUMP_IF_NOT_NONE/POP_JUMP_IF_NONE opcodes)
382+
# Python 3.11a4 3474 (Add RESUME opcode)
383+
384+
# Python 3.12 will start with magic number 3500
382385

383386
#
384387
# MAGIC must change whenever the bytecode emitted by the compiler may no
385388
# longer be understood by older implementations of the eval loop (usually
386389
# due to the addition of new opcodes).
387390
#
391+
# Starting with Python 3.11, Python 3.n starts with magic number 2900+50n.
392+
#
388393
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
389394
# in PC/launcher.c must also be updated.
390395

391-
MAGIC_NUMBER = (3473).to_bytes(2, 'little') + b'\r\n'
396+
MAGIC_NUMBER = (3474).to_bytes(2, 'little') + b'\r\n'
392397
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
393398

394399
_PYCACHE = '__pycache__'

‎Lib/opcode.py

Copy file name to clipboardExpand all lines: Lib/opcode.py
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ def jabs_op(name, op):
178178
hasfree.append(148)
179179
def_op('COPY_FREE_VARS', 149)
180180

181+
def_op('RESUME', 151)
181182
def_op('MATCH_CLASS', 152)
182183

183184
def_op('FORMAT_VALUE', 155)

‎Lib/test/test_code.py

Copy file name to clipboardExpand all lines: Lib/test/test_code.py
+11-4Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ def test_co_positions_artificial_instructions(self):
367367
# get assigned the first_lineno but they don't have other positions.
368368
# There is no easy way of inferring them at that stage, so for now
369369
# we don't support it.
370-
self.assertTrue(positions.count(None) in [0, 4])
370+
self.assertIn(positions.count(None), [0, 3, 4])
371371

372372
if not any(positions):
373373
artificial_instructions.append(instr)
@@ -378,6 +378,7 @@ def test_co_positions_artificial_instructions(self):
378378
for instruction in artificial_instructions
379379
],
380380
[
381+
('RESUME', 0),
381382
("PUSH_EXC_INFO", None),
382383
("LOAD_CONST", None), # artificial 'None'
383384
("STORE_NAME", "e"), # XX: we know the location for this
@@ -419,7 +420,9 @@ def test_co_positions_empty_linetable(self):
419420
def func():
420421
x = 1
421422
new_code = func.__code__.replace(co_linetable=b'')
422-
for line, end_line, column, end_column in new_code.co_positions():
423+
positions = new_code.co_positions()
424+
next(positions) # Skip RESUME at start
425+
for line, end_line, column, end_column in positions:
423426
self.assertIsNone(line)
424427
self.assertEqual(end_line, new_code.co_firstlineno + 1)
425428

@@ -428,7 +431,9 @@ def test_co_positions_empty_endlinetable(self):
428431
def func():
429432
x = 1
430433
new_code = func.__code__.replace(co_endlinetable=b'')
431-
for line, end_line, column, end_column in new_code.co_positions():
434+
positions = new_code.co_positions()
435+
next(positions) # Skip RESUME at start
436+
for line, end_line, column, end_column in positions:
432437
self.assertEqual(line, new_code.co_firstlineno + 1)
433438
self.assertIsNone(end_line)
434439

@@ -437,7 +442,9 @@ def test_co_positions_empty_columntable(self):
437442
def func():
438443
x = 1
439444
new_code = func.__code__.replace(co_columntable=b'')
440-
for line, end_line, column, end_column in new_code.co_positions():
445+
positions = new_code.co_positions()
446+
next(positions) # Skip RESUME at start
447+
for line, end_line, column, end_column in positions:
441448
self.assertEqual(line, new_code.co_firstlineno + 1)
442449
self.assertEqual(end_line, new_code.co_firstlineno + 1)
443450
self.assertIsNone(column)

‎Lib/test/test_compile.py

Copy file name to clipboardExpand all lines: Lib/test/test_compile.py
+16-15Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ def test_leading_newlines(self):
158158
s256 = "".join(["\n"] * 256 + ["spam"])
159159
co = compile(s256, 'fn', 'exec')
160160
self.assertEqual(co.co_firstlineno, 1)
161-
self.assertEqual(list(co.co_lines()), [(0, 8, 257)])
161+
self.assertEqual(list(co.co_lines()), [(0, 2, None), (2, 10, 257)])
162162

163163
def test_literals_with_leading_zeroes(self):
164164
for arg in ["077787", "0xj", "0x.", "0e", "090000000000000",
@@ -759,7 +759,7 @@ def unused_block_while_else():
759759

760760
for func in funcs:
761761
opcodes = list(dis.get_instructions(func))
762-
self.assertLessEqual(len(opcodes), 3)
762+
self.assertLessEqual(len(opcodes), 4)
763763
self.assertEqual('LOAD_CONST', opcodes[-2].opname)
764764
self.assertEqual(None, opcodes[-2].argval)
765765
self.assertEqual('RETURN_VALUE', opcodes[-1].opname)
@@ -778,10 +778,10 @@ def continue_in_while():
778778
# Check that we did not raise but we also don't generate bytecode
779779
for func in funcs:
780780
opcodes = list(dis.get_instructions(func))
781-
self.assertEqual(2, len(opcodes))
782-
self.assertEqual('LOAD_CONST', opcodes[0].opname)
783-
self.assertEqual(None, opcodes[0].argval)
784-
self.assertEqual('RETURN_VALUE', opcodes[1].opname)
781+
self.assertEqual(3, len(opcodes))
782+
self.assertEqual('LOAD_CONST', opcodes[1].opname)
783+
self.assertEqual(None, opcodes[1].argval)
784+
self.assertEqual('RETURN_VALUE', opcodes[2].opname)
785785

786786
def test_consts_in_conditionals(self):
787787
def and_true(x):
@@ -802,9 +802,9 @@ def or_false(x):
802802
for func in funcs:
803803
with self.subTest(func=func):
804804
opcodes = list(dis.get_instructions(func))
805-
self.assertEqual(2, len(opcodes))
806-
self.assertIn('LOAD_', opcodes[0].opname)
807-
self.assertEqual('RETURN_VALUE', opcodes[1].opname)
805+
self.assertLessEqual(len(opcodes), 3)
806+
self.assertIn('LOAD_', opcodes[-2].opname)
807+
self.assertEqual('RETURN_VALUE', opcodes[-1].opname)
808808

809809
def test_imported_load_method(self):
810810
sources = [
@@ -906,7 +906,7 @@ def load_attr():
906906
o.
907907
a
908908
)
909-
load_attr_lines = [ 2, 3, 1 ]
909+
load_attr_lines = [ 0, 2, 3, 1 ]
910910

911911
def load_method():
912912
return (
@@ -915,7 +915,7 @@ def load_method():
915915
0
916916
)
917917
)
918-
load_method_lines = [ 2, 3, 4, 3, 1 ]
918+
load_method_lines = [ 0, 2, 3, 4, 3, 1 ]
919919

920920
def store_attr():
921921
(
@@ -924,7 +924,7 @@ def store_attr():
924924
) = (
925925
v
926926
)
927-
store_attr_lines = [ 5, 2, 3 ]
927+
store_attr_lines = [ 0, 5, 2, 3 ]
928928

929929
def aug_store_attr():
930930
(
@@ -933,7 +933,7 @@ def aug_store_attr():
933933
) += (
934934
v
935935
)
936-
aug_store_attr_lines = [ 2, 3, 5, 1, 3 ]
936+
aug_store_attr_lines = [ 0, 2, 3, 5, 1, 3 ]
937937

938938
funcs = [ load_attr, load_method, store_attr, aug_store_attr]
939939
func_lines = [ load_attr_lines, load_method_lines,
@@ -942,7 +942,8 @@ def aug_store_attr():
942942
for func, lines in zip(funcs, func_lines, strict=True):
943943
with self.subTest(func=func):
944944
code_lines = [ line-func.__code__.co_firstlineno
945-
for (_, _, line) in func.__code__.co_lines() ]
945+
for (_, _, line) in func.__code__.co_lines()
946+
if line is not None ]
946947
self.assertEqual(lines, code_lines)
947948

948949
def test_line_number_genexp(self):
@@ -966,7 +967,7 @@ async def test(aseq):
966967
async for i in aseq:
967968
body
968969

969-
expected_lines = [None, 1, 2, 1]
970+
expected_lines = [None, 0, 1, 2, 1]
970971
code_lines = [ None if line is None else line-test.__code__.co_firstlineno
971972
for (_, _, line) in test.__code__.co_lines() ]
972973
self.assertEqual(expected_lines, code_lines)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.