If an error occurs during the fetch, we raise an HTTPError.
"""
if not isinstance(request, HTTPRequest):
- request = HTTPRequest(url=request, **kwargs)
+ request = HTTPRequest(url=request, **kwargs)
buffer = cStringIO.StringIO()
headers = httputil.HTTPHeaders()
try:
throw the exception (if any) in the callback.
"""
if not isinstance(request, HTTPRequest):
- request = HTTPRequest(url=request, **kwargs)
+ request = HTTPRequest(url=request, **kwargs)
self._requests.append((request, stack_context.wrap(callback)))
self._process_queue()
self._set_timeout(0)
curl.setopt(pycurl.URL, request.url)
# Request headers may be either a regular dict or HTTPHeaders object
if isinstance(request.headers, httputil.HTTPHeaders):
- curl.setopt(pycurl.HTTPHEADER,
- [_utf8("%s: %s" % i) for i in request.headers.get_all()])
+ curl.setopt(pycurl.HTTPHEADER,
+ [_utf8("%s: %s" % i) for i in request.headers.get_all()])
else:
curl.setopt(pycurl.HTTPHEADER,
[_utf8("%s: %s" % i) for i in request.headers.iteritems()])
if __name__ == "__main__":
main()
-
ioloop.IOLoop.READ)
def stop(self):
- self.io_loop.remove_handler(self._socket.fileno())
- self._socket.close()
+ self.io_loop.remove_handler(self._socket.fileno())
+ self._socket.close()
def _handle_events(self, fd, events):
while True:
args = ", ".join(["%s=%r" % (n, getattr(self, n)) for n in attrs])
return "%s(%s, headers=%s)" % (
self.__class__.__name__, args, dict(self.headers))
-
import threading
class _State(threading.local):
- def __init__(self):
- self.contexts = ()
+ def __init__(self):
+ self.contexts = ()
_state = _State()
@contextlib.contextmanager
def StackContext(context_factory):
- '''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):
- '''
- old_contexts = _state.contexts
- try:
- _state.contexts = old_contexts + (context_factory,)
- with context_factory():
- yield
- finally:
- _state.contexts = old_contexts
+ '''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):
+ '''
+ old_contexts = _state.contexts
+ try:
+ _state.contexts = old_contexts + (context_factory,)
+ with context_factory():
+ yield
+ finally:
+ _state.contexts = old_contexts
@contextlib.contextmanager
def NullContext():
- '''Resets the StackContext.
+ '''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.
- '''
- old_contexts = _state.contexts
- try:
- _state.contexts = ()
- yield
- finally:
- _state.contexts = old_contexts
+ 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.
+ '''
+ old_contexts = _state.contexts
+ try:
+ _state.contexts = ()
+ yield
+ finally:
+ _state.contexts = old_contexts
def wrap(fn, *args, **kwargs):
- '''Returns a callable object that will resore 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).
-
- As a convenience, also binds parameters to the given function
- like functools.partial.
- '''
- # functools.wraps doesn't appear to work on functools.partial objects
- #@functools.wraps(fn)
- def wrapped(callback, contexts, *args, **kwargs):
- # _state.contexts and contexts may share a common prefix.
- # For each element of contexts not in that prefix, create a new
- # StackContext object.
- # TODO(bdarnell): do we want to be strict about the order,
- # or is what we really want just set(contexts) - set(_state.contexts)?
- # I think we do want to be strict about using identity comparison,
- # so a set may not be quite right. Conversely, it's not very stack-like
- # to have new contexts pop up in the middle, so would we want to
- # ensure there are no existing contexts not in the stack being restored?
- # That feels right, but given the difficulty of handling errors at this
- # level I'm not going to check for it now.
- pairs = itertools.izip(itertools.chain(_state.contexts,
- itertools.repeat(None)),
- contexts)
- new_contexts = []
- for old, new in itertools.dropwhile(lambda x: x[0] is x[1], pairs):
- new_contexts.append(StackContext(new))
- if new_contexts:
- with contextlib.nested(*new_contexts):
- callback(*args, **kwargs)
+ '''Returns a callable object that will resore 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).
+
+ As a convenience, also binds parameters to the given function
+ like functools.partial.
+ '''
+ # functools.wraps doesn't appear to work on functools.partial objects
+ #@functools.wraps(fn)
+ def wrapped(callback, contexts, *args, **kwargs):
+ # _state.contexts and contexts may share a common prefix.
+ # For each element of contexts not in that prefix, create a new
+ # StackContext object.
+ # TODO(bdarnell): do we want to be strict about the order,
+ # or is what we really want just set(contexts) - set(_state.contexts)?
+ # I think we do want to be strict about using identity comparison,
+ # so a set may not be quite right. Conversely, it's not very stack-like
+ # to have new contexts pop up in the middle, so would we want to
+ # ensure there are no existing contexts not in the stack being restored?
+ # That feels right, but given the difficulty of handling errors at this
+ # level I'm not going to check for it now.
+ pairs = itertools.izip(itertools.chain(_state.contexts,
+ itertools.repeat(None)),
+ contexts)
+ new_contexts = []
+ for old, new in itertools.dropwhile(lambda x: x[0] is x[1], pairs):
+ new_contexts.append(StackContext(new))
+ if new_contexts:
+ with contextlib.nested(*new_contexts):
+ 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(*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
- result = functools.partial(wrapped, callback, contexts, *args, **kwargs)
- result.stack_context_wrapped = True
- return result
-
+ callback = fn
+ contexts = _state.contexts
+ result = functools.partial(wrapped, callback, contexts, *args, **kwargs)
+ result.stack_context_wrapped = True
+ return result
self.templates = {}
def reset(self):
- self.templates = {}
+ self.templates = {}
def resolve_path(self, name, parent_path=None):
if parent_path and not parent_path.startswith("<") and \
import urllib
class AuthRedirectRequestHandler(RequestHandler):
- def initialize(self, login_url):
- self.login_url = login_url
+ def initialize(self, login_url):
+ self.login_url = login_url
- def get_login_url(self):
- return self.login_url
+ def get_login_url(self):
+ return self.login_url
- @authenticated
- def get(self):
- # we'll never actually get here because the test doesn't follow redirects
- self.send_error(500)
+ @authenticated
+ def get(self):
+ # we'll never actually get here because the test doesn't follow redirects
+ self.send_error(500)
class AuthRedirectTest(AsyncHTTPTestCase, LogTrapTestCase):
- def get_app(self):
- return Application([('/relative', AuthRedirectRequestHandler,
- dict(login_url='/login')),
- ('/absolute', AuthRedirectRequestHandler,
- dict(login_url='http://example.com/login'))])
+ def get_app(self):
+ return Application([('/relative', AuthRedirectRequestHandler,
+ dict(login_url='/login')),
+ ('/absolute', AuthRedirectRequestHandler,
+ dict(login_url='http://example.com/login'))])
- def test_relative_auth_redirect(self):
- self.http_client.fetch(self.get_url('/relative'), self.stop,
- follow_redirects=False)
- response = self.wait()
- self.assertEqual(response.code, 302)
- self.assertEqual(response.headers['Location'], '/login?next=%2Frelative')
+ def test_relative_auth_redirect(self):
+ self.http_client.fetch(self.get_url('/relative'), self.stop,
+ follow_redirects=False)
+ response = self.wait()
+ self.assertEqual(response.code, 302)
+ self.assertEqual(response.headers['Location'], '/login?next=%2Frelative')
- def test_absolute_auth_redirect(self):
- self.http_client.fetch(self.get_url('/absolute'), self.stop,
- follow_redirects=False)
- response = self.wait()
- self.assertEqual(response.code, 302)
- self.assertTrue(re.match(
- 'http://example.com/login\?next=http%3A%2F%2Flocalhost%3A[0-9]+%2Fabsolute',
- response.headers['Location']), response.headers['Location'])
+ def test_absolute_auth_redirect(self):
+ self.http_client.fetch(self.get_url('/absolute'), self.stop,
+ follow_redirects=False)
+ response = self.wait()
+ self.assertEqual(response.code, 302)
+ self.assertTrue(re.match(
+ 'http://example.com/login\?next=http%3A%2F%2Flocalhost%3A[0-9]+%2Fabsolute',
+ response.headers['Location']), response.headers['Location'])
import unittest
class TestRequestHandler(RequestHandler):
- def __init__(self, app, request, io_loop):
- super(TestRequestHandler, self).__init__(app, request)
- self.io_loop = io_loop
+ def __init__(self, app, request, io_loop):
+ super(TestRequestHandler, self).__init__(app, request)
+ self.io_loop = io_loop
- @asynchronous
- def get(self):
- logging.info('in get()')
- # call self.part2 without a self.async_callback wrapper. Its
- # exception should still get thrown
- self.io_loop.add_callback(self.part2)
+ @asynchronous
+ def get(self):
+ logging.info('in get()')
+ # call self.part2 without a self.async_callback wrapper. Its
+ # exception should still get thrown
+ self.io_loop.add_callback(self.part2)
- def part2(self):
- logging.info('in part2()')
- # Go through a third layer to make sure that contexts once restored
- # are again passed on to future callbacks
- self.io_loop.add_callback(self.part3)
+ def part2(self):
+ logging.info('in part2()')
+ # Go through a third layer to make sure that contexts once restored
+ # are again passed on to future callbacks
+ self.io_loop.add_callback(self.part3)
- def part3(self):
- logging.info('in part3()')
- raise Exception('test exception')
+ def part3(self):
+ logging.info('in part3()')
+ raise Exception('test exception')
- def get_error_html(self, status_code, **kwargs):
- if 'exception' in kwargs and str(kwargs['exception']) == 'test exception':
- return 'got expected exception'
- else:
- return 'unexpected failure'
+ def get_error_html(self, status_code, **kwargs):
+ if 'exception' in kwargs and str(kwargs['exception']) == 'test exception':
+ return 'got expected exception'
+ else:
+ return 'unexpected failure'
class HTTPStackContextTest(AsyncHTTPTestCase, LogTrapTestCase):
- def get_app(self):
- return Application([('/', TestRequestHandler,
- dict(io_loop=self.io_loop))])
+ def get_app(self):
+ return Application([('/', TestRequestHandler,
+ dict(io_loop=self.io_loop))])
- def test_stack_context(self):
- self.http_client.fetch(self.get_url('/'), self.handle_response)
- self.wait()
- self.assertEquals(self.response.code, 500)
- self.assertTrue('got expected exception' in self.response.body)
+ def test_stack_context(self):
+ self.http_client.fetch(self.get_url('/'), self.handle_response)
+ self.wait()
+ self.assertEquals(self.response.code, 500)
+ self.assertTrue('got expected exception' in self.response.body)
- def handle_response(self, response):
- self.response = response
- self.stop()
+ def handle_response(self, response):
+ self.response = response
+ self.stop()
class StackContextTest(AsyncTestCase, LogTrapTestCase):
- def setUp(self):
- super(StackContextTest, self).setUp()
- self.active_contexts = set()
+ 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)
+ @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()
+ # 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()
+ unittest.main()
from tornado.testing import AsyncTestCase, LogTrapTestCase
class AsyncTestCaseTest(AsyncTestCase, LogTrapTestCase):
- def test_exception_in_callback(self):
- self.io_loop.add_callback(lambda: 1/0)
- try:
- self.wait()
- self.fail("did not get expected exception")
- except ZeroDivisionError:
- pass
+ def test_exception_in_callback(self):
+ self.io_loop.add_callback(lambda: 1/0)
+ try:
+ self.wait()
+ self.fail("did not get expected exception")
+ except ZeroDivisionError:
+ pass
if __name__ == '__main__':
- unittest.main
+ unittest.main
# request so you don't need to restart to see changes
if self.settings.get("debug"):
if getattr(RequestHandler, "_templates", None):
- map(lambda loader: loader.reset(),
- RequestHandler._templates.values())
+ map(lambda loader: loader.reset(),
+ RequestHandler._templates.values())
RequestHandler._static_hashes = {}
handler._execute(transforms, *args, **kwargs)
file.close()
def set_extra_headers(self, path):
- """For subclass to add extra headers to the response"""
- pass
+ """For subclass to add extra headers to the response"""
+ pass
class FallbackHandler(RequestHandler):
summary = request.method + " " + request.uri + " (" + \
request.remote_ip + ")"
log_method("%d %s %.2fms", status_code, summary, request_time)
-