Skip to content

[ty] Support variance keywords in ParamSpec#24927

Merged
charliermarsh merged 1 commit into
mainfrom
charlie/paramspec
Apr 29, 2026
Merged

[ty] Support variance keywords in ParamSpec#24927
charliermarsh merged 1 commit into
mainfrom
charlie/paramspec

Conversation

@charliermarsh
Copy link
Copy Markdown
Member

@charliermarsh charliermarsh commented Apr 29, 2026

Summary

This PR adds covariant, contravariant, and infer_variance support to ParamSpec.

See: python/typing#2215.

See: #24479 (review).

@astral-sh-bot astral-sh-bot Bot added the ty Multi-file analysis & type inference label Apr 29, 2026
@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot Bot commented Apr 29, 2026

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 88.79%. The percentage of expected errors that received a diagnostic held steady at 84.63%. The number of fully passing files held steady at 84/134.

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot Bot commented Apr 29, 2026

Memory usage report

Summary

Project Old New Diff Outcome
flake8 47.73MB 47.73MB -
sphinx 259.64MB 259.61MB -0.01% (28.75kB) ⬇️
trio 116.92MB 116.88MB -0.04% (46.57kB) ⬇️
prefect 692.15MB 675.30MB -2.43% (16.85MB) ⬇️

Significant changes

Click to expand detailed breakdown

sphinx

Name Old New Diff Outcome
infer_definition_types 23.58MB 23.58MB -0.03% (6.31kB) ⬇️
infer_deferred_types 5.27MB 5.26MB -0.10% (5.42kB) ⬇️
FunctionType<'db>::signature_ 2.39MB 2.38MB -0.11% (2.77kB) ⬇️
function_known_decorators 1.11MB 1.11MB -0.19% (2.13kB) ⬇️
StaticClassLiteral<'db>::variance_of_ 26.06kB 24.03kB -7.78% (2.03kB) ⬇️
infer_expression_types_impl 20.45MB 20.45MB -0.01% (1.18kB) ⬇️
GenericContext 135.62kB 134.50kB -0.83% (1.12kB) ⬇️
Type<'db>::apply_specialization_ 1.63MB 1.63MB -0.06% (1020.00B) ⬇️
FunctionType 3.18MB 3.18MB -0.03% (960.00B) ⬇️
StaticClassLiteral<'db>::variance_of_::interned_arguments 21.94kB 21.02kB -4.17% (936.00B) ⬇️
OverloadLiteral 1020.04kB 1019.38kB -0.06% (672.00B) ⬇️
BoundTypeVarInstance 625.08kB 624.45kB -0.10% (648.00B) ⬇️
code_generator_of_static_class 742.89kB 742.45kB -0.06% (448.00B) ⬇️
overloads_and_implementation_inner 767.93kB 767.50kB -0.05% (432.00B) ⬇️
InferableTypeVarsInner 84.89kB 84.48kB -0.49% (424.00B) ⬇️
... 26 more

trio

Name Old New Diff Outcome
infer_deferred_types 2.34MB 2.33MB -0.34% (8.23kB) ⬇️
infer_definition_types 7.64MB 7.64MB -0.10% (7.84kB) ⬇️
FunctionType<'db>::signature_ 1.12MB 1.11MB -0.37% (4.21kB) ⬇️
StaticClassLiteral<'db>::variance_of_ 18.04kB 15.75kB -12.73% (2.30kB) ⬇️
function_known_decorators 350.58kB 348.73kB -0.53% (1.85kB) ⬇️
FunctionType 1.53MB 1.53MB -0.11% (1.75kB) ⬇️
StaticClassLiteral<'db>::try_mro_ 795.25kB 793.50kB -0.22% (1.75kB) ⬇️
is_redundant_with_impl::interned_arguments 289.18kB 287.80kB -0.48% (1.38kB) ⬇️
is_redundant_with_impl 247.96kB 246.61kB -0.54% (1.35kB) ⬇️
CallableType 607.77kB 606.52kB -0.21% (1.25kB) ⬇️
GenericContext 125.84kB 124.61kB -0.98% (1.23kB) ⬇️
Type<'db>::apply_specialization_ 708.10kB 706.90kB -0.17% (1.20kB) ⬇️
infer_expression_types_impl 6.96MB 6.96MB -0.02% (1.17kB) ⬇️
Type<'db>::class_member_with_policy_ 2.04MB 2.04MB -0.06% (1.17kB) ⬇️
StaticClassLiteral<'db>::variance_of_::interned_arguments 15.05kB 13.99kB -7.01% (1.05kB) ⬇️
... 27 more

prefect

Name Old New Diff Outcome
infer_expression_types_impl 60.04MB 55.72MB -7.19% (4.32MB) ⬇️
infer_expression_type_impl 11.36MB 7.41MB -34.80% (3.95MB) ⬇️
infer_definition_types 87.45MB 84.88MB -2.95% (2.58MB) ⬇️
StaticClassLiteral<'db>::implicit_attribute_inner_ 8.90MB 6.58MB -26.11% (2.32MB) ⬇️
Type<'db>::member_lookup_with_policy_ 17.18MB 15.77MB -8.24% (1.42MB) ⬇️
Type<'db>::class_member_with_policy_ 17.71MB 16.85MB -4.81% (872.94kB) ⬇️
all_narrowing_constraints_for_expression 6.67MB 5.99MB -10.18% (695.63kB) ⬇️
all_negative_narrowing_constraints_for_expression 2.21MB 1.93MB -12.60% (284.52kB) ⬇️
StaticClassLiteral<'db>::variance_of_ 177.81kB 53.30kB -70.02% (124.51kB) ⬇️
infer_scope_types_impl 55.28MB 55.19MB -0.16% (89.11kB) ⬇️
infer_deferred_types 11.01MB 10.94MB -0.67% (75.73kB) ⬇️
Type<'db>::apply_specialization_ 3.67MB 3.61MB -1.48% (55.55kB) ⬇️
is_redundant_with_impl 1.93MB 1.88MB -2.31% (45.50kB) ⬇️
FunctionType<'db>::last_definition_signature_ 833.64kB 810.58kB -2.77% (23.07kB) ⬇️
GenericAlias<'db>::variance_of_ 58.04kB 40.72kB -29.84% (17.32kB) ⬇️
... 39 more

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot Bot commented Apr 29, 2026

ecosystem-analyzer results

Lint rule Added Removed Changed
unresolved-attribute 0 0 5
invalid-argument-type 0 1 0
invalid-return-type 0 0 1
Total 0 1 6

Raw diff:

antidote (https://github.com/Finistere/antidote)
- src/antidote/core/_inject.py:295:36 error[invalid-argument-type] Argument to `classmethod.__init__` is incorrect: Expected `(type[Unknown], /, *args: object, **kwargs: object) -> object`, found `Top[(...) -> object]`
- src/antidote/core/_injection.py:298:9 error[unresolved-attribute] Object of type `((...) -> object) & ~staticmethod[Top[(...)], object] & ~Top[classmethod[Unknown, Top[(...)], object]]` has no attribute `__qualname__`
+ src/antidote/core/_injection.py:298:9 error[unresolved-attribute] Object of type `((...) -> object) & ~Top[staticmethod[(...), object]] & ~Top[classmethod[Unknown, (...), object]]` has no attribute `__qualname__`
- src/antidote/core/_injection.py:298:30 error[unresolved-attribute] Object of type `((...) -> object) & ~staticmethod[Top[(...)], object] & ~Top[classmethod[Unknown, Top[(...)], object]]` has no attribute `__name__`
+ src/antidote/core/_injection.py:298:30 error[unresolved-attribute] Object of type `((...) -> object) & ~Top[staticmethod[(...), object]] & ~Top[classmethod[Unknown, (...), object]]` has no attribute `__name__`
- src/antidote/core/_injection.py:302:17 error[unresolved-attribute] Object of type `((...) -> object) & ~staticmethod[Top[(...)], object] & ~Top[classmethod[Unknown, Top[(...)], object]] & ~MethodType` has no attribute `__qualname__`
+ src/antidote/core/_injection.py:302:17 error[unresolved-attribute] Object of type `((...) -> object) & ~Top[staticmethod[(...), object]] & ~Top[classmethod[Unknown, (...), object]] & ~MethodType` has no attribute `__qualname__`
- src/antidote/core/_injection.py:302:42 error[unresolved-attribute] Object of type `((...) -> object) & ~staticmethod[Top[(...)], object] & ~Top[classmethod[Unknown, Top[(...)], object]] & ~MethodType` has no attribute `__name__`
+ src/antidote/core/_injection.py:302:42 error[unresolved-attribute] Object of type `((...) -> object) & ~Top[staticmethod[(...), object]] & ~Top[classmethod[Unknown, (...), object]] & ~MethodType` has no attribute `__name__`

discord.py (https://github.com/Rapptz/discord.py)
- discord/app_commands/errors.py:453:95 error[unresolved-attribute] Object of type `Top[(...) -> Coroutine[Any, Any, object]]` has no attribute `__qualname__`
+ discord/app_commands/errors.py:453:95 error[unresolved-attribute] Object of type `((Interaction[Any], /, *args: Unknown, **kwargs: Unknown) -> Coroutine[Any, Any, object]) | ((Any, Interaction[Any], /, *args: Any, **kwargs: Any) -> Coroutine[Any, Any, Any])` has no attribute `__qualname__`

strawberry (https://github.com/strawberry-graphql/strawberry)
- strawberry/types/fields/resolver.py:417:20 error[invalid-return-type] Return type does not match returned value: expected `(...) -> T@StrawberryResolver`, found `Top[(...) -> object]`
+ strawberry/types/fields/resolver.py:417:20 error[invalid-return-type] Return type does not match returned value: expected `(...) -> T@StrawberryResolver`, found `(...) -> object`

Full report with detailed diff (timing results)

@charliermarsh charliermarsh changed the title [ty] Support legacy ParamSpec variance [ty] Support variance keywords in ParamSpec Apr 29, 2026
@charliermarsh charliermarsh marked this pull request as ready for review April 29, 2026 14:41
Copy link
Copy Markdown
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, thank you!

(true, false, false) => Some(TypeVarVariance::Covariant),
(false, true, false) => Some(TypeVarVariance::Contravariant),
(false, false, false) => Some(TypeVarVariance::Invariant),
(false, false, true) => None,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is all it takes to support infer_variance=True, we should make this change for regular legacy typevars right away! (But in separate PR.)

@charliermarsh charliermarsh merged commit 3cec7f2 into main Apr 29, 2026
58 checks passed
@charliermarsh charliermarsh deleted the charlie/paramspec branch April 29, 2026 15:30
charliermarsh added a commit that referenced this pull request Apr 29, 2026
thejchap pushed a commit to thejchap/ruff that referenced this pull request May 23, 2026
## Summary

This PR adds `covariant`, `contravariant`, and `infer_variance` support
to `ParamSpec`.

See: python/typing#2215.

See:
astral-sh#24479 (review).
thejchap pushed a commit to thejchap/ruff that referenced this pull request May 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants