all: Support lazy imports of submodules

A getattr hook in the top-level "tornado" package now imports submodules
automatically, eliminating the need to explicitly reference multiple submodules
in imports
This commit is contained in:
Ben Darnell 2022-11-27 15:06:08 -05:00
parent 41e70ac920
commit 6c2aae090c
22 changed files with 91 additions and 73 deletions

View File

@ -21,8 +21,7 @@ Here is a simple "Hello, world" example web app for Tornado:
.. code-block:: python
import asyncio
import tornado.web
import tornado
class MainHandler(tornado.web.RequestHandler):
def get(self):

View File

@ -21,12 +21,7 @@ import markdown
import os.path
import psycopg2
import re
import tornado.escape
import tornado.httpserver
import tornado.ioloop
import tornado.locks
import tornado.options
import tornado.web
import tornado
import unicodedata
from tornado.options import define, options

View File

@ -15,9 +15,7 @@
# under the License.
import asyncio
import tornado.escape
import tornado.locks
import tornado.web
import tornado
import os.path
import uuid

View File

@ -16,11 +16,7 @@
import asyncio
import os.path
import tornado.auth
import tornado.escape
import tornado.httpserver
import tornado.options
import tornado.web
import tornado
from tornado.options import define, options

View File

@ -12,7 +12,7 @@ import asyncio
import logging
from urllib.parse import unquote
import tornado.web
import tornado
from tornado import options

View File

@ -15,9 +15,7 @@
# under the License.
import asyncio
import tornado.httpserver
import tornado.options
import tornado.web
import tornado
from tornado.options import define, options

View File

@ -20,10 +20,7 @@ Authentication, error handling, etc are left as an exercise for the reader :)
import asyncio
import logging
import tornado.escape
import tornado.options
import tornado.web
import tornado.websocket
import tornado
import os.path
import uuid

View File

@ -3,7 +3,7 @@
.. testsetup::
import tornado.auth, tornado.gen, tornado.web
import tornado
.. automodule:: tornado.auth

View File

@ -3,8 +3,7 @@ Authentication and security
.. testsetup::
import tornado.auth
import tornado.web
import tornado
Cookies and secure cookies
~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -2,7 +2,7 @@
.. testsetup::
import tornado.web
import tornado
Structure of a Tornado web application
======================================
@ -17,8 +17,7 @@ A minimal "hello world" example looks something like this:
.. testcode::
import asyncio
import tornado.web
import tornado
class MainHandler(tornado.web.RequestHandler):
def get(self):

View File

@ -3,7 +3,7 @@ Templates and UI
.. testsetup::
import tornado.web
import tornado
Tornado includes a simple, fast, and flexible templating language.
This section describes that language as well as related issues

View File

@ -32,8 +32,7 @@ Hello, world
Here is a simple "Hello, world" example web app for Tornado::
import asyncio
import tornado.web
import tornado
class MainHandler(tornado.web.RequestHandler):
def get(self):

View File

@ -3,7 +3,7 @@
.. testsetup::
import tornado.websocket
import tornado
.. automodule:: tornado.websocket

View File

@ -24,3 +24,44 @@
# number has been incremented)
version = "6.3.dev1"
version_info = (6, 3, 0, -100)
import importlib
import typing
__all__ = [
"auth",
"autoreload",
"concurrent",
"curl_httpclient",
"escape",
"gen",
"http1connection",
"httpclient",
"httpserver",
"httputil",
"ioloop",
"iostream",
"locale",
"locks",
"log",
"netutil",
"options",
"platform",
"process",
"queues",
"routing",
"simple_httpclient",
"tcpclient",
"tcpserver",
"template",
"testing",
"util",
"web",
]
# Copied from https://peps.python.org/pep-0562/
def __getattr__(name: str) -> typing.Any:
if name in __all__:
return importlib.import_module("." + name, __name__)
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

View File

@ -83,7 +83,7 @@ class IOLoop(Configurable):
import functools
import socket
import tornado.ioloop
import tornado
from tornado.iostream import IOStream
async def handle_connection(connection, address):

View File

@ -1069,9 +1069,8 @@ class IOStream(BaseIOStream):
.. testcode::
import tornado.ioloop
import tornado.iostream
import socket
import tornado
async def main():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)

View File

@ -56,7 +56,7 @@ Your ``main()`` method can parse the command line or parse a config file with
either `parse_command_line` or `parse_config_file`::
import myapp.db, myapp.server
import tornado.options
import tornado
if __name__ == '__main__':
tornado.options.parse_command_line()

View File

@ -1,6 +1,6 @@
import unittest
import tornado.escape
import tornado
from tornado.escape import (
utf8,
xhtml_escape,

View File

@ -11,38 +11,31 @@ _import_everything = b"""
import asyncio
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.tcpserver
import tornado.tcpclient
import tornado.template
import tornado.testing
import tornado.util
import tornado.web
import tornado.websocket
import tornado.wsgi
import importlib
import tornado
try:
import pycurl
except ImportError:
pass
else:
import tornado.curl_httpclient
for mod in tornado.__all__:
if mod == "curl_httpclient":
# This module has extra dependencies; skip it if they're not installed.
try:
import pycurl
except ImportError:
continue
importlib.import_module(f"tornado.{mod}")
"""
_import_lazy = b"""
import sys
import tornado
if "tornado.web" in sys.modules:
raise Exception("unexpected eager import")
# Trigger a lazy import by referring to something in a submodule.
tornado.web.RequestHandler
if "tornado.web" not in sys.modules:
raise Exception("lazy import did not update sys.modules")
"""
@ -56,6 +49,12 @@ class ImportTest(unittest.TestCase):
proc.communicate(_import_everything)
self.assertEqual(proc.returncode, 0)
def test_lazy_import(self):
# Test that submodules can be referenced lazily after "import tornado"
proc = subprocess.Popen([sys.executable], stdin=subprocess.PIPE)
proc.communicate(_import_lazy)
self.assertEqual(proc.returncode, 0)
def test_import_aliases(self):
# Ensure we don't delete formerly-documented aliases accidentally.
import tornado.ioloop

View File

@ -4,7 +4,7 @@ import sys
import datetime
import unittest
import tornado.escape
import tornado
from tornado.escape import utf8
from tornado.util import (
raise_exc_info,

View File

@ -23,7 +23,7 @@ Here is a simple "Hello, world" example app:
.. testcode::
import asyncio
import tornado.web
import tornado
class MainHandler(tornado.web.RequestHandler):
def get(self):

View File

@ -23,7 +23,6 @@ import hashlib
import os
import sys
import struct
import tornado.escape
import tornado.web
from urllib.parse import urlparse
import zlib