diff --git a/channels/seriehd.py b/channels/seriehd.py index 0da31041..66a8cab5 100644 --- a/channels/seriehd.py +++ b/channels/seriehd.py @@ -33,7 +33,7 @@ def mainlist(item): @support.scrape def peliculas(item): #findhost() - debug=True + # debug=True patron = r'
" not in value:
- value = filter_whitespace(self.whitespace, value)
-
- if value:
- writer.write_line('_tt_append(%r)' % escape.utf8(value), self.line)
-
-
-class ParseError(Exception):
- """Raised for template syntax errors.
-
- ``ParseError`` instances have ``filename`` and ``lineno`` attributes
- indicating the position of the error.
-
- .. versionchanged:: 4.3
- Added ``filename`` and ``lineno`` attributes.
- """
- def __init__(self, message, filename=None, lineno=0):
- self.message = message
- # The names "filename" and "lineno" are chosen for consistency
- # with python SyntaxError.
- self.filename = filename
- self.lineno = lineno
-
- def __str__(self):
- return '%s at %s:%d' % (self.message, self.filename, self.lineno)
-
-
-class _CodeWriter(object):
- def __init__(self, file, named_blocks, loader, current_template):
- self.file = file
- self.named_blocks = named_blocks
- self.loader = loader
- self.current_template = current_template
- self.apply_counter = 0
- self.include_stack = []
- self._indent = 0
-
- def indent_size(self):
- return self._indent
-
- def indent(self):
- class Indenter(object):
- def __enter__(_):
- self._indent += 1
- return self
-
- def __exit__(_, *args):
- assert self._indent > 0
- self._indent -= 1
-
- return Indenter()
-
- def include(self, template, line):
- self.include_stack.append((self.current_template, line))
- self.current_template = template
-
- class IncludeTemplate(object):
- def __enter__(_):
- return self
-
- def __exit__(_, *args):
- self.current_template = self.include_stack.pop()[0]
-
- return IncludeTemplate()
-
- def write_line(self, line, line_number, indent=None):
- if indent is None:
- indent = self._indent
- line_comment = ' # %s:%d' % (self.current_template.name, line_number)
- if self.include_stack:
- ancestors = ["%s:%d" % (tmpl.name, lineno)
- for (tmpl, lineno) in self.include_stack]
- line_comment += ' (via %s)' % ', '.join(reversed(ancestors))
- print(" " * indent + line + line_comment, file=self.file)
-
-
-class _TemplateReader(object):
- def __init__(self, name, text, whitespace):
- self.name = name
- self.text = text
- self.whitespace = whitespace
- self.line = 1
- self.pos = 0
-
- def find(self, needle, start=0, end=None):
- assert start >= 0, start
- pos = self.pos
- start += pos
- if end is None:
- index = self.text.find(needle, start)
- else:
- end += pos
- assert end >= start
- index = self.text.find(needle, start, end)
- if index != -1:
- index -= pos
- return index
-
- def consume(self, count=None):
- if count is None:
- count = len(self.text) - self.pos
- newpos = self.pos + count
- self.line += self.text.count("\n", self.pos, newpos)
- s = self.text[self.pos:newpos]
- self.pos = newpos
- return s
-
- def remaining(self):
- return len(self.text) - self.pos
-
- def __len__(self):
- return self.remaining()
-
- def __getitem__(self, key):
- if type(key) is slice:
- size = len(self)
- start, stop, step = key.indices(size)
- if start is None:
- start = self.pos
- else:
- start += self.pos
- if stop is not None:
- stop += self.pos
- return self.text[slice(start, stop, step)]
- elif key < 0:
- return self.text[key]
- else:
- return self.text[self.pos + key]
-
- def __str__(self):
- return self.text[self.pos:]
-
- def raise_parse_error(self, msg):
- raise ParseError(msg, self.name, self.line)
-
-
-def _format_code(code):
- lines = code.splitlines()
- format = "%%%dd %%s\n" % len(repr(len(lines) + 1))
- return "".join([format % (i + 1, line) for (i, line) in enumerate(lines)])
-
-
-def _parse(reader, template, in_block=None, in_loop=None):
- body = _ChunkList([])
- while True:
- # Find next template directive
- curly = 0
- while True:
- curly = reader.find("{", curly)
- if curly == -1 or curly + 1 == reader.remaining():
- # EOF
- if in_block:
- reader.raise_parse_error(
- "Missing {%% end %%} block for %s" % in_block)
- body.chunks.append(_Text(reader.consume(), reader.line,
- reader.whitespace))
- return body
- # If the first curly brace is not the start of a special token,
- # start searching from the character after it
- if reader[curly + 1] not in ("{", "%", "#"):
- curly += 1
- continue
- # When there are more than 2 curlies in a row, use the
- # innermost ones. This is useful when generating languages
- # like latex where curlies are also meaningful
- if (curly + 2 < reader.remaining() and
- reader[curly + 1] == '{' and reader[curly + 2] == '{'):
- curly += 1
- continue
- break
-
- # Append any text before the special token
- if curly > 0:
- cons = reader.consume(curly)
- body.chunks.append(_Text(cons, reader.line,
- reader.whitespace))
-
- start_brace = reader.consume(2)
- line = reader.line
-
- # Template directives may be escaped as "{{!" or "{%!".
- # In this case output the braces and consume the "!".
- # This is especially useful in conjunction with jquery templates,
- # which also use double braces.
- if reader.remaining() and reader[0] == "!":
- reader.consume(1)
- body.chunks.append(_Text(start_brace, line,
- reader.whitespace))
- continue
-
- # Comment
- if start_brace == "{#":
- end = reader.find("#}")
- if end == -1:
- reader.raise_parse_error("Missing end comment #}")
- contents = reader.consume(end).strip()
- reader.consume(2)
- continue
-
- # Expression
- if start_brace == "{{":
- end = reader.find("}}")
- if end == -1:
- reader.raise_parse_error("Missing end expression }}")
- contents = reader.consume(end).strip()
- reader.consume(2)
- if not contents:
- reader.raise_parse_error("Empty expression")
- body.chunks.append(_Expression(contents, line))
- continue
-
- # Block
- assert start_brace == "{%", start_brace
- end = reader.find("%}")
- if end == -1:
- reader.raise_parse_error("Missing end block %}")
- contents = reader.consume(end).strip()
- reader.consume(2)
- if not contents:
- reader.raise_parse_error("Empty block tag ({% %})")
-
- operator, space, suffix = contents.partition(" ")
- suffix = suffix.strip()
-
- # Intermediate ("else", "elif", etc) blocks
- intermediate_blocks = {
- "else": set(["if", "for", "while", "try"]),
- "elif": set(["if"]),
- "except": set(["try"]),
- "finally": set(["try"]),
- }
- allowed_parents = intermediate_blocks.get(operator)
- if allowed_parents is not None:
- if not in_block:
- reader.raise_parse_error("%s outside %s block" %
- (operator, allowed_parents))
- if in_block not in allowed_parents:
- reader.raise_parse_error(
- "%s block cannot be attached to %s block" %
- (operator, in_block))
- body.chunks.append(_IntermediateControlBlock(contents, line))
- continue
-
- # End tag
- elif operator == "end":
- if not in_block:
- reader.raise_parse_error("Extra {% end %} block")
- return body
-
- elif operator in ("extends", "include", "set", "import", "from",
- "comment", "autoescape", "whitespace", "raw",
- "module"):
- if operator == "comment":
- continue
- if operator == "extends":
- suffix = suffix.strip('"').strip("'")
- if not suffix:
- reader.raise_parse_error("extends missing file path")
- block = _ExtendsBlock(suffix)
- elif operator in ("import", "from"):
- if not suffix:
- reader.raise_parse_error("import missing statement")
- block = _Statement(contents, line)
- elif operator == "include":
- suffix = suffix.strip('"').strip("'")
- if not suffix:
- reader.raise_parse_error("include missing file path")
- block = _IncludeBlock(suffix, reader, line)
- elif operator == "set":
- if not suffix:
- reader.raise_parse_error("set missing statement")
- block = _Statement(suffix, line)
- elif operator == "autoescape":
- fn = suffix.strip()
- if fn == "None":
- fn = None
- template.autoescape = fn
- continue
- elif operator == "whitespace":
- mode = suffix.strip()
- # Validate the selected mode
- filter_whitespace(mode, '')
- reader.whitespace = mode
- continue
- elif operator == "raw":
- block = _Expression(suffix, line, raw=True)
- elif operator == "module":
- block = _Module(suffix, line)
- body.chunks.append(block)
- continue
-
- elif operator in ("apply", "block", "try", "if", "for", "while"):
- # parse inner body recursively
- if operator in ("for", "while"):
- block_body = _parse(reader, template, operator, operator)
- elif operator == "apply":
- # apply creates a nested function so syntactically it's not
- # in the loop.
- block_body = _parse(reader, template, operator, None)
- else:
- block_body = _parse(reader, template, operator, in_loop)
-
- if operator == "apply":
- if not suffix:
- reader.raise_parse_error("apply missing method name")
- block = _ApplyBlock(suffix, line, block_body)
- elif operator == "block":
- if not suffix:
- reader.raise_parse_error("block missing name")
- block = _NamedBlock(suffix, block_body, template, line)
- else:
- block = _ControlBlock(contents, line, block_body)
- body.chunks.append(block)
- continue
-
- elif operator in ("break", "continue"):
- if not in_loop:
- reader.raise_parse_error("%s outside %s block" %
- (operator, set(["for", "while"])))
- body.chunks.append(_Statement(contents, line))
- continue
-
- else:
- reader.raise_parse_error("unknown operator: %r" % operator)
diff --git a/lib/tornado/test/__main__.py b/lib/tornado/test/__main__.py
deleted file mode 100755
index c78478cb..00000000
--- a/lib/tornado/test/__main__.py
+++ /dev/null
@@ -1,14 +0,0 @@
-"""Shim to allow python -m tornado.test.
-
-This only works in python 2.7+.
-"""
-from __future__ import absolute_import, division, print_function
-
-from tornado.test.runtests import all, main
-
-# tornado.testing.main autodiscovery relies on 'all' being present in
-# the main module, so import it here even though it is not used directly.
-# The following line prevents a pyflakes warning.
-all = all
-
-main()
diff --git a/lib/tornado/test/asyncio_test.py b/lib/tornado/test/asyncio_test.py
deleted file mode 100755
index a7c75649..00000000
--- a/lib/tornado/test/asyncio_test.py
+++ /dev/null
@@ -1,206 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import absolute_import, division, print_function
-
-from concurrent.futures import ThreadPoolExecutor
-from tornado import gen
-from tornado.ioloop import IOLoop
-from tornado.testing import AsyncTestCase, gen_test
-from tornado.test.util import unittest, skipBefore33, skipBefore35, exec_test
-
-try:
- from tornado.platform.asyncio import asyncio
-except ImportError:
- asyncio = None
-else:
- from tornado.platform.asyncio import AsyncIOLoop, to_asyncio_future, AnyThreadEventLoopPolicy
- # This is used in dynamically-evaluated code, so silence pyflakes.
- to_asyncio_future
-
-
-@unittest.skipIf(asyncio is None, "asyncio module not present")
-class AsyncIOLoopTest(AsyncTestCase):
- def get_new_ioloop(self):
- io_loop = AsyncIOLoop()
- return io_loop
-
- def test_asyncio_callback(self):
- # Basic test that the asyncio loop is set up correctly.
- asyncio.get_event_loop().call_soon(self.stop)
- self.wait()
-
- @gen_test
- def test_asyncio_future(self):
- # Test that we can yield an asyncio future from a tornado coroutine.
- # Without 'yield from', we must wrap coroutines in ensure_future,
- # which was introduced during Python 3.4, deprecating the prior "async".
- if hasattr(asyncio, 'ensure_future'):
- ensure_future = asyncio.ensure_future
- else:
- # async is a reserved word in Python 3.7
- ensure_future = getattr(asyncio, 'async')
-
- x = yield ensure_future(
- asyncio.get_event_loop().run_in_executor(None, lambda: 42))
- self.assertEqual(x, 42)
-
- @skipBefore33
- @gen_test
- def test_asyncio_yield_from(self):
- # Test that we can use asyncio coroutines with 'yield from'
- # instead of asyncio.async(). This requires python 3.3 syntax.
- namespace = exec_test(globals(), locals(), """
- @gen.coroutine
- def f():
- event_loop = asyncio.get_event_loop()
- x = yield from event_loop.run_in_executor(None, lambda: 42)
- return x
- """)
- result = yield namespace['f']()
- self.assertEqual(result, 42)
-
- @skipBefore35
- def test_asyncio_adapter(self):
- # This test demonstrates that when using the asyncio coroutine
- # runner (i.e. run_until_complete), the to_asyncio_future
- # adapter is needed. No adapter is needed in the other direction,
- # as demonstrated by other tests in the package.
- @gen.coroutine
- def tornado_coroutine():
- yield gen.moment
- raise gen.Return(42)
- native_coroutine_without_adapter = exec_test(globals(), locals(), """
- async def native_coroutine_without_adapter():
- return await tornado_coroutine()
- """)["native_coroutine_without_adapter"]
-
- native_coroutine_with_adapter = exec_test(globals(), locals(), """
- async def native_coroutine_with_adapter():
- return await to_asyncio_future(tornado_coroutine())
- """)["native_coroutine_with_adapter"]
-
- # Use the adapter, but two degrees from the tornado coroutine.
- native_coroutine_with_adapter2 = exec_test(globals(), locals(), """
- async def native_coroutine_with_adapter2():
- return await to_asyncio_future(native_coroutine_without_adapter())
- """)["native_coroutine_with_adapter2"]
-
- # Tornado supports native coroutines both with and without adapters
- self.assertEqual(
- self.io_loop.run_sync(native_coroutine_without_adapter),
- 42)
- self.assertEqual(
- self.io_loop.run_sync(native_coroutine_with_adapter),
- 42)
- self.assertEqual(
- self.io_loop.run_sync(native_coroutine_with_adapter2),
- 42)
-
- # Asyncio only supports coroutines that yield asyncio-compatible
- # Futures (which our Future is since 5.0).
- self.assertEqual(
- asyncio.get_event_loop().run_until_complete(
- native_coroutine_without_adapter()),
- 42)
- self.assertEqual(
- asyncio.get_event_loop().run_until_complete(
- native_coroutine_with_adapter()),
- 42)
- self.assertEqual(
- asyncio.get_event_loop().run_until_complete(
- native_coroutine_with_adapter2()),
- 42)
-
-
-@unittest.skipIf(asyncio is None, "asyncio module not present")
-class LeakTest(unittest.TestCase):
- def setUp(self):
- # Trigger a cleanup of the mapping so we start with a clean slate.
- AsyncIOLoop().close()
- # If we don't clean up after ourselves other tests may fail on
- # py34.
- self.orig_policy = asyncio.get_event_loop_policy()
- asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy())
-
- def tearDown(self):
- asyncio.get_event_loop().close()
- asyncio.set_event_loop_policy(self.orig_policy)
-
- def test_ioloop_close_leak(self):
- orig_count = len(IOLoop._ioloop_for_asyncio)
- for i in range(10):
- # Create and close an AsyncIOLoop using Tornado interfaces.
- loop = AsyncIOLoop()
- loop.close()
- new_count = len(IOLoop._ioloop_for_asyncio) - orig_count
- self.assertEqual(new_count, 0)
-
- def test_asyncio_close_leak(self):
- orig_count = len(IOLoop._ioloop_for_asyncio)
- for i in range(10):
- # Create and close an AsyncIOMainLoop using asyncio interfaces.
- loop = asyncio.new_event_loop()
- loop.call_soon(IOLoop.current)
- loop.call_soon(loop.stop)
- loop.run_forever()
- loop.close()
- new_count = len(IOLoop._ioloop_for_asyncio) - orig_count
- # Because the cleanup is run on new loop creation, we have one
- # dangling entry in the map (but only one).
- self.assertEqual(new_count, 1)
-
-
-@unittest.skipIf(asyncio is None, "asyncio module not present")
-class AnyThreadEventLoopPolicyTest(unittest.TestCase):
- def setUp(self):
- self.orig_policy = asyncio.get_event_loop_policy()
- self.executor = ThreadPoolExecutor(1)
-
- def tearDown(self):
- asyncio.set_event_loop_policy(self.orig_policy)
- self.executor.shutdown()
-
- def get_event_loop_on_thread(self):
- def get_and_close_event_loop():
- """Get the event loop. Close it if one is returned.
-
- Returns the (closed) event loop. This is a silly thing
- to do and leaves the thread in a broken state, but it's
- enough for this test. Closing the loop avoids resource
- leak warnings.
- """
- loop = asyncio.get_event_loop()
- loop.close()
- return loop
- future = self.executor.submit(get_and_close_event_loop)
- return future.result()
-
- def run_policy_test(self, accessor, expected_type):
- # With the default policy, non-main threads don't get an event
- # loop.
- self.assertRaises((RuntimeError, AssertionError),
- self.executor.submit(accessor).result)
- # Set the policy and we can get a loop.
- asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())
- self.assertIsInstance(
- self.executor.submit(accessor).result(),
- expected_type)
- # Clean up to silence leak warnings. Always use asyncio since
- # IOLoop doesn't (currently) close the underlying loop.
- self.executor.submit(lambda: asyncio.get_event_loop().close()).result()
-
- def test_asyncio_accessor(self):
- self.run_policy_test(asyncio.get_event_loop, asyncio.AbstractEventLoop)
-
- def test_tornado_accessor(self):
- self.run_policy_test(IOLoop.current, IOLoop)
diff --git a/lib/tornado/test/auth_test.py b/lib/tornado/test/auth_test.py
deleted file mode 100755
index 14bc3353..00000000
--- a/lib/tornado/test/auth_test.py
+++ /dev/null
@@ -1,735 +0,0 @@
-# These tests do not currently do much to verify the correct implementation
-# of the openid/oauth protocols, they just exercise the major code paths
-# and ensure that it doesn't blow up (e.g. with unicode/bytes issues in
-# python 3)
-
-
-from __future__ import absolute_import, division, print_function
-
-import unittest
-import warnings
-
-from tornado.auth import (
- AuthError, OpenIdMixin, OAuthMixin, OAuth2Mixin,
- GoogleOAuth2Mixin, FacebookGraphMixin, TwitterMixin,
-)
-from tornado.concurrent import Future
-from tornado.escape import json_decode
-from tornado import gen
-from tornado.httputil import url_concat
-from tornado.log import gen_log, app_log
-from tornado.testing import AsyncHTTPTestCase, ExpectLog
-from tornado.test.util import ignore_deprecation
-from tornado.web import RequestHandler, Application, asynchronous, HTTPError
-
-try:
- from unittest import mock
-except ImportError:
- mock = None
-
-
-class OpenIdClientLoginHandlerLegacy(RequestHandler, OpenIdMixin):
- def initialize(self, test):
- self._OPENID_ENDPOINT = test.get_url('/openid/server/authenticate')
-
- with ignore_deprecation():
- @asynchronous
- def get(self):
- if self.get_argument('openid.mode', None):
- with warnings.catch_warnings():
- warnings.simplefilter('ignore', DeprecationWarning)
- self.get_authenticated_user(
- self.on_user, http_client=self.settings['http_client'])
- return
- res = self.authenticate_redirect()
- assert isinstance(res, Future)
- assert res.done()
-
- def on_user(self, user):
- if user is None:
- raise Exception("user is None")
- self.finish(user)
-
-
-class OpenIdClientLoginHandler(RequestHandler, OpenIdMixin):
- def initialize(self, test):
- self._OPENID_ENDPOINT = test.get_url('/openid/server/authenticate')
-
- @gen.coroutine
- def get(self):
- if self.get_argument('openid.mode', None):
- user = yield self.get_authenticated_user(http_client=self.settings['http_client'])
- if user is None:
- raise Exception("user is None")
- self.finish(user)
- return
- res = self.authenticate_redirect()
- assert isinstance(res, Future)
- assert res.done()
-
-
-class OpenIdServerAuthenticateHandler(RequestHandler):
- def post(self):
- if self.get_argument('openid.mode') != 'check_authentication':
- raise Exception("incorrect openid.mode %r")
- self.write('is_valid:true')
-
-
-class OAuth1ClientLoginHandlerLegacy(RequestHandler, OAuthMixin):
- def initialize(self, test, version):
- self._OAUTH_VERSION = version
- self._OAUTH_REQUEST_TOKEN_URL = test.get_url('/oauth1/server/request_token')
- self._OAUTH_AUTHORIZE_URL = test.get_url('/oauth1/server/authorize')
- self._OAUTH_ACCESS_TOKEN_URL = test.get_url('/oauth1/server/access_token')
-
- def _oauth_consumer_token(self):
- return dict(key='asdf', secret='qwer')
-
- with ignore_deprecation():
- @asynchronous
- def get(self):
- if self.get_argument('oauth_token', None):
- with warnings.catch_warnings():
- warnings.simplefilter('ignore', DeprecationWarning)
- self.get_authenticated_user(
- self.on_user, http_client=self.settings['http_client'])
- return
- res = self.authorize_redirect(http_client=self.settings['http_client'])
- assert isinstance(res, Future)
-
- def on_user(self, user):
- if user is None:
- raise Exception("user is None")
- self.finish(user)
-
- def _oauth_get_user(self, access_token, callback):
- if self.get_argument('fail_in_get_user', None):
- raise Exception("failing in get_user")
- if access_token != dict(key='uiop', secret='5678'):
- raise Exception("incorrect access token %r" % access_token)
- callback(dict(email='foo@example.com'))
-
-
-class OAuth1ClientLoginHandler(RequestHandler, OAuthMixin):
- def initialize(self, test, version):
- self._OAUTH_VERSION = version
- self._OAUTH_REQUEST_TOKEN_URL = test.get_url('/oauth1/server/request_token')
- self._OAUTH_AUTHORIZE_URL = test.get_url('/oauth1/server/authorize')
- self._OAUTH_ACCESS_TOKEN_URL = test.get_url('/oauth1/server/access_token')
-
- def _oauth_consumer_token(self):
- return dict(key='asdf', secret='qwer')
-
- @gen.coroutine
- def get(self):
- if self.get_argument('oauth_token', None):
- user = yield self.get_authenticated_user(http_client=self.settings['http_client'])
- if user is None:
- raise Exception("user is None")
- self.finish(user)
- return
- yield self.authorize_redirect(http_client=self.settings['http_client'])
-
- @gen.coroutine
- def _oauth_get_user_future(self, access_token):
- if self.get_argument('fail_in_get_user', None):
- raise Exception("failing in get_user")
- if access_token != dict(key='uiop', secret='5678'):
- raise Exception("incorrect access token %r" % access_token)
- return dict(email='foo@example.com')
-
-
-class OAuth1ClientLoginCoroutineHandler(OAuth1ClientLoginHandler):
- """Replaces OAuth1ClientLoginCoroutineHandler's get() with a coroutine."""
- @gen.coroutine
- def get(self):
- if self.get_argument('oauth_token', None):
- # Ensure that any exceptions are set on the returned Future,
- # not simply thrown into the surrounding StackContext.
- try:
- yield self.get_authenticated_user()
- except Exception as e:
- self.set_status(503)
- self.write("got exception: %s" % e)
- else:
- yield self.authorize_redirect()
-
-
-class OAuth1ClientRequestParametersHandler(RequestHandler, OAuthMixin):
- def initialize(self, version):
- self._OAUTH_VERSION = version
-
- def _oauth_consumer_token(self):
- return dict(key='asdf', secret='qwer')
-
- def get(self):
- params = self._oauth_request_parameters(
- 'http://www.example.com/api/asdf',
- dict(key='uiop', secret='5678'),
- parameters=dict(foo='bar'))
- self.write(params)
-
-
-class OAuth1ServerRequestTokenHandler(RequestHandler):
- def get(self):
- self.write('oauth_token=zxcv&oauth_token_secret=1234')
-
-
-class OAuth1ServerAccessTokenHandler(RequestHandler):
- def get(self):
- self.write('oauth_token=uiop&oauth_token_secret=5678')
-
-
-class OAuth2ClientLoginHandler(RequestHandler, OAuth2Mixin):
- def initialize(self, test):
- self._OAUTH_AUTHORIZE_URL = test.get_url('/oauth2/server/authorize')
-
- def get(self):
- res = self.authorize_redirect()
- assert isinstance(res, Future)
- assert res.done()
-
-
-class FacebookClientLoginHandler(RequestHandler, FacebookGraphMixin):
- def initialize(self, test):
- self._OAUTH_AUTHORIZE_URL = test.get_url('/facebook/server/authorize')
- self._OAUTH_ACCESS_TOKEN_URL = test.get_url('/facebook/server/access_token')
- self._FACEBOOK_BASE_URL = test.get_url('/facebook/server')
-
- @gen.coroutine
- def get(self):
- if self.get_argument("code", None):
- user = yield self.get_authenticated_user(
- redirect_uri=self.request.full_url(),
- client_id=self.settings["facebook_api_key"],
- client_secret=self.settings["facebook_secret"],
- code=self.get_argument("code"))
- self.write(user)
- else:
- yield self.authorize_redirect(
- redirect_uri=self.request.full_url(),
- client_id=self.settings["facebook_api_key"],
- extra_params={"scope": "read_stream,offline_access"})
-
-
-class FacebookServerAccessTokenHandler(RequestHandler):
- def get(self):
- self.write(dict(access_token="asdf", expires_in=3600))
-
-
-class FacebookServerMeHandler(RequestHandler):
- def get(self):
- self.write('{}')
-
-
-class TwitterClientHandler(RequestHandler, TwitterMixin):
- def initialize(self, test):
- self._OAUTH_REQUEST_TOKEN_URL = test.get_url('/oauth1/server/request_token')
- self._OAUTH_ACCESS_TOKEN_URL = test.get_url('/twitter/server/access_token')
- self._OAUTH_AUTHORIZE_URL = test.get_url('/oauth1/server/authorize')
- self._OAUTH_AUTHENTICATE_URL = test.get_url('/twitter/server/authenticate')
- self._TWITTER_BASE_URL = test.get_url('/twitter/api')
-
- def get_auth_http_client(self):
- return self.settings['http_client']
-
-
-class TwitterClientLoginHandlerLegacy(TwitterClientHandler):
- with ignore_deprecation():
- @asynchronous
- def get(self):
- if self.get_argument("oauth_token", None):
- self.get_authenticated_user(self.on_user)
- return
- self.authorize_redirect()
-
- def on_user(self, user):
- if user is None:
- raise Exception("user is None")
- self.finish(user)
-
-
-class TwitterClientLoginHandler(TwitterClientHandler):
- @gen.coroutine
- def get(self):
- if self.get_argument("oauth_token", None):
- user = yield self.get_authenticated_user()
- if user is None:
- raise Exception("user is None")
- self.finish(user)
- return
- yield self.authorize_redirect()
-
-
-class TwitterClientAuthenticateHandler(TwitterClientHandler):
- # Like TwitterClientLoginHandler, but uses authenticate_redirect
- # instead of authorize_redirect.
- @gen.coroutine
- def get(self):
- if self.get_argument("oauth_token", None):
- user = yield self.get_authenticated_user()
- if user is None:
- raise Exception("user is None")
- self.finish(user)
- return
- yield self.authenticate_redirect()
-
-
-class TwitterClientLoginGenEngineHandler(TwitterClientHandler):
- with ignore_deprecation():
- @asynchronous
- @gen.engine
- def get(self):
- if self.get_argument("oauth_token", None):
- user = yield self.get_authenticated_user()
- self.finish(user)
- else:
- # Old style: with @gen.engine we can ignore the Future from
- # authorize_redirect.
- self.authorize_redirect()
-
-
-class TwitterClientLoginGenCoroutineHandler(TwitterClientHandler):
- @gen.coroutine
- def get(self):
- if self.get_argument("oauth_token", None):
- user = yield self.get_authenticated_user()
- self.finish(user)
- else:
- # New style: with @gen.coroutine the result must be yielded
- # or else the request will be auto-finished too soon.
- yield self.authorize_redirect()
-
-
-class TwitterClientShowUserHandlerLegacy(TwitterClientHandler):
- with ignore_deprecation():
- @asynchronous
- @gen.engine
- def get(self):
- # TODO: would be nice to go through the login flow instead of
- # cheating with a hard-coded access token.
- with warnings.catch_warnings():
- warnings.simplefilter('ignore', DeprecationWarning)
- response = yield gen.Task(self.twitter_request,
- '/users/show/%s' % self.get_argument('name'),
- access_token=dict(key='hjkl', secret='vbnm'))
- if response is None:
- self.set_status(500)
- self.finish('error from twitter request')
- else:
- self.finish(response)
-
-
-class TwitterClientShowUserHandler(TwitterClientHandler):
- @gen.coroutine
- def get(self):
- # TODO: would be nice to go through the login flow instead of
- # cheating with a hard-coded access token.
- try:
- response = yield self.twitter_request(
- '/users/show/%s' % self.get_argument('name'),
- access_token=dict(key='hjkl', secret='vbnm'))
- except AuthError:
- self.set_status(500)
- self.finish('error from twitter request')
- else:
- self.finish(response)
-
-
-class TwitterServerAccessTokenHandler(RequestHandler):
- def get(self):
- self.write('oauth_token=hjkl&oauth_token_secret=vbnm&screen_name=foo')
-
-
-class TwitterServerShowUserHandler(RequestHandler):
- def get(self, screen_name):
- if screen_name == 'error':
- raise HTTPError(500)
- assert 'oauth_nonce' in self.request.arguments
- assert 'oauth_timestamp' in self.request.arguments
- assert 'oauth_signature' in self.request.arguments
- assert self.get_argument('oauth_consumer_key') == 'test_twitter_consumer_key'
- assert self.get_argument('oauth_signature_method') == 'HMAC-SHA1'
- assert self.get_argument('oauth_version') == '1.0'
- assert self.get_argument('oauth_token') == 'hjkl'
- self.write(dict(screen_name=screen_name, name=screen_name.capitalize()))
-
-
-class TwitterServerVerifyCredentialsHandler(RequestHandler):
- def get(self):
- assert 'oauth_nonce' in self.request.arguments
- assert 'oauth_timestamp' in self.request.arguments
- assert 'oauth_signature' in self.request.arguments
- assert self.get_argument('oauth_consumer_key') == 'test_twitter_consumer_key'
- assert self.get_argument('oauth_signature_method') == 'HMAC-SHA1'
- assert self.get_argument('oauth_version') == '1.0'
- assert self.get_argument('oauth_token') == 'hjkl'
- self.write(dict(screen_name='foo', name='Foo'))
-
-
-class AuthTest(AsyncHTTPTestCase):
- def get_app(self):
- return Application(
- [
- # test endpoints
- ('/legacy/openid/client/login', OpenIdClientLoginHandlerLegacy, dict(test=self)),
- ('/openid/client/login', OpenIdClientLoginHandler, dict(test=self)),
- ('/legacy/oauth10/client/login', OAuth1ClientLoginHandlerLegacy,
- dict(test=self, version='1.0')),
- ('/oauth10/client/login', OAuth1ClientLoginHandler,
- dict(test=self, version='1.0')),
- ('/oauth10/client/request_params',
- OAuth1ClientRequestParametersHandler,
- dict(version='1.0')),
- ('/legacy/oauth10a/client/login', OAuth1ClientLoginHandlerLegacy,
- dict(test=self, version='1.0a')),
- ('/oauth10a/client/login', OAuth1ClientLoginHandler,
- dict(test=self, version='1.0a')),
- ('/oauth10a/client/login_coroutine',
- OAuth1ClientLoginCoroutineHandler,
- dict(test=self, version='1.0a')),
- ('/oauth10a/client/request_params',
- OAuth1ClientRequestParametersHandler,
- dict(version='1.0a')),
- ('/oauth2/client/login', OAuth2ClientLoginHandler, dict(test=self)),
-
- ('/facebook/client/login', FacebookClientLoginHandler, dict(test=self)),
-
- ('/legacy/twitter/client/login', TwitterClientLoginHandlerLegacy, dict(test=self)),
- ('/twitter/client/login', TwitterClientLoginHandler, dict(test=self)),
- ('/twitter/client/authenticate', TwitterClientAuthenticateHandler, dict(test=self)),
- ('/twitter/client/login_gen_engine',
- TwitterClientLoginGenEngineHandler, dict(test=self)),
- ('/twitter/client/login_gen_coroutine',
- TwitterClientLoginGenCoroutineHandler, dict(test=self)),
- ('/legacy/twitter/client/show_user',
- TwitterClientShowUserHandlerLegacy, dict(test=self)),
- ('/twitter/client/show_user',
- TwitterClientShowUserHandler, dict(test=self)),
-
- # simulated servers
- ('/openid/server/authenticate', OpenIdServerAuthenticateHandler),
- ('/oauth1/server/request_token', OAuth1ServerRequestTokenHandler),
- ('/oauth1/server/access_token', OAuth1ServerAccessTokenHandler),
-
- ('/facebook/server/access_token', FacebookServerAccessTokenHandler),
- ('/facebook/server/me', FacebookServerMeHandler),
- ('/twitter/server/access_token', TwitterServerAccessTokenHandler),
- (r'/twitter/api/users/show/(.*)\.json', TwitterServerShowUserHandler),
- (r'/twitter/api/account/verify_credentials\.json',
- TwitterServerVerifyCredentialsHandler),
- ],
- http_client=self.http_client,
- twitter_consumer_key='test_twitter_consumer_key',
- twitter_consumer_secret='test_twitter_consumer_secret',
- facebook_api_key='test_facebook_api_key',
- facebook_secret='test_facebook_secret')
-
- def test_openid_redirect_legacy(self):
- response = self.fetch('/legacy/openid/client/login', follow_redirects=False)
- self.assertEqual(response.code, 302)
- self.assertTrue(
- '/openid/server/authenticate?' in response.headers['Location'])
-
- def test_openid_get_user_legacy(self):
- response = self.fetch('/legacy/openid/client/login?openid.mode=blah'
- '&openid.ns.ax=http://openid.net/srv/ax/1.0'
- '&openid.ax.type.email=http://axschema.org/contact/email'
- '&openid.ax.value.email=foo@example.com')
- response.rethrow()
- parsed = json_decode(response.body)
- self.assertEqual(parsed["email"], "foo@example.com")
-
- def test_openid_redirect(self):
- response = self.fetch('/openid/client/login', follow_redirects=False)
- self.assertEqual(response.code, 302)
- self.assertTrue(
- '/openid/server/authenticate?' in response.headers['Location'])
-
- def test_openid_get_user(self):
- response = self.fetch('/openid/client/login?openid.mode=blah'
- '&openid.ns.ax=http://openid.net/srv/ax/1.0'
- '&openid.ax.type.email=http://axschema.org/contact/email'
- '&openid.ax.value.email=foo@example.com')
- response.rethrow()
- parsed = json_decode(response.body)
- self.assertEqual(parsed["email"], "foo@example.com")
-
- def test_oauth10_redirect_legacy(self):
- response = self.fetch('/legacy/oauth10/client/login', follow_redirects=False)
- self.assertEqual(response.code, 302)
- self.assertTrue(response.headers['Location'].endswith(
- '/oauth1/server/authorize?oauth_token=zxcv'))
- # the cookie is base64('zxcv')|base64('1234')
- self.assertTrue(
- '_oauth_request_token="enhjdg==|MTIzNA=="' in response.headers['Set-Cookie'],
- response.headers['Set-Cookie'])
-
- def test_oauth10_redirect(self):
- response = self.fetch('/oauth10/client/login', follow_redirects=False)
- self.assertEqual(response.code, 302)
- self.assertTrue(response.headers['Location'].endswith(
- '/oauth1/server/authorize?oauth_token=zxcv'))
- # the cookie is base64('zxcv')|base64('1234')
- self.assertTrue(
- '_oauth_request_token="enhjdg==|MTIzNA=="' in response.headers['Set-Cookie'],
- response.headers['Set-Cookie'])
-
- def test_oauth10_get_user_legacy(self):
- with ignore_deprecation():
- response = self.fetch(
- '/legacy/oauth10/client/login?oauth_token=zxcv',
- headers={'Cookie': '_oauth_request_token=enhjdg==|MTIzNA=='})
- response.rethrow()
- parsed = json_decode(response.body)
- self.assertEqual(parsed['email'], 'foo@example.com')
- self.assertEqual(parsed['access_token'], dict(key='uiop', secret='5678'))
-
- def test_oauth10_get_user(self):
- response = self.fetch(
- '/oauth10/client/login?oauth_token=zxcv',
- headers={'Cookie': '_oauth_request_token=enhjdg==|MTIzNA=='})
- response.rethrow()
- parsed = json_decode(response.body)
- self.assertEqual(parsed['email'], 'foo@example.com')
- self.assertEqual(parsed['access_token'], dict(key='uiop', secret='5678'))
-
- def test_oauth10_request_parameters(self):
- response = self.fetch('/oauth10/client/request_params')
- response.rethrow()
- parsed = json_decode(response.body)
- self.assertEqual(parsed['oauth_consumer_key'], 'asdf')
- self.assertEqual(parsed['oauth_token'], 'uiop')
- self.assertTrue('oauth_nonce' in parsed)
- self.assertTrue('oauth_signature' in parsed)
-
- def test_oauth10a_redirect_legacy(self):
- response = self.fetch('/legacy/oauth10a/client/login', follow_redirects=False)
- self.assertEqual(response.code, 302)
- self.assertTrue(response.headers['Location'].endswith(
- '/oauth1/server/authorize?oauth_token=zxcv'))
- # the cookie is base64('zxcv')|base64('1234')
- self.assertTrue(
- '_oauth_request_token="enhjdg==|MTIzNA=="' in response.headers['Set-Cookie'],
- response.headers['Set-Cookie'])
-
- def test_oauth10a_get_user_legacy(self):
- with ignore_deprecation():
- response = self.fetch(
- '/legacy/oauth10a/client/login?oauth_token=zxcv',
- headers={'Cookie': '_oauth_request_token=enhjdg==|MTIzNA=='})
- response.rethrow()
- parsed = json_decode(response.body)
- self.assertEqual(parsed['email'], 'foo@example.com')
- self.assertEqual(parsed['access_token'], dict(key='uiop', secret='5678'))
-
- def test_oauth10a_redirect(self):
- response = self.fetch('/oauth10a/client/login', follow_redirects=False)
- self.assertEqual(response.code, 302)
- self.assertTrue(response.headers['Location'].endswith(
- '/oauth1/server/authorize?oauth_token=zxcv'))
- # the cookie is base64('zxcv')|base64('1234')
- self.assertTrue(
- '_oauth_request_token="enhjdg==|MTIzNA=="' in response.headers['Set-Cookie'],
- response.headers['Set-Cookie'])
-
- @unittest.skipIf(mock is None, 'mock package not present')
- def test_oauth10a_redirect_error(self):
- with mock.patch.object(OAuth1ServerRequestTokenHandler, 'get') as get:
- get.side_effect = Exception("boom")
- with ExpectLog(app_log, "Uncaught exception"):
- response = self.fetch('/oauth10a/client/login', follow_redirects=False)
- self.assertEqual(response.code, 500)
-
- def test_oauth10a_get_user(self):
- response = self.fetch(
- '/oauth10a/client/login?oauth_token=zxcv',
- headers={'Cookie': '_oauth_request_token=enhjdg==|MTIzNA=='})
- response.rethrow()
- parsed = json_decode(response.body)
- self.assertEqual(parsed['email'], 'foo@example.com')
- self.assertEqual(parsed['access_token'], dict(key='uiop', secret='5678'))
-
- def test_oauth10a_request_parameters(self):
- response = self.fetch('/oauth10a/client/request_params')
- response.rethrow()
- parsed = json_decode(response.body)
- self.assertEqual(parsed['oauth_consumer_key'], 'asdf')
- self.assertEqual(parsed['oauth_token'], 'uiop')
- self.assertTrue('oauth_nonce' in parsed)
- self.assertTrue('oauth_signature' in parsed)
-
- def test_oauth10a_get_user_coroutine_exception(self):
- response = self.fetch(
- '/oauth10a/client/login_coroutine?oauth_token=zxcv&fail_in_get_user=true',
- headers={'Cookie': '_oauth_request_token=enhjdg==|MTIzNA=='})
- self.assertEqual(response.code, 503)
-
- def test_oauth2_redirect(self):
- response = self.fetch('/oauth2/client/login', follow_redirects=False)
- self.assertEqual(response.code, 302)
- self.assertTrue('/oauth2/server/authorize?' in response.headers['Location'])
-
- def test_facebook_login(self):
- response = self.fetch('/facebook/client/login', follow_redirects=False)
- self.assertEqual(response.code, 302)
- self.assertTrue('/facebook/server/authorize?' in response.headers['Location'])
- response = self.fetch('/facebook/client/login?code=1234', follow_redirects=False)
- self.assertEqual(response.code, 200)
- user = json_decode(response.body)
- self.assertEqual(user['access_token'], 'asdf')
- self.assertEqual(user['session_expires'], '3600')
-
- def base_twitter_redirect(self, url):
- # Same as test_oauth10a_redirect
- response = self.fetch(url, follow_redirects=False)
- self.assertEqual(response.code, 302)
- self.assertTrue(response.headers['Location'].endswith(
- '/oauth1/server/authorize?oauth_token=zxcv'))
- # the cookie is base64('zxcv')|base64('1234')
- self.assertTrue(
- '_oauth_request_token="enhjdg==|MTIzNA=="' in response.headers['Set-Cookie'],
- response.headers['Set-Cookie'])
-
- def test_twitter_redirect_legacy(self):
- self.base_twitter_redirect('/legacy/twitter/client/login')
-
- def test_twitter_redirect(self):
- self.base_twitter_redirect('/twitter/client/login')
-
- def test_twitter_redirect_gen_engine(self):
- self.base_twitter_redirect('/twitter/client/login_gen_engine')
-
- def test_twitter_redirect_gen_coroutine(self):
- self.base_twitter_redirect('/twitter/client/login_gen_coroutine')
-
- def test_twitter_authenticate_redirect(self):
- response = self.fetch('/twitter/client/authenticate', follow_redirects=False)
- self.assertEqual(response.code, 302)
- self.assertTrue(response.headers['Location'].endswith(
- '/twitter/server/authenticate?oauth_token=zxcv'), response.headers['Location'])
- # the cookie is base64('zxcv')|base64('1234')
- self.assertTrue(
- '_oauth_request_token="enhjdg==|MTIzNA=="' in response.headers['Set-Cookie'],
- response.headers['Set-Cookie'])
-
- def test_twitter_get_user(self):
- response = self.fetch(
- '/twitter/client/login?oauth_token=zxcv',
- headers={'Cookie': '_oauth_request_token=enhjdg==|MTIzNA=='})
- response.rethrow()
- parsed = json_decode(response.body)
- self.assertEqual(parsed,
- {u'access_token': {u'key': u'hjkl',
- u'screen_name': u'foo',
- u'secret': u'vbnm'},
- u'name': u'Foo',
- u'screen_name': u'foo',
- u'username': u'foo'})
-
- def test_twitter_show_user_legacy(self):
- response = self.fetch('/legacy/twitter/client/show_user?name=somebody')
- response.rethrow()
- self.assertEqual(json_decode(response.body),
- {'name': 'Somebody', 'screen_name': 'somebody'})
-
- def test_twitter_show_user_error_legacy(self):
- with ExpectLog(gen_log, 'Error response HTTP 500'):
- response = self.fetch('/legacy/twitter/client/show_user?name=error')
- self.assertEqual(response.code, 500)
- self.assertEqual(response.body, b'error from twitter request')
-
- def test_twitter_show_user(self):
- response = self.fetch('/twitter/client/show_user?name=somebody')
- response.rethrow()
- self.assertEqual(json_decode(response.body),
- {'name': 'Somebody', 'screen_name': 'somebody'})
-
- def test_twitter_show_user_error(self):
- response = self.fetch('/twitter/client/show_user?name=error')
- self.assertEqual(response.code, 500)
- self.assertEqual(response.body, b'error from twitter request')
-
-
-class GoogleLoginHandler(RequestHandler, GoogleOAuth2Mixin):
- def initialize(self, test):
- self.test = test
- self._OAUTH_REDIRECT_URI = test.get_url('/client/login')
- self._OAUTH_AUTHORIZE_URL = test.get_url('/google/oauth2/authorize')
- self._OAUTH_ACCESS_TOKEN_URL = test.get_url('/google/oauth2/token')
-
- @gen.coroutine
- def get(self):
- code = self.get_argument('code', None)
- if code is not None:
- # retrieve authenticate google user
- access = yield self.get_authenticated_user(self._OAUTH_REDIRECT_URI,
- code)
- user = yield self.oauth2_request(
- self.test.get_url("/google/oauth2/userinfo"),
- access_token=access["access_token"])
- # return the user and access token as json
- user["access_token"] = access["access_token"]
- self.write(user)
- else:
- yield self.authorize_redirect(
- redirect_uri=self._OAUTH_REDIRECT_URI,
- client_id=self.settings['google_oauth']['key'],
- client_secret=self.settings['google_oauth']['secret'],
- scope=['profile', 'email'],
- response_type='code',
- extra_params={'prompt': 'select_account'})
-
-
-class GoogleOAuth2AuthorizeHandler(RequestHandler):
- def get(self):
- # issue a fake auth code and redirect to redirect_uri
- code = 'fake-authorization-code'
- self.redirect(url_concat(self.get_argument('redirect_uri'),
- dict(code=code)))
-
-
-class GoogleOAuth2TokenHandler(RequestHandler):
- def post(self):
- assert self.get_argument('code') == 'fake-authorization-code'
- # issue a fake token
- self.finish({
- 'access_token': 'fake-access-token',
- 'expires_in': 'never-expires'
- })
-
-
-class GoogleOAuth2UserinfoHandler(RequestHandler):
- def get(self):
- assert self.get_argument('access_token') == 'fake-access-token'
- # return a fake user
- self.finish({
- 'name': 'Foo',
- 'email': 'foo@example.com'
- })
-
-
-class GoogleOAuth2Test(AsyncHTTPTestCase):
- def get_app(self):
- return Application(
- [
- # test endpoints
- ('/client/login', GoogleLoginHandler, dict(test=self)),
-
- # simulated google authorization server endpoints
- ('/google/oauth2/authorize', GoogleOAuth2AuthorizeHandler),
- ('/google/oauth2/token', GoogleOAuth2TokenHandler),
- ('/google/oauth2/userinfo', GoogleOAuth2UserinfoHandler),
- ],
- google_oauth={
- "key": 'fake_google_client_id',
- "secret": 'fake_google_client_secret'
- })
-
- def test_google_login(self):
- response = self.fetch('/client/login')
- self.assertDictEqual({
- u'name': u'Foo',
- u'email': u'foo@example.com',
- u'access_token': u'fake-access-token',
- }, json_decode(response.body))
diff --git a/lib/tornado/test/autoreload_test.py b/lib/tornado/test/autoreload_test.py
deleted file mode 100755
index 5cbdc2ee..00000000
--- a/lib/tornado/test/autoreload_test.py
+++ /dev/null
@@ -1,114 +0,0 @@
-from __future__ import absolute_import, division, print_function
-import os
-import shutil
-import subprocess
-from subprocess import Popen
-import sys
-from tempfile import mkdtemp
-import time
-
-from tornado.test.util import unittest
-
-
-class AutoreloadTest(unittest.TestCase):
-
- def test_reload_module(self):
- main = """\
-import os
-import sys
-
-from tornado import autoreload
-
-# This import will fail if path is not set up correctly
-import testapp
-
-print('Starting')
-if 'TESTAPP_STARTED' not in os.environ:
- os.environ['TESTAPP_STARTED'] = '1'
- sys.stdout.flush()
- autoreload._reload()
-"""
-
- # Create temporary test application
- path = mkdtemp()
- self.addCleanup(shutil.rmtree, path)
- os.mkdir(os.path.join(path, 'testapp'))
- open(os.path.join(path, 'testapp/__init__.py'), 'w').close()
- with open(os.path.join(path, 'testapp/__main__.py'), 'w') as f:
- f.write(main)
-
- # Make sure the tornado module under test is available to the test
- # application
- pythonpath = os.getcwd()
- if 'PYTHONPATH' in os.environ:
- pythonpath += os.pathsep + os.environ['PYTHONPATH']
-
- p = Popen(
- [sys.executable, '-m', 'testapp'], stdout=subprocess.PIPE,
- cwd=path, env=dict(os.environ, PYTHONPATH=pythonpath),
- universal_newlines=True)
- out = p.communicate()[0]
- self.assertEqual(out, 'Starting\nStarting\n')
-
- def test_reload_wrapper_preservation(self):
- # This test verifies that when `python -m tornado.autoreload`
- # is used on an application that also has an internal
- # autoreload, the reload wrapper is preserved on restart.
- main = """\
-import os
-import sys
-
-# This import will fail if path is not set up correctly
-import testapp
-
-if 'tornado.autoreload' not in sys.modules:
- raise Exception('started without autoreload wrapper')
-
-import tornado.autoreload
-
-print('Starting')
-sys.stdout.flush()
-if 'TESTAPP_STARTED' not in os.environ:
- os.environ['TESTAPP_STARTED'] = '1'
- # Simulate an internal autoreload (one not caused
- # by the wrapper).
- tornado.autoreload._reload()
-else:
- # Exit directly so autoreload doesn't catch it.
- os._exit(0)
-"""
-
- # Create temporary test application
- path = mkdtemp()
- os.mkdir(os.path.join(path, 'testapp'))
- self.addCleanup(shutil.rmtree, path)
- init_file = os.path.join(path, 'testapp', '__init__.py')
- open(init_file, 'w').close()
- main_file = os.path.join(path, 'testapp', '__main__.py')
- with open(main_file, 'w') as f:
- f.write(main)
-
- # Make sure the tornado module under test is available to the test
- # application
- pythonpath = os.getcwd()
- if 'PYTHONPATH' in os.environ:
- pythonpath += os.pathsep + os.environ['PYTHONPATH']
-
- autoreload_proc = Popen(
- [sys.executable, '-m', 'tornado.autoreload', '-m', 'testapp'],
- stdout=subprocess.PIPE, cwd=path,
- env=dict(os.environ, PYTHONPATH=pythonpath),
- universal_newlines=True)
-
- # This timeout needs to be fairly generous for pypy due to jit
- # warmup costs.
- for i in range(40):
- if autoreload_proc.poll() is not None:
- break
- time.sleep(0.1)
- else:
- autoreload_proc.kill()
- raise Exception("subprocess failed to terminate")
-
- out = autoreload_proc.communicate()[0]
- self.assertEqual(out, 'Starting\n' * 2)
diff --git a/lib/tornado/test/concurrent_test.py b/lib/tornado/test/concurrent_test.py
deleted file mode 100755
index 737c13e1..00000000
--- a/lib/tornado/test/concurrent_test.py
+++ /dev/null
@@ -1,496 +0,0 @@
-#
-# Copyright 2012 Facebook
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import absolute_import, division, print_function
-
-import gc
-import logging
-import re
-import socket
-import sys
-import traceback
-import warnings
-
-from tornado.concurrent import (Future, return_future, ReturnValueIgnoredError,
- run_on_executor, future_set_result_unless_cancelled)
-from tornado.escape import utf8, to_unicode
-from tornado import gen
-from tornado.ioloop import IOLoop
-from tornado.iostream import IOStream
-from tornado.log import app_log
-from tornado import stack_context
-from tornado.tcpserver import TCPServer
-from tornado.testing import AsyncTestCase, ExpectLog, bind_unused_port, gen_test
-from tornado.test.util import unittest, skipBefore35, exec_test, ignore_deprecation
-
-
-try:
- from concurrent import futures
-except ImportError:
- futures = None
-
-
-class MiscFutureTest(AsyncTestCase):
-
- def test_future_set_result_unless_cancelled(self):
- fut = Future()
- future_set_result_unless_cancelled(fut, 42)
- self.assertEqual(fut.result(), 42)
- self.assertFalse(fut.cancelled())
-
- fut = Future()
- fut.cancel()
- is_cancelled = fut.cancelled()
- future_set_result_unless_cancelled(fut, 42)
- self.assertEqual(fut.cancelled(), is_cancelled)
- if not is_cancelled:
- self.assertEqual(fut.result(), 42)
-
-
-class ReturnFutureTest(AsyncTestCase):
- with ignore_deprecation():
- @return_future
- def sync_future(self, callback):
- callback(42)
-
- @return_future
- def async_future(self, callback):
- self.io_loop.add_callback(callback, 42)
-
- @return_future
- def immediate_failure(self, callback):
- 1 / 0
-
- @return_future
- def delayed_failure(self, callback):
- self.io_loop.add_callback(lambda: 1 / 0)
-
- @return_future
- def return_value(self, callback):
- # Note that the result of both running the callback and returning
- # a value (or raising an exception) is unspecified; with current
- # implementations the last event prior to callback resolution wins.
- return 42
-
- @return_future
- def no_result_future(self, callback):
- callback()
-
- def test_immediate_failure(self):
- with self.assertRaises(ZeroDivisionError):
- # The caller sees the error just like a normal function.
- self.immediate_failure(callback=self.stop)
- # The callback is not run because the function failed synchronously.
- self.io_loop.add_timeout(self.io_loop.time() + 0.05, self.stop)
- result = self.wait()
- self.assertIs(result, None)
-
- def test_return_value(self):
- with self.assertRaises(ReturnValueIgnoredError):
- self.return_value(callback=self.stop)
-
- def test_callback_kw(self):
- with ignore_deprecation():
- future = self.sync_future(callback=self.stop)
- result = self.wait()
- self.assertEqual(result, 42)
- self.assertEqual(future.result(), 42)
-
- def test_callback_positional(self):
- # When the callback is passed in positionally, future_wrap shouldn't
- # add another callback in the kwargs.
- with ignore_deprecation():
- future = self.sync_future(self.stop)
- result = self.wait()
- self.assertEqual(result, 42)
- self.assertEqual(future.result(), 42)
-
- def test_no_callback(self):
- future = self.sync_future()
- self.assertEqual(future.result(), 42)
-
- def test_none_callback_kw(self):
- # explicitly pass None as callback
- future = self.sync_future(callback=None)
- self.assertEqual(future.result(), 42)
-
- def test_none_callback_pos(self):
- future = self.sync_future(None)
- self.assertEqual(future.result(), 42)
-
- def test_async_future(self):
- future = self.async_future()
- self.assertFalse(future.done())
- self.io_loop.add_future(future, self.stop)
- future2 = self.wait()
- self.assertIs(future, future2)
- self.assertEqual(future.result(), 42)
-
- @gen_test
- def test_async_future_gen(self):
- result = yield self.async_future()
- self.assertEqual(result, 42)
-
- def test_delayed_failure(self):
- future = self.delayed_failure()
- with ignore_deprecation():
- self.io_loop.add_future(future, self.stop)
- future2 = self.wait()
- self.assertIs(future, future2)
- with self.assertRaises(ZeroDivisionError):
- future.result()
-
- def test_kw_only_callback(self):
- with ignore_deprecation():
- @return_future
- def f(**kwargs):
- kwargs['callback'](42)
- future = f()
- self.assertEqual(future.result(), 42)
-
- def test_error_in_callback(self):
- with ignore_deprecation():
- self.sync_future(callback=lambda future: 1 / 0)
- # The exception gets caught by our StackContext and will be re-raised
- # when we wait.
- self.assertRaises(ZeroDivisionError, self.wait)
-
- def test_no_result_future(self):
- with ignore_deprecation():
- future = self.no_result_future(self.stop)
- result = self.wait()
- self.assertIs(result, None)
- # result of this future is undefined, but not an error
- future.result()
-
- def test_no_result_future_callback(self):
- with ignore_deprecation():
- future = self.no_result_future(callback=lambda: self.stop())
- result = self.wait()
- self.assertIs(result, None)
- future.result()
-
- @gen_test
- def test_future_traceback_legacy(self):
- with ignore_deprecation():
- @return_future
- @gen.engine
- def f(callback):
- yield gen.Task(self.io_loop.add_callback)
- try:
- 1 / 0
- except ZeroDivisionError:
- self.expected_frame = traceback.extract_tb(
- sys.exc_info()[2], limit=1)[0]
- raise
- try:
- yield f()
- self.fail("didn't get expected exception")
- except ZeroDivisionError:
- tb = traceback.extract_tb(sys.exc_info()[2])
- self.assertIn(self.expected_frame, tb)
-
- @gen_test
- def test_future_traceback(self):
- @gen.coroutine
- def f():
- yield gen.moment
- try:
- 1 / 0
- except ZeroDivisionError:
- self.expected_frame = traceback.extract_tb(
- sys.exc_info()[2], limit=1)[0]
- raise
- try:
- yield f()
- self.fail("didn't get expected exception")
- except ZeroDivisionError:
- tb = traceback.extract_tb(sys.exc_info()[2])
- self.assertIn(self.expected_frame, tb)
-
- @gen_test
- def test_uncaught_exception_log(self):
- if IOLoop.configured_class().__name__.endswith('AsyncIOLoop'):
- # Install an exception handler that mirrors our
- # non-asyncio logging behavior.
- def exc_handler(loop, context):
- app_log.error('%s: %s', context['message'],
- type(context.get('exception')))
- self.io_loop.asyncio_loop.set_exception_handler(exc_handler)
-
- @gen.coroutine
- def f():
- yield gen.moment
- 1 / 0
-
- g = f()
-
- with ExpectLog(app_log,
- "(?s)Future.* exception was never retrieved:"
- ".*ZeroDivisionError"):
- yield gen.moment
- yield gen.moment
- # For some reason, TwistedIOLoop and pypy3 need a third iteration
- # in order to drain references to the future
- yield gen.moment
- del g
- gc.collect() # for PyPy
-
-
-# The following series of classes demonstrate and test various styles
-# of use, with and without generators and futures.
-
-
-class CapServer(TCPServer):
- @gen.coroutine
- def handle_stream(self, stream, address):
- data = yield stream.read_until(b"\n")
- data = to_unicode(data)
- if data == data.upper():
- stream.write(b"error\talready capitalized\n")
- else:
- # data already has \n
- stream.write(utf8("ok\t%s" % data.upper()))
- stream.close()
-
-
-class CapError(Exception):
- pass
-
-
-class BaseCapClient(object):
- def __init__(self, port):
- self.port = port
-
- def process_response(self, data):
- status, message = re.match('(.*)\t(.*)\n', to_unicode(data)).groups()
- if status == 'ok':
- return message
- else:
- raise CapError(message)
-
-
-class ManualCapClient(BaseCapClient):
- def capitalize(self, request_data, callback=None):
- logging.debug("capitalize")
- self.request_data = request_data
- self.stream = IOStream(socket.socket())
- self.stream.connect(('127.0.0.1', self.port),
- callback=self.handle_connect)
- self.future = Future()
- if callback is not None:
- self.future.add_done_callback(
- stack_context.wrap(lambda future: callback(future.result())))
- return self.future
-
- def handle_connect(self):
- logging.debug("handle_connect")
- self.stream.write(utf8(self.request_data + "\n"))
- self.stream.read_until(b'\n', callback=self.handle_read)
-
- def handle_read(self, data):
- logging.debug("handle_read")
- self.stream.close()
- try:
- self.future.set_result(self.process_response(data))
- except CapError as e:
- self.future.set_exception(e)
-
-
-class DecoratorCapClient(BaseCapClient):
- with ignore_deprecation():
- @return_future
- def capitalize(self, request_data, callback):
- logging.debug("capitalize")
- self.request_data = request_data
- self.stream = IOStream(socket.socket())
- self.stream.connect(('127.0.0.1', self.port),
- callback=self.handle_connect)
- self.callback = callback
-
- def handle_connect(self):
- logging.debug("handle_connect")
- self.stream.write(utf8(self.request_data + "\n"))
- self.stream.read_until(b'\n', callback=self.handle_read)
-
- def handle_read(self, data):
- logging.debug("handle_read")
- self.stream.close()
- self.callback(self.process_response(data))
-
-
-class GeneratorCapClient(BaseCapClient):
- @gen.coroutine
- def capitalize(self, request_data):
- logging.debug('capitalize')
- stream = IOStream(socket.socket())
- logging.debug('connecting')
- yield stream.connect(('127.0.0.1', self.port))
- stream.write(utf8(request_data + '\n'))
- logging.debug('reading')
- data = yield stream.read_until(b'\n')
- logging.debug('returning')
- stream.close()
- raise gen.Return(self.process_response(data))
-
-
-class ClientTestMixin(object):
- def setUp(self):
- super(ClientTestMixin, self).setUp() # type: ignore
- self.server = CapServer()
- sock, port = bind_unused_port()
- self.server.add_sockets([sock])
- self.client = self.client_class(port=port)
-
- def tearDown(self):
- self.server.stop()
- super(ClientTestMixin, self).tearDown() # type: ignore
-
- def test_callback(self):
- with ignore_deprecation():
- self.client.capitalize("hello", callback=self.stop)
- result = self.wait()
- self.assertEqual(result, "HELLO")
-
- def test_callback_error(self):
- with ignore_deprecation():
- self.client.capitalize("HELLO", callback=self.stop)
- self.assertRaisesRegexp(CapError, "already capitalized", self.wait)
-
- def test_future(self):
- future = self.client.capitalize("hello")
- self.io_loop.add_future(future, self.stop)
- self.wait()
- self.assertEqual(future.result(), "HELLO")
-
- def test_future_error(self):
- future = self.client.capitalize("HELLO")
- self.io_loop.add_future(future, self.stop)
- self.wait()
- self.assertRaisesRegexp(CapError, "already capitalized", future.result)
-
- def test_generator(self):
- @gen.coroutine
- def f():
- result = yield self.client.capitalize("hello")
- self.assertEqual(result, "HELLO")
- self.io_loop.run_sync(f)
-
- def test_generator_error(self):
- @gen.coroutine
- def f():
- with self.assertRaisesRegexp(CapError, "already capitalized"):
- yield self.client.capitalize("HELLO")
- self.io_loop.run_sync(f)
-
-
-class ManualClientTest(ClientTestMixin, AsyncTestCase):
- client_class = ManualCapClient
-
- def setUp(self):
- self.warning_catcher = warnings.catch_warnings()
- self.warning_catcher.__enter__()
- warnings.simplefilter('ignore', DeprecationWarning)
- super(ManualClientTest, self).setUp()
-
- def tearDown(self):
- super(ManualClientTest, self).tearDown()
- self.warning_catcher.__exit__(None, None, None)
-
-
-class DecoratorClientTest(ClientTestMixin, AsyncTestCase):
- client_class = DecoratorCapClient
-
- def setUp(self):
- self.warning_catcher = warnings.catch_warnings()
- self.warning_catcher.__enter__()
- warnings.simplefilter('ignore', DeprecationWarning)
- super(DecoratorClientTest, self).setUp()
-
- def tearDown(self):
- super(DecoratorClientTest, self).tearDown()
- self.warning_catcher.__exit__(None, None, None)
-
-
-class GeneratorClientTest(ClientTestMixin, AsyncTestCase):
- client_class = GeneratorCapClient
-
-
-@unittest.skipIf(futures is None, "concurrent.futures module not present")
-class RunOnExecutorTest(AsyncTestCase):
- @gen_test
- def test_no_calling(self):
- class Object(object):
- def __init__(self):
- self.executor = futures.thread.ThreadPoolExecutor(1)
-
- @run_on_executor
- def f(self):
- return 42
-
- o = Object()
- answer = yield o.f()
- self.assertEqual(answer, 42)
-
- @gen_test
- def test_call_with_no_args(self):
- class Object(object):
- def __init__(self):
- self.executor = futures.thread.ThreadPoolExecutor(1)
-
- @run_on_executor()
- def f(self):
- return 42
-
- o = Object()
- answer = yield o.f()
- self.assertEqual(answer, 42)
-
- @gen_test
- def test_call_with_executor(self):
- class Object(object):
- def __init__(self):
- self.__executor = futures.thread.ThreadPoolExecutor(1)
-
- @run_on_executor(executor='_Object__executor')
- def f(self):
- return 42
-
- o = Object()
- answer = yield o.f()
- self.assertEqual(answer, 42)
-
- @skipBefore35
- @gen_test
- def test_async_await(self):
- class Object(object):
- def __init__(self):
- self.executor = futures.thread.ThreadPoolExecutor(1)
-
- @run_on_executor()
- def f(self):
- return 42
-
- o = Object()
- namespace = exec_test(globals(), locals(), """
- async def f():
- answer = await o.f()
- return answer
- """)
- result = yield namespace['f']()
- self.assertEqual(result, 42)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/lib/tornado/test/csv_translations/fr_FR.csv b/lib/tornado/test/csv_translations/fr_FR.csv
deleted file mode 100755
index 6321b6e7..00000000
--- a/lib/tornado/test/csv_translations/fr_FR.csv
+++ /dev/null
@@ -1 +0,0 @@
-"school","école"
diff --git a/lib/tornado/test/curl_httpclient_test.py b/lib/tornado/test/curl_httpclient_test.py
deleted file mode 100755
index 4230d4cd..00000000
--- a/lib/tornado/test/curl_httpclient_test.py
+++ /dev/null
@@ -1,153 +0,0 @@
-# coding: utf-8
-from __future__ import absolute_import, division, print_function
-
-from hashlib import md5
-
-from tornado.escape import utf8
-from tornado.httpclient import HTTPRequest, HTTPClientError
-from tornado.locks import Event
-from tornado.stack_context import ExceptionStackContext
-from tornado.testing import AsyncHTTPTestCase, gen_test
-from tornado.test import httpclient_test
-from tornado.test.util import unittest, ignore_deprecation
-from tornado.web import Application, RequestHandler
-
-
-try:
- import pycurl # type: ignore
-except ImportError:
- pycurl = None
-
-if pycurl is not None:
- from tornado.curl_httpclient import CurlAsyncHTTPClient
-
-
-@unittest.skipIf(pycurl is None, "pycurl module not present")
-class CurlHTTPClientCommonTestCase(httpclient_test.HTTPClientCommonTestCase):
- def get_http_client(self):
- client = CurlAsyncHTTPClient(defaults=dict(allow_ipv6=False))
- # make sure AsyncHTTPClient magic doesn't give us the wrong class
- self.assertTrue(isinstance(client, CurlAsyncHTTPClient))
- return client
-
-
-class DigestAuthHandler(RequestHandler):
- def initialize(self, username, password):
- self.username = username
- self.password = password
-
- def get(self):
- realm = 'test'
- opaque = 'asdf'
- # Real implementations would use a random nonce.
- nonce = "1234"
-
- auth_header = self.request.headers.get('Authorization', None)
- if auth_header is not None:
- auth_mode, params = auth_header.split(' ', 1)
- assert auth_mode == 'Digest'
- param_dict = {}
- for pair in params.split(','):
- k, v = pair.strip().split('=', 1)
- if v[0] == '"' and v[-1] == '"':
- v = v[1:-1]
- param_dict[k] = v
- assert param_dict['realm'] == realm
- assert param_dict['opaque'] == opaque
- assert param_dict['nonce'] == nonce
- assert param_dict['username'] == self.username
- assert param_dict['uri'] == self.request.path
- h1 = md5(utf8('%s:%s:%s' % (self.username, realm, self.password))).hexdigest()
- h2 = md5(utf8('%s:%s' % (self.request.method,
- self.request.path))).hexdigest()
- digest = md5(utf8('%s:%s:%s' % (h1, nonce, h2))).hexdigest()
- if digest == param_dict['response']:
- self.write('ok')
- else:
- self.write('fail')
- else:
- self.set_status(401)
- self.set_header('WWW-Authenticate',
- 'Digest realm="%s", nonce="%s", opaque="%s"' %
- (realm, nonce, opaque))
-
-
-class CustomReasonHandler(RequestHandler):
- def get(self):
- self.set_status(200, "Custom reason")
-
-
-class CustomFailReasonHandler(RequestHandler):
- def get(self):
- self.set_status(400, "Custom reason")
-
-
-@unittest.skipIf(pycurl is None, "pycurl module not present")
-class CurlHTTPClientTestCase(AsyncHTTPTestCase):
- def setUp(self):
- super(CurlHTTPClientTestCase, self).setUp()
- self.http_client = self.create_client()
-
- def get_app(self):
- return Application([
- ('/digest', DigestAuthHandler, {'username': 'foo', 'password': 'bar'}),
- ('/digest_non_ascii', DigestAuthHandler, {'username': 'foo', 'password': 'barユ£'}),
- ('/custom_reason', CustomReasonHandler),
- ('/custom_fail_reason', CustomFailReasonHandler),
- ])
-
- def create_client(self, **kwargs):
- return CurlAsyncHTTPClient(force_instance=True,
- defaults=dict(allow_ipv6=False),
- **kwargs)
-
- @gen_test
- def test_prepare_curl_callback_stack_context(self):
- exc_info = []
- error_event = Event()
-
- def error_handler(typ, value, tb):
- exc_info.append((typ, value, tb))
- error_event.set()
- return True
-
- with ignore_deprecation():
- with ExceptionStackContext(error_handler):
- request = HTTPRequest(self.get_url('/custom_reason'),
- prepare_curl_callback=lambda curl: 1 / 0)
- yield [error_event.wait(), self.http_client.fetch(request)]
- self.assertEqual(1, len(exc_info))
- self.assertIs(exc_info[0][0], ZeroDivisionError)
-
- def test_digest_auth(self):
- response = self.fetch('/digest', auth_mode='digest',
- auth_username='foo', auth_password='bar')
- self.assertEqual(response.body, b'ok')
-
- def test_custom_reason(self):
- response = self.fetch('/custom_reason')
- self.assertEqual(response.reason, "Custom reason")
-
- def test_fail_custom_reason(self):
- response = self.fetch('/custom_fail_reason')
- self.assertEqual(str(response.error), "HTTP 400: Custom reason")
-
- def test_failed_setup(self):
- self.http_client = self.create_client(max_clients=1)
- for i in range(5):
- with ignore_deprecation():
- response = self.fetch(u'/ユニコード')
- self.assertIsNot(response.error, None)
-
- with self.assertRaises((UnicodeEncodeError, HTTPClientError)):
- # This raises UnicodeDecodeError on py3 and
- # HTTPClientError(404) on py2. The main motivation of
- # this test is to ensure that the UnicodeEncodeError
- # during the setup phase doesn't lead the request to
- # be dropped on the floor.
- response = self.fetch(u'/ユニコード', raise_error=True)
-
- def test_digest_auth_non_ascii(self):
- response = self.fetch('/digest_non_ascii', auth_mode='digest',
- auth_username='foo', auth_password='barユ£')
- self.assertEqual(response.body, b'ok')
diff --git a/lib/tornado/test/escape_test.py b/lib/tornado/test/escape_test.py
deleted file mode 100755
index f2f2902a..00000000
--- a/lib/tornado/test/escape_test.py
+++ /dev/null
@@ -1,250 +0,0 @@
-from __future__ import absolute_import, division, print_function
-
-import tornado.escape
-from tornado.escape import (
- utf8, xhtml_escape, xhtml_unescape, url_escape, url_unescape,
- to_unicode, json_decode, json_encode, squeeze, recursive_unicode,
-)
-from tornado.util import unicode_type
-from tornado.test.util import unittest
-
-linkify_tests = [
- # (input, linkify_kwargs, expected_output)
-
- ("hello http://world.com/!", {},
- u'hello http://world.com/!'),
-
- ("hello http://world.com/with?param=true&stuff=yes", {},
- u'hello http://world.com/with?param=true&stuff=yes'), # noqa: E501
-
- # an opened paren followed by many chars killed Gruber's regex
- ("http://url.com/w(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", {},
- u'http://url.com/w(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'), # noqa: E501
-
- # as did too many dots at the end
- ("http://url.com/withmany.......................................", {},
- u'http://url.com/withmany.......................................'), # noqa: E501
-
- ("http://url.com/withmany((((((((((((((((((((((((((((((((((a)", {},
- u'http://url.com/withmany((((((((((((((((((((((((((((((((((a)'), # noqa: E501
-
- # some examples from http://daringfireball.net/2009/11/liberal_regex_for_matching_urls
- # plus a fex extras (such as multiple parentheses).
- ("http://foo.com/blah_blah", {},
- u'http://foo.com/blah_blah'),
-
- ("http://foo.com/blah_blah/", {},
- u'http://foo.com/blah_blah/'),
-
- ("(Something like http://foo.com/blah_blah)", {},
- u'(Something like http://foo.com/blah_blah)'),
-
- ("http://foo.com/blah_blah_(wikipedia)", {},
- u'http://foo.com/blah_blah_(wikipedia)'),
-
- ("http://foo.com/blah_(blah)_(wikipedia)_blah", {},
- u'http://foo.com/blah_(blah)_(wikipedia)_blah'), # noqa: E501
-
- ("(Something like http://foo.com/blah_blah_(wikipedia))", {},
- u'(Something like http://foo.com/blah_blah_(wikipedia))'), # noqa: E501
-
- ("http://foo.com/blah_blah.", {},
- u'http://foo.com/blah_blah.'),
-
- ("http://foo.com/blah_blah/.", {},
- u'http://foo.com/blah_blah/.'),
-
- ("", {},
- u'<http://foo.com/blah_blah>'),
-
- (" ", {},
- u'<http://foo.com/blah_blah/>'),
-
- ("http://foo.com/blah_blah,", {},
- u'http://foo.com/blah_blah,'),
-
- ("http://www.example.com/wpstyle/?p=364.", {},
- u'http://www.example.com/wpstyle/?p=364.'),
-
- ("rdar://1234",
- {"permitted_protocols": ["http", "rdar"]},
- u'rdar://1234'),
-
- ("rdar:/1234",
- {"permitted_protocols": ["rdar"]},
- u'rdar:/1234'),
-
- ("http://userid:password@example.com:8080", {},
- u'http://userid:password@example.com:8080'), # noqa: E501
-
- ("http://userid@example.com", {},
- u'http://userid@example.com'),
-
- ("http://userid@example.com:8080", {},
- u'http://userid@example.com:8080'),
-
- ("http://userid:password@example.com", {},
- u'http://userid:password@example.com'),
-
- ("message://%3c330e7f8409726r6a4ba78dkf1fd71420c1bf6ff@mail.gmail.com%3e",
- {"permitted_protocols": ["http", "message"]},
- u''
- u'message://%3c330e7f8409726r6a4ba78dkf1fd71420c1bf6ff@mail.gmail.com%3e'),
-
- (u"http://\u27a1.ws/\u4a39", {},
- u'http://\u27a1.ws/\u4a39'),
-
- ("http://example.com ", {},
- u'<tag>http://example.com</tag>'),
-
- ("Just a www.example.com link.", {},
- u'Just a www.example.com link.'),
-
- ("Just a www.example.com link.",
- {"require_protocol": True},
- u'Just a www.example.com link.'),
-
- ("A http://reallylong.com/link/that/exceedsthelenglimit.html",
- {"require_protocol": True, "shorten": True},
- u'A http://reallylong.com/link...'), # noqa: E501
-
- ("A http://reallylongdomainnamethatwillbetoolong.com/hi!",
- {"shorten": True},
- u'A http://reallylongdomainnametha...!'), # noqa: E501
-
- ("A file:///passwords.txt and http://web.com link", {},
- u'A file:///passwords.txt and http://web.com link'),
-
- ("A file:///passwords.txt and http://web.com link",
- {"permitted_protocols": ["file"]},
- u'A file:///passwords.txt and http://web.com link'),
-
- ("www.external-link.com",
- {"extra_params": 'rel="nofollow" class="external"'},
- u'www.external-link.com'), # noqa: E501
-
- ("www.external-link.com and www.internal-link.com/blogs extra",
- {"extra_params": lambda href: 'class="internal"' if href.startswith("http://www.internal-link.com") else 'rel="nofollow" class="external"'}, # noqa: E501
- u'www.external-link.com' # noqa: E501
- u' and www.internal-link.com/blogs extra'), # noqa: E501
-
- ("www.external-link.com",
- {"extra_params": lambda href: ' rel="nofollow" class="external" '},
- u'www.external-link.com'), # noqa: E501
-]
-
-
-class EscapeTestCase(unittest.TestCase):
- def test_linkify(self):
- for text, kwargs, html in linkify_tests:
- linked = tornado.escape.linkify(text, **kwargs)
- self.assertEqual(linked, html)
-
- def test_xhtml_escape(self):
- tests = [
- ("", "<foo>"),
- (u"", u"<foo>"),
- (b"", b"<foo>"),
-
- ("<>&\"'", "<>&"'"),
- ("&", "&"),
-
- (u"<\u00e9>", u"<\u00e9>"),
- (b"<\xc3\xa9>", b"<\xc3\xa9>"),
- ]
- for unescaped, escaped in tests:
- self.assertEqual(utf8(xhtml_escape(unescaped)), utf8(escaped))
- self.assertEqual(utf8(unescaped), utf8(xhtml_unescape(escaped)))
-
- def test_xhtml_unescape_numeric(self):
- tests = [
- ('foo bar', 'foo bar'),
- ('foo bar', 'foo bar'),
- ('foo bar', 'foo bar'),
- ('foo઼bar', u'foo\u0abcbar'),
- ('fooyz;bar', 'fooyz;bar'), # invalid encoding
- ('foobar', 'foobar'), # invalid encoding
- ('foobar', 'foobar'), # invalid encoding
- ]
- for escaped, unescaped in tests:
- self.assertEqual(unescaped, xhtml_unescape(escaped))
-
- def test_url_escape_unicode(self):
- tests = [
- # byte strings are passed through as-is
- (u'\u00e9'.encode('utf8'), '%C3%A9'),
- (u'\u00e9'.encode('latin1'), '%E9'),
-
- # unicode strings become utf8
- (u'\u00e9', '%C3%A9'),
- ]
- for unescaped, escaped in tests:
- self.assertEqual(url_escape(unescaped), escaped)
-
- def test_url_unescape_unicode(self):
- tests = [
- ('%C3%A9', u'\u00e9', 'utf8'),
- ('%C3%A9', u'\u00c3\u00a9', 'latin1'),
- ('%C3%A9', utf8(u'\u00e9'), None),
- ]
- for escaped, unescaped, encoding in tests:
- # input strings to url_unescape should only contain ascii
- # characters, but make sure the function accepts both byte
- # and unicode strings.
- self.assertEqual(url_unescape(to_unicode(escaped), encoding), unescaped)
- self.assertEqual(url_unescape(utf8(escaped), encoding), unescaped)
-
- def test_url_escape_quote_plus(self):
- unescaped = '+ #%'
- plus_escaped = '%2B+%23%25'
- escaped = '%2B%20%23%25'
- self.assertEqual(url_escape(unescaped), plus_escaped)
- self.assertEqual(url_escape(unescaped, plus=False), escaped)
- self.assertEqual(url_unescape(plus_escaped), unescaped)
- self.assertEqual(url_unescape(escaped, plus=False), unescaped)
- self.assertEqual(url_unescape(plus_escaped, encoding=None),
- utf8(unescaped))
- self.assertEqual(url_unescape(escaped, encoding=None, plus=False),
- utf8(unescaped))
-
- def test_escape_return_types(self):
- # On python2 the escape methods should generally return the same
- # type as their argument
- self.assertEqual(type(xhtml_escape("foo")), str)
- self.assertEqual(type(xhtml_escape(u"foo")), unicode_type)
-
- def test_json_decode(self):
- # json_decode accepts both bytes and unicode, but strings it returns
- # are always unicode.
- self.assertEqual(json_decode(b'"foo"'), u"foo")
- self.assertEqual(json_decode(u'"foo"'), u"foo")
-
- # Non-ascii bytes are interpreted as utf8
- self.assertEqual(json_decode(utf8(u'"\u00e9"')), u"\u00e9")
-
- def test_json_encode(self):
- # json deals with strings, not bytes. On python 2 byte strings will
- # convert automatically if they are utf8; on python 3 byte strings
- # are not allowed.
- self.assertEqual(json_decode(json_encode(u"\u00e9")), u"\u00e9")
- if bytes is str:
- self.assertEqual(json_decode(json_encode(utf8(u"\u00e9"))), u"\u00e9")
- self.assertRaises(UnicodeDecodeError, json_encode, b"\xe9")
-
- def test_squeeze(self):
- self.assertEqual(squeeze(u'sequences of whitespace chars'),
- u'sequences of whitespace chars')
-
- def test_recursive_unicode(self):
- tests = {
- 'dict': {b"foo": b"bar"},
- 'list': [b"foo", b"bar"],
- 'tuple': (b"foo", b"bar"),
- 'bytes': b"foo"
- }
- self.assertEqual(recursive_unicode(tests['dict']), {u"foo": u"bar"})
- self.assertEqual(recursive_unicode(tests['list']), [u"foo", u"bar"])
- self.assertEqual(recursive_unicode(tests['tuple']), (u"foo", u"bar"))
- self.assertEqual(recursive_unicode(tests['bytes']), u"foo")
diff --git a/lib/tornado/test/gen_test.py b/lib/tornado/test/gen_test.py
deleted file mode 100755
index 346968cf..00000000
--- a/lib/tornado/test/gen_test.py
+++ /dev/null
@@ -1,1862 +0,0 @@
-from __future__ import absolute_import, division, print_function
-
-import gc
-import contextlib
-import datetime
-import functools
-import platform
-import sys
-import textwrap
-import time
-import weakref
-import warnings
-
-from tornado.concurrent import return_future, Future
-from tornado.escape import url_escape
-from tornado.httpclient import AsyncHTTPClient
-from tornado.ioloop import IOLoop
-from tornado.log import app_log
-from tornado import stack_context
-from tornado.testing import AsyncHTTPTestCase, AsyncTestCase, ExpectLog, gen_test
-from tornado.test.util import unittest, skipOnTravis, skipBefore33, skipBefore35, skipNotCPython, exec_test, ignore_deprecation # noqa: E501
-from tornado.web import Application, RequestHandler, asynchronous, HTTPError
-
-from tornado import gen
-
-try:
- from concurrent import futures
-except ImportError:
- futures = None
-
-try:
- import asyncio
-except ImportError:
- asyncio = None
-
-
-class GenEngineTest(AsyncTestCase):
- def setUp(self):
- self.warning_catcher = warnings.catch_warnings()
- self.warning_catcher.__enter__()
- warnings.simplefilter('ignore', DeprecationWarning)
- super(GenEngineTest, self).setUp()
- self.named_contexts = []
-
- def tearDown(self):
- super(GenEngineTest, self).tearDown()
- self.warning_catcher.__exit__(None, None, None)
-
- def named_context(self, name):
- @contextlib.contextmanager
- def context():
- self.named_contexts.append(name)
- try:
- yield
- finally:
- self.assertEqual(self.named_contexts.pop(), name)
- return context
-
- def run_gen(self, f):
- f()
- return self.wait()
-
- def delay_callback(self, iterations, callback, arg):
- """Runs callback(arg) after a number of IOLoop iterations."""
- if iterations == 0:
- callback(arg)
- else:
- self.io_loop.add_callback(functools.partial(
- self.delay_callback, iterations - 1, callback, arg))
-
- with ignore_deprecation():
- @return_future
- def async_future(self, result, callback):
- self.io_loop.add_callback(callback, result)
-
- @gen.coroutine
- def async_exception(self, e):
- yield gen.moment
- raise e
-
- def test_no_yield(self):
- @gen.engine
- def f():
- self.stop()
- self.run_gen(f)
-
- def test_inline_cb(self):
- @gen.engine
- def f():
- (yield gen.Callback("k1"))()
- res = yield gen.Wait("k1")
- self.assertTrue(res is None)
- self.stop()
- self.run_gen(f)
-
- def test_ioloop_cb(self):
- @gen.engine
- def f():
- self.io_loop.add_callback((yield gen.Callback("k1")))
- yield gen.Wait("k1")
- self.stop()
- self.run_gen(f)
-
- def test_exception_phase1(self):
- @gen.engine
- def f():
- 1 / 0
- self.assertRaises(ZeroDivisionError, self.run_gen, f)
-
- def test_exception_phase2(self):
- @gen.engine
- def f():
- self.io_loop.add_callback((yield gen.Callback("k1")))
- yield gen.Wait("k1")
- 1 / 0
- self.assertRaises(ZeroDivisionError, self.run_gen, f)
-
- def test_exception_in_task_phase1(self):
- def fail_task(callback):
- 1 / 0
-
- @gen.engine
- def f():
- try:
- yield gen.Task(fail_task)
- raise Exception("did not get expected exception")
- except ZeroDivisionError:
- self.stop()
- self.run_gen(f)
-
- def test_exception_in_task_phase2(self):
- # This is the case that requires the use of stack_context in gen.engine
- def fail_task(callback):
- self.io_loop.add_callback(lambda: 1 / 0)
-
- @gen.engine
- def f():
- try:
- yield gen.Task(fail_task)
- raise Exception("did not get expected exception")
- except ZeroDivisionError:
- self.stop()
- self.run_gen(f)
-
- def test_with_arg(self):
- @gen.engine
- def f():
- (yield gen.Callback("k1"))(42)
- res = yield gen.Wait("k1")
- self.assertEqual(42, res)
- self.stop()
- self.run_gen(f)
-
- def test_with_arg_tuple(self):
- @gen.engine
- def f():
- (yield gen.Callback((1, 2)))((3, 4))
- res = yield gen.Wait((1, 2))
- self.assertEqual((3, 4), res)
- self.stop()
- self.run_gen(f)
-
- def test_key_reuse(self):
- @gen.engine
- def f():
- yield gen.Callback("k1")
- yield gen.Callback("k1")
- self.stop()
- self.assertRaises(gen.KeyReuseError, self.run_gen, f)
-
- def test_key_reuse_tuple(self):
- @gen.engine
- def f():
- yield gen.Callback((1, 2))
- yield gen.Callback((1, 2))
- self.stop()
- self.assertRaises(gen.KeyReuseError, self.run_gen, f)
-
- def test_key_mismatch(self):
- @gen.engine
- def f():
- yield gen.Callback("k1")
- yield gen.Wait("k2")
- self.stop()
- self.assertRaises(gen.UnknownKeyError, self.run_gen, f)
-
- def test_key_mismatch_tuple(self):
- @gen.engine
- def f():
- yield gen.Callback((1, 2))
- yield gen.Wait((2, 3))
- self.stop()
- self.assertRaises(gen.UnknownKeyError, self.run_gen, f)
-
- def test_leaked_callback(self):
- @gen.engine
- def f():
- yield gen.Callback("k1")
- self.stop()
- self.assertRaises(gen.LeakedCallbackError, self.run_gen, f)
-
- def test_leaked_callback_tuple(self):
- @gen.engine
- def f():
- yield gen.Callback((1, 2))
- self.stop()
- self.assertRaises(gen.LeakedCallbackError, self.run_gen, f)
-
- def test_parallel_callback(self):
- @gen.engine
- def f():
- for k in range(3):
- self.io_loop.add_callback((yield gen.Callback(k)))
- yield gen.Wait(1)
- self.io_loop.add_callback((yield gen.Callback(3)))
- yield gen.Wait(0)
- yield gen.Wait(3)
- yield gen.Wait(2)
- self.stop()
- self.run_gen(f)
-
- def test_bogus_yield(self):
- @gen.engine
- def f():
- yield 42
- self.assertRaises(gen.BadYieldError, self.run_gen, f)
-
- def test_bogus_yield_tuple(self):
- @gen.engine
- def f():
- yield (1, 2)
- self.assertRaises(gen.BadYieldError, self.run_gen, f)
-
- def test_reuse(self):
- @gen.engine
- def f():
- self.io_loop.add_callback((yield gen.Callback(0)))
- yield gen.Wait(0)
- self.stop()
- self.run_gen(f)
- self.run_gen(f)
-
- def test_task(self):
- @gen.engine
- def f():
- yield gen.Task(self.io_loop.add_callback)
- self.stop()
- self.run_gen(f)
-
- def test_wait_all(self):
- @gen.engine
- def f():
- (yield gen.Callback("k1"))("v1")
- (yield gen.Callback("k2"))("v2")
- results = yield gen.WaitAll(["k1", "k2"])
- self.assertEqual(results, ["v1", "v2"])
- self.stop()
- self.run_gen(f)
-
- def test_exception_in_yield(self):
- @gen.engine
- def f():
- try:
- yield gen.Wait("k1")
- raise Exception("did not get expected exception")
- except gen.UnknownKeyError:
- pass
- self.stop()
- self.run_gen(f)
-
- def test_resume_after_exception_in_yield(self):
- @gen.engine
- def f():
- try:
- yield gen.Wait("k1")
- raise Exception("did not get expected exception")
- except gen.UnknownKeyError:
- pass
- (yield gen.Callback("k2"))("v2")
- self.assertEqual((yield gen.Wait("k2")), "v2")
- self.stop()
- self.run_gen(f)
-
- def test_orphaned_callback(self):
- @gen.engine
- def f():
- self.orphaned_callback = yield gen.Callback(1)
- try:
- self.run_gen(f)
- raise Exception("did not get expected exception")
- except gen.LeakedCallbackError:
- pass
- self.orphaned_callback()
-
- def test_none(self):
- @gen.engine
- def f():
- yield None
- self.stop()
- self.run_gen(f)
-
- def test_multi(self):
- @gen.engine
- def f():
- (yield gen.Callback("k1"))("v1")
- (yield gen.Callback("k2"))("v2")
- results = yield [gen.Wait("k1"), gen.Wait("k2")]
- self.assertEqual(results, ["v1", "v2"])
- self.stop()
- self.run_gen(f)
-
- def test_multi_dict(self):
- @gen.engine
- def f():
- (yield gen.Callback("k1"))("v1")
- (yield gen.Callback("k2"))("v2")
- results = yield dict(foo=gen.Wait("k1"), bar=gen.Wait("k2"))
- self.assertEqual(results, dict(foo="v1", bar="v2"))
- self.stop()
- self.run_gen(f)
-
- # The following tests explicitly run with both gen.Multi
- # and gen.multi_future (Task returns a Future, so it can be used
- # with either).
- def test_multi_yieldpoint_delayed(self):
- @gen.engine
- def f():
- # callbacks run at different times
- responses = yield gen.Multi([
- gen.Task(self.delay_callback, 3, arg="v1"),
- gen.Task(self.delay_callback, 1, arg="v2"),
- ])
- self.assertEqual(responses, ["v1", "v2"])
- self.stop()
- self.run_gen(f)
-
- def test_multi_yieldpoint_dict_delayed(self):
- @gen.engine
- def f():
- # callbacks run at different times
- responses = yield gen.Multi(dict(
- foo=gen.Task(self.delay_callback, 3, arg="v1"),
- bar=gen.Task(self.delay_callback, 1, arg="v2"),
- ))
- self.assertEqual(responses, dict(foo="v1", bar="v2"))
- self.stop()
- self.run_gen(f)
-
- def test_multi_future_delayed(self):
- @gen.engine
- def f():
- # callbacks run at different times
- responses = yield gen.multi_future([
- gen.Task(self.delay_callback, 3, arg="v1"),
- gen.Task(self.delay_callback, 1, arg="v2"),
- ])
- self.assertEqual(responses, ["v1", "v2"])
- self.stop()
- self.run_gen(f)
-
- def test_multi_future_dict_delayed(self):
- @gen.engine
- def f():
- # callbacks run at different times
- responses = yield gen.multi_future(dict(
- foo=gen.Task(self.delay_callback, 3, arg="v1"),
- bar=gen.Task(self.delay_callback, 1, arg="v2"),
- ))
- self.assertEqual(responses, dict(foo="v1", bar="v2"))
- self.stop()
- self.run_gen(f)
-
- @skipOnTravis
- @gen_test
- def test_multi_performance(self):
- # Yielding a list used to have quadratic performance; make
- # sure a large list stays reasonable. On my laptop a list of
- # 2000 used to take 1.8s, now it takes 0.12.
- start = time.time()
- yield [gen.Task(self.io_loop.add_callback) for i in range(2000)]
- end = time.time()
- self.assertLess(end - start, 1.0)
-
- @gen_test
- def test_multi_empty(self):
- # Empty lists or dicts should return the same type.
- x = yield []
- self.assertTrue(isinstance(x, list))
- y = yield {}
- self.assertTrue(isinstance(y, dict))
-
- @gen_test
- def test_multi_mixed_types(self):
- # A YieldPoint (Wait) and Future (Task) can be combined
- # (and use the YieldPoint codepath)
- (yield gen.Callback("k1"))("v1")
- responses = yield [gen.Wait("k1"),
- gen.Task(self.delay_callback, 3, arg="v2")]
- self.assertEqual(responses, ["v1", "v2"])
-
- @gen_test
- def test_future(self):
- result = yield self.async_future(1)
- self.assertEqual(result, 1)
-
- @gen_test
- def test_multi_future(self):
- results = yield [self.async_future(1), self.async_future(2)]
- self.assertEqual(results, [1, 2])
-
- @gen_test
- def test_multi_future_duplicate(self):
- f = self.async_future(2)
- results = yield [self.async_future(1), f, self.async_future(3), f]
- self.assertEqual(results, [1, 2, 3, 2])
-
- @gen_test
- def test_multi_dict_future(self):
- results = yield dict(foo=self.async_future(1), bar=self.async_future(2))
- self.assertEqual(results, dict(foo=1, bar=2))
-
- @gen_test
- def test_multi_exceptions(self):
- with ExpectLog(app_log, "Multiple exceptions in yield list"):
- with self.assertRaises(RuntimeError) as cm:
- yield gen.Multi([self.async_exception(RuntimeError("error 1")),
- self.async_exception(RuntimeError("error 2"))])
- self.assertEqual(str(cm.exception), "error 1")
-
- # With only one exception, no error is logged.
- with self.assertRaises(RuntimeError):
- yield gen.Multi([self.async_exception(RuntimeError("error 1")),
- self.async_future(2)])
-
- # Exception logging may be explicitly quieted.
- with self.assertRaises(RuntimeError):
- yield gen.Multi([self.async_exception(RuntimeError("error 1")),
- self.async_exception(RuntimeError("error 2"))],
- quiet_exceptions=RuntimeError)
-
- @gen_test
- def test_multi_future_exceptions(self):
- with ExpectLog(app_log, "Multiple exceptions in yield list"):
- with self.assertRaises(RuntimeError) as cm:
- yield [self.async_exception(RuntimeError("error 1")),
- self.async_exception(RuntimeError("error 2"))]
- self.assertEqual(str(cm.exception), "error 1")
-
- # With only one exception, no error is logged.
- with self.assertRaises(RuntimeError):
- yield [self.async_exception(RuntimeError("error 1")),
- self.async_future(2)]
-
- # Exception logging may be explicitly quieted.
- with self.assertRaises(RuntimeError):
- yield gen.multi_future(
- [self.async_exception(RuntimeError("error 1")),
- self.async_exception(RuntimeError("error 2"))],
- quiet_exceptions=RuntimeError)
-
- def test_arguments(self):
- @gen.engine
- def f():
- (yield gen.Callback("noargs"))()
- self.assertEqual((yield gen.Wait("noargs")), None)
- (yield gen.Callback("1arg"))(42)
- self.assertEqual((yield gen.Wait("1arg")), 42)
-
- (yield gen.Callback("kwargs"))(value=42)
- result = yield gen.Wait("kwargs")
- self.assertTrue(isinstance(result, gen.Arguments))
- self.assertEqual(((), dict(value=42)), result)
- self.assertEqual(dict(value=42), result.kwargs)
-
- (yield gen.Callback("2args"))(42, 43)
- result = yield gen.Wait("2args")
- self.assertTrue(isinstance(result, gen.Arguments))
- self.assertEqual(((42, 43), {}), result)
- self.assertEqual((42, 43), result.args)
-
- def task_func(callback):
- callback(None, error="foo")
- result = yield gen.Task(task_func)
- self.assertTrue(isinstance(result, gen.Arguments))
- self.assertEqual(((None,), dict(error="foo")), result)
-
- self.stop()
- self.run_gen(f)
-
- def test_stack_context_leak(self):
- # regression test: repeated invocations of a gen-based
- # function should not result in accumulated stack_contexts
- def _stack_depth():
- head = stack_context._state.contexts[1]
- length = 0
-
- while head is not None:
- length += 1
- head = head.old_contexts[1]
-
- return length
-
- @gen.engine
- def inner(callback):
- yield gen.Task(self.io_loop.add_callback)
- callback()
-
- @gen.engine
- def outer():
- for i in range(10):
- yield gen.Task(inner)
-
- stack_increase = _stack_depth() - initial_stack_depth
- self.assertTrue(stack_increase <= 2)
- self.stop()
- initial_stack_depth = _stack_depth()
- self.run_gen(outer)
-
- def test_stack_context_leak_exception(self):
- # same as previous, but with a function that exits with an exception
- @gen.engine
- def inner(callback):
- yield gen.Task(self.io_loop.add_callback)
- 1 / 0
-
- @gen.engine
- def outer():
- for i in range(10):
- try:
- yield gen.Task(inner)
- except ZeroDivisionError:
- pass
- stack_increase = len(stack_context._state.contexts) - initial_stack_depth
- self.assertTrue(stack_increase <= 2)
- self.stop()
- initial_stack_depth = len(stack_context._state.contexts)
- self.run_gen(outer)
-
- def function_with_stack_context(self, callback):
- # Technically this function should stack_context.wrap its callback
- # upon entry. However, it is very common for this step to be
- # omitted.
- def step2():
- self.assertEqual(self.named_contexts, ['a'])
- self.io_loop.add_callback(callback)
-
- with stack_context.StackContext(self.named_context('a')):
- self.io_loop.add_callback(step2)
-
- @gen_test
- def test_wait_transfer_stack_context(self):
- # Wait should not pick up contexts from where callback was invoked,
- # even if that function improperly fails to wrap its callback.
- cb = yield gen.Callback('k1')
- self.function_with_stack_context(cb)
- self.assertEqual(self.named_contexts, [])
- yield gen.Wait('k1')
- self.assertEqual(self.named_contexts, [])
-
- @gen_test
- def test_task_transfer_stack_context(self):
- yield gen.Task(self.function_with_stack_context)
- self.assertEqual(self.named_contexts, [])
-
- def test_raise_after_stop(self):
- # This pattern will be used in the following tests so make sure
- # the exception propagates as expected.
- @gen.engine
- def f():
- self.stop()
- 1 / 0
-
- with self.assertRaises(ZeroDivisionError):
- self.run_gen(f)
-
- def test_sync_raise_return(self):
- # gen.Return is allowed in @gen.engine, but it may not be used
- # to return a value.
- @gen.engine
- def f():
- self.stop(42)
- raise gen.Return()
-
- result = self.run_gen(f)
- self.assertEqual(result, 42)
-
- def test_async_raise_return(self):
- @gen.engine
- def f():
- yield gen.Task(self.io_loop.add_callback)
- self.stop(42)
- raise gen.Return()
-
- result = self.run_gen(f)
- self.assertEqual(result, 42)
-
- def test_sync_raise_return_value(self):
- @gen.engine
- def f():
- raise gen.Return(42)
-
- with self.assertRaises(gen.ReturnValueIgnoredError):
- self.run_gen(f)
-
- def test_sync_raise_return_value_tuple(self):
- @gen.engine
- def f():
- raise gen.Return((1, 2))
-
- with self.assertRaises(gen.ReturnValueIgnoredError):
- self.run_gen(f)
-
- def test_async_raise_return_value(self):
- @gen.engine
- def f():
- yield gen.Task(self.io_loop.add_callback)
- raise gen.Return(42)
-
- with self.assertRaises(gen.ReturnValueIgnoredError):
- self.run_gen(f)
-
- def test_async_raise_return_value_tuple(self):
- @gen.engine
- def f():
- yield gen.Task(self.io_loop.add_callback)
- raise gen.Return((1, 2))
-
- with self.assertRaises(gen.ReturnValueIgnoredError):
- self.run_gen(f)
-
- def test_return_value(self):
- # It is an error to apply @gen.engine to a function that returns
- # a value.
- @gen.engine
- def f():
- return 42
-
- with self.assertRaises(gen.ReturnValueIgnoredError):
- self.run_gen(f)
-
- def test_return_value_tuple(self):
- # It is an error to apply @gen.engine to a function that returns
- # a value.
- @gen.engine
- def f():
- return (1, 2)
-
- with self.assertRaises(gen.ReturnValueIgnoredError):
- self.run_gen(f)
-
- @skipNotCPython
- def test_task_refcounting(self):
- # On CPython, tasks and their arguments should be released immediately
- # without waiting for garbage collection.
- @gen.engine
- def f():
- class Foo(object):
- pass
- arg = Foo()
- self.arg_ref = weakref.ref(arg)
- task = gen.Task(self.io_loop.add_callback, arg=arg)
- self.task_ref = weakref.ref(task)
- yield task
- self.stop()
-
- self.run_gen(f)
- self.assertIs(self.arg_ref(), None)
- self.assertIs(self.task_ref(), None)
-
-
-# GenBasicTest duplicates the non-deprecated portions of GenEngineTest
-# with gen.coroutine to ensure we don't lose coverage when gen.engine
-# goes away.
-class GenBasicTest(AsyncTestCase):
- @gen.coroutine
- def delay(self, iterations, arg):
- """Returns arg after a number of IOLoop iterations."""
- for i in range(iterations):
- yield gen.moment
- raise gen.Return(arg)
-
- with ignore_deprecation():
- @return_future
- def async_future(self, result, callback):
- self.io_loop.add_callback(callback, result)
-
- @gen.coroutine
- def async_exception(self, e):
- yield gen.moment
- raise e
-
- @gen.coroutine
- def add_one_async(self, x):
- yield gen.moment
- raise gen.Return(x + 1)
-
- def test_no_yield(self):
- @gen.coroutine
- def f():
- pass
- self.io_loop.run_sync(f)
-
- def test_exception_phase1(self):
- @gen.coroutine
- def f():
- 1 / 0
- self.assertRaises(ZeroDivisionError, self.io_loop.run_sync, f)
-
- def test_exception_phase2(self):
- @gen.coroutine
- def f():
- yield gen.moment
- 1 / 0
- self.assertRaises(ZeroDivisionError, self.io_loop.run_sync, f)
-
- def test_bogus_yield(self):
- @gen.coroutine
- def f():
- yield 42
- self.assertRaises(gen.BadYieldError, self.io_loop.run_sync, f)
-
- def test_bogus_yield_tuple(self):
- @gen.coroutine
- def f():
- yield (1, 2)
- self.assertRaises(gen.BadYieldError, self.io_loop.run_sync, f)
-
- def test_reuse(self):
- @gen.coroutine
- def f():
- yield gen.moment
- self.io_loop.run_sync(f)
- self.io_loop.run_sync(f)
-
- def test_none(self):
- @gen.coroutine
- def f():
- yield None
- self.io_loop.run_sync(f)
-
- def test_multi(self):
- @gen.coroutine
- def f():
- results = yield [self.add_one_async(1), self.add_one_async(2)]
- self.assertEqual(results, [2, 3])
- self.io_loop.run_sync(f)
-
- def test_multi_dict(self):
- @gen.coroutine
- def f():
- results = yield dict(foo=self.add_one_async(1), bar=self.add_one_async(2))
- self.assertEqual(results, dict(foo=2, bar=3))
- self.io_loop.run_sync(f)
-
- def test_multi_delayed(self):
- @gen.coroutine
- def f():
- # callbacks run at different times
- responses = yield gen.multi_future([
- self.delay(3, "v1"),
- self.delay(1, "v2"),
- ])
- self.assertEqual(responses, ["v1", "v2"])
- self.io_loop.run_sync(f)
-
- def test_multi_dict_delayed(self):
- @gen.coroutine
- def f():
- # callbacks run at different times
- responses = yield gen.multi_future(dict(
- foo=self.delay(3, "v1"),
- bar=self.delay(1, "v2"),
- ))
- self.assertEqual(responses, dict(foo="v1", bar="v2"))
- self.io_loop.run_sync(f)
-
- @skipOnTravis
- @gen_test
- def test_multi_performance(self):
- # Yielding a list used to have quadratic performance; make
- # sure a large list stays reasonable. On my laptop a list of
- # 2000 used to take 1.8s, now it takes 0.12.
- start = time.time()
- yield [gen.moment for i in range(2000)]
- end = time.time()
- self.assertLess(end - start, 1.0)
-
- @gen_test
- def test_multi_empty(self):
- # Empty lists or dicts should return the same type.
- x = yield []
- self.assertTrue(isinstance(x, list))
- y = yield {}
- self.assertTrue(isinstance(y, dict))
-
- @gen_test
- def test_future(self):
- result = yield self.async_future(1)
- self.assertEqual(result, 1)
-
- @gen_test
- def test_multi_future(self):
- results = yield [self.async_future(1), self.async_future(2)]
- self.assertEqual(results, [1, 2])
-
- @gen_test
- def test_multi_future_duplicate(self):
- f = self.async_future(2)
- results = yield [self.async_future(1), f, self.async_future(3), f]
- self.assertEqual(results, [1, 2, 3, 2])
-
- @gen_test
- def test_multi_dict_future(self):
- results = yield dict(foo=self.async_future(1), bar=self.async_future(2))
- self.assertEqual(results, dict(foo=1, bar=2))
-
- @gen_test
- def test_multi_exceptions(self):
- with ExpectLog(app_log, "Multiple exceptions in yield list"):
- with self.assertRaises(RuntimeError) as cm:
- yield gen.Multi([self.async_exception(RuntimeError("error 1")),
- self.async_exception(RuntimeError("error 2"))])
- self.assertEqual(str(cm.exception), "error 1")
-
- # With only one exception, no error is logged.
- with self.assertRaises(RuntimeError):
- yield gen.Multi([self.async_exception(RuntimeError("error 1")),
- self.async_future(2)])
-
- # Exception logging may be explicitly quieted.
- with self.assertRaises(RuntimeError):
- yield gen.Multi([self.async_exception(RuntimeError("error 1")),
- self.async_exception(RuntimeError("error 2"))],
- quiet_exceptions=RuntimeError)
-
- @gen_test
- def test_multi_future_exceptions(self):
- with ExpectLog(app_log, "Multiple exceptions in yield list"):
- with self.assertRaises(RuntimeError) as cm:
- yield [self.async_exception(RuntimeError("error 1")),
- self.async_exception(RuntimeError("error 2"))]
- self.assertEqual(str(cm.exception), "error 1")
-
- # With only one exception, no error is logged.
- with self.assertRaises(RuntimeError):
- yield [self.async_exception(RuntimeError("error 1")),
- self.async_future(2)]
-
- # Exception logging may be explicitly quieted.
- with self.assertRaises(RuntimeError):
- yield gen.multi_future(
- [self.async_exception(RuntimeError("error 1")),
- self.async_exception(RuntimeError("error 2"))],
- quiet_exceptions=RuntimeError)
-
- def test_sync_raise_return(self):
- @gen.coroutine
- def f():
- raise gen.Return()
-
- self.io_loop.run_sync(f)
-
- def test_async_raise_return(self):
- @gen.coroutine
- def f():
- yield gen.moment
- raise gen.Return()
-
- self.io_loop.run_sync(f)
-
- def test_sync_raise_return_value(self):
- @gen.coroutine
- def f():
- raise gen.Return(42)
-
- self.assertEqual(42, self.io_loop.run_sync(f))
-
- def test_sync_raise_return_value_tuple(self):
- @gen.coroutine
- def f():
- raise gen.Return((1, 2))
-
- self.assertEqual((1, 2), self.io_loop.run_sync(f))
-
- def test_async_raise_return_value(self):
- @gen.coroutine
- def f():
- yield gen.moment
- raise gen.Return(42)
-
- self.assertEqual(42, self.io_loop.run_sync(f))
-
- def test_async_raise_return_value_tuple(self):
- @gen.coroutine
- def f():
- yield gen.moment
- raise gen.Return((1, 2))
-
- self.assertEqual((1, 2), self.io_loop.run_sync(f))
-
-
-class GenCoroutineTest(AsyncTestCase):
- def setUp(self):
- # Stray StopIteration exceptions can lead to tests exiting prematurely,
- # so we need explicit checks here to make sure the tests run all
- # the way through.
- self.finished = False
- super(GenCoroutineTest, self).setUp()
-
- def tearDown(self):
- super(GenCoroutineTest, self).tearDown()
- assert self.finished
-
- def test_attributes(self):
- self.finished = True
-
- def f():
- yield gen.moment
-
- coro = gen.coroutine(f)
- self.assertEqual(coro.__name__, f.__name__)
- self.assertEqual(coro.__module__, f.__module__)
- self.assertIs(coro.__wrapped__, f)
-
- def test_is_coroutine_function(self):
- self.finished = True
-
- def f():
- yield gen.moment
-
- coro = gen.coroutine(f)
- self.assertFalse(gen.is_coroutine_function(f))
- self.assertTrue(gen.is_coroutine_function(coro))
- self.assertFalse(gen.is_coroutine_function(coro()))
-
- @gen_test
- def test_sync_gen_return(self):
- @gen.coroutine
- def f():
- raise gen.Return(42)
- result = yield f()
- self.assertEqual(result, 42)
- self.finished = True
-
- @gen_test
- def test_async_gen_return(self):
- @gen.coroutine
- def f():
- yield gen.moment
- raise gen.Return(42)
- result = yield f()
- self.assertEqual(result, 42)
- self.finished = True
-
- @gen_test
- def test_sync_return(self):
- @gen.coroutine
- def f():
- return 42
- result = yield f()
- self.assertEqual(result, 42)
- self.finished = True
-
- @skipBefore33
- @gen_test
- def test_async_return(self):
- namespace = exec_test(globals(), locals(), """
- @gen.coroutine
- def f():
- yield gen.moment
- return 42
- """)
- result = yield namespace['f']()
- self.assertEqual(result, 42)
- self.finished = True
-
- @skipBefore33
- @gen_test
- def test_async_early_return(self):
- # A yield statement exists but is not executed, which means
- # this function "returns" via an exception. This exception
- # doesn't happen before the exception handling is set up.
- namespace = exec_test(globals(), locals(), """
- @gen.coroutine
- def f():
- if True:
- return 42
- yield gen.Task(self.io_loop.add_callback)
- """)
- result = yield namespace['f']()
- self.assertEqual(result, 42)
- self.finished = True
-
- @skipBefore35
- @gen_test
- def test_async_await(self):
- @gen.coroutine
- def f1():
- yield gen.moment
- raise gen.Return(42)
-
- # This test verifies that an async function can await a
- # yield-based gen.coroutine, and that a gen.coroutine
- # (the test method itself) can yield an async function.
- namespace = exec_test(globals(), locals(), """
- async def f2():
- result = await f1()
- return result
- """)
- result = yield namespace['f2']()
- self.assertEqual(result, 42)
- self.finished = True
-
- @skipBefore35
- @gen_test
- def test_asyncio_sleep_zero(self):
- # asyncio.sleep(0) turns into a special case (equivalent to
- # `yield None`)
- namespace = exec_test(globals(), locals(), """
- async def f():
- import asyncio
- await asyncio.sleep(0)
- return 42
- """)
- result = yield namespace['f']()
- self.assertEqual(result, 42)
- self.finished = True
-
- @skipBefore35
- @gen_test
- def test_async_await_mixed_multi_native_future(self):
- @gen.coroutine
- def f1():
- yield gen.moment
-
- namespace = exec_test(globals(), locals(), """
- async def f2():
- await f1()
- return 42
- """)
-
- @gen.coroutine
- def f3():
- yield gen.moment
- raise gen.Return(43)
-
- results = yield [namespace['f2'](), f3()]
- self.assertEqual(results, [42, 43])
- self.finished = True
-
- @skipBefore35
- @gen_test
- def test_async_await_mixed_multi_native_yieldpoint(self):
- namespace = exec_test(globals(), locals(), """
- async def f1():
- await gen.Task(self.io_loop.add_callback)
- return 42
- """)
-
- @gen.coroutine
- def f2():
- yield gen.Task(self.io_loop.add_callback)
- raise gen.Return(43)
-
- with ignore_deprecation():
- f2(callback=(yield gen.Callback('cb')))
- results = yield [namespace['f1'](), gen.Wait('cb')]
- self.assertEqual(results, [42, 43])
- self.finished = True
-
- @skipBefore35
- @gen_test
- def test_async_with_timeout(self):
- namespace = exec_test(globals(), locals(), """
- async def f1():
- return 42
- """)
-
- result = yield gen.with_timeout(datetime.timedelta(hours=1),
- namespace['f1']())
- self.assertEqual(result, 42)
- self.finished = True
-
- @gen_test
- def test_sync_return_no_value(self):
- @gen.coroutine
- def f():
- return
- result = yield f()
- self.assertEqual(result, None)
- self.finished = True
-
- @gen_test
- def test_async_return_no_value(self):
- # Without a return value we don't need python 3.3.
- @gen.coroutine
- def f():
- yield gen.moment
- return
- result = yield f()
- self.assertEqual(result, None)
- self.finished = True
-
- @gen_test
- def test_sync_raise(self):
- @gen.coroutine
- def f():
- 1 / 0
- # The exception is raised when the future is yielded
- # (or equivalently when its result method is called),
- # not when the function itself is called).
- future = f()
- with self.assertRaises(ZeroDivisionError):
- yield future
- self.finished = True
-
- @gen_test
- def test_async_raise(self):
- @gen.coroutine
- def f():
- yield gen.moment
- 1 / 0
- future = f()
- with self.assertRaises(ZeroDivisionError):
- yield future
- self.finished = True
-
- @gen_test
- def test_pass_callback(self):
- with ignore_deprecation():
- @gen.coroutine
- def f():
- raise gen.Return(42)
- result = yield gen.Task(f)
- self.assertEqual(result, 42)
- self.finished = True
-
- @gen_test
- def test_replace_yieldpoint_exception(self):
- # Test exception handling: a coroutine can catch one exception
- # raised by a yield point and raise a different one.
- @gen.coroutine
- def f1():
- 1 / 0
-
- @gen.coroutine
- def f2():
- try:
- yield f1()
- except ZeroDivisionError:
- raise KeyError()
-
- future = f2()
- with self.assertRaises(KeyError):
- yield future
- self.finished = True
-
- @gen_test
- def test_swallow_yieldpoint_exception(self):
- # Test exception handling: a coroutine can catch an exception
- # raised by a yield point and not raise a different one.
- @gen.coroutine
- def f1():
- 1 / 0
-
- @gen.coroutine
- def f2():
- try:
- yield f1()
- except ZeroDivisionError:
- raise gen.Return(42)
-
- result = yield f2()
- self.assertEqual(result, 42)
- self.finished = True
-
- @gen_test
- def test_replace_context_exception(self):
- with ignore_deprecation():
- # Test exception handling: exceptions thrown into the stack context
- # can be caught and replaced.
- # Note that this test and the following are for behavior that is
- # not really supported any more: coroutines no longer create a
- # stack context automatically; but one is created after the first
- # YieldPoint (i.e. not a Future).
- @gen.coroutine
- def f2():
- (yield gen.Callback(1))()
- yield gen.Wait(1)
- self.io_loop.add_callback(lambda: 1 / 0)
- try:
- yield gen.Task(self.io_loop.add_timeout,
- self.io_loop.time() + 10)
- except ZeroDivisionError:
- raise KeyError()
-
- future = f2()
- with self.assertRaises(KeyError):
- yield future
- self.finished = True
-
- @gen_test
- def test_swallow_context_exception(self):
- with ignore_deprecation():
- # Test exception handling: exceptions thrown into the stack context
- # can be caught and ignored.
- @gen.coroutine
- def f2():
- (yield gen.Callback(1))()
- yield gen.Wait(1)
- self.io_loop.add_callback(lambda: 1 / 0)
- try:
- yield gen.Task(self.io_loop.add_timeout,
- self.io_loop.time() + 10)
- except ZeroDivisionError:
- raise gen.Return(42)
-
- result = yield f2()
- self.assertEqual(result, 42)
- self.finished = True
-
- @gen_test
- def test_moment(self):
- calls = []
-
- @gen.coroutine
- def f(name, yieldable):
- for i in range(5):
- calls.append(name)
- yield yieldable
- # First, confirm the behavior without moment: each coroutine
- # monopolizes the event loop until it finishes.
- immediate = Future()
- immediate.set_result(None)
- yield [f('a', immediate), f('b', immediate)]
- self.assertEqual(''.join(calls), 'aaaaabbbbb')
-
- # With moment, they take turns.
- calls = []
- yield [f('a', gen.moment), f('b', gen.moment)]
- self.assertEqual(''.join(calls), 'ababababab')
- self.finished = True
-
- calls = []
- yield [f('a', gen.moment), f('b', immediate)]
- self.assertEqual(''.join(calls), 'abbbbbaaaa')
-
- @gen_test
- def test_sleep(self):
- yield gen.sleep(0.01)
- self.finished = True
-
- @skipBefore33
- @gen_test
- def test_py3_leak_exception_context(self):
- class LeakedException(Exception):
- pass
-
- @gen.coroutine
- def inner(iteration):
- raise LeakedException(iteration)
-
- try:
- yield inner(1)
- except LeakedException as e:
- self.assertEqual(str(e), "1")
- self.assertIsNone(e.__context__)
-
- try:
- yield inner(2)
- except LeakedException as e:
- self.assertEqual(str(e), "2")
- self.assertIsNone(e.__context__)
-
- self.finished = True
-
- @skipNotCPython
- @unittest.skipIf((3,) < sys.version_info < (3, 6),
- "asyncio.Future has reference cycles")
- def test_coroutine_refcounting(self):
- # On CPython, tasks and their arguments should be released immediately
- # without waiting for garbage collection.
- @gen.coroutine
- def inner():
- class Foo(object):
- pass
- local_var = Foo()
- self.local_ref = weakref.ref(local_var)
- yield gen.coroutine(lambda: None)()
- raise ValueError('Some error')
-
- @gen.coroutine
- def inner2():
- try:
- yield inner()
- except ValueError:
- pass
-
- self.io_loop.run_sync(inner2, timeout=3)
-
- self.assertIs(self.local_ref(), None)
- self.finished = True
-
- @unittest.skipIf(sys.version_info < (3,),
- "test only relevant with asyncio Futures")
- def test_asyncio_future_debug_info(self):
- self.finished = True
- # Enable debug mode
- asyncio_loop = asyncio.get_event_loop()
- self.addCleanup(asyncio_loop.set_debug, asyncio_loop.get_debug())
- asyncio_loop.set_debug(True)
-
- def f():
- yield gen.moment
-
- coro = gen.coroutine(f)()
- self.assertIsInstance(coro, asyncio.Future)
- # We expect the coroutine repr() to show the place where
- # it was instantiated
- expected = ("created at %s:%d"
- % (__file__, f.__code__.co_firstlineno + 3))
- actual = repr(coro)
- self.assertIn(expected, actual)
-
- @unittest.skipIf(asyncio is None, "asyncio module not present")
- @gen_test
- def test_asyncio_gather(self):
- # This demonstrates that tornado coroutines can be understood
- # by asyncio (This failed prior to Tornado 5.0).
- @gen.coroutine
- def f():
- yield gen.moment
- raise gen.Return(1)
-
- ret = yield asyncio.gather(f(), f())
- self.assertEqual(ret, [1, 1])
- self.finished = True
-
-
-class GenSequenceHandler(RequestHandler):
- with ignore_deprecation():
- @asynchronous
- @gen.engine
- def get(self):
- # The outer ignore_deprecation applies at definition time.
- # We need another for serving time.
- with ignore_deprecation():
- self.io_loop = self.request.connection.stream.io_loop
- self.io_loop.add_callback((yield gen.Callback("k1")))
- yield gen.Wait("k1")
- self.write("1")
- self.io_loop.add_callback((yield gen.Callback("k2")))
- yield gen.Wait("k2")
- self.write("2")
- # reuse an old key
- self.io_loop.add_callback((yield gen.Callback("k1")))
- yield gen.Wait("k1")
- self.finish("3")
-
-
-class GenCoroutineSequenceHandler(RequestHandler):
- @gen.coroutine
- def get(self):
- yield gen.moment
- self.write("1")
- yield gen.moment
- self.write("2")
- yield gen.moment
- self.finish("3")
-
-
-class GenCoroutineUnfinishedSequenceHandler(RequestHandler):
- @gen.coroutine
- def get(self):
- yield gen.moment
- self.write("1")
- yield gen.moment
- self.write("2")
- yield gen.moment
- # just write, don't finish
- self.write("3")
-
-
-class GenTaskHandler(RequestHandler):
- @gen.coroutine
- def get(self):
- client = AsyncHTTPClient()
- with ignore_deprecation():
- response = yield gen.Task(client.fetch, self.get_argument('url'))
- response.rethrow()
- self.finish(b"got response: " + response.body)
-
-
-class GenExceptionHandler(RequestHandler):
- with ignore_deprecation():
- @asynchronous
- @gen.engine
- def get(self):
- # This test depends on the order of the two decorators.
- io_loop = self.request.connection.stream.io_loop
- yield gen.Task(io_loop.add_callback)
- raise Exception("oops")
-
-
-class GenCoroutineExceptionHandler(RequestHandler):
- @gen.coroutine
- def get(self):
- # This test depends on the order of the two decorators.
- io_loop = self.request.connection.stream.io_loop
- yield gen.Task(io_loop.add_callback)
- raise Exception("oops")
-
-
-class GenYieldExceptionHandler(RequestHandler):
- @gen.coroutine
- def get(self):
- io_loop = self.request.connection.stream.io_loop
- # Test the interaction of the two stack_contexts.
- with ignore_deprecation():
- def fail_task(callback):
- io_loop.add_callback(lambda: 1 / 0)
- try:
- yield gen.Task(fail_task)
- raise Exception("did not get expected exception")
- except ZeroDivisionError:
- self.finish('ok')
-
-
-# "Undecorated" here refers to the absence of @asynchronous.
-class UndecoratedCoroutinesHandler(RequestHandler):
- @gen.coroutine
- def prepare(self):
- self.chunks = []
- yield gen.moment
- self.chunks.append('1')
-
- @gen.coroutine
- def get(self):
- self.chunks.append('2')
- yield gen.moment
- self.chunks.append('3')
- yield gen.moment
- self.write(''.join(self.chunks))
-
-
-class AsyncPrepareErrorHandler(RequestHandler):
- @gen.coroutine
- def prepare(self):
- yield gen.moment
- raise HTTPError(403)
-
- def get(self):
- self.finish('ok')
-
-
-class NativeCoroutineHandler(RequestHandler):
- if sys.version_info > (3, 5):
- exec(textwrap.dedent("""
- async def get(self):
- import asyncio
- await asyncio.sleep(0)
- self.write("ok")
- """))
-
-
-class GenWebTest(AsyncHTTPTestCase):
- def get_app(self):
- return Application([
- ('/sequence', GenSequenceHandler),
- ('/coroutine_sequence', GenCoroutineSequenceHandler),
- ('/coroutine_unfinished_sequence',
- GenCoroutineUnfinishedSequenceHandler),
- ('/task', GenTaskHandler),
- ('/exception', GenExceptionHandler),
- ('/coroutine_exception', GenCoroutineExceptionHandler),
- ('/yield_exception', GenYieldExceptionHandler),
- ('/undecorated_coroutine', UndecoratedCoroutinesHandler),
- ('/async_prepare_error', AsyncPrepareErrorHandler),
- ('/native_coroutine', NativeCoroutineHandler),
- ])
-
- def test_sequence_handler(self):
- response = self.fetch('/sequence')
- self.assertEqual(response.body, b"123")
-
- def test_coroutine_sequence_handler(self):
- response = self.fetch('/coroutine_sequence')
- self.assertEqual(response.body, b"123")
-
- def test_coroutine_unfinished_sequence_handler(self):
- response = self.fetch('/coroutine_unfinished_sequence')
- self.assertEqual(response.body, b"123")
-
- def test_task_handler(self):
- response = self.fetch('/task?url=%s' % url_escape(self.get_url('/sequence')))
- self.assertEqual(response.body, b"got response: 123")
-
- def test_exception_handler(self):
- # Make sure we get an error and not a timeout
- with ExpectLog(app_log, "Uncaught exception GET /exception"):
- response = self.fetch('/exception')
- self.assertEqual(500, response.code)
-
- def test_coroutine_exception_handler(self):
- # Make sure we get an error and not a timeout
- with ExpectLog(app_log, "Uncaught exception GET /coroutine_exception"):
- response = self.fetch('/coroutine_exception')
- self.assertEqual(500, response.code)
-
- def test_yield_exception_handler(self):
- response = self.fetch('/yield_exception')
- self.assertEqual(response.body, b'ok')
-
- def test_undecorated_coroutines(self):
- response = self.fetch('/undecorated_coroutine')
- self.assertEqual(response.body, b'123')
-
- def test_async_prepare_error_handler(self):
- response = self.fetch('/async_prepare_error')
- self.assertEqual(response.code, 403)
-
- @skipBefore35
- def test_native_coroutine_handler(self):
- response = self.fetch('/native_coroutine')
- self.assertEqual(response.code, 200)
- self.assertEqual(response.body, b'ok')
-
-
-class WithTimeoutTest(AsyncTestCase):
- @gen_test
- def test_timeout(self):
- with self.assertRaises(gen.TimeoutError):
- yield gen.with_timeout(datetime.timedelta(seconds=0.1),
- Future())
-
- @gen_test
- def test_completes_before_timeout(self):
- future = Future()
- self.io_loop.add_timeout(datetime.timedelta(seconds=0.1),
- lambda: future.set_result('asdf'))
- result = yield gen.with_timeout(datetime.timedelta(seconds=3600),
- future)
- self.assertEqual(result, 'asdf')
-
- @gen_test
- def test_fails_before_timeout(self):
- future = Future()
- self.io_loop.add_timeout(
- datetime.timedelta(seconds=0.1),
- lambda: future.set_exception(ZeroDivisionError()))
- with self.assertRaises(ZeroDivisionError):
- yield gen.with_timeout(datetime.timedelta(seconds=3600),
- future)
-
- @gen_test
- def test_already_resolved(self):
- future = Future()
- future.set_result('asdf')
- result = yield gen.with_timeout(datetime.timedelta(seconds=3600),
- future)
- self.assertEqual(result, 'asdf')
-
- @unittest.skipIf(futures is None, 'futures module not present')
- @gen_test
- def test_timeout_concurrent_future(self):
- # A concurrent future that does not resolve before the timeout.
- with futures.ThreadPoolExecutor(1) as executor:
- with self.assertRaises(gen.TimeoutError):
- yield gen.with_timeout(self.io_loop.time(),
- executor.submit(time.sleep, 0.1))
-
- @unittest.skipIf(futures is None, 'futures module not present')
- @gen_test
- def test_completed_concurrent_future(self):
- # A concurrent future that is resolved before we even submit it
- # to with_timeout.
- with futures.ThreadPoolExecutor(1) as executor:
- f = executor.submit(lambda: None)
- f.result() # wait for completion
- yield gen.with_timeout(datetime.timedelta(seconds=3600), f)
-
- @unittest.skipIf(futures is None, 'futures module not present')
- @gen_test
- def test_normal_concurrent_future(self):
- # A conccurrent future that resolves while waiting for the timeout.
- with futures.ThreadPoolExecutor(1) as executor:
- yield gen.with_timeout(datetime.timedelta(seconds=3600),
- executor.submit(lambda: time.sleep(0.01)))
-
-
-class WaitIteratorTest(AsyncTestCase):
- @gen_test
- def test_empty_iterator(self):
- g = gen.WaitIterator()
- self.assertTrue(g.done(), 'empty generator iterated')
-
- with self.assertRaises(ValueError):
- g = gen.WaitIterator(False, bar=False)
-
- self.assertEqual(g.current_index, None, "bad nil current index")
- self.assertEqual(g.current_future, None, "bad nil current future")
-
- @gen_test
- def test_already_done(self):
- f1 = Future()
- f2 = Future()
- f3 = Future()
- f1.set_result(24)
- f2.set_result(42)
- f3.set_result(84)
-
- g = gen.WaitIterator(f1, f2, f3)
- i = 0
- while not g.done():
- r = yield g.next()
- # Order is not guaranteed, but the current implementation
- # preserves ordering of already-done Futures.
- if i == 0:
- self.assertEqual(g.current_index, 0)
- self.assertIs(g.current_future, f1)
- self.assertEqual(r, 24)
- elif i == 1:
- self.assertEqual(g.current_index, 1)
- self.assertIs(g.current_future, f2)
- self.assertEqual(r, 42)
- elif i == 2:
- self.assertEqual(g.current_index, 2)
- self.assertIs(g.current_future, f3)
- self.assertEqual(r, 84)
- i += 1
-
- self.assertEqual(g.current_index, None, "bad nil current index")
- self.assertEqual(g.current_future, None, "bad nil current future")
-
- dg = gen.WaitIterator(f1=f1, f2=f2)
-
- while not dg.done():
- dr = yield dg.next()
- if dg.current_index == "f1":
- self.assertTrue(dg.current_future == f1 and dr == 24,
- "WaitIterator dict status incorrect")
- elif dg.current_index == "f2":
- self.assertTrue(dg.current_future == f2 and dr == 42,
- "WaitIterator dict status incorrect")
- else:
- self.fail("got bad WaitIterator index {}".format(
- dg.current_index))
-
- i += 1
-
- self.assertEqual(dg.current_index, None, "bad nil current index")
- self.assertEqual(dg.current_future, None, "bad nil current future")
-
- def finish_coroutines(self, iteration, futures):
- if iteration == 3:
- futures[2].set_result(24)
- elif iteration == 5:
- futures[0].set_exception(ZeroDivisionError())
- elif iteration == 8:
- futures[1].set_result(42)
- futures[3].set_result(84)
-
- if iteration < 8:
- self.io_loop.add_callback(self.finish_coroutines, iteration + 1, futures)
-
- @gen_test
- def test_iterator(self):
- futures = [Future(), Future(), Future(), Future()]
-
- self.finish_coroutines(0, futures)
-
- g = gen.WaitIterator(*futures)
-
- i = 0
- while not g.done():
- try:
- r = yield g.next()
- except ZeroDivisionError:
- self.assertIs(g.current_future, futures[0],
- 'exception future invalid')
- else:
- if i == 0:
- self.assertEqual(r, 24, 'iterator value incorrect')
- self.assertEqual(g.current_index, 2, 'wrong index')
- elif i == 2:
- self.assertEqual(r, 42, 'iterator value incorrect')
- self.assertEqual(g.current_index, 1, 'wrong index')
- elif i == 3:
- self.assertEqual(r, 84, 'iterator value incorrect')
- self.assertEqual(g.current_index, 3, 'wrong index')
- i += 1
-
- @skipBefore35
- @gen_test
- def test_iterator_async_await(self):
- # Recreate the previous test with py35 syntax. It's a little clunky
- # because of the way the previous test handles an exception on
- # a single iteration.
- futures = [Future(), Future(), Future(), Future()]
- self.finish_coroutines(0, futures)
- self.finished = False
-
- namespace = exec_test(globals(), locals(), """
- async def f():
- i = 0
- g = gen.WaitIterator(*futures)
- try:
- async for r in g:
- if i == 0:
- self.assertEqual(r, 24, 'iterator value incorrect')
- self.assertEqual(g.current_index, 2, 'wrong index')
- else:
- raise Exception("expected exception on iteration 1")
- i += 1
- except ZeroDivisionError:
- i += 1
- async for r in g:
- if i == 2:
- self.assertEqual(r, 42, 'iterator value incorrect')
- self.assertEqual(g.current_index, 1, 'wrong index')
- elif i == 3:
- self.assertEqual(r, 84, 'iterator value incorrect')
- self.assertEqual(g.current_index, 3, 'wrong index')
- else:
- raise Exception("didn't expect iteration %d" % i)
- i += 1
- self.finished = True
- """)
- yield namespace['f']()
- self.assertTrue(self.finished)
-
- @gen_test
- def test_no_ref(self):
- # In this usage, there is no direct hard reference to the
- # WaitIterator itself, only the Future it returns. Since
- # WaitIterator uses weak references internally to improve GC
- # performance, this used to cause problems.
- yield gen.with_timeout(datetime.timedelta(seconds=0.1),
- gen.WaitIterator(gen.sleep(0)).next())
-
-
-class RunnerGCTest(AsyncTestCase):
- def is_pypy3(self):
- return (platform.python_implementation() == 'PyPy' and
- sys.version_info > (3,))
-
- @gen_test
- def test_gc(self):
- # Github issue 1769: Runner objects can get GCed unexpectedly
- # while their future is alive.
- weakref_scope = [None]
-
- def callback():
- gc.collect(2)
- weakref_scope[0]().set_result(123)
-
- @gen.coroutine
- def tester():
- fut = Future()
- weakref_scope[0] = weakref.ref(fut)
- self.io_loop.add_callback(callback)
- yield fut
-
- yield gen.with_timeout(
- datetime.timedelta(seconds=0.2),
- tester()
- )
-
- def test_gc_infinite_coro(self):
- # Github issue 2229: suspended coroutines should be GCed when
- # their loop is closed, even if they're involved in a reference
- # cycle.
- if IOLoop.configured_class().__name__.endswith('TwistedIOLoop'):
- raise unittest.SkipTest("Test may fail on TwistedIOLoop")
-
- loop = self.get_new_ioloop()
- result = []
- wfut = []
-
- @gen.coroutine
- def infinite_coro():
- try:
- while True:
- yield gen.sleep(1e-3)
- result.append(True)
- finally:
- # coroutine finalizer
- result.append(None)
-
- @gen.coroutine
- def do_something():
- fut = infinite_coro()
- fut._refcycle = fut
- wfut.append(weakref.ref(fut))
- yield gen.sleep(0.2)
-
- loop.run_sync(do_something)
- loop.close()
- gc.collect()
- # Future was collected
- self.assertIs(wfut[0](), None)
- # At least one wakeup
- self.assertGreaterEqual(len(result), 2)
- if not self.is_pypy3():
- # coroutine finalizer was called (not on PyPy3 apparently)
- self.assertIs(result[-1], None)
-
- @skipBefore35
- def test_gc_infinite_async_await(self):
- # Same as test_gc_infinite_coro, but with a `async def` function
- import asyncio
-
- namespace = exec_test(globals(), locals(), """
- async def infinite_coro(result):
- try:
- while True:
- await gen.sleep(1e-3)
- result.append(True)
- finally:
- # coroutine finalizer
- result.append(None)
- """)
-
- infinite_coro = namespace['infinite_coro']
- loop = self.get_new_ioloop()
- result = []
- wfut = []
-
- @gen.coroutine
- def do_something():
- fut = asyncio.get_event_loop().create_task(infinite_coro(result))
- fut._refcycle = fut
- wfut.append(weakref.ref(fut))
- yield gen.sleep(0.2)
-
- loop.run_sync(do_something)
- with ExpectLog('asyncio', "Task was destroyed but it is pending"):
- loop.close()
- gc.collect()
- # Future was collected
- self.assertIs(wfut[0](), None)
- # At least one wakeup and one finally
- self.assertGreaterEqual(len(result), 2)
- if not self.is_pypy3():
- # coroutine finalizer was called (not on PyPy3 apparently)
- self.assertIs(result[-1], None)
-
- def test_multi_moment(self):
- # Test gen.multi with moment
- # now that it's not a real Future
- @gen.coroutine
- def wait_a_moment():
- result = yield gen.multi([gen.moment, gen.moment])
- raise gen.Return(result)
-
- loop = self.get_new_ioloop()
- result = loop.run_sync(wait_a_moment)
- self.assertEqual(result, [None, None])
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/lib/tornado/test/gettext_translations/extract_me.py b/lib/tornado/test/gettext_translations/extract_me.py
deleted file mode 100755
index 283c13f4..00000000
--- a/lib/tornado/test/gettext_translations/extract_me.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# flake8: noqa
-# Dummy source file to allow creation of the initial .po file in the
-# same way as a real project. I'm not entirely sure about the real
-# workflow here, but this seems to work.
-#
-# 1) xgettext --language=Python --keyword=_:1,2 --keyword=pgettext:1c,2 --keyword=pgettext:1c,2,3 extract_me.py -o tornado_test.po
-# 2) Edit tornado_test.po, setting CHARSET, Plural-Forms and setting msgstr
-# 3) msgfmt tornado_test.po -o tornado_test.mo
-# 4) Put the file in the proper location: $LANG/LC_MESSAGES
-
-from __future__ import absolute_import, division, print_function
-_("school")
-pgettext("law", "right")
-pgettext("good", "right")
-pgettext("organization", "club", "clubs", 1)
-pgettext("stick", "club", "clubs", 1)
diff --git a/lib/tornado/test/gettext_translations/fr_FR/LC_MESSAGES/tornado_test.mo b/lib/tornado/test/gettext_translations/fr_FR/LC_MESSAGES/tornado_test.mo
deleted file mode 100755
index a97bf9c5..00000000
Binary files a/lib/tornado/test/gettext_translations/fr_FR/LC_MESSAGES/tornado_test.mo and /dev/null differ
diff --git a/lib/tornado/test/gettext_translations/fr_FR/LC_MESSAGES/tornado_test.po b/lib/tornado/test/gettext_translations/fr_FR/LC_MESSAGES/tornado_test.po
deleted file mode 100755
index 88d72c86..00000000
--- a/lib/tornado/test/gettext_translations/fr_FR/LC_MESSAGES/tornado_test.po
+++ /dev/null
@@ -1,47 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR , YEAR.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-01-27 11:05+0300\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: LANGUAGE \n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-#: extract_me.py:11
-msgid "school"
-msgstr "école"
-
-#: extract_me.py:12
-msgctxt "law"
-msgid "right"
-msgstr "le droit"
-
-#: extract_me.py:13
-msgctxt "good"
-msgid "right"
-msgstr "le bien"
-
-#: extract_me.py:14
-msgctxt "organization"
-msgid "club"
-msgid_plural "clubs"
-msgstr[0] "le club"
-msgstr[1] "les clubs"
-
-#: extract_me.py:15
-msgctxt "stick"
-msgid "club"
-msgid_plural "clubs"
-msgstr[0] "le bâton"
-msgstr[1] "les bâtons"
diff --git a/lib/tornado/test/http1connection_test.py b/lib/tornado/test/http1connection_test.py
deleted file mode 100755
index 8aaaaf35..00000000
--- a/lib/tornado/test/http1connection_test.py
+++ /dev/null
@@ -1,61 +0,0 @@
-from __future__ import absolute_import, division, print_function
-
-import socket
-
-from tornado.http1connection import HTTP1Connection
-from tornado.httputil import HTTPMessageDelegate
-from tornado.iostream import IOStream
-from tornado.locks import Event
-from tornado.netutil import add_accept_handler
-from tornado.testing import AsyncTestCase, bind_unused_port, gen_test
-
-
-class HTTP1ConnectionTest(AsyncTestCase):
- def setUp(self):
- super(HTTP1ConnectionTest, self).setUp()
- self.asyncSetUp()
-
- @gen_test
- def asyncSetUp(self):
- listener, port = bind_unused_port()
- event = Event()
-
- def accept_callback(conn, addr):
- self.server_stream = IOStream(conn)
- self.addCleanup(self.server_stream.close)
- event.set()
-
- add_accept_handler(listener, accept_callback)
- self.client_stream = IOStream(socket.socket())
- self.addCleanup(self.client_stream.close)
- yield [self.client_stream.connect(('127.0.0.1', port)),
- event.wait()]
- self.io_loop.remove_handler(listener)
- listener.close()
-
- @gen_test
- def test_http10_no_content_length(self):
- # Regression test for a bug in which can_keep_alive would crash
- # for an HTTP/1.0 (not 1.1) response with no content-length.
- conn = HTTP1Connection(self.client_stream, True)
- self.server_stream.write(b"HTTP/1.0 200 Not Modified\r\n\r\nhello")
- self.server_stream.close()
-
- event = Event()
- test = self
- body = []
-
- class Delegate(HTTPMessageDelegate):
- def headers_received(self, start_line, headers):
- test.code = start_line.code
-
- def data_received(self, data):
- body.append(data)
-
- def finish(self):
- event.set()
-
- yield conn.read_response(Delegate())
- yield event.wait()
- self.assertEqual(self.code, 200)
- self.assertEqual(b''.join(body), b'hello')
diff --git a/lib/tornado/test/httpclient_test.py b/lib/tornado/test/httpclient_test.py
deleted file mode 100755
index 35426a7d..00000000
--- a/lib/tornado/test/httpclient_test.py
+++ /dev/null
@@ -1,718 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, division, print_function
-
-import base64
-import binascii
-from contextlib import closing
-import copy
-import sys
-import threading
-import datetime
-from io import BytesIO
-import time
-import unicodedata
-
-from tornado.escape import utf8, native_str
-from tornado import gen
-from tornado.httpclient import HTTPRequest, HTTPResponse, _RequestProxy, HTTPError, HTTPClient
-from tornado.httpserver import HTTPServer
-from tornado.ioloop import IOLoop
-from tornado.iostream import IOStream
-from tornado.log import gen_log
-from tornado import netutil
-from tornado.stack_context import ExceptionStackContext, NullContext
-from tornado.testing import AsyncHTTPTestCase, bind_unused_port, gen_test, ExpectLog
-from tornado.test.util import unittest, skipOnTravis, ignore_deprecation
-from tornado.web import Application, RequestHandler, url
-from tornado.httputil import format_timestamp, HTTPHeaders
-
-
-class HelloWorldHandler(RequestHandler):
- def get(self):
- name = self.get_argument("name", "world")
- self.set_header("Content-Type", "text/plain")
- self.finish("Hello %s!" % name)
-
-
-class PostHandler(RequestHandler):
- def post(self):
- self.finish("Post arg1: %s, arg2: %s" % (
- self.get_argument("arg1"), self.get_argument("arg2")))
-
-
-class PutHandler(RequestHandler):
- def put(self):
- self.write("Put body: ")
- self.write(self.request.body)
-
-
-class RedirectHandler(RequestHandler):
- def prepare(self):
- self.write('redirects can have bodies too')
- self.redirect(self.get_argument("url"),
- status=int(self.get_argument("status", "302")))
-
-
-class ChunkHandler(RequestHandler):
- @gen.coroutine
- def get(self):
- self.write("asdf")
- self.flush()
- # Wait a bit to ensure the chunks are sent and received separately.
- yield gen.sleep(0.01)
- self.write("qwer")
-
-
-class AuthHandler(RequestHandler):
- def get(self):
- self.finish(self.request.headers["Authorization"])
-
-
-class CountdownHandler(RequestHandler):
- def get(self, count):
- count = int(count)
- if count > 0:
- self.redirect(self.reverse_url("countdown", count - 1))
- else:
- self.write("Zero")
-
-
-class EchoPostHandler(RequestHandler):
- def post(self):
- self.write(self.request.body)
-
-
-class UserAgentHandler(RequestHandler):
- def get(self):
- self.write(self.request.headers.get('User-Agent', 'User agent not set'))
-
-
-class ContentLength304Handler(RequestHandler):
- def get(self):
- self.set_status(304)
- self.set_header('Content-Length', 42)
-
- def _clear_headers_for_304(self):
- # Tornado strips content-length from 304 responses, but here we
- # want to simulate servers that include the headers anyway.
- pass
-
-
-class PatchHandler(RequestHandler):
-
- def patch(self):
- "Return the request payload - so we can check it is being kept"
- self.write(self.request.body)
-
-
-class AllMethodsHandler(RequestHandler):
- SUPPORTED_METHODS = RequestHandler.SUPPORTED_METHODS + ('OTHER',)
-
- def method(self):
- self.write(self.request.method)
-
- get = post = put = delete = options = patch = other = method
-
-
-class SetHeaderHandler(RequestHandler):
- def get(self):
- # Use get_arguments for keys to get strings, but
- # request.arguments for values to get bytes.
- for k, v in zip(self.get_arguments('k'),
- self.request.arguments['v']):
- self.set_header(k, v)
-
-# These tests end up getting run redundantly: once here with the default
-# HTTPClient implementation, and then again in each implementation's own
-# test suite.
-
-
-class HTTPClientCommonTestCase(AsyncHTTPTestCase):
- def get_app(self):
- return Application([
- url("/hello", HelloWorldHandler),
- url("/post", PostHandler),
- url("/put", PutHandler),
- url("/redirect", RedirectHandler),
- url("/chunk", ChunkHandler),
- url("/auth", AuthHandler),
- url("/countdown/([0-9]+)", CountdownHandler, name="countdown"),
- url("/echopost", EchoPostHandler),
- url("/user_agent", UserAgentHandler),
- url("/304_with_content_length", ContentLength304Handler),
- url("/all_methods", AllMethodsHandler),
- url('/patch', PatchHandler),
- url('/set_header', SetHeaderHandler),
- ], gzip=True)
-
- def test_patch_receives_payload(self):
- body = b"some patch data"
- response = self.fetch("/patch", method='PATCH', body=body)
- self.assertEqual(response.code, 200)
- self.assertEqual(response.body, body)
-
- @skipOnTravis
- def test_hello_world(self):
- response = self.fetch("/hello")
- self.assertEqual(response.code, 200)
- self.assertEqual(response.headers["Content-Type"], "text/plain")
- self.assertEqual(response.body, b"Hello world!")
- self.assertEqual(int(response.request_time), 0)
-
- response = self.fetch("/hello?name=Ben")
- self.assertEqual(response.body, b"Hello Ben!")
-
- def test_streaming_callback(self):
- # streaming_callback is also tested in test_chunked
- chunks = []
- response = self.fetch("/hello",
- streaming_callback=chunks.append)
- # with streaming_callback, data goes to the callback and not response.body
- self.assertEqual(chunks, [b"Hello world!"])
- self.assertFalse(response.body)
-
- def test_post(self):
- response = self.fetch("/post", method="POST",
- body="arg1=foo&arg2=bar")
- self.assertEqual(response.code, 200)
- self.assertEqual(response.body, b"Post arg1: foo, arg2: bar")
-
- def test_chunked(self):
- response = self.fetch("/chunk")
- self.assertEqual(response.body, b"asdfqwer")
-
- chunks = []
- response = self.fetch("/chunk",
- streaming_callback=chunks.append)
- self.assertEqual(chunks, [b"asdf", b"qwer"])
- self.assertFalse(response.body)
-
- def test_chunked_close(self):
- # test case in which chunks spread read-callback processing
- # over several ioloop iterations, but the connection is already closed.
- sock, port = bind_unused_port()
- with closing(sock):
- @gen.coroutine
- def accept_callback(conn, address):
- # fake an HTTP server using chunked encoding where the final chunks
- # and connection close all happen at once
- stream = IOStream(conn)
- request_data = yield stream.read_until(b"\r\n\r\n")
- if b"HTTP/1." not in request_data:
- self.skipTest("requires HTTP/1.x")
- yield stream.write(b"""\
-HTTP/1.1 200 OK
-Transfer-Encoding: chunked
-
-1
-1
-1
-2
-0
-
-""".replace(b"\n", b"\r\n"))
- stream.close()
- netutil.add_accept_handler(sock, accept_callback)
- resp = self.fetch("http://127.0.0.1:%d/" % port)
- resp.rethrow()
- self.assertEqual(resp.body, b"12")
- self.io_loop.remove_handler(sock.fileno())
-
- def test_streaming_stack_context(self):
- chunks = []
- exc_info = []
-
- def error_handler(typ, value, tb):
- exc_info.append((typ, value, tb))
- return True
-
- def streaming_cb(chunk):
- chunks.append(chunk)
- if chunk == b'qwer':
- 1 / 0
-
- with ignore_deprecation():
- with ExceptionStackContext(error_handler):
- self.fetch('/chunk', streaming_callback=streaming_cb)
-
- self.assertEqual(chunks, [b'asdf', b'qwer'])
- self.assertEqual(1, len(exc_info))
- self.assertIs(exc_info[0][0], ZeroDivisionError)
-
- def test_basic_auth(self):
- # This test data appears in section 2 of RFC 7617.
- self.assertEqual(self.fetch("/auth", auth_username="Aladdin",
- auth_password="open sesame").body,
- b"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==")
-
- def test_basic_auth_explicit_mode(self):
- self.assertEqual(self.fetch("/auth", auth_username="Aladdin",
- auth_password="open sesame",
- auth_mode="basic").body,
- b"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==")
-
- def test_basic_auth_unicode(self):
- # This test data appears in section 2.1 of RFC 7617.
- self.assertEqual(self.fetch("/auth", auth_username="test",
- auth_password="123£").body,
- b"Basic dGVzdDoxMjPCow==")
-
- # The standard mandates NFC. Give it a decomposed username
- # and ensure it is normalized to composed form.
- username = unicodedata.normalize("NFD", u"josé")
- self.assertEqual(self.fetch("/auth",
- auth_username=username,
- auth_password="səcrət").body,
- b"Basic am9zw6k6c8mZY3LJmXQ=")
-
- def test_unsupported_auth_mode(self):
- # curl and simple clients handle errors a bit differently; the
- # important thing is that they don't fall back to basic auth
- # on an unknown mode.
- with ExpectLog(gen_log, "uncaught exception", required=False):
- with self.assertRaises((ValueError, HTTPError)):
- self.fetch("/auth", auth_username="Aladdin",
- auth_password="open sesame",
- auth_mode="asdf",
- raise_error=True)
-
- def test_follow_redirect(self):
- response = self.fetch("/countdown/2", follow_redirects=False)
- self.assertEqual(302, response.code)
- self.assertTrue(response.headers["Location"].endswith("/countdown/1"))
-
- response = self.fetch("/countdown/2")
- self.assertEqual(200, response.code)
- self.assertTrue(response.effective_url.endswith("/countdown/0"))
- self.assertEqual(b"Zero", response.body)
-
- def test_credentials_in_url(self):
- url = self.get_url("/auth").replace("http://", "http://me:secret@")
- response = self.fetch(url)
- self.assertEqual(b"Basic " + base64.b64encode(b"me:secret"),
- response.body)
-
- def test_body_encoding(self):
- unicode_body = u"\xe9"
- byte_body = binascii.a2b_hex(b"e9")
-
- # unicode string in body gets converted to utf8
- response = self.fetch("/echopost", method="POST", body=unicode_body,
- headers={"Content-Type": "application/blah"})
- self.assertEqual(response.headers["Content-Length"], "2")
- self.assertEqual(response.body, utf8(unicode_body))
-
- # byte strings pass through directly
- response = self.fetch("/echopost", method="POST",
- body=byte_body,
- headers={"Content-Type": "application/blah"})
- self.assertEqual(response.headers["Content-Length"], "1")
- self.assertEqual(response.body, byte_body)
-
- # Mixing unicode in headers and byte string bodies shouldn't
- # break anything
- response = self.fetch("/echopost", method="POST", body=byte_body,
- headers={"Content-Type": "application/blah"},
- user_agent=u"foo")
- self.assertEqual(response.headers["Content-Length"], "1")
- self.assertEqual(response.body, byte_body)
-
- def test_types(self):
- response = self.fetch("/hello")
- self.assertEqual(type(response.body), bytes)
- self.assertEqual(type(response.headers["Content-Type"]), str)
- self.assertEqual(type(response.code), int)
- self.assertEqual(type(response.effective_url), str)
-
- def test_header_callback(self):
- first_line = []
- headers = {}
- chunks = []
-
- def header_callback(header_line):
- if header_line.startswith('HTTP/1.1 101'):
- # Upgrading to HTTP/2
- pass
- elif header_line.startswith('HTTP/'):
- first_line.append(header_line)
- elif header_line != '\r\n':
- k, v = header_line.split(':', 1)
- headers[k.lower()] = v.strip()
-
- def streaming_callback(chunk):
- # All header callbacks are run before any streaming callbacks,
- # so the header data is available to process the data as it
- # comes in.
- self.assertEqual(headers['content-type'], 'text/html; charset=UTF-8')
- chunks.append(chunk)
-
- self.fetch('/chunk', header_callback=header_callback,
- streaming_callback=streaming_callback)
- self.assertEqual(len(first_line), 1, first_line)
- self.assertRegexpMatches(first_line[0], 'HTTP/[0-9]\\.[0-9] 200.*\r\n')
- self.assertEqual(chunks, [b'asdf', b'qwer'])
-
- def test_header_callback_stack_context(self):
- exc_info = []
-
- def error_handler(typ, value, tb):
- exc_info.append((typ, value, tb))
- return True
-
- def header_callback(header_line):
- if header_line.lower().startswith('content-type:'):
- 1 / 0
-
- with ignore_deprecation():
- with ExceptionStackContext(error_handler):
- self.fetch('/chunk', header_callback=header_callback)
- self.assertEqual(len(exc_info), 1)
- self.assertIs(exc_info[0][0], ZeroDivisionError)
-
- @gen_test
- def test_configure_defaults(self):
- defaults = dict(user_agent='TestDefaultUserAgent', allow_ipv6=False)
- # Construct a new instance of the configured client class
- client = self.http_client.__class__(force_instance=True,
- defaults=defaults)
- try:
- response = yield client.fetch(self.get_url('/user_agent'))
- self.assertEqual(response.body, b'TestDefaultUserAgent')
- finally:
- client.close()
-
- def test_header_types(self):
- # Header values may be passed as character or utf8 byte strings,
- # in a plain dictionary or an HTTPHeaders object.
- # Keys must always be the native str type.
- # All combinations should have the same results on the wire.
- for value in [u"MyUserAgent", b"MyUserAgent"]:
- for container in [dict, HTTPHeaders]:
- headers = container()
- headers['User-Agent'] = value
- resp = self.fetch('/user_agent', headers=headers)
- self.assertEqual(
- resp.body, b"MyUserAgent",
- "response=%r, value=%r, container=%r" %
- (resp.body, value, container))
-
- def test_multi_line_headers(self):
- # Multi-line http headers are rare but rfc-allowed
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
- sock, port = bind_unused_port()
- with closing(sock):
- @gen.coroutine
- def accept_callback(conn, address):
- stream = IOStream(conn)
- request_data = yield stream.read_until(b"\r\n\r\n")
- if b"HTTP/1." not in request_data:
- self.skipTest("requires HTTP/1.x")
- yield stream.write(b"""\
-HTTP/1.1 200 OK
-X-XSS-Protection: 1;
-\tmode=block
-
-""".replace(b"\n", b"\r\n"))
- stream.close()
-
- netutil.add_accept_handler(sock, accept_callback)
- resp = self.fetch("http://127.0.0.1:%d/" % port)
- resp.rethrow()
- self.assertEqual(resp.headers['X-XSS-Protection'], "1; mode=block")
- self.io_loop.remove_handler(sock.fileno())
-
- def test_304_with_content_length(self):
- # According to the spec 304 responses SHOULD NOT include
- # Content-Length or other entity headers, but some servers do it
- # anyway.
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
- response = self.fetch('/304_with_content_length')
- self.assertEqual(response.code, 304)
- self.assertEqual(response.headers['Content-Length'], '42')
-
- def test_final_callback_stack_context(self):
- # The final callback should be run outside of the httpclient's
- # stack_context. We want to ensure that there is not stack_context
- # between the user's callback and the IOLoop, so monkey-patch
- # IOLoop.handle_callback_exception and disable the test harness's
- # context with a NullContext.
- # Note that this does not apply to secondary callbacks (header
- # and streaming_callback), as errors there must be seen as errors
- # by the http client so it can clean up the connection.
- exc_info = []
-
- def handle_callback_exception(callback):
- exc_info.append(sys.exc_info())
- self.stop()
- self.io_loop.handle_callback_exception = handle_callback_exception
- with NullContext():
- with ignore_deprecation():
- self.http_client.fetch(self.get_url('/hello'),
- lambda response: 1 / 0)
- self.wait()
- self.assertEqual(exc_info[0][0], ZeroDivisionError)
-
- @gen_test
- def test_future_interface(self):
- response = yield self.http_client.fetch(self.get_url('/hello'))
- self.assertEqual(response.body, b'Hello world!')
-
- @gen_test
- def test_future_http_error(self):
- with self.assertRaises(HTTPError) as context:
- yield self.http_client.fetch(self.get_url('/notfound'))
- self.assertEqual(context.exception.code, 404)
- self.assertEqual(context.exception.response.code, 404)
-
- @gen_test
- def test_future_http_error_no_raise(self):
- response = yield self.http_client.fetch(self.get_url('/notfound'), raise_error=False)
- self.assertEqual(response.code, 404)
-
- @gen_test
- def test_reuse_request_from_response(self):
- # The response.request attribute should be an HTTPRequest, not
- # a _RequestProxy.
- # This test uses self.http_client.fetch because self.fetch calls
- # self.get_url on the input unconditionally.
- url = self.get_url('/hello')
- response = yield self.http_client.fetch(url)
- self.assertEqual(response.request.url, url)
- self.assertTrue(isinstance(response.request, HTTPRequest))
- response2 = yield self.http_client.fetch(response.request)
- self.assertEqual(response2.body, b'Hello world!')
-
- def test_all_methods(self):
- for method in ['GET', 'DELETE', 'OPTIONS']:
- response = self.fetch('/all_methods', method=method)
- self.assertEqual(response.body, utf8(method))
- for method in ['POST', 'PUT', 'PATCH']:
- response = self.fetch('/all_methods', method=method, body=b'')
- self.assertEqual(response.body, utf8(method))
- response = self.fetch('/all_methods', method='HEAD')
- self.assertEqual(response.body, b'')
- response = self.fetch('/all_methods', method='OTHER',
- allow_nonstandard_methods=True)
- self.assertEqual(response.body, b'OTHER')
-
- def test_body_sanity_checks(self):
- # These methods require a body.
- for method in ('POST', 'PUT', 'PATCH'):
- with self.assertRaises(ValueError) as context:
- self.fetch('/all_methods', method=method, raise_error=True)
- self.assertIn('must not be None', str(context.exception))
-
- resp = self.fetch('/all_methods', method=method,
- allow_nonstandard_methods=True)
- self.assertEqual(resp.code, 200)
-
- # These methods don't allow a body.
- for method in ('GET', 'DELETE', 'OPTIONS'):
- with self.assertRaises(ValueError) as context:
- self.fetch('/all_methods', method=method, body=b'asdf', raise_error=True)
- self.assertIn('must be None', str(context.exception))
-
- # In most cases this can be overridden, but curl_httpclient
- # does not allow body with a GET at all.
- if method != 'GET':
- self.fetch('/all_methods', method=method, body=b'asdf',
- allow_nonstandard_methods=True, raise_error=True)
- self.assertEqual(resp.code, 200)
-
- # This test causes odd failures with the combination of
- # curl_httpclient (at least with the version of libcurl available
- # on ubuntu 12.04), TwistedIOLoop, and epoll. For POST (but not PUT),
- # curl decides the response came back too soon and closes the connection
- # to start again. It does this *before* telling the socket callback to
- # unregister the FD. Some IOLoop implementations have special kernel
- # integration to discover this immediately. Tornado's IOLoops
- # ignore errors on remove_handler to accommodate this behavior, but
- # Twisted's reactor does not. The removeReader call fails and so
- # do all future removeAll calls (which our tests do at cleanup).
- #
- # def test_post_307(self):
- # response = self.fetch("/redirect?status=307&url=/post",
- # method="POST", body=b"arg1=foo&arg2=bar")
- # self.assertEqual(response.body, b"Post arg1: foo, arg2: bar")
-
- def test_put_307(self):
- response = self.fetch("/redirect?status=307&url=/put",
- method="PUT", body=b"hello")
- response.rethrow()
- self.assertEqual(response.body, b"Put body: hello")
-
- def test_non_ascii_header(self):
- # Non-ascii headers are sent as latin1.
- response = self.fetch("/set_header?k=foo&v=%E9")
- response.rethrow()
- self.assertEqual(response.headers["Foo"], native_str(u"\u00e9"))
-
- def test_response_times(self):
- # A few simple sanity checks of the response time fields to
- # make sure they're using the right basis (between the
- # wall-time and monotonic clocks).
- start_time = time.time()
- response = self.fetch("/hello")
- response.rethrow()
- self.assertGreaterEqual(response.request_time, 0)
- self.assertLess(response.request_time, 1.0)
- # A very crude check to make sure that start_time is based on
- # wall time and not the monotonic clock.
- self.assertLess(abs(response.start_time - start_time), 1.0)
-
- for k, v in response.time_info.items():
- self.assertTrue(0 <= v < 1.0, "time_info[%s] out of bounds: %s" % (k, v))
-
-
-class RequestProxyTest(unittest.TestCase):
- def test_request_set(self):
- proxy = _RequestProxy(HTTPRequest('http://example.com/',
- user_agent='foo'),
- dict())
- self.assertEqual(proxy.user_agent, 'foo')
-
- def test_default_set(self):
- proxy = _RequestProxy(HTTPRequest('http://example.com/'),
- dict(network_interface='foo'))
- self.assertEqual(proxy.network_interface, 'foo')
-
- def test_both_set(self):
- proxy = _RequestProxy(HTTPRequest('http://example.com/',
- proxy_host='foo'),
- dict(proxy_host='bar'))
- self.assertEqual(proxy.proxy_host, 'foo')
-
- def test_neither_set(self):
- proxy = _RequestProxy(HTTPRequest('http://example.com/'),
- dict())
- self.assertIs(proxy.auth_username, None)
-
- def test_bad_attribute(self):
- proxy = _RequestProxy(HTTPRequest('http://example.com/'),
- dict())
- with self.assertRaises(AttributeError):
- proxy.foo
-
- def test_defaults_none(self):
- proxy = _RequestProxy(HTTPRequest('http://example.com/'), None)
- self.assertIs(proxy.auth_username, None)
-
-
-class HTTPResponseTestCase(unittest.TestCase):
- def test_str(self):
- response = HTTPResponse(HTTPRequest('http://example.com'),
- 200, headers={}, buffer=BytesIO())
- s = str(response)
- self.assertTrue(s.startswith('HTTPResponse('))
- self.assertIn('code=200', s)
-
-
-class SyncHTTPClientTest(unittest.TestCase):
- def setUp(self):
- if IOLoop.configured_class().__name__ == 'TwistedIOLoop':
- # TwistedIOLoop only supports the global reactor, so we can't have
- # separate IOLoops for client and server threads.
- raise unittest.SkipTest(
- 'Sync HTTPClient not compatible with TwistedIOLoop')
- self.server_ioloop = IOLoop()
-
- @gen.coroutine
- def init_server():
- sock, self.port = bind_unused_port()
- app = Application([('/', HelloWorldHandler)])
- self.server = HTTPServer(app)
- self.server.add_socket(sock)
- self.server_ioloop.run_sync(init_server)
-
- self.server_thread = threading.Thread(target=self.server_ioloop.start)
- self.server_thread.start()
-
- self.http_client = HTTPClient()
-
- def tearDown(self):
- def stop_server():
- self.server.stop()
- # Delay the shutdown of the IOLoop by several iterations because
- # the server may still have some cleanup work left when
- # the client finishes with the response (this is noticeable
- # with http/2, which leaves a Future with an unexamined
- # StreamClosedError on the loop).
-
- @gen.coroutine
- def slow_stop():
- # The number of iterations is difficult to predict. Typically,
- # one is sufficient, although sometimes it needs more.
- for i in range(5):
- yield
- self.server_ioloop.stop()
- self.server_ioloop.add_callback(slow_stop)
- self.server_ioloop.add_callback(stop_server)
- self.server_thread.join()
- self.http_client.close()
- self.server_ioloop.close(all_fds=True)
-
- def get_url(self, path):
- return 'http://127.0.0.1:%d%s' % (self.port, path)
-
- def test_sync_client(self):
- response = self.http_client.fetch(self.get_url('/'))
- self.assertEqual(b'Hello world!', response.body)
-
- def test_sync_client_error(self):
- # Synchronous HTTPClient raises errors directly; no need for
- # response.rethrow()
- with self.assertRaises(HTTPError) as assertion:
- self.http_client.fetch(self.get_url('/notfound'))
- self.assertEqual(assertion.exception.code, 404)
-
-
-class HTTPRequestTestCase(unittest.TestCase):
- def test_headers(self):
- request = HTTPRequest('http://example.com', headers={'foo': 'bar'})
- self.assertEqual(request.headers, {'foo': 'bar'})
-
- def test_headers_setter(self):
- request = HTTPRequest('http://example.com')
- request.headers = {'bar': 'baz'}
- self.assertEqual(request.headers, {'bar': 'baz'})
-
- def test_null_headers_setter(self):
- request = HTTPRequest('http://example.com')
- request.headers = None
- self.assertEqual(request.headers, {})
-
- def test_body(self):
- request = HTTPRequest('http://example.com', body='foo')
- self.assertEqual(request.body, utf8('foo'))
-
- def test_body_setter(self):
- request = HTTPRequest('http://example.com')
- request.body = 'foo'
- self.assertEqual(request.body, utf8('foo'))
-
- def test_if_modified_since(self):
- http_date = datetime.datetime.utcnow()
- request = HTTPRequest('http://example.com', if_modified_since=http_date)
- self.assertEqual(request.headers,
- {'If-Modified-Since': format_timestamp(http_date)})
-
-
-class HTTPErrorTestCase(unittest.TestCase):
- def test_copy(self):
- e = HTTPError(403)
- e2 = copy.copy(e)
- self.assertIsNot(e, e2)
- self.assertEqual(e.code, e2.code)
-
- def test_plain_error(self):
- e = HTTPError(403)
- self.assertEqual(str(e), "HTTP 403: Forbidden")
- self.assertEqual(repr(e), "HTTP 403: Forbidden")
-
- def test_error_with_response(self):
- resp = HTTPResponse(HTTPRequest('http://example.com/'), 403)
- with self.assertRaises(HTTPError) as cm:
- resp.rethrow()
- e = cm.exception
- self.assertEqual(str(e), "HTTP 403: Forbidden")
- self.assertEqual(repr(e), "HTTP 403: Forbidden")
diff --git a/lib/tornado/test/httpserver_test.py b/lib/tornado/test/httpserver_test.py
deleted file mode 100755
index acaf2a00..00000000
--- a/lib/tornado/test/httpserver_test.py
+++ /dev/null
@@ -1,1167 +0,0 @@
-from __future__ import absolute_import, division, print_function
-
-from tornado import gen, netutil
-from tornado.concurrent import Future
-from tornado.escape import json_decode, json_encode, utf8, _unicode, recursive_unicode, native_str
-from tornado.http1connection import HTTP1Connection
-from tornado.httpclient import HTTPError
-from tornado.httpserver import HTTPServer
-from tornado.httputil import HTTPHeaders, HTTPMessageDelegate, HTTPServerConnectionDelegate, ResponseStartLine # noqa: E501
-from tornado.iostream import IOStream
-from tornado.locks import Event
-from tornado.log import gen_log
-from tornado.netutil import ssl_options_to_context
-from tornado.simple_httpclient import SimpleAsyncHTTPClient
-from tornado.testing import AsyncHTTPTestCase, AsyncHTTPSTestCase, AsyncTestCase, ExpectLog, gen_test # noqa: E501
-from tornado.test.util import unittest, skipOnTravis
-from tornado.web import Application, RequestHandler, stream_request_body
-
-from contextlib import closing
-import datetime
-import gzip
-import os
-import shutil
-import socket
-import ssl
-import sys
-import tempfile
-from io import BytesIO
-
-
-def read_stream_body(stream, callback):
- """Reads an HTTP response from `stream` and runs callback with its
- start_line, headers and body."""
- chunks = []
-
- class Delegate(HTTPMessageDelegate):
- def headers_received(self, start_line, headers):
- self.headers = headers
- self.start_line = start_line
-
- def data_received(self, chunk):
- chunks.append(chunk)
-
- def finish(self):
- conn.detach()
- callback((self.start_line, self.headers, b''.join(chunks)))
- conn = HTTP1Connection(stream, True)
- conn.read_response(Delegate())
-
-
-class HandlerBaseTestCase(AsyncHTTPTestCase):
- def get_app(self):
- return Application([('/', self.__class__.Handler)])
-
- def fetch_json(self, *args, **kwargs):
- response = self.fetch(*args, **kwargs)
- response.rethrow()
- return json_decode(response.body)
-
-
-class HelloWorldRequestHandler(RequestHandler):
- def initialize(self, protocol="http"):
- self.expected_protocol = protocol
-
- def get(self):
- if self.request.protocol != self.expected_protocol:
- raise Exception("unexpected protocol")
- self.finish("Hello world")
-
- def post(self):
- self.finish("Got %d bytes in POST" % len(self.request.body))
-
-
-# In pre-1.0 versions of openssl, SSLv23 clients always send SSLv2
-# ClientHello messages, which are rejected by SSLv3 and TLSv1
-# servers. Note that while the OPENSSL_VERSION_INFO was formally
-# introduced in python3.2, it was present but undocumented in
-# python 2.7
-skipIfOldSSL = unittest.skipIf(
- getattr(ssl, 'OPENSSL_VERSION_INFO', (0, 0)) < (1, 0),
- "old version of ssl module and/or openssl")
-
-
-class BaseSSLTest(AsyncHTTPSTestCase):
- def get_app(self):
- return Application([('/', HelloWorldRequestHandler,
- dict(protocol="https"))])
-
-
-class SSLTestMixin(object):
- def get_ssl_options(self):
- return dict(ssl_version=self.get_ssl_version(), # type: ignore
- **AsyncHTTPSTestCase.get_ssl_options())
-
- def get_ssl_version(self):
- raise NotImplementedError()
-
- def test_ssl(self):
- response = self.fetch('/')
- self.assertEqual(response.body, b"Hello world")
-
- def test_large_post(self):
- response = self.fetch('/',
- method='POST',
- body='A' * 5000)
- self.assertEqual(response.body, b"Got 5000 bytes in POST")
-
- def test_non_ssl_request(self):
- # Make sure the server closes the connection when it gets a non-ssl
- # connection, rather than waiting for a timeout or otherwise
- # misbehaving.
- with ExpectLog(gen_log, '(SSL Error|uncaught exception)'):
- with ExpectLog(gen_log, 'Uncaught exception', required=False):
- with self.assertRaises((IOError, HTTPError)):
- self.fetch(
- self.get_url("/").replace('https:', 'http:'),
- request_timeout=3600,
- connect_timeout=3600,
- raise_error=True)
-
- def test_error_logging(self):
- # No stack traces are logged for SSL errors.
- with ExpectLog(gen_log, 'SSL Error') as expect_log:
- with self.assertRaises((IOError, HTTPError)):
- self.fetch(self.get_url("/").replace("https:", "http:"),
- raise_error=True)
- self.assertFalse(expect_log.logged_stack)
-
-# Python's SSL implementation differs significantly between versions.
-# For example, SSLv3 and TLSv1 throw an exception if you try to read
-# from the socket before the handshake is complete, but the default
-# of SSLv23 allows it.
-
-
-class SSLv23Test(BaseSSLTest, SSLTestMixin):
- def get_ssl_version(self):
- return ssl.PROTOCOL_SSLv23
-
-
-@skipIfOldSSL
-class SSLv3Test(BaseSSLTest, SSLTestMixin):
- def get_ssl_version(self):
- return ssl.PROTOCOL_SSLv3
-
-
-@skipIfOldSSL
-class TLSv1Test(BaseSSLTest, SSLTestMixin):
- def get_ssl_version(self):
- return ssl.PROTOCOL_TLSv1
-
-
-class SSLContextTest(BaseSSLTest, SSLTestMixin):
- def get_ssl_options(self):
- context = ssl_options_to_context(
- AsyncHTTPSTestCase.get_ssl_options(self))
- assert isinstance(context, ssl.SSLContext)
- return context
-
-
-class BadSSLOptionsTest(unittest.TestCase):
- def test_missing_arguments(self):
- application = Application()
- self.assertRaises(KeyError, HTTPServer, application, ssl_options={
- "keyfile": "/__missing__.crt",
- })
-
- def test_missing_key(self):
- """A missing SSL key should cause an immediate exception."""
-
- application = Application()
- module_dir = os.path.dirname(__file__)
- existing_certificate = os.path.join(module_dir, 'test.crt')
- existing_key = os.path.join(module_dir, 'test.key')
-
- self.assertRaises((ValueError, IOError),
- HTTPServer, application, ssl_options={
- "certfile": "/__mising__.crt",
- })
- self.assertRaises((ValueError, IOError),
- HTTPServer, application, ssl_options={
- "certfile": existing_certificate,
- "keyfile": "/__missing__.key"
- })
-
- # This actually works because both files exist
- HTTPServer(application, ssl_options={
- "certfile": existing_certificate,
- "keyfile": existing_key,
- })
-
-
-class MultipartTestHandler(RequestHandler):
- def post(self):
- self.finish({"header": self.request.headers["X-Header-Encoding-Test"],
- "argument": self.get_argument("argument"),
- "filename": self.request.files["files"][0].filename,
- "filebody": _unicode(self.request.files["files"][0]["body"]),
- })
-
-
-# This test is also called from wsgi_test
-class HTTPConnectionTest(AsyncHTTPTestCase):
- def get_handlers(self):
- return [("/multipart", MultipartTestHandler),
- ("/hello", HelloWorldRequestHandler)]
-
- def get_app(self):
- return Application(self.get_handlers())
-
- def raw_fetch(self, headers, body, newline=b"\r\n"):
- with closing(IOStream(socket.socket())) as stream:
- self.io_loop.run_sync(lambda: stream.connect(('127.0.0.1', self.get_http_port())))
- stream.write(
- newline.join(headers +
- [utf8("Content-Length: %d" % len(body))]) +
- newline + newline + body)
- read_stream_body(stream, self.stop)
- start_line, headers, body = self.wait()
- return body
-
- def test_multipart_form(self):
- # Encodings here are tricky: Headers are latin1, bodies can be
- # anything (we use utf8 by default).
- response = self.raw_fetch([
- b"POST /multipart HTTP/1.0",
- b"Content-Type: multipart/form-data; boundary=1234567890",
- b"X-Header-encoding-test: \xe9",
- ],
- b"\r\n".join([
- b"Content-Disposition: form-data; name=argument",
- b"",
- u"\u00e1".encode("utf-8"),
- b"--1234567890",
- u'Content-Disposition: form-data; name="files"; filename="\u00f3"'.encode("utf8"),
- b"",
- u"\u00fa".encode("utf-8"),
- b"--1234567890--",
- b"",
- ]))
- data = json_decode(response)
- self.assertEqual(u"\u00e9", data["header"])
- self.assertEqual(u"\u00e1", data["argument"])
- self.assertEqual(u"\u00f3", data["filename"])
- self.assertEqual(u"\u00fa", data["filebody"])
-
- def test_newlines(self):
- # We support both CRLF and bare LF as line separators.
- for newline in (b"\r\n", b"\n"):
- response = self.raw_fetch([b"GET /hello HTTP/1.0"], b"",
- newline=newline)
- self.assertEqual(response, b'Hello world')
-
- @gen_test
- def test_100_continue(self):
- # Run through a 100-continue interaction by hand:
- # When given Expect: 100-continue, we get a 100 response after the
- # headers, and then the real response after the body.
- stream = IOStream(socket.socket())
- yield stream.connect(("127.0.0.1", self.get_http_port()))
- yield stream.write(b"\r\n".join([
- b"POST /hello HTTP/1.1",
- b"Content-Length: 1024",
- b"Expect: 100-continue",
- b"Connection: close",
- b"\r\n"]))
- data = yield stream.read_until(b"\r\n\r\n")
- self.assertTrue(data.startswith(b"HTTP/1.1 100 "), data)
- stream.write(b"a" * 1024)
- first_line = yield stream.read_until(b"\r\n")
- self.assertTrue(first_line.startswith(b"HTTP/1.1 200"), first_line)
- header_data = yield stream.read_until(b"\r\n\r\n")
- headers = HTTPHeaders.parse(native_str(header_data.decode('latin1')))
- body = yield stream.read_bytes(int(headers["Content-Length"]))
- self.assertEqual(body, b"Got 1024 bytes in POST")
- stream.close()
-
-
-class EchoHandler(RequestHandler):
- def get(self):
- self.write(recursive_unicode(self.request.arguments))
-
- def post(self):
- self.write(recursive_unicode(self.request.arguments))
-
-
-class TypeCheckHandler(RequestHandler):
- def prepare(self):
- self.errors = {}
- fields = [
- ('method', str),
- ('uri', str),
- ('version', str),
- ('remote_ip', str),
- ('protocol', str),
- ('host', str),
- ('path', str),
- ('query', str),
- ]
- for field, expected_type in fields:
- self.check_type(field, getattr(self.request, field), expected_type)
-
- self.check_type('header_key', list(self.request.headers.keys())[0], str)
- self.check_type('header_value', list(self.request.headers.values())[0], str)
-
- self.check_type('cookie_key', list(self.request.cookies.keys())[0], str)
- self.check_type('cookie_value', list(self.request.cookies.values())[0].value, str)
- # secure cookies
-
- self.check_type('arg_key', list(self.request.arguments.keys())[0], str)
- self.check_type('arg_value', list(self.request.arguments.values())[0][0], bytes)
-
- def post(self):
- self.check_type('body', self.request.body, bytes)
- self.write(self.errors)
-
- def get(self):
- self.write(self.errors)
-
- def check_type(self, name, obj, expected_type):
- actual_type = type(obj)
- if expected_type != actual_type:
- self.errors[name] = "expected %s, got %s" % (expected_type,
- actual_type)
-
-
-class HTTPServerTest(AsyncHTTPTestCase):
- def get_app(self):
- return Application([("/echo", EchoHandler),
- ("/typecheck", TypeCheckHandler),
- ("//doubleslash", EchoHandler),
- ])
-
- def test_query_string_encoding(self):
- response = self.fetch("/echo?foo=%C3%A9")
- data = json_decode(response.body)
- self.assertEqual(data, {u"foo": [u"\u00e9"]})
-
- def test_empty_query_string(self):
- response = self.fetch("/echo?foo=&foo=")
- data = json_decode(response.body)
- self.assertEqual(data, {u"foo": [u"", u""]})
-
- def test_empty_post_parameters(self):
- response = self.fetch("/echo", method="POST", body="foo=&bar=")
- data = json_decode(response.body)
- self.assertEqual(data, {u"foo": [u""], u"bar": [u""]})
-
- def test_types(self):
- headers = {"Cookie": "foo=bar"}
- response = self.fetch("/typecheck?foo=bar", headers=headers)
- data = json_decode(response.body)
- self.assertEqual(data, {})
-
- response = self.fetch("/typecheck", method="POST", body="foo=bar", headers=headers)
- data = json_decode(response.body)
- self.assertEqual(data, {})
-
- def test_double_slash(self):
- # urlparse.urlsplit (which tornado.httpserver used to use
- # incorrectly) would parse paths beginning with "//" as
- # protocol-relative urls.
- response = self.fetch("//doubleslash")
- self.assertEqual(200, response.code)
- self.assertEqual(json_decode(response.body), {})
-
- def test_malformed_body(self):
- # parse_qs is pretty forgiving, but it will fail on python 3
- # if the data is not utf8. On python 2 parse_qs will work,
- # but then the recursive_unicode call in EchoHandler will
- # fail.
- if str is bytes:
- return
- with ExpectLog(gen_log, 'Invalid x-www-form-urlencoded body'):
- response = self.fetch(
- '/echo', method="POST",
- headers={'Content-Type': 'application/x-www-form-urlencoded'},
- body=b'\xe9')
- self.assertEqual(200, response.code)
- self.assertEqual(b'{}', response.body)
-
-
-class HTTPServerRawTest(AsyncHTTPTestCase):
- def get_app(self):
- return Application([
- ('/echo', EchoHandler),
- ])
-
- def setUp(self):
- super(HTTPServerRawTest, self).setUp()
- self.stream = IOStream(socket.socket())
- self.io_loop.run_sync(lambda: self.stream.connect(('127.0.0.1', self.get_http_port())))
-
- def tearDown(self):
- self.stream.close()
- super(HTTPServerRawTest, self).tearDown()
-
- def test_empty_request(self):
- self.stream.close()
- self.io_loop.add_timeout(datetime.timedelta(seconds=0.001), self.stop)
- self.wait()
-
- def test_malformed_first_line_response(self):
- with ExpectLog(gen_log, '.*Malformed HTTP request line'):
- self.stream.write(b'asdf\r\n\r\n')
- read_stream_body(self.stream, self.stop)
- start_line, headers, response = self.wait()
- self.assertEqual('HTTP/1.1', start_line.version)
- self.assertEqual(400, start_line.code)
- self.assertEqual('Bad Request', start_line.reason)
-
- def test_malformed_first_line_log(self):
- with ExpectLog(gen_log, '.*Malformed HTTP request line'):
- self.stream.write(b'asdf\r\n\r\n')
- # TODO: need an async version of ExpectLog so we don't need
- # hard-coded timeouts here.
- self.io_loop.add_timeout(datetime.timedelta(seconds=0.05),
- self.stop)
- self.wait()
-
- def test_malformed_headers(self):
- with ExpectLog(gen_log, '.*Malformed HTTP message.*no colon in header line'):
- self.stream.write(b'GET / HTTP/1.0\r\nasdf\r\n\r\n')
- self.io_loop.add_timeout(datetime.timedelta(seconds=0.05),
- self.stop)
- self.wait()
-
- def test_chunked_request_body(self):
- # Chunked requests are not widely supported and we don't have a way
- # to generate them in AsyncHTTPClient, but HTTPServer will read them.
- self.stream.write(b"""\
-POST /echo HTTP/1.1
-Transfer-Encoding: chunked
-Content-Type: application/x-www-form-urlencoded
-
-4
-foo=
-3
-bar
-0
-
-""".replace(b"\n", b"\r\n"))
- read_stream_body(self.stream, self.stop)
- start_line, headers, response = self.wait()
- self.assertEqual(json_decode(response), {u'foo': [u'bar']})
-
- def test_chunked_request_uppercase(self):
- # As per RFC 2616 section 3.6, "Transfer-Encoding" header's value is
- # case-insensitive.
- self.stream.write(b"""\
-POST /echo HTTP/1.1
-Transfer-Encoding: Chunked
-Content-Type: application/x-www-form-urlencoded
-
-4
-foo=
-3
-bar
-0
-
-""".replace(b"\n", b"\r\n"))
- read_stream_body(self.stream, self.stop)
- start_line, headers, response = self.wait()
- self.assertEqual(json_decode(response), {u'foo': [u'bar']})
-
- @gen_test
- def test_invalid_content_length(self):
- with ExpectLog(gen_log, '.*Only integer Content-Length is allowed'):
- self.stream.write(b"""\
-POST /echo HTTP/1.1
-Content-Length: foo
-
-bar
-
-""".replace(b"\n", b"\r\n"))
- yield self.stream.read_until_close()
-
-
-class XHeaderTest(HandlerBaseTestCase):
- class Handler(RequestHandler):
- def get(self):
- self.set_header('request-version', self.request.version)
- self.write(dict(remote_ip=self.request.remote_ip,
- remote_protocol=self.request.protocol))
-
- def get_httpserver_options(self):
- return dict(xheaders=True, trusted_downstream=['5.5.5.5'])
-
- def test_ip_headers(self):
- self.assertEqual(self.fetch_json("/")["remote_ip"], "127.0.0.1")
-
- valid_ipv4 = {"X-Real-IP": "4.4.4.4"}
- self.assertEqual(
- self.fetch_json("/", headers=valid_ipv4)["remote_ip"],
- "4.4.4.4")
-
- valid_ipv4_list = {"X-Forwarded-For": "127.0.0.1, 4.4.4.4"}
- self.assertEqual(
- self.fetch_json("/", headers=valid_ipv4_list)["remote_ip"],
- "4.4.4.4")
-
- valid_ipv6 = {"X-Real-IP": "2620:0:1cfe:face:b00c::3"}
- self.assertEqual(
- self.fetch_json("/", headers=valid_ipv6)["remote_ip"],
- "2620:0:1cfe:face:b00c::3")
-
- valid_ipv6_list = {"X-Forwarded-For": "::1, 2620:0:1cfe:face:b00c::3"}
- self.assertEqual(
- self.fetch_json("/", headers=valid_ipv6_list)["remote_ip"],
- "2620:0:1cfe:face:b00c::3")
-
- invalid_chars = {"X-Real-IP": "4.4.4.4
-
-
-