2

Recently I noticed pylint complaining with error message "raise-missing-from (W0707)" when using a raise statement inside an except block, but I don't see any difference when adding this to a piece of code. Here's an example without raise ... from ...:

class MyExc(Exception):
    pass


def calc(n):
    try:
        return n / 2
    except TypeError:
        raise MyExc()


def main():
    calc("foo")


if __name__ == "__main__":
    main()

Which gives the following output:

Traceback (most recent call last):
  File "foo.py", line 7, in calc
    return n / 2
TypeError: unsupported operand type(s) for /: 'str' and 'int'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "foo.py", line 17, in <module>
    main()
  File "foo.py", line 13, in main
    calc("foo")
  File "foo.py", line 9, in calc
    raise MyExc()
__main__.MyExc

With raise ... from ...

Now, adding the ... from ... clause:

class MyExc(Exception):
    pass


def calc(n):
    try:
        return n / 2
    except TypeError as exc:
        raise MyExc() from exc


def main():
    calc("foo")


if __name__ == "__main__":
    main()

Which give the following output:

Traceback (most recent call last):
  File "foo.py", line 7, in calc
    return n / 2
TypeError: unsupported operand type(s) for /: 'str' and 'int'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "foo.py", line 17, in <module>
    main()
  File "foo.py", line 13, in main
    calc("foo")
  File "foo.py", line 9, in calc
    raise MyExc() from exc
__main__.MyExc

Diff

A diff of the two tracebacks shows only a difference in the message between the two tracebacks. This seems minuscule. I still see the full chain in both cases. So I wonder: what are the useful/practical benefits of this?

--- a   2020-08-28 13:30:00.762576720 +0200
+++ b   2020-08-28 13:30:04.738544485 +0200
@@ -3,7 +3,7 @@
     return n / 2
 TypeError: unsupported operand type(s) for /: 'str' and 'int'
 
-During handling of the above exception, another exception occurred:
+The above exception was the direct cause of the following exception:
 
 Traceback (most recent call last):
   File "foo.py", line 17, in <module>
@@ -11,6 +11,6 @@
   File "foo.py", line 13, in main
     calc("foo")
   File "foo.py", line 9, in calc
-    raise MyExc()
+    raise MyExc() from exc
 __main__.MyExc
exhuma
  • 20,071
  • 12
  • 90
  • 123
  • The messages are similar because you reraised in the `try-except` block, if you had left the block and then wanted to get the complete traceback, then `raise ... from ...` would be the way to achieve that. – Olivier Melançon Aug 28 '20 at 11:37
  • 1
    The page you linked to has an explanation: *Not using raise from makes the traceback inaccurate, because the message implies there is a bug in the exception-handling code itself, which is a separate situation than wrapping an exception*. If you don't think that's important, then you can configure Pylint to ignore the message. Here's [a thread](https://groups.google.com/g/django-developers/c/ibEOt3A9c2M/m/7qcS82hWDwAJ) on the django-developers mailing list, where they decided not to switch to `raise from`, because the code churn was not worth the change. – Alasdair Aug 28 '20 at 11:42

0 Answers0