From c69db0983dab7bad9f5e233fd6286a50d419bb47 Mon Sep 17 00:00:00 2001 From: Tom Prince Date: Wed, 14 Jul 2021 17:01:52 -0600 Subject: [PATCH] Capture correct tracebacks when using `inline_callbacks`. When an inlineCallback completes synchronously, such as in tests, twisted will string-ify the traceback, which means that when the exception is then thrown into another inlineCallback function, the stack trace is lost[1]. Twisted works around this by inspect the stack and finding the Failure instance from the inner call, to get traceback there. However, this only works if `Failure.throwExceptionIntoGenerator` is used. This adjusts eliot_friendly_generator_function to use `Failure`, instead of an exc_info tuple, when used via `inline_callabcks`. [1] The string-ified traceback can't be passed to `.send`. --- eliot/_generators.py | 13 ++++++++++++- eliot/twisted.py | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/eliot/_generators.py b/eliot/_generators.py index 1a7b0a1..9eb596e 100644 --- a/eliot/_generators.py +++ b/eliot/_generators.py @@ -10,6 +10,11 @@ from contextvars import copy_context from weakref import WeakKeyDictionary +try: + from twisted.python.failure import Failure +except ImportError: + pass + from . import log_message @@ -95,6 +100,8 @@ def wrapper(*a, **kw): def go(): if ok: value_out = gen.send(value_in) + elif wrapper._use_failure: + value_out = value_in.throwExceptionIntoGenerator(gen) else: value_out = gen.throw(*value_in) # We have obtained a value from the generator. In @@ -130,9 +137,13 @@ def go(): # contextmanager. But @contextmanager extremely # conveniently eats it for us! Thanks, @contextmanager! ok = False - value_in = exc_info() + if wrapper._use_failure: + value_in = Failure() + else: + value_in = exc_info() else: ok = True wrapper.debug = False + wrapper._use_failure = False return wrapper diff --git a/eliot/twisted.py b/eliot/twisted.py index 5f83094..37f3396 100644 --- a/eliot/twisted.py +++ b/eliot/twisted.py @@ -262,6 +262,7 @@ def inline_callbacks(original, debug=False): function. """ f = eliot_friendly_generator_function(original) + f._use_failure = True if debug: f.debug = True return inlineCallbacks(f)