125 lines
2.9 KiB
Plaintext
125 lines
2.9 KiB
Plaintext
This rule raise an issue if something other than `None` or a valid exception is provided as the cause of an exception chain.
|
|
|
|
== Why is this an issue?
|
|
|
|
Exception chaining enables users to see if an exception is the direct consequence of another exception (see https://peps.python.org/pep-3134/[PEP-3134]). This is useful to propagate the original context of the error.
|
|
|
|
Exceptions are chained using either of the following syntax:
|
|
|
|
* With the `from` keyword
|
|
|
|
[source,python]
|
|
----
|
|
try:
|
|
...
|
|
except OSError as e:
|
|
raise RuntimeError("Something went wrong") from e
|
|
----
|
|
|
|
* With the ``++__cause__++`` property
|
|
|
|
[source,python]
|
|
----
|
|
try:
|
|
...
|
|
except OSError as e:
|
|
new_exception = RuntimeError("Something went wrong")
|
|
new_exception.__cause__ = e
|
|
raise new_exception
|
|
----
|
|
|
|
It is also possible to erase a chaining by setting ``++new_exception.__cause__ = None++`` or using `raise new_exception from None` (see https://peps.python.org/pep-0409/[PEP-409]).
|
|
|
|
|
|
Chaining will fail and raise a `TypeError` if something other than `None` or a valid exception, i.e. an instance or a subclass of `BaseException`, is provided.
|
|
|
|
== How to fix it
|
|
|
|
Make sure the cause of a chain of exceptions is either `None` or a valid exception.
|
|
|
|
=== Code examples
|
|
|
|
==== Noncompliant code example
|
|
|
|
[source,python]
|
|
----
|
|
class A:
|
|
pass
|
|
|
|
try:
|
|
raise ValueError("orig")
|
|
except ValueError as e:
|
|
new_exc = TypeError("new")
|
|
new_exc.__cause__ = A() # Noncompliant: A is not a subclass of BaseException.
|
|
raise new_exc
|
|
|
|
try:
|
|
raise ValueError("orig")
|
|
except ValueError as e:
|
|
raise TypeError("new") from "test" # Noncompliant: "test" is not a valid exception.
|
|
----
|
|
|
|
|
|
==== Compliant solution
|
|
|
|
[source,python]
|
|
----
|
|
try:
|
|
raise ValueError("orig")
|
|
except ValueError as e:
|
|
new_exc = TypeError("new")
|
|
new_exc.__cause__ = None # Ok
|
|
raise new_exc
|
|
|
|
try:
|
|
raise ValueError("orig")
|
|
except ValueError as e:
|
|
new_exc = TypeError("new")
|
|
new_exc.__cause__ = e # Ok
|
|
raise new_exc
|
|
|
|
try:
|
|
raise ValueError("orig")
|
|
except ValueError as e:
|
|
raise TypeError("new") from None # Ok
|
|
|
|
try:
|
|
raise ValueError("orig")
|
|
except ValueError as e:
|
|
raise TypeError("new") from e # Ok
|
|
----
|
|
|
|
|
|
== Resources
|
|
|
|
=== Documentation
|
|
|
|
* https://docs.python.org/3/library/exceptions.html[Built-in Exceptions]
|
|
|
|
=== Standards
|
|
|
|
* https://peps.python.org/pep-3134/[Exception Chaining and Embedded Tracebacks] - PEP 3134
|
|
* https://peps.python.org/pep-0409/[Suppressing exception context] - PEP 409
|
|
* https://peps.python.org/pep-0352/#exception-hierarchy-changes[Required Superclass for Exceptions] - PEP 352
|
|
|
|
ifdef::env-github,rspecator-view[]
|
|
|
|
'''
|
|
== Implementation Specification
|
|
(visible only on this page)
|
|
|
|
=== Message
|
|
|
|
Replace this expression of type X with an exception or None
|
|
|
|
|
|
=== Highlighting
|
|
|
|
* In a "raise X from Y" statement:
|
|
** highlight Y
|
|
* In an "myexception.__cause__ = Y" statement:
|
|
** highlight Y
|
|
|
|
|
|
endif::env-github,rspecator-view[]
|