]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
all: Support lazy imports of submodules 3201/head
authorBen Darnell <ben@bendarnell.com>
Sun, 27 Nov 2022 20:06:08 +0000 (15:06 -0500)
committerBen Darnell <ben@bendarnell.com>
Sun, 27 Nov 2022 20:41:27 +0000 (15:41 -0500)
A getattr hook in the top-level "tornado" package now imports submodules
automatically, eliminating the need to explicitly reference multiple submodules
in imports

22 files changed:
README.rst
demos/blog/blog.py
demos/chat/chatdemo.py
demos/facebook/facebook.py
demos/file_upload/file_receiver.py
demos/helloworld/helloworld.py
demos/websocket/chatdemo.py
docs/auth.rst
docs/guide/security.rst
docs/guide/structure.rst
docs/guide/templates.rst
docs/index.rst
docs/websocket.rst
tornado/__init__.py
tornado/ioloop.py
tornado/iostream.py
tornado/options.py
tornado/test/escape_test.py
tornado/test/import_test.py
tornado/test/util_test.py
tornado/web.py
tornado/websocket.py

index 9b33a10bd4d825cfb0e3a529ca495ace9b82834e..1c689f5c154269519be8bda66962dc276b2b5f39 100644 (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):
index 66e42b69a9db406b0e98a266ee5ff81f866d1266..215c40509f76e9753e9164ae9c118063e8c4f65c 100755 (executable)
@@ -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
index f96ac0515b9344ee4bccef2e1a54b93eb7bd10d6..28c12108f27d29a2f31fe7bf92ada287d444b3ec 100755 (executable)
@@ -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
 
index c23c024740df840a1ea89263f40d3da6d27eb3be..480c8028781314155a19fab7790421df8bd52a46 100755 (executable)
 
 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
 
index 360c8aa58df03eee4870a5e2e50b708b3ddb68c7..5390715e5fe7dec1782a0226a40f3e8de4641b76 100755 (executable)
@@ -12,7 +12,7 @@ import asyncio
 import logging
 from urllib.parse import unquote
 
-import tornado.web
+import tornado
 from tornado import options
 
 
index e19b61bb164e52836a0fdda6255290f1acc9a81c..f33440cff61e95ba9ccbd68e1e218a494bb656ef 100755 (executable)
@@ -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
 
index 594a7c5e0988b3214db570a7c1afd9aa5123f974..05781c757ed846b86eafe2266eada3dcd070a174 100755 (executable)
@@ -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
 
index dfbe3ed9849b0567b07fc6b51fdd08c6c3e3aaad..5033948155bcb88b184c5df6ba43562d4d8c8e0d 100644 (file)
@@ -3,7 +3,7 @@
 
 .. testsetup::
 
-   import tornado.auth, tornado.gen, tornado.web
+   import tornado
 
 .. automodule:: tornado.auth
 
index 008d614e53e7a6a398faac9d5f6703df2a75532f..ea2b87ff1d4cb2e02ef58787d4ca384ea0c740d5 100644 (file)
@@ -3,8 +3,7 @@ Authentication and security
 
 .. testsetup::
 
-   import tornado.auth
-   import tornado.web
+   import tornado
 
 Cookies and secure cookies
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
index 42a8e63b43ee2c5080657f99d2a10068488224f1..ad927606bf9d0b30c62a2996642bc66f3aed5528 100644 (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):
index 61ce753e6a955a6c89ed1830cbe452748b3cd68e..73440dae17cb5c55b51a7318559b9341d5ca6c3b 100644 (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
index 024bc393acff2b7f671bae9b4bae566b7c28beac..c33fb0b0803970293d9d5c7682917e379a7a7f62 100644 (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):
index 76bc05227e115482f88f856af76450bcd7da8fb2..b56a4ec30a959922404fc7f8228e7bebc88be3fd 100644 (file)
@@ -3,7 +3,7 @@
 
 .. testsetup::
 
-   import tornado.websocket
+   import tornado
 
 .. automodule:: tornado.websocket
 
index f93e8e2acdf6dd32bced9549b131389463576140..060b836a9c8120b28cb633212f9d26a8f7853e23 100644 (file)
 # 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}")
index 2c05755db2e7211f912abebecb7efc68a4d29968..25609790d52e7333ce8c946ea0cf61c92fba2b4b 100644 (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):
index 96b47f5b1af9b16d456e432f1119d494d7ab761b..a408be59cd8b59f0ca00832316a1cc651a914fe0 100644 (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)
index 6ec58996484de2466c2eaa2157f6893e18bf8dd6..b82966910b16b0cbba6c19dfac2f77fda7ac9f4a 100644 (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()
index d8f95e426e27dcbadacaf463010613df27459f15..d067f645087c774ff782c5770ade42200fab4a0d 100644 (file)
@@ -1,6 +1,6 @@
 import unittest
 
-import tornado.escape
+import tornado
 from tornado.escape import (
     utf8,
     xhtml_escape,
index cae103810ac27c974ce14e2248eb116e46ea38dc..bff661d748231a2d7fb35740640195ca04610eec 100644 (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
index 0cbc13c60f358a0878d959c3e840b4c2d6cf95f5..ec8ee121bad29da4c1f822bd5b215c7917c9ec74 100644 (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,
index cd6a81b4b50ed3f9d6c234234db542698dfa0ef3..75bb46c9433070b015ec83b3f0c6e97a702cd953 100644 (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):
index 43142b328108329c3703eac53abeb98aa0c942b2..1d42e10baa2a4284f97c0f530b0b7d4933bc860e 100644 (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