[Python-checkins] gh-86178: Add wsgiref.types (GH-32335)

JelleZijlstra webhook-mailer at python.org
Sat Apr 16 13:38:16 EDT 2022


https://github.com/python/cpython/commit/0ddc63b240340a952692b11dfe0810973393ed11
commit: 0ddc63b240340a952692b11dfe0810973393ed11
branch: main
author: Sebastian Rittau <srittau at rittau.biz>
committer: JelleZijlstra <jelle.zijlstra at gmail.com>
date: 2022-04-16T10:37:58-07:00
summary:

gh-86178: Add wsgiref.types (GH-32335)

files:
A Lib/wsgiref/types.py
A Misc/NEWS.d/next/Library/2022-04-05-17-18-13.bpo-42012.zMocQz.rst
M Doc/library/wsgiref.rst
M Doc/whatsnew/3.11.rst
M Lib/wsgiref/__init__.py

diff --git a/Doc/library/wsgiref.rst b/Doc/library/wsgiref.rst
index e924448b86d7f..0ca7b003bf4dd 100644
--- a/Doc/library/wsgiref.rst
+++ b/Doc/library/wsgiref.rst
@@ -23,6 +23,7 @@ an existing framework.
 be used to add WSGI support to a web server or framework.  It provides utilities
 for manipulating WSGI environment variables and response headers, base classes
 for implementing WSGI servers, a demo HTTP server that serves WSGI applications,
+types for static type checking,
 and a validation tool that checks WSGI servers and applications for conformance
 to the WSGI specification (:pep:`3333`).
 
@@ -43,7 +44,9 @@ This module provides a variety of utility functions for working with WSGI
 environments.  A WSGI environment is a dictionary containing HTTP request
 variables as described in :pep:`3333`.  All of the functions taking an *environ*
 parameter expect a WSGI-compliant dictionary to be supplied; please see
-:pep:`3333` for a detailed specification.
+:pep:`3333` for a detailed specification and
+:data:`~wsgiref.types.WSGIEnvironment` for a type alias that can be used
+in type annotations.
 
 
 .. function:: guess_scheme(environ)
@@ -150,7 +153,9 @@ also provides these miscellaneous utilities:
 
 .. class:: FileWrapper(filelike, blksize=8192)
 
-   A wrapper to convert a file-like object to an :term:`iterator`.  The resulting objects
+   A concrete implementation of the :class:`wsgiref.types.FileWrapper`
+   protocol used to convert a file-like object to an :term:`iterator`.
+   The resulting objects
    are :term:`iterable`\ s. As the object is iterated over, the
    optional *blksize* parameter will be repeatedly passed to the *filelike*
    object's :meth:`read` method to obtain bytestrings to yield.  When :meth:`read`
@@ -349,7 +354,8 @@ request.  (E.g., using the :func:`shift_path_info` function from
 
    .. method:: WSGIRequestHandler.get_environ()
 
-      Returns a dictionary containing the WSGI environment for a request.  The default
+      Return a :data:`~wsgiref.types.WSGIEnvironment` dictionary for a
+      request.  The default
       implementation copies the contents of the :class:`WSGIServer` object's
       :attr:`base_environ` dictionary attribute and then adds various headers derived
       from the HTTP request.  Each call to this method should return a new dictionary
@@ -558,13 +564,15 @@ input, output, and error streams.
 
    .. method:: BaseHandler.get_stdin()
 
-      Return an input stream object suitable for use as the ``wsgi.input`` of the
+      Return an object compatible with :class:`~wsgiref.types.InputStream`
+      suitable for use as the ``wsgi.input`` of the
       request currently being processed.
 
 
    .. method:: BaseHandler.get_stderr()
 
-      Return an output stream object suitable for use as the ``wsgi.errors`` of the
+      Return an object compatible with :class:`~wsgiref.types.ErrorStream`
+      suitable for use as the ``wsgi.errors`` of the
       request currently being processed.
 
 
@@ -703,8 +711,9 @@ input, output, and error streams.
 
    .. attribute:: BaseHandler.wsgi_file_wrapper
 
-      A ``wsgi.file_wrapper`` factory, or ``None``.  The default value of this
-      attribute is the :class:`wsgiref.util.FileWrapper` class.
+      A ``wsgi.file_wrapper`` factory, compatible with
+      :class:`wsgiref.types.FileWrapper`, or ``None``.  The default value
+      of this attribute is the :class:`wsgiref.util.FileWrapper` class.
 
 
    .. method:: BaseHandler.sendfile()
@@ -754,6 +763,51 @@ input, output, and error streams.
    .. versionadded:: 3.2
 
 
+:mod:`wsgiref.types` -- WSGI types for static type checking
+-----------------------------------------------------------
+
+.. module:: wsgiref.types
+   :synopsis: WSGI types for static type checking
+
+
+This module provides various types for static type checking as described
+in :pep:`3333`.
+
+.. versionadded:: 3.11
+
+
+.. class:: StartResponse()
+
+   A :class:`typing.Protocol` describing `start_response()
+   <https://peps.python.org/pep-3333/#the-start-response-callable>`_
+   callables (:pep:`3333`).
+
+.. data:: WSGIEnvironment
+
+   A type alias describing a WSGI environment dictionary.
+
+.. data:: WSGIApplication
+
+   A type alias describing a WSGI application callable.
+
+.. class:: InputStream()
+
+   A :class:`typing.Protocol` describing a `WSGI Input Stream
+   <https://peps.python.org/pep-3333/#input-and-error-streams>`_.
+
+.. class:: ErrorStream()
+
+   A :class:`typing.Protocol` describing a `WSGI Error Stream
+   <https://peps.python.org/pep-3333/#input-and-error-streams>`_.
+
+.. class:: FileWrapper()
+
+   A :class:`typing.Protocol` describing a `file wrapper
+   <https://peps.python.org/pep-3333/#optional-platform-specific-file-handling>`_.
+   See :class:`wsgiref.util.FileWrapper` for a concrete implementation of this
+   protocol.
+
+
 Examples
 --------
 
diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index b5b2a7648c55e..19e3d2f92fb94 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -233,6 +233,10 @@ New Modules
 * A new module, :mod:`tomllib`, was added for parsing TOML.
   (Contributed by Taneli Hukkinen in :issue:`40059`.)
 
+* :mod:`wsgiref.types`, containing WSGI-specific types for static type
+  checking, was added.
+  (Contributed by Sebastian Rittau in :issue:`42012`.)
+
 
 Improved Modules
 ================
diff --git a/Lib/wsgiref/__init__.py b/Lib/wsgiref/__init__.py
index 1efbba01a306e..59ee48fddec23 100644
--- a/Lib/wsgiref/__init__.py
+++ b/Lib/wsgiref/__init__.py
@@ -13,6 +13,8 @@
 * validate -- validation wrapper that sits between an app and a server
   to detect errors in either
 
+* types -- collection of WSGI-related types for static type checking
+
 To-Do:
 
 * cgi_gateway -- Run WSGI apps under CGI (pending a deployment standard)
diff --git a/Lib/wsgiref/types.py b/Lib/wsgiref/types.py
new file mode 100644
index 0000000000000..4a519e5682f3c
--- /dev/null
+++ b/Lib/wsgiref/types.py
@@ -0,0 +1,54 @@
+"""WSGI-related types for static type checking"""
+
+from collections.abc import Callable, Iterable
+from types import TracebackType
+from typing import Any, Protocol, TypeAlias
+
+__all__ = [
+    "StartResponse",
+    "WSGIEnvironment",
+    "WSGIApplication",
+    "InputStream",
+    "ErrorStream",
+    "FileWrapper",
+]
+
+_ExcInfo = tuple[type[BaseException], BaseException, TracebackType]
+_OptExcInfo = _ExcInfo | tuple[None, None, None]
+
+class StartResponse(Protocol):
+    """start_response() callable as defined in PEP 3333"""
+    def __call__(
+        self,
+        status: str,
+        headers: list[tuple[str, str]],
+        exc_info: _OptExcInfo | None = ...,
+        /,
+    ) -> Callable[[bytes], object]: ...
+
+WSGIEnvironment: TypeAlias = dict[str, Any]
+WSGIApplication: TypeAlias = Callable[[WSGIEnvironment, StartResponse],
+    Iterable[bytes]]
+
+class InputStream(Protocol):
+    """WSGI input stream as defined in PEP 3333"""
+    def read(self, size: int = ..., /) -> bytes: ...
+    def readline(self, size: int = ..., /) -> bytes: ...
+    def readlines(self, hint: int = ..., /) -> list[bytes]: ...
+    def __iter__(self) -> Iterable[bytes]: ...
+
+class ErrorStream(Protocol):
+    """WSGI error stream as defined in PEP 3333"""
+    def flush(self) -> object: ...
+    def write(self, s: str, /) -> object: ...
+    def writelines(self, seq: list[str], /) -> object: ...
+
+class _Readable(Protocol):
+    def read(self, size: int = ..., /) -> bytes: ...
+    # Optional: def close(self) -> object: ...
+
+class FileWrapper(Protocol):
+    """WSGI file wrapper as defined in PEP 3333"""
+    def __call__(
+        self, file: _Readable, block_size: int = ..., /,
+    ) -> Iterable[bytes]: ...
diff --git a/Misc/NEWS.d/next/Library/2022-04-05-17-18-13.bpo-42012.zMocQz.rst b/Misc/NEWS.d/next/Library/2022-04-05-17-18-13.bpo-42012.zMocQz.rst
new file mode 100644
index 0000000000000..ba84041782563
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-04-05-17-18-13.bpo-42012.zMocQz.rst
@@ -0,0 +1,2 @@
+Add :mod:`wsgiref.types`, containing WSGI-specific types for static type
+checking.



More information about the Python-checkins mailing list