Skip to content

Instantly share code, notes, and snippets.

@nedbat
Last active December 14, 2020 00:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nedbat/6c5dedde9df8d2de13de8a6a39a5f112 to your computer and use it in GitHub Desktop.
Save nedbat/6c5dedde9df8d2de13de8a6a39a5f112 to your computer and use it in GitHub Desktop.

PEP 626 Changes

These are changes I'm seeing in CPython 3.10 as a result of the PEP 626 changes.

The code comes from the coverage.py test suite. Numbers used in the code are often the line number.

Code after break/continue is no longer compiled

Unreachable lines after break/continue used to be compiled. Coverage.py would report them as runnable but not run. Now they will appear as not runnable.

for i in range(10):
    a = i
    break       # 3
    a = 99
assert a == 0   # 5
Python 3.9 dis Python 3.10 dis
1           0 LOAD_NAME                0 (range)
            2 LOAD_CONST               0 (10)
            4 CALL_FUNCTION            1
            6 GET_ITER
      >>    8 FOR_ITER                16 (to 26)
           10 STORE_NAME               1 (i)

2          12 LOAD_NAME                1 (i)
           14 STORE_NAME               2 (a)

3          16 POP_TOP
           18 JUMP_ABSOLUTE           26

4          20 LOAD_CONST               1 (99)
           22 STORE_NAME               2 (a)
           24 JUMP_ABSOLUTE            8

5     >>   26 LOAD_NAME                2 (a)
           28 LOAD_CONST               2 (0)
           30 COMPARE_OP               2 (==)
           32 POP_JUMP_IF_TRUE        38
           34 LOAD_ASSERTION_ERROR
           36 RAISE_VARARGS            1
      >>   38 LOAD_CONST               3 (None)
           40 RETURN_VALUE
1           0 LOAD_NAME                0 (range)
            2 LOAD_CONST               0 (10)
            4 CALL_FUNCTION            1
            6 GET_ITER
            8 FOR_ITER                10 (to 20)
           10 STORE_NAME               1 (i)

2          12 LOAD_NAME                1 (i)
           14 STORE_NAME               2 (a)

3          16 POP_TOP
           18 JUMP_ABSOLUTE           20

5     >>   20 LOAD_NAME                2 (a)
           22 LOAD_CONST               2 (0)
           24 COMPARE_OP               2 (==)
           26 POP_JUMP_IF_TRUE        32
           28 LOAD_ASSERTION_ERROR
           30 RAISE_VARARGS            1
      >>   32 LOAD_CONST               3 (None)
           34 RETURN_VALUE

Affected tests:

  • LoopArcTest.test_break
  • LoopArcTest.test_continue

First line number of modules

# line 1
a = 2
b = 3
# line 4
c = 5
Python 3.9 show_pyc Python 3.10 show_pyc
code
   name '<module>'
   argcount 0
   nlocals 0
   stacksize 1
   flags 0040: CO_NOFREE
   code 64005a0064015a0164025a0264035300
  2           0 LOAD_CONST               0 (2)
              2 STORE_NAME               0 (a)

  3           4 LOAD_CONST               1 (3)
              6 STORE_NAME               1 (b)

  5           8 LOAD_CONST               2 (5)
             10 STORE_NAME               2 (c)
             12 LOAD_CONST               3 (None)
             14 RETURN_VALUE
   consts
      0: 2
      1: 3
      2: 5
      3: None
   names ('a', 'b', 'c')
   varnames ()
   freevars ()
   cellvars ()
   filename 'simple_sequence.py'
   firstlineno 2
   lnotab 04010402
code
   name '<module>'
   argcount 0
   nlocals 0
   stacksize 1
   flags 0040: CO_NOFREE
   code 64005a0064015a0164025a0264035300
  2           0 LOAD_CONST               0 (2)
              2 STORE_NAME               0 (a)

  3           4 LOAD_CONST               1 (3)
              6 STORE_NAME               1 (b)

  5           8 LOAD_CONST               2 (5)
             10 STORE_NAME               2 (c)
             12 LOAD_CONST               3 (None)
             14 RETURN_VALUE
   consts
      0: 2
      1: 3
      2: 5
      3: None
   names ('a', 'b', 'c')
   varnames ()
   freevars ()
   cellvars ()
   filename 'simple_sequence.py'
   firstlineno 1
   lnotab 000104010402

Affected tests:

  • SimpleArcTest.test_simple_sequence
  • LoopArcTest.test_multiline_dict_comp

Except clause when no exception

This code jumps from line 3 to 5 to 7 even though no exception happens:

a, b, c = 1, 1, 1
try:
    a = 3
except:
    b = 5
finally:
    c = 7
assert a == 3 and b == 1 and c == 7
Python 3.9 dis Python 3.10 dis
1           0 LOAD_CONST               0 ((1, 1, 1))
            2 UNPACK_SEQUENCE          3
            4 STORE_NAME               0 (a)
            6 STORE_NAME               1 (b)
            8 STORE_NAME               2 (c)

2          10 SETUP_FINALLY           34 (to 46)
           12 SETUP_FINALLY            8 (to 22)

3          14 LOAD_CONST               1 (3)
           16 STORE_NAME               0 (a)
           18 POP_BLOCK
           20 JUMP_FORWARD            16 (to 38)

4     >>   22 POP_TOP
           24 POP_TOP
           26 POP_TOP

5          28 LOAD_CONST               2 (5)
           30 STORE_NAME               1 (b)
           32 POP_EXCEPT
           34 JUMP_FORWARD             2 (to 38)
           36 RERAISE
      >>   38 POP_BLOCK

7          40 LOAD_CONST               3 (7)
           42 STORE_NAME               2 (c)
           44 JUMP_FORWARD             6 (to 52)
      >>   46 LOAD_CONST               3 (7)
           48 STORE_NAME               2 (c)
           50 RERAISE

8     >>   52 LOAD_NAME                0 (a)
           54 LOAD_CONST               1 (3)
           56 COMPARE_OP               2 (==)
           58 POP_JUMP_IF_FALSE       76
           60 LOAD_NAME                1 (b)
           62 LOAD_CONST               4 (1)
           64 COMPARE_OP               2 (==)
           66 POP_JUMP_IF_FALSE       76
           68 LOAD_NAME                2 (c)
           70 LOAD_CONST               3 (7)
           72 COMPARE_OP               2 (==)
           74 POP_JUMP_IF_TRUE        80
      >>   76 LOAD_ASSERTION_ERROR
           78 RAISE_VARARGS            1
      >>   80 LOAD_CONST               5 (None)
           82 RETURN_VALUE
1           0 LOAD_CONST               0 ((1, 1, 1))
            2 UNPACK_SEQUENCE          3
            4 STORE_NAME               0 (a)
            6 STORE_NAME               1 (b)
            8 STORE_NAME               2 (c)

2          10 SETUP_FINALLY           32 (to 44)
           12 SETUP_FINALLY            8 (to 22)

3          14 LOAD_CONST               1 (3)
           16 STORE_NAME               0 (a)
           18 POP_BLOCK
           20 JUMP_FORWARD            14 (to 36)

4     >>   22 POP_TOP
           24 POP_TOP
           26 POP_TOP

5          28 LOAD_CONST               2 (5)
           30 STORE_NAME               1 (b)
           32 POP_EXCEPT
           34 JUMP_FORWARD             0 (to 36)
      >>   36 POP_BLOCK

7          38 LOAD_CONST               3 (7)
           40 STORE_NAME               2 (c)
           42 JUMP_FORWARD             6 (to 50)
      >>   44 LOAD_CONST               3 (7)
           46 STORE_NAME               2 (c)
           48 RERAISE

8     >>   50 LOAD_NAME                0 (a)
           52 LOAD_CONST               1 (3)
           54 COMPARE_OP               2 (==)
           56 POP_JUMP_IF_FALSE       74
           58 LOAD_NAME                1 (b)
           60 LOAD_CONST               4 (1)
           62 COMPARE_OP               2 (==)
           64 POP_JUMP_IF_FALSE       74
           66 LOAD_NAME                2 (c)
           68 LOAD_CONST               3 (7)
           70 COMPARE_OP               2 (==)
           72 POP_JUMP_IF_TRUE        78
      >>   74 LOAD_ASSERTION_ERROR
           76 RAISE_VARARGS            1
      >>   78 LOAD_CONST               5 (None)
           80 RETURN_VALUE

Affected tests:

  • ExceptionArcTest.test_except_finally

Double loops

for i in range(3):
    for j in range(3):
        a = i + j
assert a == 4

In this code, 3.10 jumps from line 3 to line 1. All other versions jump from 3 to 2, and from 2 to 1.

Affected tests:

  • LoopArcTest.test_nested_loop

if-break

An if-break used to jump from the if to the break and then to the exit of the loop. Now the break is skipped, and the if jumps to the exit.

def whileelse(seq):
    while seq:
        n = seq.pop()
        if n > 4:
            break   # line 5
    else:
        n = 99
    return n        # line 8
Python 3.9 dis Python 3.10 dis
1           0 LOAD_CONST               0 (<code object whileelse at 0x10da57660, file "while_else.py", line 1>)
            2 LOAD_CONST               1 ('whileelse')
            4 MAKE_FUNCTION            0
            6 STORE_NAME               0 (whileelse)
            8 LOAD_CONST               2 (None)
           10 RETURN_VALUE
Disassembly of <code object whileelse at 0x10da57660, file "while_else.py", line 1>:
2 >> 0 LOAD_FAST 0 (seq)

2 POP_JUMP_IF_FALSE 24

3 4 LOAD_FAST 0 (seq)

6 LOAD_METHOD 0 (pop) 8 CALL_METHOD 0

10 STORE_FAST 1 (n)

4 12 LOAD_FAST 1 (n)

14 LOAD_CONST 1 (4) 16 COMPARE_OP 4 (>) 18 POP_JUMP_IF_FALSE 0

5 20 JUMP_ABSOLUTE 28

22 JUMP_ABSOLUTE 0

7 >> 24 LOAD_CONST 2 (99)

26 STORE_FAST 1 (n)

8 >> 28 LOAD_FAST 1 (n)

30 RETURN_VALUE

1           0 LOAD_CONST               0 (<code object whileelse at 0x10ade4030, file "while_else.py", line 1>)
            2 LOAD_CONST               1 ('whileelse')
            4 MAKE_FUNCTION            0
            6 STORE_NAME               0 (whileelse)
            8 LOAD_CONST               2 (None)
           10 RETURN_VALUE
Disassembly of <code object whileelse at 0x10ade4030, file "while_else.py", line 1>:
2 >> 0 LOAD_FAST 0 (seq)

2 POP_JUMP_IF_FALSE 24

3 4 LOAD_FAST 0 (seq)

6 LOAD_METHOD 0 (pop) 8 CALL_METHOD 0

10 STORE_FAST 1 (n)

4 12 LOAD_FAST 1 (n)

14 LOAD_CONST 1 (4) 16 COMPARE_OP 4 (>) 18 POP_JUMP_IF_FALSE 0

8 20 LOAD_FAST 1 (n)

22 RETURN_VALUE

7 >> 24 LOAD_CONST 2 (99)

26 STORE_FAST 1 (n)

8 28 LOAD_FAST 1 (n)

30 RETURN_VALUE

Affected tests:

  • LoopArcTest.test_while_else

End-of-loop jumps

The last iteration of a for loop used to jump back to the for-statement, which then jumped to after the for loop. Now the last statement in the for loop jumps to the statement after the for loop.

Affected tests:

  • LoopArcTest.test_for_if_else_for

Finally handling

When the break happens, this code used to (in Python 3.8 and 3.9) jump from 7 to 10 to 7 to 13. Now (in Python 3.10) it jumps from 7 to 10 to 13.

a, c, d, i = 1, 1, 1, 99
try:
    for i in range(3):
        try:
            a = 5
            if i > 0:
                break                   # line 7
            a = 8
        finally:
            c = 10
except:
    d = 12                              # line 12
assert a == 5 and c == 10 and d == 1    # line 13
Python 3.9 dis Python 3.10 dis
1           0 LOAD_CONST               0 ((1, 1, 1, 99))
            2 UNPACK_SEQUENCE          4
            4 STORE_NAME               0 (a)
            6 STORE_NAME               1 (c)
            8 STORE_NAME               2 (d)
           10 STORE_NAME               3 (i)

2          12 SETUP_FINALLY           60 (to 74)

3          14 LOAD_NAME                4 (range)
           16 LOAD_CONST               1 (3)
           18 CALL_FUNCTION            1
           20 GET_ITER
      >>   22 FOR_ITER                46 (to 70)
           24 STORE_NAME               3 (i)

4          26 SETUP_FINALLY           34 (to 62)

5          28 LOAD_CONST               2 (5)
           30 STORE_NAME               0 (a)

6          32 LOAD_NAME                3 (i)
           34 LOAD_CONST               3 (0)
           36 COMPARE_OP               4 (>)
           38 POP_JUMP_IF_FALSE       50

7          40 POP_BLOCK
10 42 LOAD_CONST 4 (10)

44 STORE_NAME 1 (c)

7 46 POP_TOP

48 JUMP_ABSOLUTE 70

8 >> 50 LOAD_CONST 5 (8)

52 STORE_NAME 0 (a) 54 POP_BLOCK

10 56 LOAD_CONST 4 (10)

58 STORE_NAME 1 (c) 60 JUMP_ABSOLUTE 22

>> 62 LOAD_CONST 4 (10)

64 STORE_NAME 1 (c) 66 RERAISE 68 JUMP_ABSOLUTE 22

>> 70 POP_BLOCK

72 JUMP_FORWARD 16 (to 90)

11 >> 74 POP_TOP

76 POP_TOP 78 POP_TOP

12 80 LOAD_CONST 6 (12)

82 STORE_NAME 2 (d) 84 POP_EXCEPT 86 JUMP_FORWARD 2 (to 90) 88 RERAISE

13 >> 90 LOAD_NAME 0 (a)

92 LOAD_CONST 2 (5) 94 COMPARE_OP 2 (==) 96 POP_JUMP_IF_FALSE 114 98 LOAD_NAME 1 (c)

100 LOAD_CONST 4 (10)

102 COMPARE_OP 2 (==) 104 POP_JUMP_IF_FALSE 114 106 LOAD_NAME 2 (d) 108 LOAD_CONST 7 (1) 110 COMPARE_OP 2 (==) 112 POP_JUMP_IF_TRUE 118

>> 114 LOAD_ASSERTION_ERROR

116 RAISE_VARARGS 1

>> 118 LOAD_CONST 8 (None)

120 RETURN_VALUE

1           0 LOAD_CONST               0 ((1, 1, 1, 99))
            2 UNPACK_SEQUENCE          4
            4 STORE_NAME               0 (a)
            6 STORE_NAME               1 (c)
            8 STORE_NAME               2 (d)
           10 STORE_NAME               3 (i)

2          12 SETUP_FINALLY           58 (to 72)

3          14 LOAD_NAME                4 (range)
           16 LOAD_CONST               1 (3)
           18 CALL_FUNCTION            1
           20 GET_ITER
      >>   22 FOR_ITER                44 (to 68)
           24 STORE_NAME               3 (i)

4          26 SETUP_FINALLY           34 (to 62)

5          28 LOAD_CONST               2 (5)
           30 STORE_NAME               0 (a)

6          32 LOAD_NAME                3 (i)
           34 LOAD_CONST               3 (0)
           36 COMPARE_OP               4 (>)
           38 POP_JUMP_IF_FALSE       50

7          40 POP_BLOCK
10 42 LOAD_CONST 4 (10)

44 STORE_NAME 1 (c)

7 46 POP_TOP

48 JUMP_ABSOLUTE 68

8 >> 50 LOAD_CONST 5 (8)

52 STORE_NAME 0 (a) 54 POP_BLOCK

10 56 LOAD_CONST 4 (10)

58 STORE_NAME 1 (c) 60 JUMP_ABSOLUTE 22

>> 62 LOAD_CONST 4 (10)

64 STORE_NAME 1 (c) 66 RERAISE

>> 68 POP_BLOCK

70 JUMP_FORWARD 14 (to 86)

11 >> 72 POP_TOP

74 POP_TOP 76 POP_TOP

12 78 LOAD_CONST 6 (12)

80 STORE_NAME 2 (d) 82 POP_EXCEPT 84 JUMP_FORWARD 0 (to 86)

13 >> 86 LOAD_NAME 0 (a)

88 LOAD_CONST 2 (5) 90 COMPARE_OP 2 (==) 92 POP_JUMP_IF_FALSE 110 94 LOAD_NAME 1 (c) 96 LOAD_CONST 4 (10) 98 COMPARE_OP 2 (==)

100 POP_JUMP_IF_FALSE 110

102 LOAD_NAME 2 (d) 104 LOAD_CONST 7 (1) 106 COMPARE_OP 2 (==) 108 POP_JUMP_IF_TRUE 114

>> 110 LOAD_ASSERTION_ERROR

112 RAISE_VARARGS 1

>> 114 LOAD_CONST 8 (None)

116 RETURN_VALUE

Another example is harder to understand. Here we used to have a jump from 3 to 13, which no longer happens, but we get a new jump from 3 to 10, which I don't understand:

a, b, c, d, i = 1, 1, 1, 1, 99
try:
    for i in range(5):
        try:
            a = 5
            if i > 0:
                continue                # line 7
            b = 8
        finally:
            c = 10
except:
    d = 12                              # line 12
assert (a, b, c, d) == (5, 8, 10, 1)    # line 13
Python 3.9 dis Python 3.10 dis
1           0 LOAD_CONST               0 ((1, 1, 1, 1, 99))
            2 UNPACK_SEQUENCE          5
            4 STORE_NAME               0 (a)
            6 STORE_NAME               1 (b)
            8 STORE_NAME               2 (c)
           10 STORE_NAME               3 (d)
           12 STORE_NAME               4 (i)

2          14 SETUP_FINALLY           58 (to 74)

3          16 LOAD_NAME                5 (range)
           18 LOAD_CONST               1 (5)
           20 CALL_FUNCTION            1
           22 GET_ITER
      >>   24 FOR_ITER                44 (to 70)
           26 STORE_NAME               4 (i)

4          28 SETUP_FINALLY           32 (to 62)

5          30 LOAD_CONST               1 (5)
           32 STORE_NAME               0 (a)

6          34 LOAD_NAME                4 (i)
           36 LOAD_CONST               2 (0)
           38 COMPARE_OP               4 (>)
           40 POP_JUMP_IF_FALSE       50

7          42 POP_BLOCK
10 44 LOAD_CONST 3 (10)

46 STORE_NAME 2 (c)

7 48 JUMP_ABSOLUTE 24

8 >> 50 LOAD_CONST 4 (8)

52 STORE_NAME 1 (b) 54 POP_BLOCK

10 56 LOAD_CONST 3 (10)

58 STORE_NAME 2 (c) 60 JUMP_ABSOLUTE 24

>> 62 LOAD_CONST 3 (10)

64 STORE_NAME 2 (c) 66 RERAISE 68 JUMP_ABSOLUTE 24

>> 70 POP_BLOCK

72 JUMP_FORWARD 16 (to 90)

11 >> 74 POP_TOP

76 POP_TOP 78 POP_TOP

12 80 LOAD_CONST 5 (12)

82 STORE_NAME 3 (d) 84 POP_EXCEPT 86 JUMP_FORWARD 2 (to 90) 88 RERAISE

13 >> 90 LOAD_NAME 0 (a)

92 LOAD_NAME 1 (b) 94 LOAD_NAME 2 (c) 96 LOAD_NAME 3 (d) 98 BUILD_TUPLE 4

100 LOAD_CONST 6 ((5, 8, 10, 1))

102 COMPARE_OP 2 (==) 104 POP_JUMP_IF_TRUE 110 106 LOAD_ASSERTION_ERROR 108 RAISE_VARARGS 1

>> 110 LOAD_CONST 7 (None)

112 RETURN_VALUE

1           0 LOAD_CONST               0 ((1, 1, 1, 1, 99))
            2 UNPACK_SEQUENCE          5
            4 STORE_NAME               0 (a)
            6 STORE_NAME               1 (b)
            8 STORE_NAME               2 (c)
           10 STORE_NAME               3 (d)
           12 STORE_NAME               4 (i)

2          14 SETUP_FINALLY           56 (to 72)

3          16 LOAD_NAME                5 (range)
           18 LOAD_CONST               1 (5)
           20 CALL_FUNCTION            1
           22 GET_ITER
      >>   24 FOR_ITER                42 (to 68)
           26 STORE_NAME               4 (i)

4          28 SETUP_FINALLY           32 (to 62)

5          30 LOAD_CONST               1 (5)
           32 STORE_NAME               0 (a)

6          34 LOAD_NAME                4 (i)
           36 LOAD_CONST               2 (0)
           38 COMPARE_OP               4 (>)
           40 POP_JUMP_IF_FALSE       50

7          42 POP_BLOCK
10 44 LOAD_CONST 3 (10)

46 STORE_NAME 2 (c)

7 48 JUMP_ABSOLUTE 24

8 >> 50 LOAD_CONST 4 (8)

52 STORE_NAME 1 (b) 54 POP_BLOCK

10 56 LOAD_CONST 3 (10)

58 STORE_NAME 2 (c) 60 JUMP_ABSOLUTE 24

>> 62 LOAD_CONST 3 (10)

64 STORE_NAME 2 (c) 66 RERAISE

>> 68 POP_BLOCK

70 JUMP_FORWARD 14 (to 86)

11 >> 72 POP_TOP

74 POP_TOP 76 POP_TOP

12 78 LOAD_CONST 5 (12)

80 STORE_NAME 3 (d) 82 POP_EXCEPT 84 JUMP_FORWARD 0 (to 86)

13 >> 86 LOAD_NAME 0 (a)

88 LOAD_NAME 1 (b) 90 LOAD_NAME 2 (c) 92 LOAD_NAME 3 (d) 94 BUILD_TUPLE 4 96 LOAD_CONST 6 ((5, 8, 10, 1)) 98 COMPARE_OP 2 (==)

100 POP_JUMP_IF_TRUE 106

102 LOAD_ASSERTION_ERROR 104 RAISE_VARARGS 1

>> 106 LOAD_CONST 7 (None)

108 RETURN_VALUE

Affected tests:

  • ExceptionArcTest.test_break_through_finally
  • ExceptionArcTest.test_continue_through_finally
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment