mirror of
https://github.com/davidhalter/typeshed.git
synced 2025-12-27 06:11:24 +08:00
This shuffles sections around between README.md and CONTRIBUTING.md. CONTRIBUTING now contains information pertaining to opening PRs, README all other information. I have also moved the list of maintainers to a separate file. I have kept most information intact for now, with two main exceptions: I removed duplicated information. For brevity's sake, I trimmed some explanations from the section about version checks. I have restructured the CONTRIBUTING file to follow the order of the introductory "contribution process at a glance" section. This now serves as a bit of a table of contents. Closes: #5422
534 lines
22 KiB
Markdown
534 lines
22 KiB
Markdown
# Contributing to typeshed
|
|
|
|
Welcome! typeshed is a community project that aims to work for a wide
|
|
range of Python users and Python codebases. If you're trying a type
|
|
checker on your Python code, your experience and what you can contribute
|
|
are important to the project's success.
|
|
|
|
|
|
## The contribution process at a glance
|
|
|
|
1. Find out [where to make your changes](#where-to-make-changes).
|
|
2. [Prepare your changes](#preparing-changes):
|
|
* Small fixes and additions can be submitted directly as pull requests,
|
|
but [contact us](README.md#discussion) before starting significant work.
|
|
* Create your stubs, considering [what to include](#what-to-include) and
|
|
conforming to the [coding style](#stub-file-coding-style).
|
|
* Reformat your stubs with `black` and `isort`.
|
|
3. If you want, set up your environment to be able to [run tests](#running-the-tests).
|
|
This can be useful for big pull requests or fixing specific errors, but
|
|
usually is not needed, because the tests run automatically on GitHub Actions
|
|
for all pull requests.
|
|
4. [Submit your changes](#submitting-changes) by opening a pull request.
|
|
5. You can expect a reply within a few days, but please be patient when
|
|
it takes a bit longer.
|
|
|
|
For more details, read below.
|
|
|
|
|
|
## Where to make changes
|
|
|
|
### Standard library stubs
|
|
|
|
The `stdlib` directory contains stubs for modules in the
|
|
Python standard library -- which
|
|
includes pure Python modules, dynamically loaded extension modules,
|
|
hard-linked extension modules, and the builtins. The `VERSIONS` file lists
|
|
the versions of Python where the module is available.
|
|
|
|
The structure of the `VERSIONS` file is as follows:
|
|
- Blank lines and lines starting with `#` are ignored.
|
|
- Lines contain the name of a top-level module, followed by a colon,
|
|
a space, and a version range (for example: `symbol: 2.7-3.9`).
|
|
|
|
Version ranges may be of the form "X.Y-A.B" or "X.Y-". The
|
|
first form means that a module was introduced in version X.Y and last
|
|
available in version A.B. The second form means that the module was
|
|
introduced in version X.Y and is still available in the latest
|
|
version of Python.
|
|
|
|
Python versions before 2.7 are ignored, so any module that was already
|
|
present in 2.7 will have "2.7" as its minimum version. Version ranges
|
|
for unsupported versions of Python 3 (currently 3.5 and lower) are
|
|
generally accurate but we do not guarantee their correctness.
|
|
|
|
The `stdlib/@python2` subdirectory contains Python 2-only stubs,
|
|
both for modules that must be kept different for Python 2 and 3, like
|
|
`builtins.pyi`, and for modules that only existed in Python 2, like
|
|
`ConfigParser.pyi`. The latter group of modules are not listed in
|
|
`VERSIONS`.
|
|
|
|
Note that if a package is present in `@python2`, any stub in the main
|
|
`stdlib` directory should be ignored when looking for Python 2 stubs. For
|
|
example, typeshed contains files `stdlib/@python2/collections.pyi` and
|
|
`stdlib/collections/abc.pyi`. A client looking for stubs for
|
|
`collections.abc` in Python 2 should not pick up the latter file, but
|
|
instead report that the module does not exist.
|
|
|
|
### Third-party library stubs
|
|
|
|
Modules that are not shipped with Python but have a type description in Python
|
|
go into `stubs`. Each subdirectory there represents a PyPI distribution, and
|
|
contains the following:
|
|
* `METADATA.toml` that specifies oldest version of the source library for
|
|
which the stubs are applicable, supported Python versions (Python 3 defaults
|
|
to `True`, Python 2 defaults to `False`), and dependency on other type stub
|
|
packages.
|
|
* Stubs (i.e. `*.pyi` files) for packages and modules that are shipped in the
|
|
source distribution. Similar to standard library, if the Python 2 version of
|
|
the stubs must be kept *separate*, it can be put in a `@python` subdirectory.
|
|
* (Rarely) some docs specific to a given type stub package in `README` file.
|
|
|
|
When a third party stub is
|
|
modified, an updated version of the corresponding distribution will be
|
|
automatically uploaded to PyPI within a few hours.
|
|
Each time this happens the least significant
|
|
version level is incremented. For example, if `stubs/foo/METADATA.toml` has
|
|
`version = "x.y"` the package on PyPI will be updated from `types-foo-x.y.n`
|
|
to `types-foo-x.y.n+1`.
|
|
|
|
*Note:* In its current implementation, typeshed cannot contain stubs for
|
|
multiple versions of the same third-party library. Prefer to generate
|
|
stubs for the latest version released on PyPI at the time of your
|
|
stubbing. The oldest version of the library for which the stubs are still
|
|
applicable (i.e. reflect the actual runtime behaviour) can be indicated
|
|
in `METADATA.toml` as `version = "x.y"`. Note that only two most significant
|
|
version levels are supported (i.e. only single dot). When a significant change
|
|
is made in the library, the version of the stub should be bumped (note that
|
|
previous versions are still available on PyPI).
|
|
|
|
## Preparing Changes
|
|
|
|
### Before you begin
|
|
|
|
If your change will be a significant amount of work to write, we highly
|
|
recommend starting by opening an issue laying out what you want to do.
|
|
That lets a conversation happen early in case other contributors disagree
|
|
with what you'd like to do or have ideas that will help you do it.
|
|
|
|
### Format
|
|
|
|
Each Python module is represented by a `.pyi` "stub file". This is a
|
|
syntactically valid Python file, although it usually cannot be run by
|
|
Python 3 (since forward references don't require string quotes). All
|
|
the methods are empty.
|
|
|
|
Python function annotations ([PEP 3107](https://www.python.org/dev/peps/pep-3107/))
|
|
are used to describe the signature of each function or method.
|
|
|
|
See [PEP 484](http://www.python.org/dev/peps/pep-0484/) for the exact
|
|
syntax of the stub files and [below](#stub-file-coding-style) for the
|
|
coding style used in typeshed.
|
|
|
|
### What to include
|
|
|
|
Stubs should include the complete interface (classes, functions,
|
|
constants, etc.) of the module they cover, but it is not always
|
|
clear exactly what is part of the interface.
|
|
|
|
The following should always be included:
|
|
- All objects listed in the module's documentation.
|
|
- All objects included in ``__all__`` (if present).
|
|
|
|
Other objects may be included if they are being used in practice
|
|
or if they are not prefixed with an underscore. This means
|
|
that typeshed will generally accept contributions that add missing
|
|
objects, even if they are undocumented. Undocumented objects should
|
|
be marked with a comment of the form ``# undocumented``.
|
|
Example:
|
|
|
|
```python
|
|
def list2cmdline(seq: Sequence[str]) -> str: ... # undocumented
|
|
```
|
|
|
|
We accept such undocumented objects because omitting objects can confuse
|
|
users. Users who see an error like "module X has no attribute Y" will
|
|
not know whether the error appeared because their code had a bug or
|
|
because the stub is wrong. Although it may also be helpful for a type
|
|
checker to point out usage of private objects, we usually prefer false
|
|
negatives (no errors for wrong code) over false positives (type errors
|
|
for correct code). In addition, even for private objects a type checker
|
|
can be helpful in pointing out that an incorrect type was used.
|
|
|
|
### What to do when a project's documentation and implementation disagree
|
|
|
|
Type stubs are meant to be external type annotations for a given
|
|
library. While they are useful documentation in its own merit, they
|
|
augment the project's concrete implementation, not the project's
|
|
documentation. Whenever you find them disagreeing, model the type
|
|
information after the actual implementation and file an issue on the
|
|
project's tracker to fix their documentation.
|
|
|
|
### Stub versioning
|
|
|
|
You can use checks
|
|
like `if sys.version_info >= (3, 8):` to denote new functionality introduced
|
|
in a given Python version or solve type differences. When doing so, only use
|
|
one-tuples or two-tuples. Because of this, if a given functionality was
|
|
introduced in, say, Python 3.7.4, your check:
|
|
|
|
* should be expressed as `if sys.version_info >= (3, 7):`
|
|
* should NOT be expressed as `if sys.version_info >= (3, 7, 4):`
|
|
* should NOT be expressed as `if sys.version_info >= (3, 8):`
|
|
|
|
When your stub contains if statements for different Python versions,
|
|
always put the code for the most recent Python version first.
|
|
|
|
### Incomplete stubs
|
|
|
|
We accept partial stubs, especially for larger packages. These need to
|
|
follow the following guidelines:
|
|
|
|
* Included functions and methods must list all arguments, but the arguments
|
|
can be left unannotated. Do not use `Any` to mark unannotated arguments
|
|
or return values.
|
|
* Partial classes must include a `__getattr__()` method marked with an
|
|
`# incomplete` comment (see example below).
|
|
* Partial modules (i.e. modules that are missing some or all classes,
|
|
functions, or attributes) must include a top-level `__getattr__()`
|
|
function marked with an `# incomplete` comment (see example below).
|
|
* Partial packages (i.e. packages that are missing one or more sub-modules)
|
|
must have a `__init__.pyi` stub that is marked as incomplete (see above).
|
|
A better alternative is to create empty stubs for all sub-modules and
|
|
mark them as incomplete individually.
|
|
|
|
Example of a partial module with a partial class `Foo` and a partially
|
|
annotated function `bar()`:
|
|
|
|
```python
|
|
def __getattr__(name: str) -> Any: ... # incomplete
|
|
|
|
class Foo:
|
|
def __getattr__(self, name: str) -> Any: ... # incomplete
|
|
x: int
|
|
y: str
|
|
|
|
def bar(x: str, y, *, z=...): ...
|
|
```
|
|
|
|
### Using stubgen
|
|
|
|
Mypy includes a tool called [stubgen](https://mypy.readthedocs.io/en/latest/stubgen.html)
|
|
that auto-generates stubs for Python and C modules using static analysis,
|
|
Sphinx docs, and runtime introspection. It can be used to get a starting
|
|
point for your stubs. Note that this generator is currently unable to
|
|
determine most argument and return types and omits them or uses ``Any`` in
|
|
their place. Fill out manually the types that you know.
|
|
|
|
## Stub file coding style
|
|
|
|
### Syntax example
|
|
|
|
The below is an excerpt from the types for the `datetime` module.
|
|
|
|
```python
|
|
MAXYEAR: int
|
|
MINYEAR: int
|
|
|
|
class date:
|
|
def __init__(self, year: int, month: int, day: int) -> None: ...
|
|
@classmethod
|
|
def fromtimestamp(cls, timestamp: float) -> date: ...
|
|
@classmethod
|
|
def today(cls) -> date: ...
|
|
@classmethod
|
|
def fromordinal(cls, ordinal: int) -> date: ...
|
|
@property
|
|
def year(self) -> int: ...
|
|
def replace(self, year: int = ..., month: int = ..., day: int = ...) -> date: ...
|
|
def ctime(self) -> str: ...
|
|
def weekday(self) -> int: ...
|
|
```
|
|
|
|
### Conventions
|
|
|
|
Stub files are *like* Python files and you should generally expect them
|
|
to look the same. Your tools should be able to successfully treat them
|
|
as regular Python files. However, there are a few important differences
|
|
you should know about.
|
|
|
|
Style conventions for stub files are different from PEP 8. The general
|
|
rule is that they should be as concise as possible. Specifically:
|
|
* all function bodies should be empty;
|
|
* prefer ``...`` over ``pass``;
|
|
* prefer ``...`` on the same line as the class/function signature;
|
|
* avoid vertical whitespace between consecutive module-level functions,
|
|
names, or methods and fields within a single class;
|
|
* use a single blank line between top-level class definitions, or none
|
|
if the classes are very small;
|
|
* do not use docstrings;
|
|
* use variable annotations instead of type comments, even for stubs
|
|
that target older versions of Python.
|
|
|
|
Stubs should be reformatted with the formatters
|
|
[black](https://github.com/psf/black) and
|
|
[isort](https://github.com/PyCQA/isort) before submission.
|
|
These formatters are included in typeshed's `requirements-tests-py3.txt` file.
|
|
A sample `pre-commit` file is included in the typeshed repository. Copy it
|
|
to `.git/hooks` and adjust the path to your virtual environment's `bin`
|
|
directory to automatically reformat stubs before commit.
|
|
|
|
Stub files should only contain information necessary for the type
|
|
checker, and leave out unnecessary detail:
|
|
* for arguments with a default, use `...` instead of the actual
|
|
default;
|
|
* for arguments that default to `None`, use `Optional[]` explicitly
|
|
(see below for details);
|
|
* use `float` instead of `Union[int, float]`.
|
|
|
|
Some further tips for good type hints:
|
|
* use built-in generics (`list`, `dict`, `tuple`, `set`), instead
|
|
of importing them from `typing`, **except** for arbitrary length tuples
|
|
(`Tuple[int, ...]`) (see
|
|
[python/mypy#9980](https://github.com/python/mypy/issues/9980));
|
|
* in Python 3 stubs, import collections (`Mapping`, `Iterable`, etc.)
|
|
from `collections.abc` instead of `typing`;
|
|
* avoid invariant collection types (`list`, `dict`) in argument
|
|
positions, in favor of covariant types like `Mapping` or `Sequence`;
|
|
* avoid Union return types: https://github.com/python/mypy/issues/1693;
|
|
* in Python 2, whenever possible, use `unicode` if that's the only
|
|
possible type, and `Text` if it can be either `unicode` or `bytes`;
|
|
* use platform checks like `if sys.platform == 'win32'` to denote
|
|
platform-dependent APIs.
|
|
|
|
Imports in stubs are considered private (not part of the exported API)
|
|
unless:
|
|
* they use the form ``from library import name as name`` (sic, using
|
|
explicit ``as`` even if the name stays the same); or
|
|
* they use the form ``from library import *`` which means all names
|
|
from that library are exported.
|
|
|
|
When adding type hints, avoid using the `Any` type when possible. Reserve
|
|
the use of `Any` for when:
|
|
* the correct type cannot be expressed in the current type system; and
|
|
* to avoid Union returns (see above).
|
|
|
|
Note that `Any` is not the correct type to use if you want to indicate
|
|
that some function can accept literally anything: in those cases use
|
|
`object` instead.
|
|
|
|
Stub files support forward references natively. In other words, the
|
|
order of class declarations and type aliases does not matter in
|
|
a stub file. You can also use the name of the class within its own
|
|
body. Focus on making your stubs clear to the reader. Avoid using
|
|
string literals in type annotations.
|
|
|
|
Type variables and aliases you introduce purely for legibility reasons
|
|
should be prefixed with an underscore to make it obvious to the reader
|
|
they are not part of the stubbed API.
|
|
|
|
When adding type annotations for context manager classes, annotate
|
|
the return type of `__exit__` as bool only if the context manager
|
|
sometimes suppresses exceptions -- if it sometimes returns `True`
|
|
at runtime. If the context manager never suppresses exceptions,
|
|
have the return type be either `None` or `Optional[bool]`. If you
|
|
are not sure whether exceptions are suppressed or not or if the
|
|
context manager is meant to be subclassed, pick `Optional[bool]`.
|
|
See https://github.com/python/mypy/issues/7214 for more details.
|
|
|
|
A few guidelines for protocol names below. In cases that don't fall
|
|
into any of those categories, use your best judgement.
|
|
|
|
* Use plain names for protocols that represent a clear concept
|
|
(e.g. `Iterator`, `Container`).
|
|
* Use `SupportsX` for protocols that provide callable methods (e.g.
|
|
`SupportsInt`, `SupportsRead`, `SupportsReadSeek`).
|
|
* Use `HasX` for protocols that have readable and/or writable attributes
|
|
or getter/setter methods (e.g. `HasItems`, `HasFileno`).
|
|
|
|
## Running the tests
|
|
|
|
The tests are automatically run on every PR and push to the repo.
|
|
Therefore you don't need to run them locally, unless you want to run
|
|
them before making a pull request or you want to debug some problem without
|
|
creating several small commits.
|
|
|
|
There are several tests:
|
|
- `tests/mypy_test.py`
|
|
tests typeshed with [mypy](https://github.com/python/mypy/)
|
|
- `tests/pytype_test.py` tests typeshed with
|
|
[pytype](https://github.com/google/pytype/).
|
|
- `tests/pyright_test.py` tests typeshed with
|
|
[pyright](https://github.com/microsoft/pyright).
|
|
- `tests/mypy_test_suite.py` runs a subset of mypy's test suite using this version of
|
|
typeshed.
|
|
- `tests/check_consistent.py` checks certain files in typeshed remain
|
|
consistent with each other.
|
|
- `tests/stubtest_test.py` checks stubs against the objects at runtime.
|
|
- `flake8` enforces a style guide.
|
|
|
|
### Setup
|
|
|
|
Run:
|
|
```
|
|
$ python3 -m venv .venv3
|
|
$ source .venv3/bin/activate
|
|
(.venv3)$ pip install -U pip
|
|
(.venv3)$ pip install -r requirements-tests-py3.txt
|
|
```
|
|
This will install mypy (you need the latest master branch from GitHub),
|
|
typed-ast, flake8 (and plugins), pytype, black and isort.
|
|
|
|
If you want to run the pyright tests, you need to have
|
|
[Node.js](https://nodejs.org/) installed.
|
|
|
|
### mypy\_test.py
|
|
|
|
This test requires Python 3.6 or higher; Python 3.6.1 or higher is recommended.
|
|
Run using:
|
|
```
|
|
(.venv3)$ python3 tests/mypy_test.py
|
|
```
|
|
|
|
This test is shallow — it verifies that all stubs can be
|
|
imported but doesn't check whether stubs match their implementation
|
|
(in the Python standard library or a third-party package). It has an exclude list of
|
|
modules that are not tested at all, which also lives in the tests directory.
|
|
|
|
You can restrict mypy tests to a single version by passing `-p2` or `-p3.9`:
|
|
```bash
|
|
(.venv3)$ python3 tests/mypy_test.py -p3.9
|
|
```
|
|
|
|
### pytype\_test.py
|
|
|
|
This test requires Python 2.7 and Python 3.6. Pytype will
|
|
find these automatically if they're in `PATH`.
|
|
Run using:
|
|
```
|
|
(.venv3)$ python3 tests/pytype_test.py
|
|
```
|
|
|
|
This test works similarly to `mypy_test.py`, except it uses `pytype`.
|
|
|
|
### pyright\_test.py
|
|
|
|
This test requires Node.js to be installed. It is currently not part of the CI,
|
|
but it uses the same pyright version and configuration as the CI.
|
|
```
|
|
(.venv3)$ python3 tests/pyright_test.py # Check all files
|
|
(.venv3)$ python3 tests/pyright_test.py stdlib/sys.pyi # Check one file
|
|
```
|
|
|
|
### mypy\_test\_suite.py
|
|
|
|
This test requires Python 3.5 or higher; Python 3.6.1 or higher is recommended.
|
|
Run using:
|
|
```
|
|
(.venv3)$ python3 tests/mypy_test_suite.py
|
|
```
|
|
|
|
This test runs mypy's own test suite using the typeshed code in your repo. This
|
|
will sometimes catch issues with incorrectly typed stubs, but is much slower
|
|
than the other tests.
|
|
|
|
### check\_consistent.py
|
|
|
|
Run using:
|
|
```
|
|
python3 tests/check_consistent.py
|
|
```
|
|
|
|
### stubtest\_test.py
|
|
|
|
This test requires Python 3.6 or higher.
|
|
Run using
|
|
```
|
|
(.venv3)$ python3 tests/stubtest_test.py
|
|
```
|
|
|
|
This test compares the stdlib stubs against the objects at runtime. Because of
|
|
this, the output depends on which version of Python and on what kind of system
|
|
it is run.
|
|
Thus the easiest way to run this test is via Github Actions on your fork;
|
|
if you run it locally, it'll likely complain about system-specific
|
|
differences (in e.g, `socket`) that the type system cannot capture.
|
|
If you need a specific version of Python to repro a CI failure,
|
|
[pyenv](https://github.com/pyenv/pyenv) can help.
|
|
|
|
Due to its dynamic nature, you may run into false positives. In this case, you
|
|
can add to the whitelists for each affected Python version in
|
|
`tests/stubtest_whitelists`. Please file issues for stubtest false positives
|
|
at [mypy](https://github.com/python/mypy/issues).
|
|
|
|
To run stubtest against third party stubs, it's easiest to use stubtest
|
|
directly, with
|
|
```
|
|
(.venv3)$ python3 -m mypy.stubtest \
|
|
--custom-typeshed-dir <path-to-typeshed> \
|
|
<third-party-module>
|
|
```
|
|
stubtest can also help you find things missing from the stubs.
|
|
|
|
|
|
### flake8
|
|
|
|
flake8 requires Python 3.6 or higher. Run using:
|
|
```
|
|
(.venv3)$ flake8
|
|
```
|
|
|
|
Note typeshed uses the `flake8-pyi` and `flake8-bugbear` plugins.
|
|
|
|
## Submitting Changes
|
|
|
|
Even more excellent than a good bug report is a fix for a bug, or the
|
|
implementation of a much-needed stub. We'd love to have
|
|
your contributions.
|
|
|
|
We use the usual GitHub pull-request flow, which may be familiar to
|
|
you if you've contributed to other projects on GitHub. For the
|
|
mechanics, see [Mypy's git and GitHub workflow help page](https://github.com/python/mypy/wiki/Using-Git-And-GitHub),
|
|
or [GitHub's own documentation](https://help.github.com/articles/using-pull-requests/).
|
|
|
|
Anyone interested in type stubs may review your code. One of the
|
|
maintainers will merge your pull request when they think it's ready.
|
|
For every pull request, we aim to promptly either merge it or say why
|
|
it's not yet ready; if you go a few days without a reply, please feel
|
|
free to ping the thread by adding a new comment.
|
|
|
|
To get your pull request merged sooner, you should explain why you are
|
|
making the change. For example, you can point to a code sample that is
|
|
processed incorrectly by a type checker. It is also helpful to add
|
|
links to online documentation or to the implementation of the code
|
|
you are changing.
|
|
|
|
Also, do not squash your commits or use `git commit --amend` after you have submitted a pull request, as this
|
|
erases context during review. We will squash commits when the pull request is merged.
|
|
This way, your pull request will appear as a single commit in our git history, even
|
|
if it consisted of several smaller commits.
|
|
|
|
## Third-party library removal policy
|
|
|
|
Third-party packages are generally removed from typeshed when one of the
|
|
following criteria is met:
|
|
|
|
* The upstream package ships a py.typed file for at least 6-12 months, or
|
|
* the package does not support any of the Python versions supported by
|
|
typeshed.
|
|
|
|
## Maintainer guidelines
|
|
|
|
The process for preparing and submitting changes also applies to
|
|
maintainers. This ensures high quality contributions and keeps
|
|
everybody on the same page. Avoid direct pushes to the repository.
|
|
|
|
Maintainers should follow these rules when processing pull requests:
|
|
|
|
* Always wait for tests to pass before merging PRs.
|
|
* Use "[Squash and merge](https://github.com/blog/2141-squash-your-commits)" to merge PRs.
|
|
* Delete branches for merged PRs (by maintainers pushing to the main repo).
|
|
* Make sure commit messages to master are meaningful. For example, remove irrelevant
|
|
intermediate commit messages.
|
|
* If stubs for a new library are submitted, notify the library's maintainers.
|
|
|
|
When reviewing pull requests, follow these guidelines:
|
|
|
|
* Typing is hard. Try to be helpful and explain issues with the PR,
|
|
especially to new contributors.
|
|
* When reviewing auto-generated stubs, just scan for red flags and obvious
|
|
errors. Leave possible manual improvements for separate PRs.
|
|
* When reviewing large, hand-crafted PRs, you only need to look for red flags
|
|
and general issues, and do a few spot checks.
|
|
* Review smaller, hand-crafted PRs thoroughly.
|