Skip to content

Instantly share code, notes, and snippets.

@nedbat
Last active December 14, 2020 00:23
Show Gist options
  • 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