callback(*args, **kwargs)
else:
callback(*args, **kwargs)
+ if getattr(fn, 'stack_context_wrapped', False):
+ return fn
if args or kwargs:
callback = functools.partial(fn, *args, **kwargs)
else:
callback = fn
contexts = _state.contexts
- if contexts:
- return functools.partial(wrapped, callback, contexts, *args, **kwargs)
- else:
- return callback
+ result = functools.partial(wrapped, callback, contexts, *args, **kwargs)
+ result.stack_context_wrapped = True
+ return result
#!/usr/bin/env python
-from tornado.testing import AsyncHTTPTestCase, LogTrapTestCase
+from tornado.stack_context import StackContext, wrap
+from tornado.testing import AsyncHTTPTestCase, AsyncTestCase, LogTrapTestCase
from tornado.web import asynchronous, Application, RequestHandler
+import contextlib
+import functools
import logging
import unittest
else:
return 'unexpected failure'
-class StackContextTest(AsyncHTTPTestCase, LogTrapTestCase):
+class HTTPStackContextTest(AsyncHTTPTestCase, LogTrapTestCase):
def get_app(self):
return Application([('/', TestRequestHandler,
dict(io_loop=self.io_loop))])
self.response = response
self.stop()
+class StackContextTest(AsyncTestCase, LogTrapTestCase):
+ def setUp(self):
+ super(StackContextTest, self).setUp()
+ self.active_contexts = set()
+
+ @contextlib.contextmanager
+ def context(self, name):
+ assert name not in self.active_contexts
+ self.active_contexts.add(name)
+ yield
+ assert name in self.active_contexts
+ self.active_contexts.remove(name)
+
+ # Simulates the effect of an asynchronous library that uses its own
+ # StackContext internally and then returns control to the application.
+ def test_exit_library_context(self):
+ def library_function(callback):
+ # capture the caller's context before introducing our own
+ callback = wrap(callback)
+ with StackContext(functools.partial(self.context, 'library')):
+ self.io_loop.add_callback(
+ functools.partial(library_inner_callback, callback))
+ def library_inner_callback(callback):
+ assert 'application' in self.active_contexts
+ assert 'library' in self.active_contexts
+ # pass the callback out to the IOLoop to get out of the library
+ # context (could also use a NullContext here, but that would result
+ # in multiple instantiations of the application context)
+ self.io_loop.add_callback(callback)
+ def final_callback():
+ assert 'application' in self.active_contexts
+ assert 'library' not in self.active_contexts
+ self.stop()
+ with StackContext(functools.partial(self.context, 'application')):
+ library_function(final_callback)
+ self.wait()
+
if __name__ == '__main__':
unittest.main()