Skip to content

Commit 6856262

Browse files
committed
Merge remote-tracking branch 'origin/main' into zuban-ignore
2 parents 5cbcac4 + 4c02514 commit 6856262

367 files changed

Lines changed: 4795 additions & 1310 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/conformance.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Conformance
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
permissions:
8+
contents: read
9+
10+
jobs:
11+
conformance:
12+
name: Run conformance suite
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- uses: actions/checkout@v6
17+
18+
- name: Set up Python 3.12
19+
uses: actions/setup-python@v6
20+
with:
21+
python-version: "3.12"
22+
cache: "pip"
23+
24+
- name: Install uv
25+
run: |
26+
python -m pip install --upgrade pip
27+
python -m pip install uv
28+
29+
- name: Run conformance suite
30+
working-directory: conformance
31+
run: |
32+
uv sync --python 3.12 --frozen
33+
uv run --python 3.12 --frozen python src/main.py
34+
35+
- name: Validate conformance invariants
36+
working-directory: conformance
37+
run: |
38+
uv run --python 3.12 --frozen python src/validate_results.py
39+
40+
- name: Assert conformance results are up to date
41+
run: |
42+
if [ -n "$(git status --porcelain -- conformance/results)" ]; then
43+
git status --short conformance/results
44+
git diff -- conformance/results
45+
echo "Conformance results are out of date. Run conformance/src/main.py and commit updated results."
46+
exit 1
47+
fi

conformance/README.md

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ This project contains test cases for behaviors defined in the Python typing spec
1212

1313
* [concepts](https://typing.python.org/en/latest/spec/concepts.html)
1414
* [annotations](https://typing.python.org/en/latest/spec/annotations.html)
15+
* [typeforms](https://typing.python.org/en/latest/spec/type-forms.html)
1516
* [specialtypes](https://typing.python.org/en/latest/spec/special-types.html)
1617
* [generics](https://typing.python.org/en/latest/spec/generics.html)
1718
* [qualifiers](https://typing.python.org/en/latest/spec/qualifiers.html)
@@ -70,17 +71,17 @@ by the scoring system.
7071

7172
To run the conformance test suite:
7273
* Clone the https://github.com/python/typing repo.
73-
* Create and activate a Python 3.12 virtual environment.
74-
* Switch to the `conformance` subdirectory and install all dependencies (`pip install -r requirements.txt`).
75-
* Switch to the `src` subdirectory and run `python main.py`.
74+
* Install [uv](https://docs.astral.sh/uv/) and ensure Python 3.12 is available.
75+
* Switch to the `conformance` subdirectory and install locked dependencies (`uv sync --python 3.12 --frozen`).
76+
* Run the conformance tool (`uv run --python 3.12 --frozen python src/main.py`).
7677

7778
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.
7879

7980
## Reporting Conformance Results
8081

8182
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.
8283

83-
Conformance results are reported and summarized for each supported type checker. Currently, results are reported for mypy, pyrefly, pyright, and zuban. It is the goal and desire to add additional type checkers over time.
84+
[Conformance results](https://htmlpreview.github.io/?https://github.com/python/typing/blob/main/conformance/results/results.html) are reported and summarized for each supported type checker. Currently, results are reported for mypy, pyrefly, pyright, zuban and ty. It is the goal and desire to add additional type checkers over time.
8485

8586
## Adding a New Test Case
8687

@@ -92,7 +93,22 @@ If a test is updated (augmented or fixed), the process is similar to when adding
9293

9394
## Updating a Type Checker
9495

95-
If a new version of a type checker is released, re-run the test tool with the new version. If the type checker output has changed for any test cases, the tool will supply the old and new outputs. Examine these to determine whether the conformance status has changed. Once the conformance status has been updated, re-run the test tool again to regenerate the summary report.
96+
Type checker versions are locked in `uv.lock`.
97+
98+
To bump all supported checkers to their latest released versions:
99+
100+
```bash
101+
python scripts/bump_type_checkers.py
102+
```
103+
104+
After bumping, install the new lockfile and rerun conformance:
105+
106+
```bash
107+
uv sync --python 3.12 --frozen
108+
uv run --python 3.12 --frozen python src/main.py
109+
```
110+
111+
If checker output changes for any test cases, examine those deltas to determine whether the conformance status has changed. Once the conformance status has been updated, rerun the tool to regenerate the summary report.
96112

97113
## Automated Conformance Checking
98114

conformance/pyproject.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[project]
2+
name = "typing-conformance"
3+
version = "0.1.0"
4+
requires-python = "==3.12.*"
5+
dependencies = [
6+
"mypy",
7+
"pyrefly",
8+
"pyright",
9+
"tomli",
10+
"tomlkit",
11+
"zuban",
12+
"ty",
13+
]
14+
15+
[tool.uv]
16+
package = false

conformance/requirements.txt

Lines changed: 0 additions & 7 deletions
This file was deleted.

conformance/results/mypy/aliases_recursive.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ output = """
33
aliases_recursive.py:19: error: Dict entry 1 has incompatible type "str": "complex"; expected "str": "int | str | float | list[Json] | dict[str, Json] | None" [dict-item]
44
aliases_recursive.py:20: error: List item 1 has incompatible type "complex"; expected "int | str | float | list[Json] | dict[str, Json] | None" [list-item]
55
aliases_recursive.py:38: error: Incompatible types in assignment (expression has type "tuple[int, tuple[str, int], tuple[int, tuple[int, list[int]]]]", variable has type "RecursiveTuple") [assignment]
6-
aliases_recursive.py:39: error: Name "t6" already defined on line 38 [no-redef]
6+
aliases_recursive.py:39: error: Incompatible types in assignment (expression has type "tuple[int, list[int]]", variable has type "RecursiveTuple") [assignment]
77
aliases_recursive.py:50: error: Dict entry 0 has incompatible type "str": "list[int]"; expected "str": "str | int | Mapping[str, RecursiveMapping]" [dict-item]
88
aliases_recursive.py:51: error: Dict entry 2 has incompatible type "str": "list[int]"; expected "str": "str | int | Mapping[str, RecursiveMapping]" [dict-item]
99
aliases_recursive.py:52: error: Dict entry 2 has incompatible type "str": "list[int]"; expected "str": "str | int | Mapping[str, RecursiveMapping]" [dict-item]

conformance/results/mypy/callables_kwargs.toml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ notes = """
33
Allows callable without kwargs to be assigned to callable with unpacked kwargs
44
"""
55
output = """
6-
callables_kwargs.py:22: note: "func1" defined here
76
callables_kwargs.py:46: error: Missing named argument "v1" for "func1" [call-arg]
87
callables_kwargs.py:46: error: Missing named argument "v3" for "func1" [call-arg]
98
callables_kwargs.py:51: error: Unexpected keyword argument "v4" for "func1" [call-arg]
@@ -15,14 +14,14 @@ callables_kwargs.py:63: error: "func1" gets multiple values for keyword argument
1514
callables_kwargs.py:64: error: "func2" gets multiple values for keyword argument "v3" [misc]
1615
callables_kwargs.py:64: error: Argument 1 to "func2" has incompatible type "int"; expected "str" [arg-type]
1716
callables_kwargs.py:65: error: "func2" gets multiple values for keyword argument "v1" [misc]
18-
callables_kwargs.py:101: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol3") [assignment]
17+
callables_kwargs.py:101: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol3") [assignment]
1918
callables_kwargs.py:101: note: "TDProtocol3.__call__" has type "def __call__(self, *, v1: int, v2: int, v3: str) -> None"
20-
callables_kwargs.py:102: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol4") [assignment]
19+
callables_kwargs.py:102: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol4") [assignment]
2120
callables_kwargs.py:102: note: "TDProtocol4.__call__" has type "def __call__(self, *, v1: int) -> None"
22-
callables_kwargs.py:103: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol5") [assignment]
21+
callables_kwargs.py:103: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol5") [assignment]
2322
callables_kwargs.py:103: note: "TDProtocol5.__call__" has type "def __call__(self, v1: int, v3: str) -> None"
24-
callables_kwargs.py:111: error: Overlap between argument names and ** TypedDict items: "v1" [misc]
25-
callables_kwargs.py:122: error: Unpack item in ** argument must be a TypedDict [misc]
23+
callables_kwargs.py:111: error: Overlap between parameter names and ** TypedDict items: "v1" [misc]
24+
callables_kwargs.py:122: error: Unpack item in ** parameter must be a TypedDict [misc]
2625
"""
2726
conformance_automated = "Fail"
2827
errors_diff = """
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
conformant = "Unupported"
1+
conformant = "Unsupported"
22
notes = """
33
Does not honor metaclass __call__ method when evaluating constructor call.
44
Does not skip evaluation of __new__ and __init__ if custom metaclass call returns non-class.
@@ -7,14 +7,12 @@ conformance_automated = "Fail"
77
errors_diff = """
88
Line 26: Unexpected errors ['constructors_call_metaclass.py:26: error: Expression is of type "Class1", not "Never" [assert-type]', 'constructors_call_metaclass.py:26: error: Missing positional argument "x" in call to "Class1" [call-arg]']
99
Line 39: Unexpected errors ['constructors_call_metaclass.py:39: error: Expression is of type "Class2", not "int | Meta2" [assert-type]', 'constructors_call_metaclass.py:39: error: Missing positional argument "x" in call to "Class2" [call-arg]']
10-
Line 46: Unexpected errors ['constructors_call_metaclass.py:46: error: Argument 2 for "super" not an instance of argument 1 [misc]']
1110
"""
1211
output = """
1312
constructors_call_metaclass.py:26: error: Expression is of type "Class1", not "Never" [assert-type]
1413
constructors_call_metaclass.py:26: error: Missing positional argument "x" in call to "Class1" [call-arg]
1514
constructors_call_metaclass.py:39: error: Expression is of type "Class2", not "int | Meta2" [assert-type]
1615
constructors_call_metaclass.py:39: error: Missing positional argument "x" in call to "Class2" [call-arg]
17-
constructors_call_metaclass.py:46: error: Argument 2 for "super" not an instance of argument 1 [misc]
1816
constructors_call_metaclass.py:54: error: Missing positional argument "x" in call to "Class3" [call-arg]
1917
constructors_call_metaclass.py:68: error: Missing positional argument "x" in call to "Class4" [call-arg]
2018
"""

conformance/results/mypy/constructors_callable.toml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,33 +17,33 @@ Line 128: Unexpected errors ['constructors_callable.py:128: error: Expression is
1717
Line 145: Unexpected errors ['constructors_callable.py:145: error: Expression is of type "Class6Any", not "Any" [assert-type]', 'constructors_callable.py:145: error: Too few arguments [call-arg]']
1818
"""
1919
output = """
20-
constructors_callable.py:36: note: Revealed type is "def (x: builtins.int) -> constructors_callable.Class1"
20+
constructors_callable.py:36: note: Revealed type is "def (x: int) -> constructors_callable.Class1"
2121
constructors_callable.py:38: error: Too few arguments [call-arg]
2222
constructors_callable.py:39: error: Unexpected keyword argument "y" [call-arg]
2323
constructors_callable.py:49: note: Revealed type is "def () -> constructors_callable.Class2"
2424
constructors_callable.py:51: error: Too many arguments [call-arg]
25-
constructors_callable.py:64: note: Revealed type is "def (x: builtins.int) -> constructors_callable.Class3"
25+
constructors_callable.py:64: note: Revealed type is "def (x: int) -> constructors_callable.Class3"
2626
constructors_callable.py:66: error: Too few arguments [call-arg]
2727
constructors_callable.py:67: error: Unexpected keyword argument "y" [call-arg]
2828
constructors_callable.py:68: error: Too many arguments [call-arg]
2929
constructors_callable.py:74: error: Incompatible return type for "__new__" (returns "int", but must return a subtype of "Class4") [misc]
30-
constructors_callable.py:79: note: Revealed type is "def (x: builtins.int) -> constructors_callable.Class4"
30+
constructors_callable.py:79: note: Revealed type is "def (x: int) -> constructors_callable.Class4"
3131
constructors_callable.py:80: error: Expression is of type "Class4", not "int" [assert-type]
3232
constructors_callable.py:81: error: Too few arguments [call-arg]
3333
constructors_callable.py:82: error: Unexpected keyword argument "y" [call-arg]
3434
constructors_callable.py:99: note: Revealed type is "def (*args: Any, **kwargs: Any) -> constructors_callable.Class5"
3535
constructors_callable.py:102: error: Expression is of type "Class5", not "Never" [assert-type]
3636
constructors_callable.py:107: error: Expression is of type "Class5", not "Never" [assert-type]
3737
constructors_callable.py:118: error: Incompatible return type for "__new__" (returns "Class6Proxy", but must return a subtype of "Class6") [misc]
38-
constructors_callable.py:127: note: Revealed type is "def (x: builtins.int) -> constructors_callable.Class6"
38+
constructors_callable.py:127: note: Revealed type is "def (x: int) -> constructors_callable.Class6"
3939
constructors_callable.py:128: error: Expression is of type "Class6", not "Class6Proxy" [assert-type]
4040
constructors_callable.py:128: error: Too few arguments [call-arg]
41-
constructors_callable.py:144: note: Revealed type is "def (x: builtins.int) -> constructors_callable.Class6Any"
41+
constructors_callable.py:144: note: Revealed type is "def (x: int) -> constructors_callable.Class6Any"
4242
constructors_callable.py:145: error: Expression is of type "Class6Any", not "Any" [assert-type]
4343
constructors_callable.py:145: error: Too few arguments [call-arg]
44-
constructors_callable.py:164: note: Revealed type is "Overload(def (x: builtins.int) -> constructors_callable.Class7[builtins.int], def (x: builtins.str) -> constructors_callable.Class7[builtins.str])"
45-
constructors_callable.py:184: note: Revealed type is "def [T] (x: builtins.list[T`1], y: builtins.list[T`1]) -> constructors_callable.Class8[T`1]"
44+
constructors_callable.py:164: note: Revealed type is "Overload(def (x: int) -> constructors_callable.Class7[int], def (x: str) -> constructors_callable.Class7[str])"
45+
constructors_callable.py:184: note: Revealed type is "def [T] (x: list[T], y: list[T]) -> constructors_callable.Class8[T]"
4646
constructors_callable.py:186: error: Cannot infer function type argument [misc]
47-
constructors_callable.py:195: note: Revealed type is "def [T] (x: builtins.list[T`-1], y: builtins.list[T`-1]) -> constructors_callable.Class9"
47+
constructors_callable.py:195: note: Revealed type is "def [T] (x: list[T], y: list[T]) -> constructors_callable.Class9"
4848
constructors_callable.py:197: error: Cannot infer function type argument [misc]
4949
"""
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
conformant = "Partial"
22
notes = """
3+
Assumes descriptor behavior only when field is assigned in class body.
34
Does not correctly evaluate type of descriptor access.
45
"""
56
output = """
7+
dataclasses_descriptors.py:61: error: Cannot access instance-only attribute "x" on class object [misc]
8+
dataclasses_descriptors.py:62: error: Cannot access instance-only attribute "y" on class object [misc]
69
dataclasses_descriptors.py:66: error: Expression is of type "Desc2[int]", not "int" [assert-type]
710
dataclasses_descriptors.py:67: error: Expression is of type "Desc2[str]", not "str" [assert-type]
811
"""
912
conformance_automated = "Fail"
1013
errors_diff = """
14+
Line 61: Unexpected errors ['dataclasses_descriptors.py:61: error: Cannot access instance-only attribute "x" on class object [misc]']
15+
Line 62: Unexpected errors ['dataclasses_descriptors.py:62: error: Cannot access instance-only attribute "y" on class object [misc]']
1116
Line 66: Unexpected errors ['dataclasses_descriptors.py:66: error: Expression is of type "Desc2[int]", not "int" [assert-type]']
1217
Line 67: Unexpected errors ['dataclasses_descriptors.py:67: error: Expression is of type "Desc2[str]", not "str" [assert-type]']
1318
"""
Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
1-
conformant = "Partial"
1+
conformant = "Unsupported"
22
notes = """
3+
Does not synthesize `__hash__ = None` as a class attribute for unhashable dataclasses.
4+
Does not report when an unhashable dataclass has `__hash__` called directly on an instance.
35
Does not report when dataclass is not compatible with Hashable protocol.
46
"""
57
output = """
8+
dataclasses_hash.py:14: error: Expression is of type "Callable[[object], int]", not "None" [assert-type]
9+
dataclasses_hash.py:36: error: Expression is of type "Callable[[object], int]", not "None" [assert-type]
610
"""
711
conformance_automated = "Fail"
812
errors_diff = """
9-
Line 15: Expected 1 errors
10-
Line 32: Expected 1 errors
13+
Line 17: Expected 1 errors
14+
Line 18: Expected 1 errors
15+
Line 39: Expected 1 errors
16+
Line 40: Expected 1 errors
17+
Line 14: Unexpected errors ['dataclasses_hash.py:14: error: Expression is of type "Callable[[object], int]", not "None" [assert-type]']
18+
Line 36: Unexpected errors ['dataclasses_hash.py:36: error: Expression is of type "Callable[[object], int]", not "None" [assert-type]']
1119
"""

0 commit comments

Comments
 (0)