Skip to content

Commit f253587

Browse files
committed
add Incomplete vs Any, the Any trick sections; add reference to error suppression formats section
1 parent d61da81 commit f253587

File tree

1 file changed

+53
-4
lines changed

1 file changed

+53
-4
lines changed

docs/guides/writing_stubs.rst

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Liskov substitutability or detecting problematic overloads.
9393
It may be instructive to examine `typeshed <https://github.com/python/typeshed/>`__'s
9494
`setup for testing stubs <https://github.com/python/typeshed/blob/main/tests/README.md>`__.
9595

96-
To suppress type errors in stubs, use `# type: ignore` comments. Refer to the style guide for
96+
To suppress type errors in stubs, use `# type: ignore` comments. Refer to the :ref:`type-checker-error-suppression` section of the style guide for
9797
error suppression formats specific to individual typecheckers.
9898

9999
..
@@ -213,15 +213,28 @@ to use them freely to describe simple structural types.
213213
Incomplete Stubs
214214
----------------
215215

216+
When writing new stubs, it is not necessary to fully annotate all arguments,
217+
return types, and fields. Some items may be left unannotated or
218+
annotated with `_typeshed.Incomplete` (`documentation <https://github.com/python/typeshed/blob/main/stdlib/_typeshed/README.md>`_).::
219+
220+
from _typeshed import Incomplete
221+
222+
field: Incomplete # unannotated
223+
224+
def foo(x): ... # unannotated argument and return type
225+
226+
`Incomplete` can also be used for partially known types::
227+
228+
def foo(x: Incomplete | None = None) -> list[Incomplete]: ...
229+
216230
Partial stubs can be useful, especially for larger packages, but they should
217231
follow the following guidelines:
218232

219233
* Included functions and methods should list all arguments, but the arguments
220234
can be left unannotated.
221235
* Do not use ``Any`` to mark unannotated or partially annotated values. Leave
222236
function parameters and return values unannotated. In all other cases, use
223-
``_typeshed.Incomplete``
224-
(`documentation <https://github.com/python/typeshed/blob/main/stdlib/_typeshed/README.md>`_)::
237+
``_typeshed.Incomplete``::
225238

226239
from _typeshed import Incomplete
227240

@@ -254,6 +267,40 @@ annotated function ``bar()``::
254267

255268
def bar(x: str, y, *, z=...): ...
256269

270+
`Any` vs. `Incomplete`
271+
----------------------
272+
273+
While `Incomplete` is a type alias of `Any`, they serve difference purposes:
274+
`Incomplete` is a placeholder where a proper type might be substituted.
275+
It's a "to do" item and should be replaced if possible. `Any` is used when
276+
it's not possible to accurately type an item using the current type system.
277+
It should be used sparingly.
278+
279+
The `Any` trick
280+
---------------
281+
282+
In cases where a function or method can return `None`, but where forcing the
283+
user to explicitly check for `None` can be detrimental, use
284+
`_typeshed.MaybeNone` (an alias to `Any`), instead of `None`.
285+
286+
Consider the following (simplified) signature of `re.Match[str].group`::
287+
288+
class Match:
289+
def group(self, group: str | int, /) -> str | MaybeNone: ...
290+
291+
This avoid forcing the user to check for `None`::
292+
293+
match = re.fullmatch(r"\d+_(.*)", some_string)
294+
assert match is not None
295+
name_group = match.group(1) # The user knows that this will never be None
296+
return name_group.uper() # This typo will be flagged by the type checker
297+
298+
In this case, the user of `match.group()` must be prepared to handle a `str`,
299+
but type checkers are happy with `if name_group is None` checks, because we're
300+
saying it can also be something else than an `str`.
301+
302+
This is sometimes called "the Any trick".
303+
257304
Attribute Access
258305
----------------
259306

@@ -800,7 +847,9 @@ into any of those categories, use your best judgement.
800847
* Use `HasX` for protocols that have readable and/or writable attributes
801848
or getter/setter methods (e.g. `HasItems`, `HasFileno`).
802849

803-
Type Checker Error Suppression formats
850+
:: _type-checker-error-suppression:
851+
852+
Type Checker Error Suppression Formats
804853
--------------------------------------
805854

806855
* Use mypy error codes for mypy-specific `# type: ignore` annotations, e.g. `# type: ignore[override]` for Liskov Substitution Principle violations.

0 commit comments

Comments
 (0)