-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[ty] support PEP 613 type aliases #21394
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
Conversation
Diagnostic diff on typing conformance testsChanges were detected when running ty on typing conformance tests--- old-output.txt 2025-11-21 01:41:28.885585207 +0000
+++ new-output.txt 2025-11-21 01:41:32.391601775 +0000
@@ -5,14 +5,12 @@
_directives_deprecated_library.py:41:25: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `int | float`
_directives_deprecated_library.py:45:24: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `str`
aliases_explicit.py:41:24: error[invalid-type-form] List literals are not allowed in this context in a type expression: Did you mean `tuple[str, str]`?
-aliases_explicit.py:45:10: error[invalid-type-form] Variable of type `Literal["int | str"]` is not allowed in a type expression
aliases_explicit.py:52:5: error[type-assertion-failure] Type `list[int]` does not match asserted type `@Todo(specialized generic alias in type expression)`
aliases_explicit.py:53:5: error[type-assertion-failure] Type `tuple[str, ...] | list[str]` does not match asserted type `@Todo(Generic specialization of types.UnionType)`
aliases_explicit.py:54:5: error[type-assertion-failure] Type `tuple[int, int, int, str]` does not match asserted type `@Todo(specialized generic alias in type expression)`
aliases_explicit.py:56:5: error[type-assertion-failure] Type `(int, str, /) -> str` does not match asserted type `@Todo(Generic specialization of typing.Callable)`
aliases_explicit.py:57:5: error[type-assertion-failure] Type `(int, str, str, /) -> None` does not match asserted type `@Todo(Generic specialization of typing.Callable)`
aliases_explicit.py:59:5: error[type-assertion-failure] Type `int | str | None | list[list[int]]` does not match asserted type `int | str | None | list[@Todo(specialized generic alias in type expression)]`
-aliases_explicit.py:61:5: error[type-assertion-failure] Type `int | str` does not match asserted type `Unknown`
aliases_explicit.py:101:6: error[call-non-callable] Object of type `UnionType` is not callable
aliases_implicit.py:54:24: error[invalid-type-form] List literals are not allowed in this context in a type expression: Did you mean `tuple[str, str]`?
aliases_implicit.py:63:5: error[type-assertion-failure] Type `list[int]` does not match asserted type `@Todo(specialized generic alias in type expression)`
@@ -909,21 +907,13 @@
tuples_type_compat.py:47:40: error[invalid-assignment] Object of type `tuple[Any, ...]` is not assignable to `tuple[int, *tuple[str, ...]]`
tuples_type_compat.py:62:26: error[invalid-assignment] Object of type `tuple[int, ...]` is not assignable to `tuple[int, int]`
tuples_type_compat.py:75:9: error[type-assertion-failure] Type `tuple[int]` does not match asserted type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]`
-tuples_type_compat.py:76:9: error[type-assertion-failure] Type `@Todo(Support for `typing.TypeAlias`)` does not match asserted type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]`
tuples_type_compat.py:80:9: error[type-assertion-failure] Type `tuple[str, str] | tuple[int, int]` does not match asserted type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]`
-tuples_type_compat.py:81:9: error[type-assertion-failure] Type `@Todo(Support for `typing.TypeAlias`)` does not match asserted type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]`
tuples_type_compat.py:85:9: error[type-assertion-failure] Type `tuple[int, str, int]` does not match asserted type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]`
-tuples_type_compat.py:86:9: error[type-assertion-failure] Type `@Todo(Support for `typing.TypeAlias`)` does not match asserted type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]`
tuples_type_compat.py:101:13: error[type-assertion-failure] Type `tuple[int]` does not match asserted type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]`
-tuples_type_compat.py:102:13: error[type-assertion-failure] Type `@Todo(Support for `typing.TypeAlias`)` does not match asserted type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]`
tuples_type_compat.py:106:13: error[type-assertion-failure] Type `tuple[str, str] | tuple[int, int]` does not match asserted type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]`
-tuples_type_compat.py:107:13: error[type-assertion-failure] Type `@Todo(Support for `typing.TypeAlias`)` does not match asserted type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]`
tuples_type_compat.py:111:13: error[type-assertion-failure] Type `tuple[int, str, int]` does not match asserted type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]`
-tuples_type_compat.py:112:13: error[type-assertion-failure] Type `@Todo(Support for `typing.TypeAlias`)` does not match asserted type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]`
tuples_type_compat.py:126:13: error[type-assertion-failure] Type `tuple[int | str, str]` does not match asserted type `tuple[int | str, int | str]`
-tuples_type_compat.py:127:13: error[type-assertion-failure] Type `@Todo(Support for `typing.TypeAlias`)` does not match asserted type `tuple[int | str, int | str]`
tuples_type_compat.py:129:13: error[type-assertion-failure] Type `tuple[int | str, int]` does not match asserted type `tuple[int | str, int | str]`
-tuples_type_compat.py:130:13: error[type-assertion-failure] Type `@Todo(Support for `typing.TypeAlias`)` does not match asserted type `tuple[int | str, int | str]`
tuples_type_compat.py:157:6: error[invalid-assignment] Object of type `tuple[Literal[1], Literal[""], Literal[""]]` is not assignable to `tuple[int, str]`
tuples_type_compat.py:162:6: error[invalid-assignment] Object of type `tuple[Literal[1], Literal[1], Literal[""]]` is not assignable to `tuple[int, *tuple[str, ...]]`
tuples_type_compat.py:163:6: error[invalid-assignment] Object of type `tuple[Literal[1], Literal[""], Literal[1]]` is not assignable to `tuple[int, *tuple[str, ...]]`
@@ -1011,5 +1001,5 @@
typeddicts_usage.py:28:17: error[missing-typed-dict-key] Missing required key 'name' in TypedDict `Movie` constructor
typeddicts_usage.py:28:18: error[invalid-key] Unknown key "title" for TypedDict `Movie`: Unknown key "title"
typeddicts_usage.py:40:24: error[invalid-type-form] The special form `typing.TypedDict` is not allowed in type expressions. Did you mean to use a concrete TypedDict or `collections.abc.Mapping[str, object]` instead?
-Found 1013 diagnostics
+Found 1003 diagnostics
WARN A fatal error occurred while checking some files. Not all project files were analyzed. See the diagnostics list above for details.
|
|
CodSpeed Performance ReportMerging #21394 will degrade performances by 55.21%Comparing Summary
Benchmarks breakdown
|
496ca84 to
178e96a
Compare
178e96a to
f24a820
Compare
|
f24a820 to
8572040
Compare
|
| Lint rule | Added | Removed | Changed |
|---|---|---|---|
invalid-argument-type |
1,645 | 121 | 255 |
invalid-key |
1,837 | 0 | 0 |
type-assertion-failure |
170 | 432 | 327 |
no-matching-overload |
410 | 15 | 0 |
unused-ignore-comment |
13 | 333 | 0 |
possibly-missing-attribute |
155 | 10 | 135 |
unresolved-attribute |
101 | 83 | 70 |
invalid-assignment |
91 | 38 | 58 |
non-subscriptable |
97 | 38 | 8 |
invalid-return-type |
47 | 47 | 36 |
unsupported-operator |
76 | 14 | 19 |
invalid-type-form |
24 | 64 | 10 |
not-iterable |
7 | 9 | 8 |
possibly-unresolved-reference |
0 | 19 | 0 |
call-non-callable |
9 | 2 | 1 |
unknown-argument |
9 | 1 | 0 |
invalid-parameter-default |
7 | 0 | 1 |
index-out-of-bounds |
1 | 1 | 3 |
missing-argument |
5 | 0 | 0 |
invalid-await |
1 | 0 | 3 |
division-by-zero |
2 | 0 | 0 |
missing-typed-dict-key |
2 | 0 | 0 |
invalid-raise |
1 | 0 | 0 |
redundant-cast |
1 | 0 | 0 |
too-many-positional-arguments |
1 | 0 | 0 |
| Total | 4,712 | 1,227 | 934 |
|
Ah, no, David's corrected me -- the final version of that PR added no such hack. @sharkdp figured out a way of implementing #21363 without it. So don't mind me 🙃 |
8572040 to
40e2a6a
Compare
|
From the ecosystem, most diagnostics look like either true positives or unrelated existing issues, exposed by now understanding a bunch of new types. But there are quite a few |
Given that this PR implements PEP 613 aliases as if they were implicit aliases, a simple first-iteration version might be to add a new |
While true, I think it's still worth looking at the performance regression to see if anything stands out. 50% is way more than even the type-of-self PR introduced and I suspect what we're seeing is some bad run-away, similar to what we've seen with pydantic the other day. I'm not suggesting that we necessearily need to fix the performance regression, but we should at least try to understand where it's coming from and if there's something that clearly stands out. |
40e2a6a to
d328e93
Compare
d328e93 to
a92dc5d
Compare
a92dc5d to
f41a7fc
Compare
7ef3ab8 to
241de48
Compare
241de48 to
8488538
Compare
I spent some time looking at it and can't find any evidence of runaway or pathological performance. Altair is a relatively small project which checks very fast both before and after this PR. On CI it is showing 50% regression multi-threaded and 30% single-threaded; locally it shows even less regression for a single-threaded check (0.22s to 0.27s). It still appears to me that the regression here is explained mostly by resolving more types and emitting more diagnostics. I do think that we can be somewhat more efficient about how we handle implicit and PEP 613 type aliases, especially if they are large union type aliases used in many places, but @sharkdp is already fixing this in #21531 So I will mark this ready for review. |
8488538 to
b24fae4
Compare
b24fae4 to
4054767
Compare
AlexWaygood
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome!!
crates/ty_python_semantic/resources/mdtest/pep613_type_aliases.md
Outdated
Show resolved
Hide resolved
crates/ty_python_semantic/resources/mdtest/pep613_type_aliases.md
Outdated
Show resolved
Hide resolved
crates/ty_python_semantic/resources/mdtest/pep613_type_aliases.md
Outdated
Show resolved
Hide resolved
crates/ty_python_semantic/src/types/infer/builder/annotation_expression.rs
Outdated
Show resolved
Hide resolved
4054767 to
e8608b9
Compare
|
@carljm would you mind updating astral-sh/ty#445, now that type aliases are supported? |
Refs astral-sh/ty#544
Summary
Takes a more incremental approach to PEP 613 type alias support (vs #20107). Instead of eagerly inferring the RHS of a PEP 613 type alias as a type expression, infer it as a value expression, just like we do for implicit type aliases, taking advantage of the same support for e.g. unions and other type special forms.
The main reason I'm following this path instead of the one in #20107 is that we've realized that people do sometimes use PEP 613 type aliases as values, not just as types (because they are just a normal runtime assignment, unlike PEP 695 type aliases which create an opaque
TypeAliasType).This PR doesn't yet provide full support for recursive type aliases (they don't panic, but they just fall back to
Unknownat the recursion point). This is probably post-beta work.Test Plan
Added mdtests.
Many new ecosystem diagnostics, probably mostly just because we understand new types in lots of places, but I'll spot-check for potential problems.
Conformance suite changes are correct.
Performance regression is also just due to understanding lots of new types; nothing we do in this PR is inherently expensive.