At work, I stumbled upon an except
clause with an or
operator:
try:
# Do something.
except IndexError or KeyError:
# ErrorHandling
I know the exception classes should be passed as a tuple, but it bugged me that it wouldn't even cause a SyntaxError
.
So first I wanted to investigate whether it actually works. And it doesn't.
>>> def with_or_raise(exc):
... try:
... raise exc()
... except IndexError or KeyError:
... print('Got ya!')
...
>>> with_or_raise(IndexError)
Got ya!
>>> with_or_raise(KeyError)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in with_or_raise
KeyError
So it did not catch the second exception, and looking at the bytecode, it becomes clearer why:
>>> import dis
>>> dis.dis(with_or_raise)
2 0 SETUP_EXCEPT 10 (to 12)
3 2 LOAD_FAST 0 (exc)
4 CALL_FUNCTION 0
6 RAISE_VARARGS 1
8 POP_BLOCK
10 JUMP_FORWARD 32 (to 44)
4 >> 12 DUP_TOP
14 LOAD_GLOBAL 0 (IndexError)
16 JUMP_IF_TRUE_OR_POP 20
18 LOAD_GLOBAL 1 (KeyError)
>> 20 COMPARE_OP 10 (exception match)
22 POP_JUMP_IF_FALSE 42
24 POP_TOP
26 POP_TOP
28 POP_TOP
5 30 LOAD_GLOBAL 2 (print)
32 LOAD_CONST 1 ('Got ya!')
34 CALL_FUNCTION 1
36 POP_TOP
38 POP_EXCEPT
40 JUMP_FORWARD 2 (to 44)
>> 42 END_FINALLY
>> 44 LOAD_CONST 0 (None)
46 RETURN_VALUE
So we can see, instruction 14 first loads the IndexError
class onto the stack. Then it checks whether that value is True
, which it is because of Python truthiness and finally jumps directly to instruction 20 where the exception match
is done. Since instruction 18 was skipped, KeyError
was never loaded onto the stack and therefore doesn't match.
I tried with Python 2.7 and 3.6, same result.
But then, why is it valid syntax? I imagine it being one of the following:
or
within an except
clause.except
keyword.My vote is on 3 (given I saw some discussion about a new parser for Python) but I'm hoping someone can confirm that hypothesis. Because if it was 2 for example, I want to know that use case!
Also, I'm a bit clueless on how I'd continue that exploration. I imagine I would have to dig into CPython parser's source code but idk where to find it and maybe there's an easier way?
The syntax error exception occurs when the code does not conform to Python keywords, naming style, or programming structure. The interpreter sees the invalid syntax during its parsing phase and raises a SyntaxError exception.
You can catch that exception using the try-except block. The try block lets you test a block of code for errors, the except block handles the errors and the finally block lets you execute code regardless of errors.
If the statements in this block are executed without an exception, the subsequent except: block is skipped. If the exception does occur, the program flow is transferred to the except: block. The statements in the except: block are meant to handle the cause of the exception appropriately.
When an exception occurs, the Python interpreter stops the current process. It is handled by passing through the calling process. If not, the program will crash. For instance, a Python program has a function X that calls function Y, which in turn calls function Z.
In except e
, e
can be any valid Python expression:
try1_stmt ::= "try" ":" suite ("except" [expression ["as" identifier]] ":" suite)+ ...
[..] For an
except
clause with an expression, that expression is evaluated, and the clause matches the exception if the resulting object is “compatible” with the exception. An object is compatible with an exception if it is the class or a base class of the exception object or a tuple containing an item compatible with the exception.https://docs.python.org/3/reference/compound_stmts.html#the-try-statement
The expression IndexError or KeyError
yields the value IndexError
. So this is equivalent to:
except IndexError:
...
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