Why aren't exceptions caught with one-liner while statement?
Asked Answered
L

1

14

I have a code with a one-liner while and a try-except statement which behaves weirdly.

This prints 'a' on Ctrl+C:

try:
    while True:
        pass
except KeyboardInterrupt:
    print("a")

and this too:

try:
    i = 0
    while True: pass
except KeyboardInterrupt:
    print("a")

but this doesn't, and it throws a traceback:

try:
    while True: pass
except KeyboardInterrupt:
    print("a")

and neither does this code:

try:
    while True: pass
    i = 0
except KeyboardInterrupt:
    print("a")

Addition some additional details.

In 3.11, the instruction JUMP_BACKWARD was added and seems invloved with this issue see: Disassembler for Python bytecode

In 3.12 when the code in the first and the 3rd blocks are disassembled the results are:

Cannot be caught:

  0           0 RESUME                   0

  2           2 NOP

  3     >>    4 JUMP_BACKWARD            1 (to 4)
        >>    6 PUSH_EXC_INFO

  4           8 LOAD_NAME                0 (KeyboardInterrupt)
             10 CHECK_EXC_MATCH
             12 POP_JUMP_IF_FALSE       11 (to 36)
             14 POP_TOP

  5          16 PUSH_NULL
             18 LOAD_NAME                1 (print)
             20 LOAD_CONST               1 ('a')
             22 CALL                     1
             30 POP_TOP
             32 POP_EXCEPT
             34 RETURN_CONST             2 (None)

  4     >>   36 RERAISE                  0
        >>   38 COPY                     3
             40 POP_EXCEPT
             42 RERAISE                  1
ExceptionTable:
  4 to 4 -> 6 [0]
  6 to 30 -> 38 [1] lasti
  36 to 36 -> 38 [1] lasti
None

Can be caught:

  0           0 RESUME                   0

  2           2 NOP

  3           4 NOP

  4     >>    6 NOP

  3           8 JUMP_BACKWARD            2 (to 6)
        >>   10 PUSH_EXC_INFO

  5          12 LOAD_NAME                0 (KeyboardInterrupt)
             14 CHECK_EXC_MATCH
             16 POP_JUMP_IF_FALSE       11 (to 40)
             18 POP_TOP

  6          20 PUSH_NULL
             22 LOAD_NAME                1 (print)
             24 LOAD_CONST               1 ('a')
             26 CALL                     1
             34 POP_TOP
             36 POP_EXCEPT
             38 RETURN_CONST             2 (None)

  5     >>   40 RERAISE                  0
        >>   42 COPY                     3
             44 POP_EXCEPT
             46 RERAISE                  1
ExceptionTable:
  4 to 8 -> 10 [0]
  10 to 34 -> 42 [1] lasti
  40 to 40 -> 42 [1] lasti
None

The main differences that jump out are the two additional NOP and the different targets for JUMP_BACKWARD.

Note: the exception really cannot be caught as this will also throw the exception in 3.12

try:
    try:
        while True: pass
    except KeyboardInterrupt:
        print("a")
except Exception:
    print("b")
Laidlaw answered 10/5, 2024 at 17:8 Comment(8)
What does the traceback say?Colure
@LordElrond It says that the KeyboardException exception occurred during the while True: pass statement. Just as if there were no try/exceptJanessa
This is bizarre. It looks like an interpreter bug to me.Colure
This is very strange. I disassembled the two versions, they're identical except for the code that performs the assignment. The exception-related code is the same, except for something labeled "ExceptionTable" at the end of the disassembly.Janessa
Not reproducible for me using Python 3.10.9Osmund
In 3.12 the last two throw uncaught KeyboardInterruptRinarinaldi
Its a known CPython bug introduced in 3.11 and exists in 3.12. Who knows? Maybe you reported it 😀. Possible fixLipocaic
@GhorbanM.Tavakoly Would you kindly put this comment as answer?Faucher
L
7

Its a known CPython bug introduced in 3.11 and exists in 3.12.

One of comments of the bug, mentioned that partial backport of this pull request looks to be the right direction to fix the bug.

I built and tested following CPython versions from source using pyenv on Arch Linux with GCC 14.1.1 compiler:

  • 3.11-dev: Python 3.11.9+ (heads/3.11:ba43157, May 20 2024, 04:40:02)
  • 3.12-dev: Python 3.12.3+ (heads/3.12:30c687c, May 20 2024, 04:38:13)
  • 3.13.0b1: Python 3.13.0b1 (main, May 20 2024, 04:14:35)
  • 3.13-dev: Python 3.13.0b1+ (heads/3.13:27b61c1, May 20 2024, 04:24:49)
  • 3.14-dev: Python 3.14.0a0 (heads/main:0abf997, May 20 2024, 08:25:05)

In 3.13.0b1, 3.13-dev and 3.14-dev the bug is fixed 😀👍 and exception handling works as expected.

But 3.11-dev and 3.12-dev still have the bug.

I hope it will be backport to existing stable 3.11 and 3.12 versions (in-time for inclusion in the next 3.11.10 and 3.12.4 bug-fix releases respectively).

EDIT 1: 3.12.4 released in 2024-06-06: the bug didn't fixed.

Lipocaic answered 19/5, 2024 at 23:54 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.