sphinx
sphinx copied to clipboard
Add py:type directive and role for documenting type aliases
Feature or Bugfix
- Feature
Purpose
This pull request adds a py:type directive for documenting type aliases (https://typing.readthedocs.io/en/latest/spec/aliases.html#type-aliases), and a py:type role for linking to them.
.. py:type:: Alias1
:value: list[str | int]
This is my type alias.
:py:type:`Alias1` is my type alias.
Detail
-
I've chosen to make a new directive for documenting type aliases, rather than add a new option to the
dataorattributedirectives, because not all of the options on thedataandattributedirectives apply to a type alias (eg.type). In addition, type aliases seem to be classified differently from variable assignments by Python. Python documentation treats type aliases almost like first class citizens.- Documenting with a new directive means that if a user is currently using
typein their code, but they're documenting it using the workaround of documenting it as an assignment (eg... py:data:: Url\n :type: TypeAlias\n :value: str), the user will likely want to update their documentation to use this new directive. I'm assuming that the number of users usingtypein their code is quite low, and therefore this trade off seemed worth it.
- Documenting with a new directive means that if a user is currently using
-
I've chosen to name the directive and role "type" because that's also the keyword used to define a type alias in Python. I considered using "typealias", "alias", or some thing similar, but it seemed safer to choose the same word used in Python syntax because CPython may change the usage of the
typekeyword in the future to define more than just type aliases.- This overlaps with the
:type:field, which potentially makes understanding rST syntax more confusing for users who are learning rST for the first time. But this trade-off seemed worth it.
- This overlaps with the
-
I've chosen to have users specify the type being aliased in a
:value:option, rather than in the signature (eg... py:type:: MyAlias = int), because this is consistent with how thedataandattributedirectives work. So I think it makes the syntax more intuitive for users. -
I've chosen to make specifying a value of the alias optional because there could be cases where a user wants to declare the alias, but not make the type that is aliased public. For example:
type MyAlias = str def generate() -> MyAlias: ... def accept(arg: MyAlias) -> None: ... # To be used like the following: accept(generate())In the above example, a user may want to allow users to pass this type between parts of the API, without letting the user do anything else with instance of
MyAlias, by not documenting whatMyAliasis aliased from. So the user would document this as follows:.. py:type:: MyAlias .. py:function:: generate() -> MyAlias .. py:function:: accept(arg: MyAlias) -> NoneUsers can currently do something similar with classes, where they might define a class but not document any of the attributes on it.
.. py:class:: MyClass .. py:function:: generate() -> MyClass .. py:function:: accept(arg: MyClass) -> None -
I've chosen to name the option "value" because this is consistent with the
dataandattributedirectives. In addition, this is the terminology used intyping.TypeAliasTypeand in the ast node.>>> import ast >>> class A: ... type MyAlias = int ... >>> type(A.MyAlias) <class 'typing.TypeAliasType'> >>> A.MyAlias.__value__ # Note the attribute name "__value__" <class 'int'> >>> m = ast.parse("type PuzzleInput = list[tuple[list[str], int]]") >>> m.body[0].value # Note the attribute name "value" <ast.Subscript object at 0x7dd5ba91afd0>
Relates
- Closes #7896
I don't mind the use of :py:type: so I'll review it this w-e. Do you plan having an autodoc thing for this one? (either we wait for 3.12 to become the minimal version since the AST parser will be different, or we implement something to support X: TypeAlias = ...)
I wasn't planning on implementing it in autodoc myself because my use case doesn't require it. But I can implement it if preferred. Either way I figured it belonged in a separate pull request?
Yes it would. I think we can delay the autodoc implementation for now since I want to hear from other maintainers the direction we'll take for improving autodoc.
since I want to hear from other maintainers the direction we'll take for improving autodoc.
@picnixz, if you have time, it would be great if you could open a discussion, similarish to #12152 😄; trying to outline the problem, the current implementation and your understanding of its design choices, then an outline of potential solutions
Obviously from I have some thoughts on this I could add (e.g. https://github.com/sphinx-extensions2/sphinx-autodoc2?tab=readme-ov-file#design-and-comparison-to-sphinx-autoapi), but would be good to baseline the discussion first
trying to outline the problem, the current implementation and your understanding of its design choices, then an outline of potential solutions
I'll try to find time for that tomorrow. And actually, tomorrow I'll do more a review thing rather than coding (#12219 is still a draft but it could be reviewed I think? I tried to have as much coverage as possible since this is something you want to use in tests).
I'll also review this PR since I said that I would do it last month...
Thanks @AWhetter!
A