-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[ty] Understand legacy and PEP 695 ParamSpec
#21139
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
e75dca4 to
031db5d
Compare
Diagnostic diff on typing conformance testsChanges were detected when running ty on typing conformance tests--- old-output.txt 2025-11-03 15:09:54.839284582 +0000
+++ new-output.txt 2025-11-03 15:09:58.086280840 +0000
@@ -1,4 +1,4 @@
-fatal[panic] Panicked at /home/runner/.cargo/git/checkouts/salsa-e6f3bb7c2a062968/cdd0b85/src/function/execute.rs:419:17 when checking `/home/runner/work/ruff/ruff/typing/conformance/tests/aliases_typealiastype.py`: `infer_definition_types(Id(182f5)): execute: too many cycle iterations`
+fatal[panic] Panicked at /home/runner/.cargo/git/checkouts/salsa-e6f3bb7c2a062968/cdd0b85/src/function/execute.rs:419:17 when checking `/home/runner/work/ruff/ruff/typing/conformance/tests/aliases_typealiastype.py`: `infer_definition_types(Id(18371)): 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__`
@@ -146,7 +146,9 @@
callables_protocol.py:97:1: error[invalid-assignment] Object of type `def cb4_bad1(x: int) -> None` is not assignable to `Proto4`
callables_protocol.py:121:1: error[invalid-assignment] Object of type `def cb6_bad1(*vals: bytes, *, max_len: int | None = None) -> list[bytes]` is not assignable to `NotProto6`
callables_protocol.py:169:1: error[invalid-assignment] Object of type `def cb8_bad1(x: int) -> Any` is not assignable to `Proto8`
-callables_protocol.py:179:62: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `R@__call__`
+callables_protocol.py:186:5: error[invalid-assignment] Object of type `Literal["str"]` is not assignable to attribute `other_attribute` of type `int`
+callables_protocol.py:187:5: error[unresolved-attribute] Unresolved attribute `xxx` on type `Proto9[P@decorator1, R@decorator1]`.
+callables_protocol.py:197:7: error[unresolved-attribute] Object of type `Proto9[Unknown, Unknown]` has no attribute `other_attribute2`
callables_protocol.py:238:1: error[invalid-assignment] Object of type `def cb11_bad1(x: int, y: str, /) -> Any` is not assignable to `Proto11`
callables_protocol.py:260:1: error[invalid-assignment] Object of type `def cb12_bad1(*args: Any, *, kwarg0: Any) -> None` is not assignable to `Proto12`
callables_protocol.py:284:1: error[invalid-assignment] Object of type `def cb13_no_default(path: str) -> str` is not assignable to `Proto13_Default`
@@ -433,7 +435,11 @@
generics_defaults.py:59:1: error[type-assertion-failure] Argument does not have asserted type `@Todo(unsupported nested subscript in type[X])`
generics_defaults.py:63:1: error[type-assertion-failure] Argument does not have asserted type `@Todo(unsupported nested subscript in type[X])`
generics_defaults.py:79:1: error[type-assertion-failure] Argument does not have asserted type `@Todo(unsupported nested subscript in type[X])`
-generics_defaults.py:80:1: error[type-assertion-failure] Argument does not have asserted type `@Todo(specialized non-generic class)`
+generics_defaults.py:80:1: error[type-assertion-failure] Argument does not have asserted type `Unknown`
+generics_defaults.py:80:32: error[too-many-positional-arguments] Too many positional arguments to class `Class_ParamSpec`: expected 1, got 2
+generics_defaults.py:81:1: error[type-assertion-failure] Argument does not have asserted type `Unknown`
+generics_defaults.py:81:29: error[invalid-type-form] List literals are not allowed in this context in a type expression: Did you mean `tuple[bool, bool]`?
+generics_defaults.py:81:46: error[too-many-positional-arguments] Too many positional arguments to class `Class_ParamSpec`: expected 1, got 2
generics_defaults.py:91:26: error[invalid-argument-type] `@Todo(starred expression)` is not a valid argument to `Generic`
generics_defaults.py:94:1: error[type-assertion-failure] Argument does not have asserted type `@Todo(unsupported nested subscript in type[X])`
generics_defaults.py:95:1: error[type-assertion-failure] Argument does not have asserted type `@Todo(specialized non-generic class)`
@@ -453,8 +459,10 @@
generics_defaults_specialization.py:27:5: error[type-assertion-failure] Argument does not have asserted type `SomethingWithNoDefaults[int, bool]`
generics_defaults_specialization.py:30:1: error[non-subscriptable] Cannot subscript object of type `<class 'SomethingWithNoDefaults[int, typing.TypeVar]'>` with no `__class_getitem__` method
generics_defaults_specialization.py:45:1: error[type-assertion-failure] Argument does not have asserted type `@Todo(unsupported nested subscript in type[X])`
+generics_paramspec_basic.py:10:1: error[invalid-paramspec] The name of a `ParamSpec` (`NotIt`) must match the name of the variable it is assigned to (`WrongName`)
+generics_paramspec_basic.py:23:20: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `P@func1`
generics_paramspec_basic.py:27:38: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `int`
-generics_paramspec_components.py:49:20: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `tuple[@Todo(Support for `typing.ParamSpec`), ...]`
+generics_paramspec_components.py:49:20: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `tuple[@Todo(ParamSpecArgs / ParamSpecKwargs), ...]`
generics_paramspec_components.py:83:18: error[parameter-already-assigned] Multiple values provided for parameter 1 (`x`) of function `foo`
generics_paramspec_semantics.py:13:56: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `(...) -> str`
generics_paramspec_semantics.py:17:40: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `int`
@@ -465,7 +473,6 @@
generics_paramspec_semantics.py:53:34: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `int`
generics_paramspec_semantics.py:57:34: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `int`
generics_paramspec_semantics.py:76:30: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `str`
-generics_paramspec_semantics.py:82:5: error[type-assertion-failure] Argument does not have asserted type `@Todo(specialized non-generic class)`
generics_paramspec_semantics.py:82:28: error[invalid-type-form] List literals are not allowed in this context in a type expression: Did you mean `list[int]`?
generics_paramspec_semantics.py:84:5: error[type-assertion-failure] Argument does not have asserted type `(int, /) -> str`
generics_paramspec_semantics.py:87:33: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `int`
@@ -480,6 +487,7 @@
generics_paramspec_specialization.py:40:27: error[invalid-type-form] List literals are not allowed in this context in a type expression: Did you mean `tuple[()]`?
generics_paramspec_specialization.py:40:31: error[invalid-type-form] List literals are not allowed in this context in a type expression: Did you mean `tuple[()]`?
generics_paramspec_specialization.py:52:22: error[invalid-type-form] List literals are not allowed in this context in a type expression: Did you mean `tuple[int, str, bool]`?
+generics_paramspec_specialization.py:58:15: error[too-many-positional-arguments] Too many positional arguments to class `ClassC`: expected 1, got 3
generics_scoping.py:14:1: error[type-assertion-failure] Argument does not have asserted type `int`
generics_scoping.py:15:1: error[type-assertion-failure] Argument does not have asserted type `str`
generics_scoping.py:42:1: error[type-assertion-failure] Argument does not have asserted type `str`
@@ -837,7 +845,6 @@
protocols_subtyping.py:80:5: error[invalid-assignment] Object of type `Proto4[int, int]` is not assignable to `Proto5[int | float]`
protocols_subtyping.py:102:5: error[invalid-assignment] Object of type `Proto6[int | float, int | float]` is not assignable to `Proto7[int, int | float]`
protocols_subtyping.py:103:5: error[invalid-assignment] Object of type `Proto6[int | float, int | float]` is not assignable to `Proto7[int | float, object]`
-protocols_variance.py:85:62: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `R@__call__`
qualifiers_annotated.py:38:17: error[invalid-type-form] List literals are not allowed in this context in a type expression: Did you mean `tuple[int, str]`?
qualifiers_annotated.py:39:17: error[invalid-type-form] Tuple literals are not allowed in this context in a type expression
qualifiers_annotated.py:39:18: error[invalid-type-form] Tuple literals are not allowed in this context in a type expression: Did you mean `tuple[int, str]`?
@@ -988,5 +995,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] Invalid key 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 990 diagnostics
+Found 997 diagnostics
WARN A fatal error occurred while checking some files. Not all project files were analyzed. See the diagnostics list above for details. |
|
fa73053 to
a5070d1
Compare
|
| Lint rule | Added | Removed | Changed |
|---|---|---|---|
invalid-argument-type |
156 | 1 | 56 |
unresolved-attribute |
90 | 6 | 8 |
unused-ignore-comment |
0 | 58 | 0 |
invalid-return-type |
20 | 22 | 3 |
missing-argument |
38 | 0 | 0 |
invalid-assignment |
23 | 2 | 9 |
possibly-missing-attribute |
17 | 5 | 5 |
too-many-positional-arguments |
0 | 14 | 0 |
no-matching-overload |
10 | 0 | 0 |
invalid-raise |
4 | 0 | 0 |
invalid-parameter-default |
0 | 0 | 3 |
type-assertion-failure |
3 | 0 | 0 |
call-non-callable |
2 | 0 | 0 |
invalid-await |
2 | 0 | 0 |
not-iterable |
2 | 0 | 0 |
redundant-cast |
0 | 2 | 0 |
| Total | 367 | 110 | 84 |
These are correct!
This will require adding special support for allowing both
Still need to enforce rules about where a |
|
I think this is a good checkpoint to get initial feedback. Follow-up work would be to:
For (1) and (2), I think I have a good idea on how to represent them based on the following:
And,
I'm thinking to create |
There might be a short-term hack we could add to There aren't that many new diagnostics, though, so this also might be fine for now considering that we're planning on addressing these new diagnostics shortly. Still, it's nice to avoid adding false positives for existing users where possible. |
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.
nice
Summary
This PR adds support for understanding the legacy definition and PEP 695 definition for
ParamSpec.This is still very initial and doesn't really implement any of the semantics.
Part of astral-sh/ty#157
Test Plan
Add mdtest cases.
Ecosystem analysis
Most of the diagnostics in
starletteare due to the fact that ty now understandsParamSpecis not aTodotype, so the assignability check fails. The code looks something like:There are multiple diagnostics where there's an attribute access on the
Wrappedobject offunctoolswhich Pyright also raises:There are additional diagnostics that is due to the assignability checks failing because ty now infers the
ParamSpecinstead of using theTodotype which would always succeed. This results in a fewno-matching-overloaddiagnostics because the assignability checks fail.There are a few diagnostics related to astral-sh/ty#491 where there's a variable which is either a bound method or a variable that's annotated with
Callablethat doesn't contain the instance as the first parameter.Another set of (valid) diagnostics are where the code hasn't provided all the type variables. ty is now raising diagnostics for these because we include
ParamSpectype variable in the signature. For example,staticmethod[Any]which contains two type variables.