Thanks to visit codestin.com
Credit goes to github.com

Skip to content

gh-86178: Add wsgiref.types #32335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 61 additions & 7 deletions Doc/library/wsgiref.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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`).

Expand All @@ -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)
Expand Down Expand Up @@ -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`
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.


Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This documentation feels a bit thin. Perhaps we could have examples of WSGI code using some of these aliases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree about the examples, but when I procrastinated over two options, I gave up :)

  1. Add an example to this section. But right below this section there is the "Example" section and this would feel like a duplication.
  2. Add types to the example section. But in a way this distracts from the point of the example.

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
--------

Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.11.rst
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,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
================
Expand Down
2 changes: 2 additions & 0 deletions Lib/wsgiref/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
54 changes: 54 additions & 0 deletions Lib/wsgiref/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""WSGI-related types for static type checking"""
Copy link
Member

@AlexWaygood AlexWaygood Apr 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be good to add a note to the module docstring that any changes contributors want to make to this file will need to be made to typeshed simultaneously if they're to have any meaning to static type checkers

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we should mention typeshed explicitly for a few reasons:

  • I don't want to burden CPython contributors to need to contribute to typeshed.
  • In typeshed we are pretty good in picking up changes, and for CPython we have quite some time to make these changes.
  • While this is currently not the case, type checkers could in theory use the implementation module to type check instead of typeshed. In fact, I have an idea to optionally direct type checkers to the stdlib in cases like this, so it's possible that this comment would get outdated during the life time of Python 3.11.


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]: ...
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add :mod:`wsgiref.types`, containing WSGI-specific types for static type
checking.