]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
wsgi: Update docs
authorBen Darnell <ben@bendarnell.com>
Wed, 15 Feb 2023 21:19:42 +0000 (21:19 +0000)
committerBen Darnell <ben@bendarnell.com>
Thu, 16 Feb 2023 20:37:56 +0000 (20:37 +0000)
tornado/wsgi.py

index c37d7dc89b8ca3c5a16cdf84f6f50534e3919440..3b2ceb62275e116ae06008edf93bf62bbe645dc5 100644 (file)
@@ -57,20 +57,28 @@ def to_wsgi_str(s: bytes) -> str:
 
 
 class WSGIContainer(object):
-    r"""Makes a WSGI-compatible function runnable on Tornado's HTTP server.
+    r"""Makes a WSGI-compatible application runnable on Tornado's HTTP server.
 
     .. warning::
 
        WSGI is a *synchronous* interface, while Tornado's concurrency model
-       is based on single-threaded asynchronous execution.  This means that
-       running a WSGI app with Tornado's `WSGIContainer` is *less scalable*
-       than running the same app in a multi-threaded WSGI server like
-       ``gunicorn`` or ``uwsgi``.  Use `WSGIContainer` only when there are
-       benefits to combining Tornado and WSGI in the same process that
-       outweigh the reduced scalability.
+       is based on single-threaded *asynchronous* execution.  Many of Tornado's
+       distinguishing features are not available in WSGI mode, including efficient
+       long-polling and websockets. The primary purpose of `WSGIContainer` is
+       to support both WSGI applications and native Tornado ``RequestHandlers`` in
+       a single process. WSGI-only applications are likely to be better off
+       with a dedicated WSGI server such as ``gunicorn`` or ``uwsgi``.
+
+    Wrap a WSGI application in a `WSGIContainer` to make it implement the Tornado
+    `.HTTPServer` ``request_callback`` interface.  The `WSGIContainer` object can
+    then be passed to classes from the `tornado.routing` module,
+    `tornado.web.FallbackHandler`, or to `.HTTPServer` directly.
+
+    This class is intended to let other frameworks (Django, Flask, etc)
+    run on the Tornado HTTP server and I/O loop.
 
-    Wrap a WSGI function in a `WSGIContainer` and pass it to `.HTTPServer` to
-    run it. For example::
+    Realistic usage will be more complicated, but the simplest possible example uses a
+    hand-written WSGI application with `.HTTPServer`::
 
         def simple_app(environ, start_response):
             status = "200 OK"
@@ -86,32 +94,32 @@ class WSGIContainer(object):
 
         asyncio.run(main())
 
-    This class is intended to let other frameworks (Django, web.py, etc)
-    run on the Tornado HTTP server and I/O loop.
-
-    The `tornado.web.FallbackHandler` class is often useful for mixing
-    Tornado and WSGI apps in the same server.  See
-    https://github.com/bdarnell/django-tornado-demo for a complete example.
+    The recommended pattern is to use the `tornado.routing` module to set up routing
+    rules between your WSGI application and, typically, a `tornado.web.Application`.
+    Alternatively, `tornado.web.Application` can be used as the top-level router
+    and `tornado.web.FallbackHandler` can embed a `WSGIContainer` within it.
 
-    `WSGIContainer` supports executing the WSGI application in custom executors
-    using `.IOLoop.run_in_executor`. The default executor uses
-    ``tornado.concurrent.dummy_executor`` which works synchronously, but other
-    executors subclassing `concurrent.futures.Executor` may be used. To execute
-    WSGI application code in separate threads in an event-loop compatible way
-    use::
+    If the ``executor`` argument is provided, the WSGI application will be executed
+    on that executor. This must be an instance of `concurrent.futures.Executor`,
+    typically a ``ThreadPoolExecutor`` (``ProcessPoolExecutor`` is not supported).
+    If no ``executor`` is given, the application will run on the event loop thread in
+    Tornado 6.3; this will change to use an internal thread pool by default in
+    Tornado 7.0.
 
-        async def main():
-            executor = concurrent.futures.ThreadPoolExecutor()
-            # ^-- Execute requests in separate threads.
-            container = tornado.wsgi.WSGIContainer(simple_app, executor)
-            # Initialize the WSGI container with custom executor --^
-            http_server = tornado.httpserver.HTTPServer(container)
-            http_server.listen(8888)
-            await asyncio.Event().wait()
-
-    Running the WSGI app with a ``ThreadPoolExecutor`` remains *less scalable*
-    than running the same app in a multi-threaded WSGI server like ``gunicorn``
-    or ``uwsgi``.
+    .. warning::
+       By default, the WSGI application is executed on the event loop's thread. This
+       limits the server to one request at a time (per process), making it less scalable
+       than most other WSGI servers. It is therefore highly recommended that you pass
+       a ``ThreadPoolExecutor`` when constructing the `WSGIContainer`, after verifying
+       that your application is thread-safe. The default will change to use a
+       ``ThreadPoolExecutor`` in Tornado 7.0.
+
+    .. versionadded:: 6.3
+       The ``executor`` parameter.
+
+    .. deprecated:: 6.3
+       The default behavior of running the WSGI application on the event loop thread
+       is deprecated and will change in Tornado 7.0 to use a thread pool by default.
     """
 
     def __init__(