[ty] Infer ParamSpecs from call-wide constraints - #28828
Draft
dhruvmanila wants to merge 10 commits into
Draft
dhruvmanila wants to merge 10 commits into
dhruvmanila wants to merge 10 commits into
Conversation
dhruvmanila
added this pull request to stack #28829
September 23, 2026 12:37
Typing conformance resultsNo changes detected ✅Current numbersThe percentage of diagnostics emitted that were expected errors held steady at 98.24%. The percentage of expected errors that received a diagnostic held steady at 98.24%. The number of fully passing files held steady at 134/146. |
Memory usage reportSummary
Significant changesClick to expand detailed breakdowntrio
flake8
sphinx
prefect
|
|
| Lint rule | Added | Removed | Changed |
|---|---|---|---|
invalid-argument-type |
2 | 1 | 2 |
invalid-assignment |
0 | 3 | 0 |
type-assertion-failure |
0 | 0 | 2 |
unsound-return-statement |
1 | 0 | 1 |
unused-type-ignore-comment |
2 | 0 | 0 |
| Total | 5 | 4 | 5 |
Large timing changes:
| Project | Old Time | New Time | Change |
|---|---|---|---|
pytest-autoprofile |
0.10s | 0.26s | +155% |
Raw diff:
discord.py (https://github.com/Rapptz/discord.py)
- discord/app_commands/checks.py:390:42 error[invalid-assignment] Object of type `Coroutine[Any, Any, Cooldown | None] | Cooldown | None` is not assignable to `Cooldown | None`
+ discord/app_commands/commands.py:2074:16 warning[unsound-return-statement] Unsound return statement: `Command[GroupT@decorator, (...), T@decorator]` is not a subtype of `Command[GroupT@decorator, P@decorator, T@decorator]`
+ discord/ext/commands/core.py:1063:71 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ discord/ext/commands/core.py:1641:75 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
prefect (https://github.com/PrefectHQ/prefect)
- src/integrations/prefect-dask/prefect_dask/task_runners.py:474:28 error[invalid-argument-type] Argument to bound method `TaskRunner.map` is incorrect: Expected `Task[P@map, R@map | Any | CoroutineType[Any, Any, R@map | Any]]`, found `Task[P@map, R@map]`
+ src/integrations/prefect-dask/prefect_dask/task_runners.py:474:28 error[invalid-argument-type] Argument to bound method `TaskRunner.map` is incorrect: Expected `Task[P@map, Any | CoroutineType[Any, Any, Any]]`, found `Task[P@map, R@map]`
- src/integrations/prefect-ray/prefect_ray/task_runners.py:315:28 error[invalid-argument-type] Argument to bound method `TaskRunner.map` is incorrect: Expected `Task[P@map, R@RayTaskRunner | Any | CoroutineType[Any, Any, R@RayTaskRunner | Any]]`, found `Task[P@map, R@RayTaskRunner]`
+ src/integrations/prefect-ray/prefect_ray/task_runners.py:315:28 error[invalid-argument-type] Argument to bound method `TaskRunner.map` is incorrect: Expected `Task[P@map, Any | CoroutineType[Any, Any, Any]]`, found `Task[P@map, R@RayTaskRunner]`
- src/prefect/server/database/dependencies.py:289:16 warning[unsound-return-statement] Unsound return statement: `_DBInjectorMethod[T@DBInjector & ~None, P@DBInjector, Unknown]` is not a subtype of `Self@__get__ | _DBInjectorMethod[T@DBInjector, P@DBInjector, R@DBInjector]`
+ src/prefect/server/database/dependencies.py:289:16 warning[unsound-return-statement] Unsound return statement: `_DBInjectorMethod[T@DBInjector, P@DBInjector, R@DBInjector | Unknown]` is not a subtype of `Self@__get__ | _DBInjectorMethod[T@DBInjector, P@DBInjector, R@DBInjector]`
pyochain (https://github.com/OutSquareCapital/pyochain)
- tests/test_typing/check_abcs.py:46:42 error[invalid-assignment] Object of type `Set[Vec[Dog]]` is not assignable to `PyoSet[Animal]`
- tests/test_typing/check_abcs.py:47:35 error[invalid-assignment] Object of type `Seq[Vec[Dog]]` is not assignable to `Seq[Animal]`
+ tests/test_concatenate_unsafe.py:17:25 error[invalid-argument-type] Argument to bound method `Pipe.pipe` is incorrect: Expected `(Null[Literal[5]] | Some[int], /) -> Unknown`, found `(opt: Some[int]) -> Unknown`
+ tests/test_result.py:28:24 error[invalid-argument-type] Argument to bound method `Pipe.pipe` is incorrect: Expected `(Err[Literal[42], Any] | Ok[int, Any], /) -> str`, found `(res: Ok[int, Any]) -> str`
- tests/test_typing/check_mixins.py:24:18 error[type-assertion-failure] Type `Seq[Seq[int] | (Top[Pipe] & Iterable[Seq[int] | (Top[Pipe] & Iterable[Seq[int] | (Top[Pipe] & Iterable[Seq[int]])])])]` does not match asserted type `Seq[int]`
+ tests/test_typing/check_mixins.py:24:18 error[type-assertion-failure] Type `Seq[int | Seq[int]]` does not match asserted type `Seq[int]`
- tests/test_typing/check_mixins.py:24:37 error[invalid-argument-type] Argument to bound method `Pipe.pipe` is incorrect: Expected `(Seq[int] | (Iterable[Seq[int] | (Top[Pipe] & Iterable[Seq[int] | (Top[Pipe] & Iterable[Seq[int] | (Top[Pipe] & Iterable[Seq[int]])])])] & Top[Pipe]), /) -> Seq[Seq[int] | (Top[Pipe] & Iterable[Seq[int] | (Top[Pipe] & Iterable[Seq[int] | (Top[Pipe] & Iterable[Seq[int]])])])]`, found `<class 'Seq[Seq[int] | (Top[Pipe] & Iterable[Seq[int] | (Top[Pipe] & Iterable[Seq[int] | (Top[Pipe] & Iterable[Seq[int]])])])]'>`
- tests/test_typing/check_mixins.py:30:19 error[type-assertion-failure] Type `Vec[int | Seq[int]]` does not match asserted type `Vec[int]`
+ tests/test_typing/check_mixins.py:30:19 error[type-assertion-failure] Type `Unknown` does not match asserted type `Vec[int]`
Merging this PR will improve performance by 5.81%
Performance Changes
Tip Curious why performance improved? Comment Comparing Footnotes
|
dhruvmanila
force-pushed
the
dhruv/paramspec-inference
branch
from
September 30, 2026 09:42
23eeae5 to
5f29031
Compare
dhruvmanila
force-pushed
the
dhruv/paramspec-inference
branch
from
October 1, 2026 07:35
5f29031 to
e6ac6e3
Compare
Captured parameter lists can share type variables with the surrounding call's pending return constraints. Treating their stored generic context as local to a constraint-set comparison freshens and quantifies away those variables, allowing conditional relationships to appear unconditional. For two callback occurrences, this can discard the condition T_run = T_keep and incorrectly connect their independent ParamSpecs. Preserve the captured variables during lazy signature comparisons. Keep the stored generic context available for callback invocation. The deferred-quantification proposal in #25559 changes when callable-local variables are removed, not which variables belong to the comparison. Its is_always_satisfied check still applies deferred quantification, so marking captured variables for later removal would still lose the condition when testing unconditional equivalence. These variables must remain outside the comparison's cleanup set even when cleanup is deferred. Retain eager comparison behavior for compatibility inference while both checking paths exist. Once eager evaluation is removed, the captured-parameter-list distinction should remain without the TypeVarEvaluation condition. Add a focused regression for equality between captured parameter lists. The existing callback tests cover independent invocations and preserved generics.
Cover concrete parameter lists, consumers, variance, repeated ParamSpecs, defaults, constructor signatures, and callable receivers in both generic syntaxes. Specify the first-seen policy for repeated ParamSpecs. Exercise parameter-list retention and defaults in Python when inference for another type variable exceeds its budget. Document the existing limitation when capturing parameter lists from both __new__ and __init__, including the missing argument diagnostic that combining those lists should enable.
Solve ParamSpecs from pending call constraints while keeping TypeVarTuple inference on the compatibility path. Preserve the existing merged specialization behavior: keep the first available parameter list and union-merge ordinary type variables across alternatives, including incomplete inference. Missing bindings use the usual defaults. Use the streaming projection for merged specializations. Preserve incomplete solution metadata and whole-projection budget recovery. Distinguishing a ParamSpec binding lost to budget exhaustion from missing evidence remains a documented limitation. Keep the first supplied parameter list while support for combining repeated ParamSpec bounds is deferred. Apply this policy when recording individual mappings and complete constraint sets, preserving requirements on other type variables. Combining the parameter lists from both __new__ and __init__ remains a documented limitation in the constructor tests. Specialize callable receivers from captured parameter-list lower bounds. Follow the first-seen policy described in astral-sh/ty#3555.
dhruvmanila
force-pushed
the
dhruv/paramspec-inference
branch
from
October 6, 2026 06:21
e6ac6e3 to
9b63b52
Compare
Cover concrete parameter lists, consumers, variance, repeated ParamSpecs, defaults, constructor signatures, and callable receivers in both generic syntaxes. Specify the first-seen policy for repeated ParamSpecs. Exercise parameter-list retention and defaults in Python when inference for another type variable exceeds its budget. Document the existing limitation when capturing parameter lists from both __new__ and __init__, including the missing argument diagnostic that combining those lists should enable.
Solve ParamSpecs from pending call constraints while keeping TypeVarTuple inference on the compatibility path. Preserve the existing merged specialization behavior: keep the first available parameter list and union-merge ordinary type variables across alternatives, including incomplete inference. Missing bindings use the usual defaults. Use the streaming projection for merged specializations. Preserve incomplete solution metadata and whole-projection budget recovery. Distinguishing a ParamSpec binding lost to budget exhaustion from missing evidence remains a documented limitation. Keep the first supplied parameter list while support for combining repeated ParamSpec bounds is deferred. Apply this policy when recording individual mappings and complete constraint sets, preserving requirements on other type variables. Combining the parameter lists from both __new__ and __init__ remains a documented limitation in the constructor tests. Specialize callable receivers from captured parameter-list lower bounds. Follow the first-seen policy described in astral-sh/ty#3555.
dhruvmanila
force-pushed
the
dhruv/paramspec-inference
branch
from
October 6, 2026 06:35
9b63b52 to
582539b
Compare
dhruvmanila
had a problem deploying
to
automations
October 6, 2026 06:35 — with
GitHub Actions
Error
Binding a method can remove its only type variable and leave Some(empty), while an otherwise identical nongeneric callable has no generic context. The differing signature identities duplicate ParamSpec constraint choices and can exhaust the solution budget before overload expansion. Drop empty contexts after removing Self. Restore the direct combine(x, x, x) ParamSpec example so it checks all three parameter lists in one inference operation and retains the existing 64/80-alternative expansion assertions.
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Infer ParamSpecs from the call's pending constraint set, following the first-supplied-parameter-list policy described in astral-sh/ty#3555. Preserve ordinary type-variable inference alongside those parameter lists, use defaults for missing bindings, and retain known parameter lists when inference for another variable exceeds its work limit.
Also specialize callable receivers from captured parameter lists. Combining repeated ParamSpec bounds and both constructor parameter lists remains unsupported.
Test Plan
Add legacy and PEP 695 mdtests covering parameter shapes, consumers, variance, repeated ParamSpecs, defaults, incomplete inference, callable receivers, and constructor limitations. The full semantic test suite passed: 962 passed and 34 skipped.