Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
An example of using a StackContext to store request data globally in Tornado. See https://groups.google.com/d/msg/python-tornado/8izNLhYjyHw/TNKGa9fgvpUJ for motivation and further discussion.
import tornado
class RequestContextHandler(tornado.web.RequestHandler):
def _execute(self, transforms, *args, **kwargs):
# following the example of:
# https://github.com/bdarnell/tornado_tracing/blob/master/tornado_tracing/recording.py
global_data = {} # add whatever here, e.g. self.request
with tornado.stack_context.StackContext(functools.partial(ThreadRequestContext, **global_data)):
super(RequestContextHandler, self)._execute(transforms, *args, **kwargs)
# elsewhere, use ThreadRequestContext.data => a dict
import threading
class ThreadRequestContext(object):
"""A context manager that saves some per-thread state globally.
Intended for use with Tornado's StackContext.
Provide arbitrary data as kwargs upon creation,
then use ThreadRequestContext.data to access it.
"""
_state = threading.local()
_state.data = {}
class __metaclass__(type):
# property() doesn't work on classmethods,
# see http://stackoverflow.com/q/128573/1231454
@property
def data(cls):
if not hasattr(cls._state, 'data'):
return {}
return cls._state.data
def __init__(self, **data):
self._data = data
def __enter__(self):
self._prev_data = self.__class__.data
self.__class__._state.data = self._data
def __exit__(self, *exc):
self.__class__._state.data = self._prev_data
del self._prev_data
return False
@breerly

This comment has been minimized.

Copy link

commented Feb 25, 2015

Can you add a practical example of how to use this? Literally just access ThreadRequestContext.data from anywhere and it will respect the context switches?

@breerly

This comment has been minimized.

Copy link

commented Feb 25, 2015

For some reason when I try and use this gist (tornado 4.1) I get this exception:

Traceback (most recent call last):
  File "/home/uber/zipkintegration/clay-tornado/env/local/lib/python2.7/site-packages/tornado/http1connection.py", line 236, in _read_message
    delegate.finish()
  File "/home/uber/zipkintegration/clay-tornado/env/local/lib/python2.7/site-packages/tornado/httpserver.py", line 269, in finish
    self.delegate.finish()
  File "/home/uber/zipkintegration/clay-tornado/env/local/lib/python2.7/site-packages/tornado/web.py", line 1898, in finish
    self.execute()
  File "/home/uber/zipkintegration/clay-tornado/env/local/lib/python2.7/site-packages/tornado/web.py", line 1930, in execute
    f.add_done_callback(lambda f: f.exception())
AttributeError: 'NoneType' object has no attribute 'add_done_callback'

When I update line 13 to include a return I don't get the exception anymore:

        with tornado.stack_context.StackContext(functools.partial(ThreadRequestContext, **global_data)):
            return super(RequestContextHandler, self)._execute(transforms, *args, **kwargs)

You think this is the correct fix? Perhaps we can update the gist to reflect this.

@EricBuist

This comment has been minimized.

Copy link

commented Nov 12, 2015

For which version of Python and Tornado is this for? I'm trying to use this and that just completely fails. The RequestThreadContext.data throws a NameError: data is not a property of the class. The metaclass seems just to not work. I tried to replace it with a class method and change data for data() in my code. It works, but the thread local information is not transmitted across method boundaries. Use of internal undocumented Tornado method (_execute, saw that nowhere) and funky metaclass stuff is likely to bind this to very specific environments.
I'm using Python 3.4.3 and Tornado 3.2.

@simon-weber

This comment has been minimized.

Copy link
Owner Author

commented Jan 14, 2016

Hey, sorry for missing these comments -- I didn't realize github doesn't notify on gist comments and mentions don't work. Shoot me a tweet to let me know if you post something new.

likely to bind this to very specific environments.

Ah, yeah. This is working for me in python 2.7.x + tornado 3.2.2, but I haven't tested it elsewhere.

Can you add a practical example of how to use this?

I hook this into a root logging filter, so all logging from the application can have the user id prepended when it's available.

@eddyzhou

This comment has been minimized.

Copy link

commented Feb 24, 2016

Not work well with coroutine

@virtuald

This comment has been minimized.

Copy link

commented Apr 20, 2016

Those who come by here may want to try https://gist.github.com/virtuald/50bf7cacdc8cfb05e323f350539f0efa instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.