126 lines
4.4 KiB
Plaintext
Raw Permalink Normal View History

2023-07-04 10:50:29 +02:00
This rule raises an issue when an `except` statement has had all its exceptions caught by a previous `except` clause.
2023-07-04 10:50:29 +02:00
== Why is this an issue?
2020-06-30 12:47:33 +02:00
2023-07-04 10:50:29 +02:00
Exceptions handlers (`except`) are evaluated in the order they are written. Once a match is found, the evaluation stops.
2021-02-02 15:02:10 +01:00
2023-07-04 10:50:29 +02:00
In some contexts, an except block is dead code as it will never catch any exception:
2020-06-30 12:47:33 +02:00
* If there is a handler for a base class followed by a handler for class derived from that base class, the second handler will never trigger: The handler for the base class will match the derived class, and will be the only executed handler.
2023-07-04 10:50:29 +02:00
* When multiple `except` statements try to catch the same exception class, only the first one will be executed.
* In Python 3, `BaseException` is the parent of every exception class. When a `BaseException` is caught by an `except` clause, none of the subsequent `except` statement will catch anything. This is true as well for the bare except statement (`except:`).
== How to fix it
When using multiple `except` statements, make sure to:
* Order the `except` blocks from the most specialzed exception to the most generic, i.e when wanting to catch a `FloatingPointError` and an `ArithemticError`, as `FloatingPointError` is a subclass of `ArithmeticError`, the first `except` statement should be `FloatingPointError`.
2020-06-30 12:47:33 +02:00
2023-07-04 10:50:29 +02:00
* Catch the same exception only once.
2020-06-30 12:47:33 +02:00
2023-07-04 10:50:29 +02:00
* Catch a `BaseException` only once with either an `except BaseException:` statement or a bare `except:` statement, as the two statements are equivalent.
2020-06-30 12:47:33 +02:00
2023-07-04 10:50:29 +02:00
=== Code examples
==== Noncompliant code example
[source,python,diff-id=1,diff-type=noncompliant]
2020-06-30 12:47:33 +02:00
----
def foo():
try:
raise FloatingPointError()
except (ArithmeticError, RuntimeError) as e:
print(e)
2023-07-04 10:50:29 +02:00
except FloatingPointError as e: # Noncompliant: FloatingPointError is a subclass of ArithmeticError.
2020-06-30 12:47:33 +02:00
print("Never executed")
2023-07-04 10:50:29 +02:00
except OverflowError as e: # Noncompliant: OverflowError is a subclass of ArithmeticError.
2020-06-30 12:47:33 +02:00
print("Never executed")
try:
raise TypeError()
except TypeError as e:
print(e)
2023-07-04 10:50:29 +02:00
except TypeError as e: # Noncompliant: duplicate except.
2020-06-30 12:47:33 +02:00
print("Never executed")
try:
raise ValueError()
except BaseException as e:
print(e)
2023-07-04 10:50:29 +02:00
except: # Noncompliant: this is equivalent to "except BaseException" block.
2020-06-30 12:47:33 +02:00
print("Never executed")
----
2023-07-04 10:50:29 +02:00
==== Compliant solution
2020-06-30 12:47:33 +02:00
2023-07-04 10:50:29 +02:00
[source,python,diff-id=1,diff-type=compliant]
2020-06-30 12:47:33 +02:00
----
def foo():
try:
raise FloatingPointError()
except FloatingPointError as e:
print("Executed")
except OverflowError as e:
print("Executed")
except (ArithmeticError, RuntimeError) as e:
print(e)
try:
raise TypeError()
except TypeError as e:
print(e)
try:
raise ValueError()
except BaseException as e:
print(e)
----
2023-07-04 10:50:29 +02:00
*Note*: __It is generally not recommended to try catching `BaseException`, as it is the base class for all built-in exceptions in Python, including system-exiting exceptions like ``++SystemExit++`` or ``++KeyboardInterrupt++``, which are typically not meant to be caught. See https://www.python.org/dev/peps/pep-0352/#exception-hierarchy-changes[PEP 352] for more information.__
== Resources
2020-06-30 12:47:33 +02:00
2023-07-04 10:50:29 +02:00
=== Documentation
* https://docs.python.org/3/reference/compound_stmts.html#the-try-statement[The `try` statement]
* https://docs.python.org/3/library/exceptions.html#exception-hierarchy[Exception hierarchy]
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
=== Highlighting
Primary:
* All exceptions which will never match because they are subclasses or duplicate of the first exception caught.
* The "except:" statement if "BaseException" is caught before.
Secondary:
* The exception class which shadows other except blocks.
message should be: 'Exceptions will be caught here.'
=== Message
* When a parent except block is caught before derived classes
* Catch this exception only once; it is already handled by a previous except clause.
* When the same exception class is caught multiple times:
* Catch this exception only once; it is already handled by a previous except clause.
* When an "BaseException" is caught in the same try-except as a bare "except:"
* Merge this bare "except:" with the "BaseException" catch
'''
== Comments And Links
(visible only on this page)
include::../comments-and-links.adoc[]
endif::env-github,rspecator-view[]