diff --git a/peps/pep-0727.rst b/peps/pep-0727.rst index b30b34608..a6663a87b 100644 --- a/peps/pep-0727.rst +++ b/peps/pep-0727.rst @@ -136,11 +136,12 @@ conventions: * Elimination of the possibility of having inconsistencies between the name of a parameter in the signature and the name in the docstring when it is renamed. -* Reuse of documentation for symbols used in multiple places via type aliases. - * Access to the documentation string for each symbol at runtime, including existing (older) Python versions. +* A more formalized way to document other symbols, like type aliases, that could + use :py:class:`~typing.Annotated`. + * No microsyntax to learn for newcomers, it's just Python syntax. * Parameter documentation inheritance for functions captured @@ -171,9 +172,8 @@ For example: :py:class:`~typing.Annotated` is normally used as a type annotation, in those cases, any ``typing.Doc`` inside of it would document the symbol being annotated. -When :py:class:`~typing.Annotated` is used to declare a type alias and that type -alias is used in an annotation, ``typing.Doc`` would document the symbol being -annotated instead of exclusively the type alias. +When :py:class:`~typing.Annotated` is used to declare a type alias, ``typing.Doc`` +would then document the type alias symbol. For example: @@ -181,51 +181,29 @@ For example: from typing import Annotated, Doc, TypeAlias + from external_library import UserResolver - UserName: TypeAlias = Annotated[str, Doc("The user's name")] + CurrentUser: TypeAlias = Annotated[str, Doc("The current system user"), UserResolver()] + + def create_user(name: Annotated[str, Doc("The user's name")]): ... + + def delete_user(name: Annotated[str, Doc("The user to delete")]): ... - def create_user(name: UserName): ... - - def delete_user(name: UserName): ... - - -When a type alias is put inside of :py:class:`~typing.Annotated` and it has a -``typing.Doc``, the last one used (the top-most) takes precedence, this allows -overriding the documentation. - -For example: - -.. code:: python - - from typing import Annotated, Doc, TypeAlias - - - UserName: TypeAlias = Annotated[str, Doc("The user's name")] - - - def create_user(name: UserName): ... - - def delete_user(name: Annotated[UserName, Doc("The user to delete")]): ... - - -In this case, for the ``name`` parameter in ``delete_user()``, the documentation string -would be ``"The user to delete"``. +In this case, if a user imported ``CurrentUser``, tools like editors could provide +a tooltip with the documentation string when a user hovers over that symbol, or +documentation tools could include the type alias with its documentation in their +generated output. For tools extracting the information at runtime, they would normally use :py:func:`~typing.get_type_hints` with the parameter ``include_extras=True``, and as :py:class:`~typing.Annotated` is normalized (even with type aliases), this -would mean they should use the last ``typing.Doc`` available, as that is the last -one used. +would mean they should use the last ``typing.Doc`` available, if more than one is +used, as that is the last one used. At runtime, ``typing.Doc`` instances have an attribute ``documentation`` with the string passed to it. -When a type alias is used on its own, without annotating any additional symbol, -``typing.Doc`` documents the type alias itself. This would be useful if the -type alias is included on its own in documentation systems or if it's used directly -in some way, to show tooltips in editors. - When a function's signature is captured by a :py:class:`~typing.ParamSpec`, any documentation strings associated with the parameters should be retained. @@ -625,6 +603,50 @@ difficult to combine it with :py:class:`~typing.Annotated` for other purposes ( e.g. with FastAPI metadata, Pydantic fields, etc.) or adding additional metadata apart from the documentation string (e.g. deprecation). + +Transferring Documentation from Type aliases +-------------------------------------------- + +A previous version of this proposal specified that when type aliases declared with +:py:class:`~typing.Annotated` were used, and these type aliases were used in +annotations, the documentation string would be transferred to the annotated symbol. + +For example: + +.. code:: python + + from typing import Annotated, Doc, TypeAlias + + + UserName: TypeAlias = Annotated[str, Doc("The user's name")] + + + def create_user(name: UserName): ... + + def delete_user(name: UserName): ... + + +This was rejected after receiving feedback from the maintainer of one of the main +components used to provide editor support. + + +Shorthand with Slices +--------------------- + +In the discussion, it was suggested to use a shorthand with slices: + +.. code:: python + + is_approved: Annotated[str: "The status of a PEP."] + + +Although this is a very clever idea and would remove the need for a new ``Doc`` class, +runtime executing of current versions of Python don't allow it. + +At runtime, :py:class:`~typing.Annotated` requires at least two arguments, and it +requires the first argument to be type, it crashes if it is a slice. + + Open Issues =========== @@ -665,10 +687,6 @@ features. But again, as with type annotations, this would be optional and only to be used by those that are willing to take the extra verbosity in exchange for the benefits. -Additionally, if type aliases were used, documentation could be put outside of the -signature and the docstring, reducing the total verbosity of the signature and the -function body. - Of course, more advanced users might want to look at the source code of the libraries and if the authors of those libraries adopted this, those advanced users would end up having to look at that code with additional signature verbosity instead of docstring