tornado.stack_context
— Exception handling across asynchronous callbacks¶
StackContext
allows applications to maintain threadlocal-like state
that follows execution as it moves to other execution contexts.
The motivating examples are to eliminate the need for explicit
async_callback
wrappers (as in tornado.web.RequestHandler
), and to
allow some additional context to be kept for logging.
This is slightly magic, but it’s an extension of the idea that an
exception handler is a kind of stack-local state and when that stack
is suspended and resumed in a new context that state needs to be
preserved. StackContext
shifts the burden of restoring that state
from each call site (e.g. wrapping each AsyncHTTPClient
callback
in async_callback
) to the mechanisms that transfer control from
one context to another (e.g. AsyncHTTPClient
itself, IOLoop
,
thread pools, etc).
Example usage:
@contextlib.contextmanager
def die_on_error():
try:
yield
except Exception:
logging.error("exception in asynchronous operation",exc_info=True)
sys.exit(1)
with StackContext(die_on_error):
# Any exception thrown here *or in callback and its descendants*
# will cause the process to exit instead of spinning endlessly
# in the ioloop.
http_client.fetch(url, callback)
ioloop.start()
Most applications shouldn’t have to work with StackContext
directly.
Here are a few rules of thumb for when it’s necessary:
- If you’re writing an asynchronous library that doesn’t rely on a
stack_context-aware library like
tornado.ioloop
ortornado.iostream
(for example, if you’re writing a thread pool), usestack_context.wrap()
before any asynchronous operations to capture the stack context from where the operation was started. - If you’re writing an asynchronous library that has some shared
resources (such as a connection pool), create those shared resources
within a
with stack_context.NullContext():
block. This will preventStackContexts
from leaking from one request to another. - If you want to write something like an exception handler that will
persist across asynchronous calls, create a new
StackContext
(orExceptionStackContext
), and make your asynchronous calls in awith
block that references yourStackContext
.
-
class
tornado.stack_context.
StackContext
(context_factory)[source]¶ Establishes the given context as a StackContext that will be transferred.
Note that the parameter is a callable that returns a context manager, not the context itself. That is, where for a non-transferable context manager you would say:
with my_context():
StackContext takes the function itself rather than its result:
with StackContext(my_context):
The result of
with StackContext() as cb:
is a deactivation callback. Run this callback when the StackContext is no longer needed to ensure that it is not propagated any further (note that deactivating a context does not affect any instances of that context that are currently pending). This is an advanced feature and not necessary in most applications.
-
class
tornado.stack_context.
ExceptionStackContext
(exception_handler)[source]¶ Specialization of StackContext for exception handling.
The supplied
exception_handler
function will be called in the event of an uncaught exception in this context. The semantics are similar to a try/finally clause, and intended use cases are to log an error, close a socket, or similar cleanup actions. Theexc_info
triple(type, value, traceback)
will be passed to the exception_handler function.If the exception handler returns true, the exception will be consumed and will not be propagated to other exception handlers.
-
class
tornado.stack_context.
NullContext
[source]¶ Resets the
StackContext
.Useful when creating a shared resource on demand (e.g. an
AsyncHTTPClient
) where the stack that caused the creating is not relevant to future operations.
-
tornado.stack_context.
wrap
(fn)[source]¶ Returns a callable object that will restore the current
StackContext
when executed.Use this whenever saving a callback to be executed later in a different execution context (either in a different thread or asynchronously in the same thread).
-
tornado.stack_context.
run_with_stack_context
(context, func)[source]¶ Run a coroutine
func
in the givenStackContext
.It is not safe to have a
yield
statement within awith StackContext
block, so it is difficult to use stack context withgen.coroutine
. This helper function runs the function in the correct context while keeping theyield
andwith
statements syntactically separate.Example:
@gen.coroutine def incorrect(): with StackContext(ctx): # ERROR: this will raise StackContextInconsistentError yield other_coroutine() @gen.coroutine def correct(): yield run_with_stack_context(StackContext(ctx), other_coroutine)
New in version 3.1.