8000 Proposed clarification of spec for int/float/complex promotion by JelleZijlstra · Pull Request #1748 · python/typing · GitHub
[go: up one dir, main page]

Skip to content

Proposed clarification of spec for int/float/complex promotion #1748

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Prev Previous commit
Merge remote-tracking branch 'upstream/main' into intfloat
  • Loading branch information
JelleZijlstra committed Jun 29, 2025
commit 9dd5297aaa12260232a2423fb0f55b95172a7041
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/documentation-issue.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: Documentation issue
about: Report a problem or suggest changes for the documentation at https://typing.readthedocs.io/
about: Report a problem or suggest changes for the documentation at https://typing.python.org/
title: ''
labels: 'topic: documentation'
assignees: ''
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
rev: v4.6.0
hooks:
- id: trailing-whitespace
exclude: conformance/results/.*/.*\.toml
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Documentation and Support

The documentation for Python's static typing can be found at
[typing.readthedocs.io](https://typing.readthedocs.io/). You can get
[typing.python.org](https://typing.python.org/). You can get
help in our [support forum](https://github.com/python/typing/discussions).

Improvements to the type system should be discussed on
Expand All @@ -20,10 +20,10 @@ For conversations that are more suitable to a chat platform, you can use one of

This GitHub repository is used for several things:

- The documentation at [typing.readthedocs.io](https://typing.readthedocs.io/)
- The documentation at [typing.python.org](https://typing.python.org/)
is maintained in the [docs directory](./docs). This includes the
[specification](https://typing.readthedocs.io/en/latest/spec/index.html) for the
type system. See especially [the update procedure](https://typing.readthedocs.io/en/latest/spec/meta.html)
[specification](https://typing.python.org/en/latest/spec/index.html) for the
type system. See especially [the update procedure](https://typing.python.org/en/latest/spec/meta.html)
for the spec.

- A [discussion forum](https://github.com/python/typing/discussions) for typing-related user
Expand Down
63 changes: 39 additions & 24 deletions conformance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,34 @@

## Motivation

[PEP 729](https://peps.python.org/pep-0729/) provides a structured and documented way to specify and evolve the Python type system. In support of this effort, an official [Python typing spec](https://github.com/python/typing/tree/main/docs/spec) has been drafted. This spec consolidates details from various historical typing-related PEPs. The spec will be modified over time to clarify unspecified and under-specified parts of the type system. It will also be extended to cover new features of the type system.
[PEP 729](https://peps.python.org/pep-0729/) provides a structured and documented way to specify and evolve the Python type system. In support of this effort, an official [Python typing spec](https://typing.python.org/en/latest/spec/) has been drafted. This spec consolidates details from various historical typing-related PEPs. The spec will be modified over time to clarify unspecified and under-specified parts of the type system. It will also be extended to cover new features of the type system.

Accompanying the typing specification is this conformance test suite which validates the behavior of static type checkers against the specification.

## Structure & Name

This project contains test cases for behaviors defined in the Python typing spec. Tests are structured and grouped in accordance with the specification's chapter headings.

* [concepts](https://typing.readthedocs.io/en/latest/spec/concepts.html)
* [annotations](https://typing.readthedocs.io/en/latest/spec/annotations.html)
* [specialtypes](https://typing.readthedocs.io/en/latest/spec/special-types.html)
* [generics](https://typing.readthedocs.io/en/latest/spec/generics.html)
* [qualifiers](https://typing.readthedocs.io/en/latest/spec/qualifiers.html)
* [classes](https://typing.readthedocs.io/en/latest/spec/class-compat.html)
* [aliases](https://typing.readthedocs.io/en/latest/spec/aliases.html)
* [literals](https://typing.readthedocs.io/en/latest/spec/literal.html)
* [protocols](https://typing.readthedocs.io/en/latest/spec/protocol.html)
* [callables](https://typing.readthedocs.io/en/latest/spec/callables.html)
* [constructors](https://typing.readthedocs.io/en/latest/spec/constructors.html)
* [overloads](https://typing.readthedocs.io/en/latest/spec/overload.html)
* [dataclasses](https://typing.readthedocs.io/en/latest/spec/dataclasses.html)
* [typeddicts](https://typing.readthedocs.io/en/latest/spec/typeddict.html)
* [tuples](https://typing.readthedocs.io/en/latest/spec/tuples.html)
* [namedtuples](https://typing.readthedocs.io/en/latest/spec/namedtuples.html)
* [narrowing](https://typing.readthedocs.io/en/latest/spec/narrowing.html)
* [directives](https://typing.readthedocs.io/en/latest/spec/directives.html)
* [distribution](https://typing.readthedocs.io/en/latest/spec/distributing.html)
* [historical](https://typing.readthedocs.io/en/latest/spec/historical.html)
* [concepts](https://typing.python.org/en/latest/spec/concepts.html)
* [annotations](https://typing.python.org/en/latest/spec/annotations.html)
* [specialtypes](https://typing.python.org/en/latest/spec/special-types.html)
* [generics](https://typing.python.org/en/latest/spec/generics.html)
* [qualifiers](https://typing.python.org/en/latest/spec/qualifiers.html)
* [classes](https://typing.python.org/en/latest/spec/class-compat.html)
* [aliases](https://typing.python.org/en/latest/spec/aliases.html)
* [literals](https://typing.python.org/en/latest/spec/literal.html)
* [protocols](https://typing.python.org/en/latest/spec/protocol.html)
* [callables](https://typing.python.org/en/latest/spec/callables.html)
* [constructors](https://typing.python.org/en/latest/spec/constructors.html)
* [overloads](https://typing.python.org/en/latest/spec/overload.html)
* [dataclasses](https://typing.python.org/en/latest/spec/dataclasses.html)
* [typeddicts](https://typing.python.org/en/latest/spec/typeddict.html)
* [tuples](https://typing.python.org/en/latest/spec/tuples.html)
* [namedtuples](https://typing.python.org/en/latest/spec/namedtuples.html)
* [narrowing](https://typing.python.org/en/latest/spec/narrowing.html)
* [directives](https://typing.python.org/en/latest/spec/directives.html)
* [distribution](https://typing.python.org/en/latest/spec/distributing.html)
* [historical](https://typing.python.org/en/latest/spec/historical.html)

A test file is a ".py" file. The file name should start with one of the above names followed by a description of the test (with words separated by underscores). For example, `generics_paramspec_basic_usage.py` would contain the basic usage tests for `ParamSpec`. Each test file can contain multiple individual unit tests, but these tests should be related to each other. If the number of unit tests in a single test file exceeds ten, it may be desirable to split it into separate test files. This will help maintain a consistent level of granularity across tests.

Expand All @@ -46,12 +46,26 @@ Test cases use the following conventions:
* Lines that are expected to produce a type checker error should have a comment starting with # E",
either by itself or followed by an explanation after a colon (e.g., "# E: int is not a subtype
of str"). Such explanatory comments are purely for human understanding, but type checkers are not
expected to use their exact wording.
expected to use their exact wording. There are several syntactic variations; see "Test Case Syntax"
below.
* Lines that may produce an error (e.g., because the spec allows multiple behaviors) should be
marked with "# E?" instead of "# E".
* If a test case tests conformance with a specific passage in the spec, that passage should be
quoted in a comment prefixed with "# > ".

## Test Case Syntax

Test cases support the following special comments for declaring where errors should be raised:

* `# E`: an error must be raised on this line
* `# E?`: an error may be raised on this line
* `# E[tag]`, where `tag` is an arbitrary string: must appear multiple times in a file with the same tag.
Exactly one line with this tag must raise an error.
* `# E[tag+]`: like `# E[tag]`, but errors may be raised on multiple lines.

Each comment may be followed by a colon plus an explanation of the error; the explana 8000 tion is ignored
by the scoring system.

## Running the Conformance Test Tool

To run the conformance test suite:
Expand All @@ -60,13 +74,14 @@ To run the conformance test suite:
* Switch to the `conformance` subdirectory and install all dependencies (`pip install -r requirements.txt`).
* Switch to the `src` subdirectory and run `python main.py`.

Note that some type checkers may not run on some platforms. For example, pytype cannot be installed on Windows. If a type checker fails to install, tests will be skipped for that type checker.
Note that some type checkers may not run on some platforms. If a type checker fails to install, tests will be skipped for that type checker.
Currently, the only unsupported type checker is Pyre on Windows.

## Reporting Conformance Results

Different type checkers report errors in different ways (with different wording in error messages and different line numbers or character ranges for errors). This variation makes it difficult to fully automate test validation given that tests will want to check for both false positive and false negative type errors. Some level of manual inspection will therefore be needed to determine whether a type checker is fully conformant with all tests in any given test file. This "scoring" process is required only when the output of a test changes — e.g. when a new version of that type checker is released and the tests are rerun. We assume that the output of a type checker will be the same from one run to the next unless/until a new version is released that fixes or introduces a bug. In this case, the output will need to be manually inspected and the conformance results re-scored for those tests whose output has changed.

Conformance results are reported and summarized for each supported type checker. Currently, results are reported for mypy, pyre, pyright, and pytype. It is the goal and desire to add additional type checkers over time.
Conformance results are reported and summarized for each supported type checker. Currently, results are reported for mypy, pyre, and pyright. It is the goal and desire to add additional type checkers over time.

## Adding a New Test Case

Expand Down
4 changes: 2 additions & 2 deletions conformance/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ tomlkit
tqdm
pyright
mypy
pyre-check
pytype; platform_system != "Windows"
pip
pyre-check; platform_system != "Windows"
11 changes: 5 additions & 6 deletions conformance/results/mypy/aliases_explicit.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ aliases_explicit.py:67: error: Bad number of arguments for type alias, expected
aliases_explicit.py:68: error: Bad number of arguments for type alias, expected 0, given 1 [type-arg]
aliases_explicit.py:69: error: Bad number of arguments for type alias, expected 1, given 2 [type-arg]
aliases_explicit.py:70: error: Bad number of arguments for type alias, expected 1, given 2 [type-arg]
aliases_explicit.py:71: error: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" [misc]
aliases_explicit.py:71: error: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" [valid-type]
aliases_explicit.py:79: error: Invalid type alias: expression is not a valid type [valid-type]
aliases_explicit.py:80: error: Bracketed expression "[...]" is not valid as a type [valid-type]
aliases_explicit.py:81: error: Invalid type alias: expression is not a valid type [valid-type]
aliases_explicit.py:82: error: Invalid type alias: expression is not a valid type [valid-type]
aliases_explicit.py:83: err 9E81 or: Invalid type alias: expression is not a valid type [valid-type]
aliases_explicit.py:83: error: Inline TypedDict is experimental, must be enabled with --enable-incomplete-feature=InlineTypedDict [misc]
aliases_explicit.py:83: error: Name "b" is not defined [name-defined]
aliases_explicit.py:84: error: Invalid type alias: expression is not a valid type [valid-type]
aliases_explicit.py:85: error: Invalid type alias: expression is not a valid type [valid-type]
aliases_explicit.py:86: error: Invalid type alias: expression is not a valid type [valid-type]
Expand All @@ -23,13 +24,11 @@ aliases_explicit.py:89: error: Invalid type: try using Literal[1] instead? [val
aliases_explicit.py:90: error: Invalid type alias: expression is not a valid type [valid-type]
aliases_explicit.py:90: error: Function "list" could always be true in boolean context [truthy-function]
aliases_explicit.py:91: error: Invalid type alias: expression is not a valid type [valid-type]
aliases_explicit.py:101: error: "<typing special form>" not callable [operator]
aliases_explicit.py:101: error: "UnionType[list[Any], set[Any]]" not callable [operator]
aliases_explicit.py:102: error: Bad number of arguments for type alias, expected 0, given 1 [type-arg]
"""
conformance_automated = "Fail"
errors_diff = """
Line 100: Expected 1 errors
"""
ignore_errors = [
'Function "list" could always be true in boolean context',
]
ignore_errors = ["Function \"list\" could always be true in boolean context"]
4 changes: 2 additions & 2 deletions conformance/results/mypy/aliases_implicit.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ aliases_implicit.py:76: error: Bad number of arguments for type alias, expected
aliases_implicit.py:77: error: Bad number of arguments for type alias, expected 0, given 1 [type-arg]
aliases_implicit.py:78: error: Bad number of arguments for type alias, expected 1, given 2 [type-arg]
aliases_implicit.py:79: error: Bad number of arguments for type alias, expected 1, given 2 [type-arg]
aliases_implicit.py:80: error: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" [misc]
aliases_implicit.py:80: error: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" [valid-type]
aliases_implicit.py:81: error: Type argument "str" of "GoodTypeAlias12" must be a subtype of "float" [type-var]
aliases_implicit.py:100: error: Function "list" could always be true in boolean context [truthy-function]
aliases_implicit.py:106: error: Variable "aliases_implicit.BadTypeAlias1" is not valid as a type [valid-type]
Expand Down Expand Up @@ -35,7 +35,7 @@ aliases_implicit.py:118: error: Variable "aliases_implicit.BadTypeAlias13" is no
aliases_implicit.py:118: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
aliases_implicit.py:119: error: Variable "aliases_implicit.BadTypeAlias14" is not valid as a type [valid-type]
aliases_implicit.py:119: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
aliases_implicit.py:133: error: "<typing special form>" not callable [operator]
aliases_implicit.py:133: error: "UnionType[list[Any], set[Any]]" not callable [operator]
aliases_implicit.py:135: error: Bad number of arguments for type alias, expected 0, given 1 [type-arg]
"""
conformance_automated = "Pass"
Expand Down
34 changes: 19 additions & 15 deletions conformance/results/mypy/aliases_newtype.toml
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
conformant = "Pass"
conformant = "Partial"
notes = """
`NewType`s are considered classes, not functions.
"""
output = """
aliases_newtype.py:11: error: Argument 1 to "UserId" has incompatible type "str"; expected "int" [arg-type]
aliases_newtype.py:12: error: Incompatible types in assignment (expression has type "int", variable has type "UserId") [assignment]
aliases_newtype.py:20: error: Cannot use isinstance() with NewType type [misc]
aliases_newtype.py:23: error: Cannot subclass "NewType" [misc]
aliases_newtype.py:32: error: String argument 1 "BadName" to NewType(...) does not match variable name "GoodName" [misc]
aliases_newtype.py:38: error: "GoodNewType1" expects no type arguments, but 1 given [type-arg]
aliases_newtype.py:44: error: Argument 2 to NewType(...) must be subclassable (got "int | str") [valid-newtype]
aliases_newtype.py:47: error: Type variable "aliases_newtype.T" is unbound [valid-type]
aliases_newtype.py:47: note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class)
aliases_newtype.py:47: note: (Hint: Use "T" in function signature to bind "T" inside a function)
aliases_newtype.py:49: error: NewType cannot be used with protocol classes [misc]
aliases_newtype.py:51: error: Argument 2 to NewType(...) must be subclassable (got "Literal[7]") [valid-newtype]
aliases_newtype.py:58: error: Argument 2 to NewType(...) must be subclassable (got "TD1") [valid-newtype]
aliases_newtype.py:60: error: NewType(...) expects exactly two positional arguments [misc]
aliases_newtype.py:62: error: Argument 2 to NewType(...) must be subclassable (got "Any") [valid-newtype]
aliases_newtype.py:23: error: Cannot use isinstance() with NewType type [misc]
aliases_newtype.py:26: error: Cannot subclass "NewType" [misc]
aliases_newtype.py:35: error: String argument 1 "BadName" to NewType(...) does not match variable name "GoodName" [misc]
aliases_newtype.py:41: error: "GoodNewType1" expects no type arguments, but 1 given [type-arg]
aliases_newtype.py:47: error: Argument 2 to NewType(...) must be subclassable (got "int | str") [valid-newtype]
aliases_newtype.py:50: error: Type variable "aliases_newtype.T" is unbound [valid-type]
aliases_newtype.py:50: note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class)
aliases_newtype.py:50: note: (Hint: Use "T" in function signature to bind "T" inside a function)
aliases_newtype.py:52: error: NewType cannot be used with protocol classes [misc]
aliases_newtype.py:54: error: Argument 2 to NewType(...) must be subclassable (got "Literal[7]") [valid-newtype]
aliases_newtype.py:61: error: Argument 2 to NewType(...) must be subclassable (got "TD1") [valid-newtype]
aliases_newtype.py:63: error: NewType(...) expects exactly two positional arguments [misc]
aliases_newtype.py:65: error: Argument 2 to NewType(...) must be subclassable (got "Any") [valid-newtype]
"""
conformance_automated = "Pass"
conformance_automated = "Fail"
errors_diff = """
Line 18: Expected 1 errors
"""
Loading
You are viewing a condensed version of this merge commit. You can view the full changes here.
0