Last active
July 31, 2018 13:58
-
-
Save simon-weber/7755289 to your computer and use it in GitHub Desktop.
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
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.
Not work well with coroutine
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
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.