Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add content from typeshed/CONTRIBUTING.md #1882

Merged
merged 15 commits into from
Dec 17, 2024
59 changes: 54 additions & 5 deletions docs/guides/writing_stubs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ Liskov substitutability or detecting problematic overloads.
It may be instructive to examine `typeshed <https://github.com/python/typeshed/>`__'s
`setup for testing stubs <https://github.com/python/typeshed/blob/main/tests/README.md>`__.

To suppress type errors on stubs, use a `# type: ignore` comment. Refer to the style guide for
To suppress type errors in stubs, use `# type: ignore` comments. Refer to the :ref:`type-checker-error-suppression` section of the style guide for
yangdanny97 marked this conversation as resolved.
Show resolved Hide resolved
error suppression formats specific to individual typecheckers.

..
Expand Down Expand Up @@ -213,15 +213,28 @@ to use them freely to describe simple structural types.
Incomplete Stubs
----------------

When writing new stubs, it is not necessary to fully annotate all arguments,
return types, and fields. Some items may be left unannotated or
annotated with `_typeshed.Incomplete` (`documentation <https://github.com/python/typeshed/blob/main/stdlib/_typeshed/README.md>`_).::
yangdanny97 marked this conversation as resolved.
Show resolved Hide resolved

from _typeshed import Incomplete

field: Incomplete # unannotated

def foo(x): ... # unannotated argument and return type

`Incomplete` can also be used for partially known types::

def foo(x: Incomplete | None = None) -> list[Incomplete]: ...

Partial stubs can be useful, especially for larger packages, but they should
follow the following guidelines:

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

from _typeshed import Incomplete

Expand Down Expand Up @@ -254,6 +267,40 @@ annotated function ``bar()``::

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

`Any` vs. `Incomplete`
----------------------

While `Incomplete` is a type alias of `Any`, they serve difference purposes:
`Incomplete` is a placeholder where a proper type might be substituted.
It's a "to do" item and should be replaced if possible. `Any` is used when
it's not possible to accurately type an item using the current type system.
It should be used sparingly.

The `Any` trick
---------------

In cases where a function or method can return `None`, but where forcing the
user to explicitly check for `None` can be detrimental, use
`_typeshed.MaybeNone` (an alias to `Any`), instead of `None`.

Consider the following (simplified) signature of `re.Match[str].group`::

class Match:
def group(self, group: str | int, /) -> str | MaybeNone: ...

This avoid forcing the user to check for `None`::

match = re.fullmatch(r"\d+_(.*)", some_string)
assert match is not None
name_group = match.group(1) # The user knows that this will never be None
return name_group.uper() # This typo will be flagged by the type checker

In this case, the user of `match.group()` must be prepared to handle a `str`,
but type checkers are happy with `if name_group is None` checks, because we're
saying it can also be something else than an `str`.

This is sometimes called "the Any trick".

Attribute Access
----------------

Expand Down Expand Up @@ -800,11 +847,13 @@ into any of those categories, use your best judgement.
* Use `HasX` for protocols that have readable and/or writable attributes
or getter/setter methods (e.g. `HasItems`, `HasFileno`).

Type Checker Error Suppression formats
.. _type-checker-error-suppression:

Type Checker Error Suppression Formats
--------------------------------------

* Use mypy error codes for mypy-specific `# type: ignore` annotations, e.g. `# type: ignore[override]` for Liskov Substitution Principle violations.
* Use pyright error codes for pyright-specific suppressions, e.g. `# pyright: ignore[reportGeneralTypeIssues]`. Pyright is configured to discard `# type: ignore` annotations.
* Use pyright error codes for pyright-specific suppressions, e.g. `# pyright: ignore[reportGeneralTypeIssues]`.
* If you need both on the same line, mypy's annotation needs to go first, e.g. `# type: ignore[override] # pyright: ignore[reportGeneralTypeIssues]`.


Expand Down
Loading