Skip to content

Commit 13678bd

Browse files
committed
add content from typeshed/CONTRIBUTING.md
1 parent a1d4d07 commit 13678bd

File tree

1 file changed

+145
-17
lines changed

1 file changed

+145
-17
lines changed

docs/guides/writing_stubs.rst

Lines changed: 145 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,15 @@ 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 on stubs:
97+
* use mypy error codes for mypy-specific `# type: ignore` annotations,
98+
e.g. `# type: ignore[override]` for Liskov Substitution Principle violations.
99+
* use pyright error codes for pyright-specific suppressions,
100+
e.g. `# pyright: ignore[reportGeneralTypeIssues]`.
101+
- pyright is configured to discard `# type: ignore` annotations.
102+
If you need both on the same line, mypy's annotation needs to go first,
103+
e.g. `# type: ignore[override] # pyright: ignore[reportGeneralTypeIssues]`.
104+
96105
..
97106
TODO: consider adding examples and configurations for specific type checkers
98107
@@ -113,18 +122,6 @@ Stub Content
113122
This section documents best practices on what elements to include or
114123
leave out of stub files.
115124

116-
Modules excluded fom stubs
117-
--------------------------
118-
119-
Not all modules should be included in stubs.
120-
121-
It is recommended to exclude:
122-
123-
1. Implementation details, with `multiprocessing/popen_spawn_win32.py <https://github.com/python/cpython/blob/main/Lib/multiprocessing/popen_spawn_win32.py>`_ as a notable example
124-
2. Modules that are not supposed to be imported, such as ``__main__.py``
125-
3. Protected modules that start with a single ``_`` char. However, when needed protected modules can still be added (see :ref:`undocumented-objects` section below)
126-
4. Tests
127-
128125
Public Interface
129126
----------------
130127

@@ -138,9 +135,17 @@ The following should always be included:
138135
* All objects included in ``__all__`` (if present).
139136

140137
Other objects may be included if they are not prefixed with an underscore
141-
or if they are being used in practice. (See the next section.)
138+
or if they are being used in practice.
139+
140+
Modules excluded from stubs
141+
---------------------------
142142

143-
.. _undocumented-objects:
143+
The following should not be included in stubs:
144+
145+
1. Implementation details, with `multiprocessing/popen_spawn_win32.py <https://github.com/python/cpython/blob/main/Lib/multiprocessing/popen_spawn_win32.py>`_ as a notable example
146+
2. Modules that are not supposed to be imported, such as ``__main__.py``
147+
3. Protected modules that start with a single ``_`` char. However, when needed protected modules can still be added (see :ref:`undocumented-objects` section below)
148+
4. Tests
144149

145150
Undocumented Objects
146151
--------------------
@@ -417,6 +422,28 @@ and the :ref:`best-practices`. There are a few exceptions, outlined below, that
417422
different structure of stub files into account and aim to create
418423
more concise files.
419424

425+
Syntax Example
426+
--------------
427+
428+
The below is an excerpt from the types for the `datetime` module.
429+
430+
MAXYEAR: int
431+
MINYEAR: int
432+
433+
class date:
434+
def __new__(cls, year: SupportsIndex, month: SupportsIndex, day: SupportsIndex) -> Self: ...
435+
@classmethod
436+
def fromtimestamp(cls, timestamp: float, /) -> Self: ...
437+
@classmethod
438+
def today(cls) -> Self: ...
439+
@classmethod
440+
def fromordinal(cls, n: int, /) -> Self: ...
441+
@property
442+
def year(self) -> int: ...
443+
def replace(self, year: SupportsIndex = ..., month: SupportsIndex = ..., day: SupportsIndex = ...) -> Self: ...
444+
def ctime(self) -> str: ...
445+
def weekday(self) -> int: ...
446+
420447
Maximum Line Length
421448
-------------------
422449

@@ -448,14 +475,14 @@ No::
448475

449476
def time_func() -> None: ...
450477

451-
def date_func() -> None: ... # do no leave unnecessary empty lines
478+
def date_func() -> None: ... # do not leave unnecessary empty lines
452479

453480
def ip_func() -> None: ...
454481

455482

456483
class Foo: # leave only one empty line above
457484
x: int
458-
class MyError(Exception): ... # leave an empty line between the classes
485+
class MyError(Exception): ...
459486

460487
Module Level Attributes
461488
-----------------------
@@ -575,6 +602,14 @@ No::
575602
...
576603
def to_int3(x: str) -> int: pass
577604

605+
Avoid invariant collection types (`list`, `dict`) for function parameters,
606+
in favor of covariant types like `Mapping` or `Sequence`.
607+
608+
Avoid union return types. See https://github.com/python/mypy/issues/1693
609+
610+
Use `float` instead of `int | float` for parameter annotations.
611+
See [PEP 484](https://peps.python.org/pep-0484/#the-numeric-tower).
612+
578613
Language Features
579614
-----------------
580615

@@ -604,6 +639,14 @@ No::
604639

605640
class OtherClass: ...
606641

642+
Use variable annotations instead of type comments, even for stubs that target
643+
older versions of Python.
644+
645+
Platform-dependent APIs
646+
-----------------------
647+
648+
Use platform checks like `if sys.platform == 'win32'` to denote platform-dependent APIs.
649+
607650
NamedTuple and TypedDict
608651
------------------------
609652

@@ -631,7 +674,7 @@ No::
631674
Built-in Generics
632675
-----------------
633676

634-
:pep:`585` built-in generics are supported and should be used instead
677+
:pep:`585` built-in generics (such as `list`, `dict`, `tuple`, `set`) are supported and should be used instead
635678
of the corresponding types from ``typing``::
636679

637680
from collections import defaultdict
@@ -654,3 +697,88 @@ all type checkers::
654697

655698
def foo(x: int | str) -> int | None: ... # recommended
656699
def foo(x: Union[int, str]) -> Optional[int]: ... # ok
700+
701+
Using `Any` and `object`
702+
------------------------
703+
704+
When adding type hints, avoid using the `Any` type when possible. Reserve
705+
the use of `Any` for when:
706+
* the correct type cannot be expressed in the current type system; and
707+
* to avoid union returns (see above).
708+
709+
Note that `Any` is not the correct type to use if you want to indicate
710+
that some function can accept literally anything: in those cases use
711+
`object` instead.
712+
713+
When using `Any`, document the reason for using it in a comment. Ideally,
714+
document what types could be used.
715+
716+
Context Managers
717+
----------------
718+
719+
When adding type annotations for context manager classes, annotate
720+
the return type of `__exit__` as bool only if the context manager
721+
sometimes suppresses exceptions -- if it sometimes returns `True`
722+
at runtime. If the context manager never suppresses exceptions,
723+
have the return type be either `None` or `bool | None`. If you
724+
are not sure whether exceptions are suppressed or not or if the
725+
context manager is meant to be subclassed, pick `bool | None`.
726+
See https://github.com/python/mypy/issues/7214 for more details.
727+
728+
`__enter__` methods and other methods that return instances of the
729+
current class should be annotated with `typing_extensions.Self`
730+
([example](https://github.com/python/typeshed/blob/3581846/stdlib/contextlib.pyi#L151)).
731+
732+
Naming
733+
------
734+
735+
Type variables and aliases you introduce purely for legibility reasons
736+
should be prefixed with an underscore to make it obvious to the reader
737+
they are not part of the stubbed API.
738+
739+
A few guidelines for protocol names below. In cases that don't fall
740+
into any of those categories, use your best judgement.
741+
742+
* Use plain names for protocols that represent a clear concept
743+
(e.g. `Iterator`, `Container`).
744+
* Use `SupportsX` for protocols that provide callable methods (e.g.
745+
`SupportsInt`, `SupportsRead`, `SupportsReadSeek`).
746+
* Use `HasX` for protocols that have readable and/or writable attributes
747+
or getter/setter methods (e.g. `HasItems`, `HasFileno`).
748+
749+
`@deprecated`
750+
-------------
751+
752+
The `@typing_extensions.deprecated` decorator (`@warnings.deprecated`
753+
since Python 3.13) can be used to mark deprecated functionality; see
754+
[PEP 702](https://peps.python.org/pep-0702/).
755+
756+
A few guidelines for how to use it:
757+
758+
* In the standard library, apply the decorator only in Python versions
759+
where an appropriate replacement for the deprecated functionality
760+
exists. If in doubt, apply the decorator only on versions where the
761+
functionality has been explicitly deprecated, either through runtime
762+
warnings or in the documentation. Use `if sys.version_info` checks to
763+
apply the decorator only to some versions.
764+
* Keep the deprecation message concise, but try to mention the projected
765+
version when the functionality is to be removed, and a suggested
766+
replacement.
767+
768+
Imports
769+
-------
770+
771+
Imports in stubs are considered private (not part of the exported API)
772+
unless:
773+
* they use the form ``from library import name as name`` (sic, using
774+
explicit ``as`` even if the name stays the same); or
775+
* they use the form ``from library import *`` which means all names
776+
from that library are exported.
777+
778+
Forward References
779+
------------------
780+
781+
Stub files support forward references natively. In other words, the
782+
order of class declarations and type aliases does not matter in
783+
a stub file. Unlike regular Python files, you can use the name of the class within its own
784+
body without writing it as a comment.

0 commit comments

Comments
 (0)