wsgi: Avoid touching the asyncio event loop at import time
Rewrite import_test. Its original purpose of augmenting our once-poor test coverage, but it can now be useful for ensuring the absence of import-time side effects. Fixes #2426
This commit is contained in:
parent
d68e63f55b
commit
ccc89025f2
|
@ -1,50 +1,68 @@
|
||||||
# flake8: noqa
|
# flake8: noqa
|
||||||
from __future__ import absolute_import, division, print_function
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
from tornado.test.util import unittest
|
from tornado.test.util import unittest
|
||||||
|
|
||||||
|
_import_everything = b"""
|
||||||
|
# The event loop is not fork-safe, and it's easy to initialize an asyncio.Future
|
||||||
|
# at startup, which in turn creates the default event loop and prevents forking.
|
||||||
|
# Explicitly disallow the default event loop so that an error will be raised
|
||||||
|
# if something tries to touch it.
|
||||||
|
try:
|
||||||
|
import asyncio
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
asyncio.set_event_loop(None)
|
||||||
|
|
||||||
|
import tornado.auth
|
||||||
|
import tornado.autoreload
|
||||||
|
import tornado.concurrent
|
||||||
|
import tornado.escape
|
||||||
|
import tornado.gen
|
||||||
|
import tornado.http1connection
|
||||||
|
import tornado.httpclient
|
||||||
|
import tornado.httpserver
|
||||||
|
import tornado.httputil
|
||||||
|
import tornado.ioloop
|
||||||
|
import tornado.iostream
|
||||||
|
import tornado.locale
|
||||||
|
import tornado.log
|
||||||
|
import tornado.netutil
|
||||||
|
import tornado.options
|
||||||
|
import tornado.process
|
||||||
|
import tornado.simple_httpclient
|
||||||
|
import tornado.stack_context
|
||||||
|
import tornado.tcpserver
|
||||||
|
import tornado.tcpclient
|
||||||
|
import tornado.template
|
||||||
|
import tornado.testing
|
||||||
|
import tornado.util
|
||||||
|
import tornado.web
|
||||||
|
import tornado.websocket
|
||||||
|
import tornado.wsgi
|
||||||
|
|
||||||
|
try:
|
||||||
|
import pycurl
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
import tornado.curl_httpclient
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class ImportTest(unittest.TestCase):
|
class ImportTest(unittest.TestCase):
|
||||||
def test_import_everything(self):
|
def test_import_everything(self):
|
||||||
# Some of our modules are not otherwise tested. Import them
|
# Test that all Tornado modules can be imported without side effects,
|
||||||
# all (unless they have external dependencies) here to at
|
# specifically without initializing the default asyncio event loop.
|
||||||
# least ensure that there are no syntax errors.
|
# Since we can't tell which modules may have already beein imported
|
||||||
import tornado.auth
|
# in our process, do it in a subprocess for a clean slate.
|
||||||
import tornado.autoreload
|
proc = subprocess.Popen([sys.executable], stdin=subprocess.PIPE)
|
||||||
import tornado.concurrent
|
proc.communicate(_import_everything)
|
||||||
import tornado.escape
|
self.assertEqual(proc.returncode, 0)
|
||||||
import tornado.gen
|
|
||||||
import tornado.http1connection
|
|
||||||
import tornado.httpclient
|
|
||||||
import tornado.httpserver
|
|
||||||
import tornado.httputil
|
|
||||||
import tornado.ioloop
|
|
||||||
import tornado.iostream
|
|
||||||
import tornado.locale
|
|
||||||
import tornado.log
|
|
||||||
import tornado.netutil
|
|
||||||
import tornado.options
|
|
||||||
import tornado.process
|
|
||||||
import tornado.simple_httpclient
|
|
||||||
import tornado.stack_context
|
|
||||||
import tornado.tcpserver
|
|
||||||
import tornado.tcpclient
|
|
||||||
import tornado.template
|
|
||||||
import tornado.testing
|
|
||||||
import tornado.util
|
|
||||||
import tornado.web
|
|
||||||
import tornado.websocket
|
|
||||||
import tornado.wsgi
|
|
||||||
|
|
||||||
# for modules with dependencies, if those dependencies can be loaded,
|
|
||||||
# load them too.
|
|
||||||
|
|
||||||
def test_import_pycurl(self):
|
|
||||||
try:
|
|
||||||
import pycurl # type: ignore
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
import tornado.curl_httpclient
|
|
||||||
|
|
||||||
def test_import_aliases(self):
|
def test_import_aliases(self):
|
||||||
# Ensure we don't delete formerly-documented aliases accidentally.
|
# Ensure we don't delete formerly-documented aliases accidentally.
|
||||||
|
|
|
@ -85,8 +85,10 @@ class WSGIApplication(web.Application):
|
||||||
|
|
||||||
# WSGI has no facilities for flow control, so just return an already-done
|
# WSGI has no facilities for flow control, so just return an already-done
|
||||||
# Future when the interface requires it.
|
# Future when the interface requires it.
|
||||||
_dummy_future = Future()
|
def _dummy_future():
|
||||||
_dummy_future.set_result(None)
|
f = Future()
|
||||||
|
f.set_result(None)
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
class _WSGIConnection(httputil.HTTPConnection):
|
class _WSGIConnection(httputil.HTTPConnection):
|
||||||
|
@ -118,7 +120,7 @@ class _WSGIConnection(httputil.HTTPConnection):
|
||||||
self.write(chunk, callback)
|
self.write(chunk, callback)
|
||||||
elif callback is not None:
|
elif callback is not None:
|
||||||
callback()
|
callback()
|
||||||
return _dummy_future
|
return _dummy_future()
|
||||||
|
|
||||||
def write(self, chunk, callback=None):
|
def write(self, chunk, callback=None):
|
||||||
if self._expected_content_remaining is not None:
|
if self._expected_content_remaining is not None:
|
||||||
|
@ -130,7 +132,7 @@ class _WSGIConnection(httputil.HTTPConnection):
|
||||||
self._write_buffer.append(chunk)
|
self._write_buffer.append(chunk)
|
||||||
if callback is not None:
|
if callback is not None:
|
||||||
callback()
|
callback()
|
||||||
return _dummy_future
|
return _dummy_future()
|
||||||
|
|
||||||
def finish(self):
|
def finish(self):
|
||||||
if (self._expected_content_remaining is not None and
|
if (self._expected_content_remaining is not None and
|
||||||
|
|
Loading…
Reference in New Issue