@@ -2,22 +2,22 @@ PEP: 727
2
2
Title: Documentation Metadata in Typing
3
3
Author: Sebastián Ramírez <
[email protected] >
4
4
Sponsor: 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
6
6
Status: Draft
7
7
Type: Standards Track
8
8
Topic: Typing
9
9
Content-Type: text/x-rst
10
10
Created: 28-Aug-2023
11
11
Python-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 >`__
13
13
14
14
15
15
Abstract
16
16
========
17
17
18
18
This 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).
21
21
22
22
23
23
Motivation
@@ -60,7 +60,7 @@ documentation in some other way (e.g. an API, a CLI, etc).
60
60
Some of these previous formats tried to account for the lack of type annotations
61
61
in older Python versions by including typing information in the docstrings,
62
62
but 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 >` .
64
64
65
65
66
66
Rationale
@@ -84,79 +84,56 @@ like to adopt it.
84
84
Specification
85
85
=============
86
86
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.
87
93
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.
90
97
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.
94
103
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
129
105
106
+ Examples
107
+ --------
130
108
131
- An example documenting the parameters of a function could look like this :
109
+ Class attributes may be documented :
132
110
133
- .. code-block ::
111
+ .. code :: python
134
112
135
- from typing import Annotated, doc
113
+ from typing import Annotated, doc
136
114
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" )]
137
118
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
+ ...
147
120
121
+ As can function or method parameters:
148
122
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
151
124
152
- .. code-block ::
125
+ from typing import Annotated, doc
153
126
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.
157
133
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
160
137
161
138
162
139
Additional Scenarios
@@ -171,52 +148,48 @@ but implementers are not required to support them.
171
148
172
149
173
150
Type Alias
174
- ----------
151
+ ''''''''''
175
152
176
153
When creating a type alias, like:
177
154
178
- .. code-block ::
155
+ .. code :: python
179
156
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" )]
181
158
182
159
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
184
161
with ``Username ``.
185
162
186
163
So, in a function like:
187
164
188
- .. code-block ::
165
+ .. code :: python
189
166
190
- def hi(
191
- to: Username,
192
- ) -> None: ...
167
+ def hi (to : Username) -> None : ...
193
168
194
169
195
- ...it would be equivalent to:
170
+ It would be equivalent to:
196
171
197
- .. code-block ::
172
+ .. code :: python
198
173
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 : ...
202
175
203
176
Nevertheless, implementers would not be required to support type aliases outside
204
177
of the final type annotation to be conformant with this specification, as it
205
178
could require more complex dereferencing logic.
206
179
207
180
208
181
Annotating Type Parameters
209
- --------------------------
182
+ ''''''''''''''''''''''''''
210
183
211
184
When annotating type parameters, as in:
212
185
213
- .. code-block ::
186
+ .. code :: python
214
187
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 : ...
218
191
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
220
193
case, each item in the list, not the list itself.
221
194
222
195
There are currently no practical use cases for documenting type parameters,
@@ -225,17 +198,17 @@ conformant, but it's included for completeness.
225
198
226
199
227
200
Annotating Unions
228
- -----------------
201
+ '''''''''''''''''
229
202
230
203
If used in one of the parameters of a union, as in:
231
204
232
- .. code-block ::
205
+ .. code :: python
233
206
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 : ...
237
210
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,
239
212
in this case, this documents the list itself, not its items.
240
213
241
214
In particular, the documentation would not refer to a single string passed as a
@@ -247,22 +220,23 @@ included for completeness.
247
220
248
221
249
222
Nested ``Annotated ``
250
- --------------------
223
+ ''''''''''''''''''''
251
224
252
225
Continuing with the same idea above, if ``Annotated `` was used nested and used
253
226
multiple times in the same parameter, ``doc() `` would refer to the type it
254
227
is annotating.
255
228
256
229
So, in an example like:
257
230
258
- .. code-block ::
231
+ .. code :: python
259
232
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 : ...
266
240
267
241
268
242
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
281
255
282
256
283
257
Duplication
284
- -----------
258
+ '''''''''''
285
259
286
260
If ``doc() `` is used multiple times in a single ``Annotated ``, it would be
287
261
considered invalid usage from the developer, for example:
288
262
289
- .. code-block ::
263
+ .. code :: python
290
264
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 : ...
294
268
295
269
296
270
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.
302
276
In that case, the suggestion would be to support the last one, just because
303
277
this would support overriding, for example, in:
304
278
305
- .. code-block ::
279
+ .. code :: python
306
280
307
- User = Annotated[str, doc("A user name")]
281
+ User = Annotated[str , doc(" A user name" )]
308
282
309
- CurrentUser = Annotated[User, doc("The current user name")]
283
+ CurrentUser = Annotated[User, doc(" The current user name" )]
310
284
311
285
312
286
Internally, in Python, ``CurrentUser `` here is equivalent to:
313
287
314
- .. code-block ::
288
+ .. code :: python
315
289
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" )]
317
293
318
294
319
295
For an implementation that supports the last ``doc() `` appearance, the above
320
296
example would be equivalent to:
321
297
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.]
323
321
324
- def hi(
325
- to: Annotated[str, doc("The current user name")],
326
- ) -> None: ...
327
322
323
+ Reference Implementation
324
+ ========================
328
325
329
- Early Adopters and Older Python Versions
330
- ========================================
326
+ ``typing.doc `` and ``typing.DocInfo `` are implemented as follows:
331
327
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
334
329
335
- .. code-block ::
330
+ def doc (documentation : str , / ) -> DocInfo:
331
+ return DocInfo(documentation)
336
332
337
- from typing import Annotated
333
+ class DocInfo :
334
+ def __init__ (self , documentation : str , / ):
335
+ self .documentation = documentation
338
336
339
- from typing_extensions import doc
340
337
338
+ These have been implemented in the `typing_extensions `__ package.
341
339
342
- def hi(
343
- to: Annotated[str, doc("The current user name")],
344
- ) -> None: ...
340
+ __ https://pypi.org/project/typing-extensions/
345
341
346
342
347
343
Rejected Ideas
@@ -407,8 +403,8 @@ to be used by those that are willing to take the extra verbosity in exchange
407
403
for the benefits.
408
404
409
405
410
- Doc is not Typing
411
- -----------------
406
+ Documentation is not Typing
407
+ ---------------------------
412
408
413
409
It could also be argued that documentation is not really part of typing, or that
414
410
it should live in a different module. Or that this information should not be part
0 commit comments