Skip to content

[ty] Preserve recursive class TypeVar defaults through cycles#24455

Closed
charliermarsh wants to merge 3 commits intomainfrom
charlie/bounds-cycle
Closed

[ty] Preserve recursive class TypeVar defaults through cycles#24455
charliermarsh wants to merge 3 commits intomainfrom
charlie/bounds-cycle

Conversation

@charliermarsh
Copy link
Copy Markdown
Member

Summary

Closes astral-sh/ty#3229.

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

astral-sh-bot Bot commented Apr 6, 2026

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 87.72%. The percentage of expected errors that received a diagnostic held steady at 82.85%. The number of fully passing files held steady at 74/132.

@charliermarsh charliermarsh force-pushed the charlie/bounds-cycle branch from dba2109 to 9c03835 Compare April 6, 2026 20:06
@charliermarsh charliermarsh marked this pull request as ready for review April 6, 2026 20:06
@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot Bot commented Apr 6, 2026

Memory usage report

Summary

Project Old New Diff Outcome
prefect 716.92MB 717.04MB +0.02% (115.95kB)
sphinx 264.90MB 264.94MB +0.02% (48.25kB)
trio 117.77MB 117.79MB +0.02% (21.26kB)
flake8 48.02MB 48.02MB +0.01% (5.13kB)

Significant changes

Click to expand detailed breakdown

prefect

Name Old New Diff Outcome
infer_deferred_types 14.40MB 14.50MB +0.72% (106.21kB)
infer_definition_types 89.52MB 89.52MB +0.00% (3.14kB)
infer_expression_types_impl 62.08MB 62.08MB +0.00% (2.23kB)
infer_expression_type_impl 13.31MB 13.31MB +0.01% (1.57kB)
StaticClassLiteral<'db>::implicit_attribute_inner_ 9.99MB 9.99MB +0.01% (816.00B)
all_narrowing_constraints_for_expression 7.16MB 7.16MB +0.01% (528.00B)
all_negative_narrowing_constraints_for_expression 2.63MB 2.63MB +0.01% (384.00B)
infer_scope_types_impl 54.05MB 54.05MB +0.00% (348.00B)
GenericAlias<'db>::variance_of_ 586.95kB 587.25kB +0.05% (312.00B)
FunctionType<'db>::signature_ 4.01MB 4.01MB +0.00% (120.00B)
is_redundant_with_impl 5.51MB 5.51MB +0.00% (96.00B)
Type<'db>::member_lookup_with_policy_ 16.35MB 16.35MB +0.00% (96.00B)
Type<'db>::apply_specialization_ 3.66MB 3.66MB +0.00% (72.00B)
FunctionType<'db>::last_definition_signature_ 795.34kB 795.39kB +0.01% (48.00B)
Type<'db>::class_member_with_policy_ 17.99MB 17.99MB +0.00% (24.00B)
... 1 more

sphinx

Name Old New Diff Outcome
infer_deferred_types 5.54MB 5.59MB +0.93% (52.85kB)
infer_expression_types_impl 21.54MB 21.54MB -0.02% (3.59kB)
infer_definition_types 23.82MB 23.82MB -0.01% (2.99kB)
StaticClassLiteral<'db>::try_mro_ 2.07MB 2.07MB +0.13% (2.86kB)
infer_unpack_types 440.95kB 439.38kB -0.36% (1.57kB)
infer_scope_types_impl 15.48MB 15.48MB -0.01% (1.25kB)
infer_expression_type_impl 2.93MB 2.93MB -0.04% (1.08kB)
Type<'db>::try_call_dunder_get_ 4.95MB 4.95MB +0.02% (1.04kB)
Specialization 1.03MB 1.03MB +0.07% (720.00B)
loop_header_reachability 379.83kB 379.29kB -0.14% (552.00B)
Type<'db>::apply_specialization_ 1.65MB 1.65MB +0.03% (520.00B)
StaticClassLiteral<'db>::try_mro_::interned_arguments 478.62kB 479.11kB +0.10% (504.00B)
GenericAlias 450.84kB 451.27kB +0.09% (432.00B)
Type<'db>::class_member_with_policy_ 7.70MB 7.70MB +0.00% (392.00B)
Type<'db>::member_lookup_with_policy_ 6.54MB 6.54MB +0.00% (296.00B)
... 18 more

trio

Name Old New Diff Outcome
infer_deferred_types 2.35MB 2.37MB +0.86% (20.74kB)
infer_definition_types 7.62MB 7.62MB +0.01% (528.00B)

flake8

Name Old New Diff Outcome
infer_deferred_types 693.52kB 698.43kB +0.71% (4.91kB)
infer_definition_types 1.87MB 1.87MB +0.01% (228.00B)

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot Bot commented Apr 6, 2026

ecosystem-analyzer results

Lint rule Added Removed Changed
invalid-await 0 40 0
unresolved-attribute 0 0 22
invalid-argument-type 17 0 3
invalid-assignment 1 0 2
unsupported-operator 1 0 1
invalid-return-type 0 1 0
Total 19 41 28

Changes in flaky projects detected. Raw diff output excludes flaky projects; see the HTML report for details.

Raw diff (47 changes)
boostedblob (https://github.com/hauntsaninja/boostedblob)
- boostedblob/listing.py:402:14 error[unresolved-attribute] Object of type `Element[Unknown]` has no attribute `iterchildren`
+ boostedblob/listing.py:402:14 error[unresolved-attribute] Object of type `Element[str]` has no attribute `iterchildren`

cloud-init (https://github.com/canonical/cloud-init)
- cloudinit/sources/helpers/azure.py:518:32 error[unresolved-attribute] Attribute `text` is not defined on `None` in union `Element[Unknown] | None`
+ cloudinit/sources/helpers/azure.py:518:32 error[unresolved-attribute] Attribute `text` is not defined on `None` in union `Element[str] | None`

core (https://github.com/home-assistant/core)
+ homeassistant/helpers/storage.py:464:35 error[invalid-argument-type] Argument to bound method `async_save` is incorrect: Argument type `Unknown | dict[str, Any] | list[Any] | ... omitted 4 union elements` does not satisfy upper bound `Mapping[str, Any] | Sequence[Any]` of type variable `_T`
+ homeassistant/helpers/storage.py:464:35 error[invalid-argument-type] Argument to bound method `async_save` is incorrect: Expected `_T@Store`, found `Unknown | dict[str, Any] | list[Any] | ... omitted 4 union elements`
- homeassistant/helpers/storage.py:432:12 error[unsupported-operator] Operator `not in` is not supported between objects of type `Literal["minor_version"]` and `dict[str, Any] | Unknown | list[Any] | ... omitted 4 union elements`
+ homeassistant/helpers/storage.py:432:12 error[unsupported-operator] Operator `not in` is not supported between objects of type `Literal["minor_version"]` and `dict[str, Any] | Unknown | dict[str, JsonValueType] | ... omitted 6 union elements`
+ homeassistant/helpers/storage.py:433:13 error[invalid-assignment] Invalid subscript assignment with key of type `Literal["minor_version"]` and value of type `Literal[1]` on object of type `list[JsonValueType]`
+ homeassistant/helpers/storage.py:436:13 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> dict[str, Any] | list[JsonValueType] | str | ... omitted 3 union elements, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[JsonValueType]]` cannot be called with key of type `Literal["version"]` on object of type `list[JsonValueType]`
+ homeassistant/helpers/storage.py:437:17 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> dict[str, Any] | list[JsonValueType] | str | ... omitted 3 union elements, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[JsonValueType]]` cannot be called with key of type `Literal["minor_version"]` on object of type `list[JsonValueType]`
+ homeassistant/helpers/storage.py:439:22 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> dict[str, Any] | list[JsonValueType] | str | ... omitted 3 union elements, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[JsonValueType]]` cannot be called with key of type `Literal["data"]` on object of type `list[JsonValueType]`
+ homeassistant/helpers/storage.py:441:16 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> dict[str, Any] | list[JsonValueType] | str | ... omitted 3 union elements, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[JsonValueType]]` cannot be called with key of type `Literal["version"]` on object of type `list[JsonValueType]`
+ homeassistant/helpers/storage.py:441:16 error[unsupported-operator] Operator `>` is not supported between objects of type `Any | dict[str, Any] | list[Any] | ... omitted 4 union elements` and `Unknown | int`
+ homeassistant/helpers/storage.py:443:31 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `int`, found `Any | dict[str, Any] | list[Any] | ... omitted 4 union elements`
+ homeassistant/helpers/storage.py:443:31 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> dict[str, Any] | list[JsonValueType] | str | ... omitted 3 union elements, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[JsonValueType]]` cannot be called with key of type `Literal["version"]` on object of type `list[JsonValueType]`
+ homeassistant/helpers/storage.py:448:17 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> dict[str, Any] | list[JsonValueType] | str | ... omitted 3 union elements, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[JsonValueType]]` cannot be called with key of type `Literal["version"]` on object of type `list[JsonValueType]`
+ homeassistant/helpers/storage.py:449:17 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> dict[str, Any] | list[JsonValueType] | str | ... omitted 3 union elements, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[JsonValueType]]` cannot be called with key of type `Literal["minor_version"]` on object of type `list[JsonValueType]`
+ homeassistant/helpers/storage.py:454:57 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> dict[str, Any] | list[JsonValueType] | str | ... omitted 3 union elements, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[JsonValueType]]` cannot be called with key of type `Literal["version"]` on object of type `list[JsonValueType]`
+ homeassistant/helpers/storage.py:454:74 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> dict[str, Any] | list[JsonValueType] | str | ... omitted 3 union elements, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[JsonValueType]]` cannot be called with key of type `Literal["data"]` on object of type `list[JsonValueType]`
+ homeassistant/helpers/storage.py:458:25 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> dict[str, Any] | list[JsonValueType] | str | ... omitted 3 union elements, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[JsonValueType]]` cannot be called with key of type `Literal["version"]` on object of type `list[JsonValueType]`
+ homeassistant/helpers/storage.py:458:42 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> dict[str, Any] | list[JsonValueType] | str | ... omitted 3 union elements, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[JsonValueType]]` cannot be called with key of type `Literal["minor_version"]` on object of type `list[JsonValueType]`
+ homeassistant/helpers/storage.py:458:65 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> dict[str, Any] | list[JsonValueType] | str | ... omitted 3 union elements, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[JsonValueType]]` cannot be called with key of type `Literal["data"]` on object of type `list[JsonValueType]`
+ homeassistant/helpers/storage.py:461:24 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> dict[str, Any] | list[JsonValueType] | str | ... omitted 3 union elements, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[JsonValueType]]` cannot be called with key of type `Literal["version"]` on object of type `list[JsonValueType]`
+ homeassistant/helpers/storage.py:463:30 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> dict[str, Any] | list[JsonValueType] | str | ... omitted 3 union elements, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[JsonValueType]]` cannot be called with key of type `Literal["data"]` on object of type `list[JsonValueType]`

dd-trace-py (https://github.com/DataDog/dd-trace-py)
- tests/contrib/pytest/test_pytest_atr.py:337:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[Unknown] | None`
+ tests/contrib/pytest/test_pytest_atr.py:337:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[str] | None`
- tests/contrib/pytest/test_pytest_atr.py:338:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[Unknown] | None`
+ tests/contrib/pytest/test_pytest_atr.py:338:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[str] | None`
- tests/contrib/pytest/test_pytest_atr.py:339:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[Unknown] | None`
+ tests/contrib/pytest/test_pytest_atr.py:339:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[str] | None`
- tests/contrib/pytest/test_pytest_atr.py:340:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[Unknown] | None`
+ tests/contrib/pytest/test_pytest_atr.py:340:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[str] | None`
- tests/contrib/pytest/test_pytest_attempt_to_fix.py:259:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[Unknown] | None`
+ tests/contrib/pytest/test_pytest_attempt_to_fix.py:259:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[str] | None`
- tests/contrib/pytest/test_pytest_attempt_to_fix.py:260:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[Unknown] | None`
+ tests/contrib/pytest/test_pytest_attempt_to_fix.py:260:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[str] | None`
- tests/contrib/pytest/test_pytest_attempt_to_fix.py:261:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[Unknown] | None`
+ tests/contrib/pytest/test_pytest_attempt_to_fix.py:261:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[str] | None`
- tests/contrib/pytest/test_pytest_attempt_to_fix.py:262:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[Unknown] | None`
+ tests/contrib/pytest/test_pytest_attempt_to_fix.py:262:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[str] | None`
- tests/contrib/pytest/test_pytest_attempt_to_fix.py:271:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[Unknown] | None`
+ tests/contrib/pytest/test_pytest_attempt_to_fix.py:271:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[str] | None`
- tests/contrib/pytest/test_pytest_attempt_to_fix.py:272:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[Unknown] | None`
+ tests/contrib/pytest/test_pytest_attempt_to_fix.py:272:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[str] | None`
- tests/contrib/pytest/test_pytest_attempt_to_fix.py:273:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[Unknown] | None`
+ tests/contrib/pytest/test_pytest_attempt_to_fix.py:273:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[str] | None`
- tests/contrib/pytest/test_pytest_attempt_to_fix.py:274:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[Unknown] | None`
+ tests/contrib/pytest/test_pytest_attempt_to_fix.py:274:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[str] | None`
- tests/contrib/pytest/test_pytest_efd.py:515:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[Unknown] | None`
+ tests/contrib/pytest/test_pytest_efd.py:515:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[str] | None`
- tests/contrib/pytest/test_pytest_efd.py:516:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[Unknown] | None`
+ tests/contrib/pytest/test_pytest_efd.py:516:16 error[unresolved-attribute] Attribute `attrib` is not defined on `None` in union `Element[str] | None`

meson (https://github.com/mesonbuild/meson)
- mesonbuild/mtest.py:1122:17 error[invalid-assignment] Object of type `ElementTree[Element[Unknown]]` is not assignable to attribute `junit` of type `ElementTree[Element[Unknown] | None] | None`
+ mesonbuild/mtest.py:1122:17 error[invalid-assignment] Object of type `ElementTree[Element[str]]` is not assignable to attribute `junit` of type `ElementTree[Element[str] | None] | None`
- run_project_tests.py:1283:42 error[invalid-argument-type] Argument to function `SubElement` is incorrect: Expected `Element[Any]`, found `Element[Unknown] | None`
+ run_project_tests.py:1283:42 error[invalid-argument-type] Argument to function `SubElement` is incorrect: Expected `Element[Any]`, found `Element[str] | None`
- run_project_tests.py:1299:42 error[invalid-argument-type] Argument to function `SubElement` is incorrect: Expected `Element[Any]`, found `Element[Unknown] | None`
+ run_project_tests.py:1299:42 error[invalid-argument-type] Argument to function `SubElement` is incorrect: Expected `Element[Any]`, found `Element[str] | None`
- run_project_tests.py:1358:13 error[invalid-argument-type] Argument to function `SubElement` is incorrect: Expected `Element[Any]`, found `Element[Unknown] | None`
+ run_project_tests.py:1358:13 error[invalid-argument-type] Argument to function `SubElement` is incorrect: Expected `Element[Any]`, found `Element[str] | None`

pandas (https://github.com/pandas-dev/pandas)
- pandas/io/xml.py:483:21 error[unresolved-attribute] Attribute `findall` is not defined on `None` in union `Element[Unknown] | Unknown | None`
+ pandas/io/xml.py:483:21 error[unresolved-attribute] Attribute `findall` is not defined on `None` in union `Element[str] | Unknown | None`
- pandas/io/xml.py:516:26 error[unresolved-attribute] Attribute `find` is not defined on `None` in union `Element[Unknown] | Unknown | None`
+ pandas/io/xml.py:516:26 error[unresolved-attribute] Attribute `find` is not defined on `None` in union `Element[str] | Unknown | None`
- pandas/io/xml.py:600:17 error[unresolved-attribute] Attribute `xpath` is not defined on `Element[Unknown]`, `None` in union `Element[Unknown] | Unknown | None`
+ pandas/io/xml.py:600:17 error[unresolved-attribute] Attribute `xpath` is not defined on `Element[str]`, `None` in union `Element[str] | Unknown | None`
- pandas/io/xml.py:626:28 error[unresolved-attribute] Attribute `xpath` is not defined on `Element[Unknown]`, `None` in union `Element[Unknown] | Unknown | None`
+ pandas/io/xml.py:626:28 error[unresolved-attribute] Attribute `xpath` is not defined on `Element[str]`, `None` in union `Element[str] | Unknown | None`

pytest (https://github.com/pytest-dev/pytest)
- src/_pytest/junitxml.py:258:9 error[invalid-assignment] Object of type `() -> Element[Unknown]` is not assignable to attribute `to_xml` of type `def to_xml(self) -> Element[Unknown]`
+ src/_pytest/junitxml.py:258:9 error[invalid-assignment] Object of type `() -> Element[str]` is not assignable to attribute `to_xml` of type `def to_xml(self) -> Element[str]`

spack (https://github.com/spack/spack)
- lib/spack/spack/test/conftest.py:1912:16 error[unresolved-attribute] Attribute `get` is not defined on `None` in union `Element[Unknown] | None`
+ lib/spack/spack/test/conftest.py:1912:16 error[unresolved-attribute] Attribute `get` is not defined on `None` in union `Element[str] | None`

zulip (https://github.com/zulip/zulip)
- zerver/lib/markdown/nested_code_blocks.py:51:21 error[unresolved-attribute] Attribute `tag` is not defined on `None` in union `Element[Unknown] | None`
+ zerver/lib/markdown/nested_code_blocks.py:51:21 error[unresolved-attribute] Attribute `tag` is not defined on `None` in union `Element[str] | None`

Full report with detailed diff (timing results)

@charliermarsh charliermarsh marked this pull request as draft April 6, 2026 20:13
@charliermarsh charliermarsh force-pushed the charlie/bounds-cycle branch from bdc2681 to 11c31ec Compare April 7, 2026 16:45

reveal_type(Recursive()) # revealed: Recursive[Recursive[Unknown]]
reveal_type(Nested()) # revealed: Nested[list[Nested[Unknown]]]
reveal_type(RecursivePartial()) # revealed: RecursivePartial[int, RecursivePartial[Unknown, Unknown]]
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.

Theoretically this could be RecursivePartial[int, RecursivePartial[int, Unknown]], since only the implicit U is recursively looping.

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.

Class typevar default doesn't work when default value is the class specified

3 participants