Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide a traceback (and proxy) for propagated exceptions. #17

Closed
ericsnowcurrently opened this issue Aug 24, 2018 · 2 comments
Closed
Assignees
Labels
complexity: medium moderately hard to figure out P1 medium priority size: medium requires a moderate amount of changes type: enhancement X [cross-interpreter] e.g. channels

Comments

@ericsnowcurrently
Copy link
Owner

ericsnowcurrently commented Aug 24, 2018

from PEP 554:

Regarding uncaught exceptions in Interpreter.run(), we noted that they are "effectively" propagated into the code where run() was called. To prevent leaking exceptions (and tracebacks) between interpreters, we create a surrogate of the exception and its traceback (see traceback.TracebackException), set it to cause on a new RunFailedError, and raise that.

Raising (a proxy of) the exception directly is problematic since it's harder to distinguish between an error in the run() call and an uncaught exception from the subinterpreter.


Currently uncaught exceptions from Interpreter.run() are stringified and then wrapped in a RunFailedError before being raised in the calling interpreter. This was fine in theory. However, as I've been using subinterpreters in tests I've found that this approach is extremely cumbersome for two reasons:

  1. boilerplate (much more often than expected) to deal with the original exception (see Raise the "original" exception from Interpreter.run()? #43)
  2. no tracebacks

The solution: "propagate" the original exception and traceback through to the calling interpreter. This is tricky because the exception and traceback must go through the cross-interpreter data machinery. The result would be an exception that matches the original (i.e. a proxy), including a traceback that works as an effective substitute (and any __cause__, etc. set).

Once that is sorted out, we'll set the __cause__ of the raised RunFailedError to the proxy of the original exception. We would end up with tracebacks that look like this:

>>> interp.run(script)
Traceback (most recent call last):
  File "<stdin>", line 3
    raise SpamError('eggs')
SpamError: eggs

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

Traceback (most recent call last):
  File "<stdin>", line 7
    interp.run(script)
RunFailedError: SpamError: eggs
>>> try:
...     interp.run(script)
... except RunFailedError as exc:
...     raise exc.__cause__
... 
Traceback (most recent call last):
  File "<stdin>", line 3
    raise SpamError('eggs')
SpamError: eggs

Also, how do we get the traceback (e.g. lineno) right?

@ericsnowcurrently ericsnowcurrently self-assigned this Aug 24, 2018
@ericsnowcurrently ericsnowcurrently added size: medium requires a moderate amount of changes X [cross-interpreter] e.g. channels P0 high priority type: enhancement complexity: medium moderately hard to figure out P1 medium priority and removed P0 high priority labels Aug 24, 2018
@ericsnowcurrently ericsnowcurrently changed the title propagate exceptions Provide a traceback (and proxy) for propagated exceptions. Sep 10, 2018
@ericsnowcurrently
Copy link
Owner Author

PR: python/cpython#19768

@ericsnowcurrently
Copy link
Owner Author

python/cpython@a1d9e0a

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
complexity: medium moderately hard to figure out P1 medium priority size: medium requires a moderate amount of changes type: enhancement X [cross-interpreter] e.g. channels
Projects
None yet
Development

No branches or pull requests

1 participant