rspec/rules/S5707/python/rule.adoc
2023-06-30 09:15:12 +02:00

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[]