@@ -2,22 +2,22 @@ PEP: 727
22Title: Documentation Metadata in Typing
33Author: Sebastián Ramírez <
[email protected] >
44Sponsor: Jelle Zijlstra <
[email protected] >
5- Discussions-To: https://discuss.python.org/t/pep-727-documentation-metadata-in-typing/ 32566
5+ Discussions-To: https://discuss.python.org/t/32566
66Status: Draft
77Type: Standards Track
88Topic: Typing
99Content-Type: text/x-rst
1010Created: 28-Aug-2023
1111Python-Version: 3.13
12- Post-History: `30-Aug-2023 <https://discuss.python.org/t/pep-727-documentation-metadata-in-typing/ 32566 >`__
12+ Post-History: `30-Aug-2023 <https://discuss.python.org/t/32566 >`__
1313
1414
1515Abstract
1616========
1717
1818This document proposes a way to complement docstrings to add additional documentation
19- to Python symbols using type annotations with `` Annotated `` (in class attributes,
20- function and method parameters, return values, and variables).
19+ to Python symbols using type annotations with :py:class: ` ~typing. Annotated `
20+ (in class attributes, function and method parameters, return values, and variables).
2121
2222
2323Motivation
@@ -60,7 +60,7 @@ documentation in some other way (e.g. an API, a CLI, etc).
6060Some of these previous formats tried to account for the lack of type annotations
6161in older Python versions by including typing information in the docstrings,
6262but now that information doesn't need to be in docstrings as there is now an official
63- syntax for type annotations.
63+ :pep: ` syntax for type annotations < 484 >` .
6464
6565
6666Rationale
@@ -84,79 +84,56 @@ like to adopt it.
8484Specification
8585=============
8686
87+ The main proposal is to introduce a new function, ``typing.doc() ``,
88+ to be used when documenting Python objects.
89+ This function MUST only be used within :py:class: `~typing.Annotated ` annotations.
90+ The function takes a single string argument, ``documentation ``,
91+ and returns an instance of ``typing.DocInfo ``,
92+ which stores the input string unchanged.
8793
88- ``typing.doc ``
89- --------------
94+ Any tool processing ``typing.DocInfo `` objects SHOULD interpret the string as
95+ a docstring, and therefore SHOULD normalize whitespace
96+ as if ``inspect.cleandoc() `` were used.
9097
91- The main proposal is to have a new function ``doc() `` in the ``typing `` module.
92- Even though this is not strictly related to the type annotations, it's expected
93- to go in ``Annotated `` type annotations, and to interact with type annotations.
98+ The string passed to ``typing.doc() `` SHOULD be of the form that would be a valid docstring.
99+ This means that `f-strings `__ and string operations SHOULD NOT be used.
100+ As this cannot be enforced by the Python runtime,
101+ tools SHOULD NOT rely on this behaviour,
102+ and SHOULD exit with an error if such a prohibited string is encountered.
94103
95- There's also the particular benefit that it could be implemented in the
96- ``typing_extensions `` package to have support for older versions of Python and
97- early adopters of this proposal.
98-
99- This ``doc() `` function would receive one single parameter ``documentation `` with
100- a documentation string.
101-
102- This string could be a multi-line string, in which case, when extracted by tools,
103- should be interpreted cleaning up indentation as if using ``inspect.cleandoc() ``,
104- the same procedure used for docstrings.
105-
106- This string could probably contain markup, like Markdown or reST. As that could
107- be highly debated, that decision is left for a future proposal, to focus here
108- on the main functionality.
109-
110- This specification targets static analysis tools and editors, and as such, the
111- value passed to ``doc() `` should allow static evaluation and analysis. If a
112- developer passes as the value something that requires runtime execution
113- (e.g. a function call) the behavior of static analysis tools is unspecified
114- and they could omit it from their process and results. For static analysis
115- tools to be conformant with this specification they need only to support
116- statically accessible values.
117-
118- An example documenting the attributes of a class, or in this case, the keys
119- of a ``TypedDict ``, could look like this:
120-
121- .. code-block ::
122-
123- from typing import Annotated, TypedDict, NotRequired, doc
124-
125-
126- class User(TypedDict):
127- firstname: Annotated[str, doc("The user's first name")]
128- lastname: Annotated[str, doc("The user's last name")]
104+ __ https://docs.python.org/3/reference/lexical_analysis.html#formatted-string-literals
129105
106+ Examples
107+ --------
130108
131- An example documenting the parameters of a function could look like this :
109+ Class attributes may be documented :
132110
133- .. code-block ::
111+ .. code :: python
134112
135- from typing import Annotated, doc
113+ from typing import Annotated, doc
136114
115+ class User :
116+ first_name: Annotated[str , doc(" The user's first name" )]
117+ last_name: Annotated[str , doc(" The user's last name" )]
137118
138- def create_user(
139- lastname: Annotated[str, doc("The **last name** of the newly created user")],
140- firstname: Annotated[str | None, doc("The user's **first name**")] = None,
141- ) -> Annotated[User, doc("The created user after saving in the database")]:
142- """
143- Create a new user in the system, it needs the database connection to be already
144- initialized.
145- """
146- pass
119+ ...
147120
121+ As can function or method parameters:
148122
149- The return of the ``doc() `` function is an instance of a class that can be checked
150- and used at runtime, defined similar to:
123+ .. code :: python
151124
152- .. code-block ::
125+ from typing import Annotated, doc
153126
154- class DocInfo:
155- def __init__(self, documentation: str):
156- self.documentation = documentation
127+ def create_user (
128+ first_name : Annotated[str , doc(" The user's first name" )],
129+ last_name : Annotated[str , doc(" The user's last name" )],
130+ cursor : DatabaseConnection | None = None ,
131+ ) -> Annotated[User, doc(" The created user after saving in the database" )]:
132+ """ Create a new user in the system.
157133
158- ...where the attribute ``documentation `` contains the same value string passed to
159- the function ``doc() ``.
134+ It needs the database connection to be already initialized.
135+ """
136+ pass
160137
161138
162139 Additional Scenarios
@@ -171,52 +148,48 @@ but implementers are not required to support them.
171148
172149
173150Type Alias
174- ----------
151+ ''''''''''
175152
176153When creating a type alias, like:
177154
178- .. code-block ::
155+ .. code :: python
179156
180- Username = Annotated[str, doc("The name of a user in the system")]
157+ Username = Annotated[str , doc(" The name of a user in the system" )]
181158
182159
183- ...the documentation would be considered to be carried by the parameter annotated
160+ The documentation would be considered to be carried by the parameter annotated
184161with ``Username ``.
185162
186163So, in a function like:
187164
188- .. code-block ::
165+ .. code :: python
189166
190- def hi(
191- to: Username,
192- ) -> None: ...
167+ def hi (to : Username) -> None : ...
193168
194169
195- ...it would be equivalent to:
170+ It would be equivalent to:
196171
197- .. code-block ::
172+ .. code :: python
198173
199- def hi(
200- to: Annotated[str, doc("The name of a user in the system")],
201- ) -> None: ...
174+ def hi (to : Annotated[str , doc(" The name of a user in the system" )]) -> None : ...
202175
203176 Nevertheless, implementers would not be required to support type aliases outside
204177of the final type annotation to be conformant with this specification, as it
205178could require more complex dereferencing logic.
206179
207180
208181Annotating Type Parameters
209- --------------------------
182+ ''''''''''''''''''''''''''
210183
211184When annotating type parameters, as in:
212185
213- .. code-block ::
186+ .. code :: python
214187
215- def hi(
216- to: list[Annotated[str, doc("The name of a user in a list")]],
217- ) -> None: ...
188+ def hi (
189+ to : list[Annotated[str , doc(" The name of a user in a list" )]],
190+ ) -> None : ...
218191
219- ...the documentation in ``doc() `` would refer to what it is annotating, in this
192+ The documentation in ``doc() `` would refer to what it is annotating, in this
220193case, each item in the list, not the list itself.
221194
222195There are currently no practical use cases for documenting type parameters,
@@ -225,17 +198,17 @@ conformant, but it's included for completeness.
225198
226199
227200Annotating Unions
228- -----------------
201+ '''''''''''''''''
229202
230203If used in one of the parameters of a union, as in:
231204
232- .. code-block ::
205+ .. code :: python
233206
234- def hi(
235- to: str | Annotated[list[str], doc("List of user names")],
236- ) -> None: ...
207+ def hi (
208+ to : str | Annotated[list[str ], doc(" List of user names" )],
209+ ) -> None : ...
237210
238- ...again , the documentation in ``doc() `` would refer to what it is annotating,
211+ Again , the documentation in ``doc() `` would refer to what it is annotating,
239212in this case, this documents the list itself, not its items.
240213
241214In particular, the documentation would not refer to a single string passed as a
@@ -247,22 +220,23 @@ included for completeness.
247220
248221
249222Nested ``Annotated ``
250- --------------------
223+ ''''''''''''''''''''
251224
252225Continuing with the same idea above, if ``Annotated `` was used nested and used
253226multiple times in the same parameter, ``doc() `` would refer to the type it
254227is annotating.
255228
256229So, in an example like:
257230
258- .. code-block ::
231+ .. code :: python
259232
260- def hi(
261- to: Annotated[
262- Annotated[str, doc("A user name")] | Annotated[list, doc("A list of user names")],
263- doc("Who to say hi to"),
264- ],
265- ) -> None: ...
233+ def hi (
234+ to : Annotated[
235+ Annotated[str , doc(" A user name" )]
236+ | Annotated[list , doc(" A list of user names" )],
237+ doc(" Who to say hi to" ),
238+ ],
239+ ) -> None : ...
266240
267241
268242 The documentation for the whole parameter ``to `` would be considered to be
@@ -281,16 +255,16 @@ of the parameter passed is of one type or another, but they are not required to
281255
282256
283257Duplication
284- -----------
258+ '''''''''''
285259
286260If ``doc() `` is used multiple times in a single ``Annotated ``, it would be
287261considered invalid usage from the developer, for example:
288262
289- .. code-block ::
263+ .. code :: python
290264
291- def hi(
292- to: Annotated[str, doc("A user name"), doc("The current user name")],
293- ) -> None: ...
265+ def hi (
266+ to : Annotated[str , doc(" A user name" ), doc(" The current user name" )],
267+ ) -> None : ...
294268
295269
296270 Implementers can consider this invalid and are not required to support this to be
@@ -302,46 +276,68 @@ can opt to support one of the ``doc()`` declarations.
302276In that case, the suggestion would be to support the last one, just because
303277this would support overriding, for example, in:
304278
305- .. code-block ::
279+ .. code :: python
306280
307- User = Annotated[str, doc("A user name")]
281+ User = Annotated[str , doc(" A user name" )]
308282
309- CurrentUser = Annotated[User, doc("The current user name")]
283+ CurrentUser = Annotated[User, doc(" The current user name" )]
310284
311285
312286 Internally, in Python, ``CurrentUser `` here is equivalent to:
313287
314- .. code-block ::
288+ .. code :: python
315289
316- CurrentUser = Annotated[str, doc("A user name"), doc("The current user name")]
290+ CurrentUser = Annotated[str ,
291+ doc(" A user name" ),
292+ doc(" The current user name" )]
317293
318294
319295 For an implementation that supports the last ``doc() `` appearance, the above
320296example would be equivalent to:
321297
322- .. code-block ::
298+ .. code :: python
299+
300+ def hi (to : Annotated[str , doc(" The current user name" )]) -> None : ...
301+
302+
303+ .. you need to fill these in:
304+
305+ Backwards Compatibility
306+ =======================
307+
308+ [Describe potential impact and severity on pre-existing code.]
309+
310+
311+ Security Implications
312+ =====================
313+
314+ [How could a malicious user take advantage of this new feature?]
315+
316+
317+ How to Teach This
318+ =================
319+
320+ [How to teach users, new and experienced, how to apply the PEP to their work.]
323321
324- def hi(
325- to: Annotated[str, doc("The current user name")],
326- ) -> None: ...
327322
323+ Reference Implementation
324+ ========================
328325
329- Early Adopters and Older Python Versions
330- ========================================
326+ ``typing.doc `` and ``typing.DocInfo `` are implemented as follows:
331327
332- For older versions of Python and early adopters of this proposal, ``doc() `` and
333- ``DocInfo `` can be imported from the ``typing_extensions `` package.
328+ .. code :: python
334329
335- .. code-block ::
330+ def doc (documentation : str , / ) -> DocInfo:
331+ return DocInfo(documentation)
336332
337- from typing import Annotated
333+ class DocInfo :
334+ def __init__ (self , documentation : str , / ):
335+ self .documentation = documentation
338336
339- from typing_extensions import doc
340337
338+ These have been implemented in the `typing_extensions `__ package.
341339
342- def hi(
343- to: Annotated[str, doc("The current user name")],
344- ) -> None: ...
340+ __ https://pypi.org/project/typing-extensions/
345341
346342
347343Rejected Ideas
@@ -407,8 +403,8 @@ to be used by those that are willing to take the extra verbosity in exchange
407403for the benefits.
408404
409405
410- Doc is not Typing
411- -----------------
406+ Documentation is not Typing
407+ ---------------------------
412408
413409It could also be argued that documentation is not really part of typing, or that
414410it should live in a different module. Or that this information should not be part
0 commit comments