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")
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.
EDIT 2: 3.12.5 released in 2024-08-06: the bug didn't fixed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With