Skip to content

Commit

Permalink
Support PEP 695 and PEP 696 syntax in the Python domain (#11444)
Browse files Browse the repository at this point in the history
* Generic classes can be documented with ``.. py:class::`` using PEP 695 syntax:

  .. code:: rst

     .. py:class:: Sequence[T]

* Generic functions can be documented with ``.. py:function::`` using PEP 695 syntax:

  .. code:: rst

     .. py:function:: foo[T](x: T)

* Default values for type bounds are supported.

Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
  • Loading branch information
picnixz and AA-Turner authored Jul 23, 2023
1 parent f5aadc1 commit 480630c
Show file tree
Hide file tree
Showing 15 changed files with 934 additions and 45 deletions.
6 changes: 6 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ Features added
* #11157: Keep the ``translated`` attribute on translated nodes.
* #11451: Improve the traceback displayed when using :option:`sphinx-build -T`
in parallel builds. Patch by Bénédikt Tran
* #11438: Add support for the :rst:dir:`py:class` and :rst:dir:`py:function`
directives for PEP 695 (generic classes and functions declarations) and
PEP 696 (default type parameters). Multi-line support (#11011) is enabled
for type parameters list and can be locally controlled on object description
directives, e.g., :rst:dir:`py:function:single-line-type-parameter-list`.
Patch by Bénédikt Tran.

Bugs fixed
----------
Expand Down
7 changes: 7 additions & 0 deletions doc/latex.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1464,6 +1464,7 @@ Macros
``\sphinxtermref``; ``\emph{#1}``
``\sphinxsamedocref``; ``\emph{#1}``
``\sphinxparam``; ``\emph{#1}``
``\sphinxtypeparam``; ``\emph{#1}``
``\sphinxoptional``; ``[#1]`` with larger brackets, see source

.. versionadded:: 1.4.5
Expand All @@ -1485,6 +1486,12 @@ Macros
signatures (see :confval:`maximum_signature_line_length`). It defaults
to ``\texttt{,}`` to make these end-of-line separators more distinctive.

Signatures of Python functions are rendered as ``name<space>(parameters)``
or ``name<space>[type parameters]<space>(parameters)`` (see :pep:`695`)
where the length of ``<space>`` is set to ``0pt`` by default.
This can be changed via ``\setlength{\sphinxsignaturelistskip}{1ex}``
for instance.

- More text styling:

.. csv-table::
Expand Down
21 changes: 18 additions & 3 deletions doc/usage/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3025,9 +3025,24 @@ Options for the Python domain

.. confval:: python_maximum_signature_line_length

If a signature's length in characters exceeds the number set, each
argument will be displayed on an individual logical line. This is a
domain-specific setting, overriding :confval:`maximum_signature_line_length`.
If a signature's length in characters exceeds the number set,
each argument or type parameter will be displayed on an individual logical line.
This is a domain-specific setting,
overriding :confval:`maximum_signature_line_length`.

For the Python domain, the signature length depends on whether
the type parameters or the list of arguments are being formatted.
For the former, the signature length ignores the length of the arguments list;
for the latter, the signature length ignores the length of
the type parameters list.

For instance, with `python_maximum_signature_line_length = 20`,
only the list of type parameters will be wrapped
while the arguments list will be rendered on a single line

.. code:: rst
.. py:function:: add[T: VERY_LONG_SUPER_TYPE, U: VERY_LONG_SUPER_TYPE](a: T, b: U)
.. versionadded:: 7.1

Expand Down
99 changes: 91 additions & 8 deletions doc/usage/restructuredtext/domains.rst
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,16 @@ declarations:
The following directives are provided for module and class contents:

.. rst:directive:: .. py:function:: name(parameters)
.. py:function:: name[type parameters](parameters)
Describes a module-level function. The signature should include the
parameters as given in the Python function definition, see :ref:`signatures`.
Describes a module-level function.
The signature should include the parameters,
together with optional type parameters,
as given in the Python function definition, see :ref:`signatures`.
For example::

.. py:function:: Timer.repeat(repeat=3, number=1000000)
.. py:function:: Timer.repeat(repeat=3, number=1_000_000)
.. py:function:: add[T](a: T, b: T) -> T

For methods you should use :rst:dir:`py:method`.

Expand Down Expand Up @@ -240,6 +244,15 @@ The following directives are provided for module and class contents:
.. versionadded:: 7.1
.. rst:directive:option:: single-line-type-parameter-list
:type: no value
Ensure that the function's type parameters are emitted on a single
logical line, overriding :confval:`python_maximum_signature_line_length`
and :confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. py:data:: name
Expand Down Expand Up @@ -274,9 +287,12 @@ The following directives are provided for module and class contents:
the module specified by :rst:dir:`py:currentmodule`.
.. rst:directive:: .. py:exception:: name
.. py:exception:: name(parameters)
.. py:exception:: name[type parmeters](parameters)
Describes an exception class. The signature can, but need not include
parentheses with constructor arguments.
Describes an exception class.
The signature can, but need not include parentheses with constructor arguments,
or may optionally include type parameters (see :pep:`695`).

.. rubric:: options

Expand All @@ -293,12 +309,28 @@ The following directives are provided for module and class contents:
Describe the location where the object is defined. The default value is
the module specified by :rst:dir:`py:currentmodule`.
.. rst:directive:option:: single-line-parameter-list
:type: no value
See :rst:dir:`py:class:single-line-parameter-list`.
.. versionadded:: 7.1
.. rst:directive:option:: single-line-type-parameter-list
:type: no value
See :rst:dir:`py:class:single-line-type-parameter-list`.
.. versionadded:: 7.1
.. rst:directive:: .. py:class:: name
.. py:class:: name(parameters)
.. py:class:: name[type parmeters](parameters)
Describes a class. The signature can optionally include parentheses with
parameters which will be shown as the constructor arguments. See also
:ref:`signatures`.
Describes a class.
The signature can optionally include type parameters (see :pep:`695`)
or parentheses with parameters which will be shown as the constructor arguments.
See also :ref:`signatures`.

Methods and attributes belonging to the class should be placed in this
directive's body. If they are placed outside, the supplied name should
Expand Down Expand Up @@ -348,6 +380,13 @@ The following directives are provided for module and class contents:
.. versionadded:: 7.1
.. rst:directive:option:: single-line-type-parameter-list
:type: no value
Ensure that the class type parameters are emitted on a single logical
line, overriding :confval:`python_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. rst:directive:: .. py:attribute:: name
Describes an object data attribute. The description should include
Expand Down Expand Up @@ -410,6 +449,7 @@ The following directives are provided for module and class contents:
the module specified by :rst:dir:`py:currentmodule`.
.. rst:directive:: .. py:method:: name(parameters)
.. py:method:: name[type parameters](parameters)
Describes an object method. The parameters should not include the ``self``
parameter. The description should include similar information to that
Expand Down Expand Up @@ -469,6 +509,15 @@ The following directives are provided for module and class contents:
.. versionadded:: 7.1
.. rst:directive:option:: single-line-type-parameter-list
:type: no value
Ensure that the method's type parameters are emitted on a single logical
line, overriding :confval:`python_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.2
.. rst:directive:option:: staticmethod
:type: no value
Expand All @@ -478,19 +527,22 @@ The following directives are provided for module and class contents:
.. rst:directive:: .. py:staticmethod:: name(parameters)
.. py:staticmethod:: name[type parameters](parameters)
Like :rst:dir:`py:method`, but indicates that the method is a static method.

.. versionadded:: 0.4

.. rst:directive:: .. py:classmethod:: name(parameters)
.. py:classmethod:: name[type parameters](parameters)
Like :rst:dir:`py:method`, but indicates that the method is a class method.

.. versionadded:: 0.6

.. rst:directive:: .. py:decorator:: name
.. py:decorator:: name(parameters)
.. py:decorator:: name[type parameters](parameters)
Describes a decorator function. The signature should represent the usage as
a decorator. For example, given the functions
Expand Down Expand Up @@ -531,8 +583,18 @@ The following directives are provided for module and class contents:
.. versionadded:: 7.1
.. rst:directive:option:: single-line-type-parameter-list
:type: no value
Ensure that the decorator's type parameters are emitted on a single
logical line, overriding :confval:`python_maximum_signature_line_length`
and :confval:`maximum_signature_line_length`.
.. versionadded:: 7.2
.. rst:directive:: .. py:decoratormethod:: name
.. py:decoratormethod:: name(signature)
.. py:decoratormethod:: name[type parameters](signature)
Same as :rst:dir:`py:decorator`, but for decorators that are methods.

Expand Down Expand Up @@ -561,6 +623,27 @@ argument support), you can use brackets to specify the optional parts:

It is customary to put the opening bracket before the comma.

Python 3.12 introduced *type parameters*, which are type variables
declared directly within the class or function definition:

.. code:: python
class AnimalList[AnimalT](list[AnimalT]):
...
def add[T](a: T, b: T) -> T:
return a + b
The corresponding reStructuredText documentation would be:

.. code:: rst
.. py:class:: AnimalList[AnimalT]
.. py:function:: add[T](a: T, b: T) -> T
See :pep:`695` and :pep:`696` for details and the full specification.

.. _info-field-lists:

Info field lists
Expand Down
19 changes: 19 additions & 0 deletions sphinx/addnodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 +258,27 @@ def astext(self):
return f'({super().astext()})'


class desc_type_parameter_list(nodes.Part, nodes.Inline, nodes.FixedTextElement):
"""Node for a general type parameter list.
As default the type parameters list is written in line with the rest of the signature.
Set ``multi_line_parameter_list = True`` to describe a multi-line type parameters list.
In that case each type parameter will then be written on its own, indented line.
"""
child_text_separator = ', '

def astext(self):
return f'[{super().astext()}]'


class desc_parameter(nodes.Part, nodes.Inline, nodes.FixedTextElement):
"""Node for a single parameter."""


class desc_type_parameter(nodes.Part, nodes.Inline, nodes.FixedTextElement):
"""Node for a single type parameter."""


class desc_optional(nodes.Part, nodes.Inline, nodes.FixedTextElement):
"""Node for marking optional parts of the parameter list."""
child_text_separator = ', '
Expand Down Expand Up @@ -537,7 +554,9 @@ def setup(app: Sphinx) -> dict[str, Any]:
app.add_node(desc_type)
app.add_node(desc_returns)
app.add_node(desc_parameterlist)
app.add_node(desc_type_parameter_list)
app.add_node(desc_parameter)
app.add_node(desc_type_parameter)
app.add_node(desc_optional)
app.add_node(desc_annotation)

Expand Down
Loading

0 comments on commit 480630c

Please sign in to comment.