Skip to content

Commit

Permalink
Merge pull request #3201 from bdarnell/lazy-import
Browse files Browse the repository at this point in the history
all: Support lazy imports of submodules
  • Loading branch information
bdarnell committed Nov 28, 2022
2 parents 41e70ac + 6c2aae0 commit ff23c7f
Show file tree
Hide file tree
Showing 22 changed files with 91 additions and 73 deletions.
3 changes: 1 addition & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
7 changes: 1 addition & 6 deletions demos/blog/blog.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,7 @@
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
Expand Down
4 changes: 1 addition & 3 deletions demos/chat/chatdemo.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 1 addition & 5 deletions demos/facebook/facebook.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion demos/file_upload/file_receiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import logging
from urllib.parse import unquote

import tornado.web
import tornado
from tornado import options


Expand Down
4 changes: 1 addition & 3 deletions demos/helloworld/helloworld.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 1 addition & 4 deletions demos/websocket/chatdemo.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@

import asyncio
import logging
import tornado.escape
import tornado.options
import tornado.web
import tornado.websocket
import tornado
import os.path
import uuid

Expand Down
2 changes: 1 addition & 1 deletion docs/auth.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

.. testsetup::

import tornado.auth, tornado.gen, tornado.web
import tornado

.. automodule:: tornado.auth

Expand Down
3 changes: 1 addition & 2 deletions docs/guide/security.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ Authentication and security

.. testsetup::

import tornado.auth
import tornado.web
import tornado

Cookies and secure cookies
~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
5 changes: 2 additions & 3 deletions docs/guide/structure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

.. testsetup::

import tornado.web
import tornado

Structure of a Tornado web application
======================================
Expand All @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/templates.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion docs/websocket.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

.. testsetup::

import tornado.websocket
import tornado

.. automodule:: tornado.websocket

Expand Down
41 changes: 41 additions & 0 deletions tornado/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
2 changes: 1 addition & 1 deletion tornado/ioloop.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
3 changes: 1 addition & 2 deletions tornado/iostream.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion tornado/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def start_server():
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()
Expand Down
2 changes: 1 addition & 1 deletion tornado/test/escape_test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import unittest

import tornado.escape
import tornado
from tornado.escape import (
utf8,
xhtml_escape,
Expand Down
61 changes: 30 additions & 31 deletions tornado/test/import_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,38 +11,31 @@
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")
"""


Expand All @@ -56,6 +49,12 @@ def test_import_everything(self):
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
Expand Down
2 changes: 1 addition & 1 deletion tornado/test/util_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import datetime
import unittest

import tornado.escape
import tornado
from tornado.escape import utf8
from tornado.util import (
raise_exc_info,
Expand Down
2 changes: 1 addition & 1 deletion tornado/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
.. testcode::
import asyncio
import tornado.web
import tornado
class MainHandler(tornado.web.RequestHandler):
def get(self):
Expand Down
1 change: 0 additions & 1 deletion tornado/websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import os
import sys
import struct
import tornado.escape
import tornado.web
from urllib.parse import urlparse
import zlib
Expand Down

0 comments on commit ff23c7f

Please sign in to comment.