-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[ty] Add support for generic PEP695 type aliases #20219
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
2cdea11 to
01c535e
Compare
Diagnostic diff on typing conformance testsChanges were detected when running ty on typing conformance tests--- old-output.txt 2025-09-08 20:03:49.611822703 +0000
+++ new-output.txt 2025-09-08 20:03:52.598894420 +0000
@@ -1,5 +1,6 @@
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
-fatal[panic] Panicked at /home/runner/.cargo/git/checkouts/salsa-e6f3bb7c2a062968/a3ffa22/src/function/execute.rs:228:25 when checking `/home/runner/work/ruff/ruff/typing/conformance/tests/aliases_typealiastype.py`: `infer_definition_types(Id(1227a)): execute: too many cycle iterations`
+fatal[panic] Panicked at /home/runner/.cargo/git/checkouts/salsa-e6f3bb7c2a062968/a3ffa22/src/function/execute.rs:228:25 when checking `/home/runner/work/ruff/ruff/typing/conformance/tests/aliases_type_statement.py`: `PEP695TypeAliasType < 'db >::value_type_(Id(b416)): execute: too many cycle iterations`
+fatal[panic] Panicked at /home/runner/.cargo/git/checkouts/salsa-e6f3bb7c2a062968/a3ffa22/src/function/execute.rs:228:25 when checking `/home/runner/work/ruff/ruff/typing/conformance/tests/aliases_typealiastype.py`: `infer_definition_types(Id(1267a)): execute: too many cycle iterations`
_directives_deprecated_library.py:15:31: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `int`
_directives_deprecated_library.py:30:26: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `str`
_directives_deprecated_library.py:36:41: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `Self@__add__`
@@ -50,25 +51,6 @@
aliases_newtype.py:18:1: error[invalid-assignment] Object of type `NewType` is not assignable to `type`
aliases_newtype.py:26:21: error[invalid-base] Invalid class base with type `NewType`
aliases_newtype.py:63:43: error[too-many-positional-arguments] Too many positional arguments to bound method `__init__`: expected 3, got 4
-aliases_type_statement.py:17:1: error[unresolved-attribute] Type `typing.TypeAliasType` has no attribute `bit_count`
-aliases_type_statement.py:19:1: error[call-non-callable] Object of type `TypeAliasType` is not callable
-aliases_type_statement.py:23:7: error[unresolved-attribute] Type `typing.TypeAliasType` has no attribute `other_attrib`
-aliases_type_statement.py:26:18: error[invalid-base] Invalid class base with type `typing.TypeAliasType`
-aliases_type_statement.py:37:22: error[invalid-type-form] Function calls are not allowed in type expressions
-aliases_type_statement.py:38:22: error[invalid-type-form] List literals are not allowed in this context in a type expression: Did you mean `tuple[int, str]`?
-aliases_type_statement.py:39:22: error[invalid-type-form] Tuple literals are not allowed in this context in a type expression
-aliases_type_statement.py:39:23: error[invalid-type-form] Tuple literals are not allowed in this context in a type expression: Did you mean `tuple[int, str]`?
-aliases_type_statement.py:40:22: error[invalid-type-form] List comprehensions are not allowed in type expressions
-aliases_type_statement.py:41:22: error[invalid-type-form] Dict literals are not allowed in type expressions
-aliases_type_statement.py:42:22: error[invalid-type-form] Function calls are not allowed in type expressions
-aliases_type_statement.py:43:28: error[invalid-type-form] Int literals are not allowed in this context in a type expression
-aliases_type_statement.py:44:22: error[invalid-type-form] `if` expressions are not allowed in type expressions
-aliases_type_statement.py:45:22: error[invalid-type-form] Variable of type `Literal[1]` is not allowed in a type expression
-aliases_type_statement.py:46:23: error[invalid-type-form] Boolean literals are not allowed in this context in a type expression
-aliases_type_statement.py:47:23: error[invalid-type-form] Int literals are not allowed in this context in a type expression
-aliases_type_statement.py:48:23: error[invalid-type-form] Boolean operations are not allowed in type expressions
-aliases_type_statement.py:49:23: error[fstring-type-annotation] Type expressions cannot use f-strings
-aliases_type_statement.py:80:37: error[invalid-type-form] List literals are not allowed in this context in a type expression: Did you mean `tuple[int, str]`?
aliases_variance.py:18:24: error[non-subscriptable] Cannot subscript object of type `<class 'ClassA[typing.TypeVar]'>` with no `__class_getitem__` method
aliases_variance.py:28:16: error[non-subscriptable] Cannot subscript object of type `<class 'ClassA[typing.TypeVar]'>` with no `__class_getitem__` method
aliases_variance.py:44:16: error[non-subscriptable] Cannot subscript object of type `<class 'ClassB[typing.TypeVar, typing.TypeVar]'>` with no `__class_getitem__` method
@@ -866,5 +848,5 @@
typeddicts_usage.py:28:1: error[missing-typed-dict-key] Missing required key 'name' in TypedDict `Movie` constructor
typeddicts_usage.py:28:18: error[invalid-key] Invalid key access on 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 867 diagnostics
+Found 849 diagnostics
WARN A fatal error occurred while checking some files. Not all project files were analyzed. See the diagnostics list above for details. |
|
|
Not a full review, but the new from typing import Callable
type ReturnsInt[**P] = Callable[P, int]
def call[**P](callable: ReturnsInt[P]) -> None: # ty: too-many-positional-arguments
pass |
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.
We also have resources/mdtest/pep695_type_aliases.md. Should these two files be merged?
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.
Possibly, although this file is specific to generic PEP 695 aliases.
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.
Yes. And I don't care too much about the structure/layout of the tests. Just wanted do let you know that there are some generics-related tests in that other file as well.
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.
Good point. I think we can look at this as a follow-up.
|
@sharkdp I believe that's astral-sh/ty#157. All the generics infrastructure in this PR is reused from generic classes, which emit a similar error: class ReturnsInt[**P]:
x: Callable[P, int]
def call[**P](callable: ReturnsInt[P]) -> None: # ty: too-many-positional-arguments
pass |
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.
Looking at how pyright handles this, there might a simpler way to do this that punts on several of the questions we've had.
class C[T]: ...
def _[T](c: C[T]):
reveal_type(c) # revealed: C[T@_]
reveal_type(C) # revealed: type[C[Unknown]]
reveal_type(C[int]) # revealed: type[C[int]]
type X[T] = C[T]
type Y = str
def _[T](x: X[T]):
reveal_type(x) # revealed: C[T@_]
def _(y: Y):
reveal_type(y) # revealed: str
reveal_type(X) # revealed: TypeAliasType
reveal_type(X[int]) # revealed: TypeAliasType
reveal_type(Y) # revealed: TypeAliasTypeNote that in the last two function bodies, the revealed type does not mention the type alias at all. So in ty terms, NominalInstance does not need to wrap the (specialized) type alias.
Similarly, the revealed type of X[int] is TypeAliasType, and so GenericAlias doesn't need to wrap the type alias either.
This suggests that we can roll back all of the changes to GenericAlias and NominalInstance, and just make sure that in_type_expression propagates the generic context through when it resolves a type alias into its value type.
[I made several comments below about the current implementation, which might become moot given the above, but I'm keeping them here in case my analysis is wrong and we do need to keep those changes]
And in particular, this reminds me of an earlier draft of this that you had shown me, where |
9ea2dca to
9777a06
Compare
9777a06 to
40b7c95
Compare
carljm
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.
Looking great! A few comments.
crates/ty_python_semantic/resources/mdtest/generics/pep695/aliases.md
Outdated
Show resolved
Hide resolved
|
The typing conformance suite is now panicking with type RecursiveTypeAlias1[T] = T | list[RecursiveTypeAlias1[T]]
r1_1: RecursiveTypeAlias1[int] = 1This looks like astral-sh/ty#256, which I believe is unrelated to this PR. The ecosystem failures also all seem to be cases where recognizing generic type aliases leads to more detailed diagnostics for underlying semantics that we do not support. |
b52a1dd to
0d2a272
Compare
Let me look at this. We avoid this already for PEP 695 aliases in general by creating a type alias type for the assignment without resolving the RHS, and deferring resolution of the RHS as part of |
|
Hmm I see the issue -- it's not that we resolve the RHS too eagerly in general, it's that when subscripting we resolve the value-type of the alias at that point to apply the specialization. We may need to allow |
carljm
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.
Looks great!
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.
Good point. I think we can look at this as a follow-up.
Summary
Adds support for generic PEP695 type aliases, e.g.,
Resolves astral-sh/ty#677.