Skip to content

[ty] Fix TODO for meta-type of intersections#24761

Draft
AlexWaygood wants to merge 1 commit intomainfrom
alex/inter-todo
Draft

[ty] Fix TODO for meta-type of intersections#24761
AlexWaygood wants to merge 1 commit intomainfrom
alex/inter-todo

Conversation

@AlexWaygood
Copy link
Copy Markdown
Member

Summary

Test Plan

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

astral-sh-bot Bot commented Apr 21, 2026

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 87.94%. The percentage of expected errors that received a diagnostic held steady at 83.36%. The number of fully passing files held steady at 79/133.

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot Bot commented Apr 21, 2026

Memory usage report

Summary

Project Old New Diff Outcome
sphinx 262.78MB 262.80MB +0.01% (20.09kB)
prefect 718.18MB 718.20MB +0.00% (18.90kB)
trio 117.65MB 117.65MB +0.01% (8.99kB)
flake8 47.94MB 47.94MB +0.00% (24.00B)

Significant changes

Click to expand detailed breakdown

sphinx

Name Old New Diff Outcome
Type<'db>::member_lookup_with_policy_ 6.85MB 6.85MB +0.05% (3.82kB)
try_call_bin_op_return_type_impl 207.61kB 210.82kB +1.55% (3.21kB)
IntersectionType 688.13kB 690.98kB +0.41% (2.84kB)
is_redundant_with_impl 1.49MB 1.49MB +0.18% (2.75kB)
is_redundant_with_impl::interned_arguments 1.73MB 1.73MB +0.16% (2.75kB)
Type<'db>::member_lookup_with_policy_::interned_arguments 2.67MB 2.67MB +0.03% (728.00B)
Type<'db>::try_call_dunder_get_::interned_arguments 1.16MB 1.16MB +0.06% (728.00B)
infer_expression_types_impl 20.76MB 20.76MB +0.00% (648.00B)
Type<'db>::try_call_dunder_get_ 4.88MB 4.88MB +0.01% (616.00B)
Type<'db>::class_member_with_policy_ 7.63MB 7.63MB +0.01% (520.00B)
infer_scope_types_impl 15.43MB 15.43MB +0.00% (516.00B)
StaticClassLiteral<'db>::implicit_attribute_inner_ 2.36MB 2.36MB +0.01% (336.00B)
StaticClassLiteral<'db>::implicit_attribute_inner_::interned_arguments 1.93MB 1.93MB +0.01% (288.00B)
UnionType 1.07MB 1.07MB +0.02% (224.00B)
Type<'db>::class_member_with_policy_::interned_arguments 4.03MB 4.03MB +0.00% (104.00B)
... 2 more

prefect

Name Old New Diff Outcome
Type<'db>::member_lookup_with_policy_ 17.27MB 17.27MB +0.03% (5.28kB)
IntersectionType 1.59MB 1.59MB +0.32% (5.12kB)
is_redundant_with_impl 4.29MB 4.29MB +0.06% (2.82kB)
is_redundant_with_impl::interned_arguments 4.39MB 4.39MB +0.06% (2.66kB)
StaticClassLiteral<'db>::try_mro_ 4.81MB 4.80MB -0.05% (2.29kB)
infer_scope_types_impl 54.92MB 54.93MB +0.00% (1.80kB)
try_call_bin_op_return_type_impl 257.75kB 259.40kB +0.64% (1.65kB)
Type<'db>::class_member_with_policy_ 17.67MB 17.67MB +0.01% (1.60kB)
Specialization 2.28MB 2.28MB -0.04% (864.00B)
Type<'db>::member_lookup_with_policy_::interned_arguments 5.95MB 5.95MB +0.01% (832.00B)
Type<'db>::try_call_dunder_get_ 10.64MB 10.64MB +0.01% (708.00B)
StaticClassLiteral<'db>::implicit_attribute_inner_ 10.08MB 10.08MB +0.01% (636.00B)
StaticClassLiteral<'db>::try_mro_::interned_arguments 1.16MB 1.16MB -0.04% (504.00B)
StaticClassLiteral<'db>::implicit_attribute_inner_::interned_arguments 5.39MB 5.39MB +0.01% (480.00B)
GenericAlias 1016.16kB 1015.73kB -0.04% (432.00B)
... 14 more

trio

Name Old New Diff Outcome
IntersectionType 201.64kB 203.30kB +0.83% (1.66kB)
is_redundant_with_impl::interned_arguments 507.20kB 508.15kB +0.19% (968.00B)
Type<'db>::member_lookup_with_policy_ 1.95MB 1.95MB +0.04% (820.00B)
Type<'db>::try_call_dunder_get_ 1.34MB 1.34MB +0.06% (808.00B)
infer_scope_types_impl 4.75MB 4.75MB +0.02% (768.00B)
is_redundant_with_impl 438.73kB 439.42kB +0.16% (708.00B)
lookup_dunder_new_inner 72.75kB 73.41kB +0.91% (680.00B)
Type<'db>::member_lookup_with_policy_::interned_arguments 924.62kB 925.03kB +0.04% (416.00B)
infer_expression_types_impl 7.01MB 7.01MB +0.00% (300.00B)
Type<'db>::class_member_with_policy_ 2.05MB 2.05MB +0.01% (268.00B)
TupleType 110.62kB 110.88kB +0.23% (256.00B)
try_call_bin_op_return_type_impl 50.12kB 50.37kB +0.49% (252.00B)
Type<'db>::apply_specialization_::interned_arguments 642.89kB 643.12kB +0.04% (240.00B)
UnionType 285.66kB 285.88kB +0.08% (224.00B)
infer_definition_types 7.58MB 7.58MB +0.00% (204.00B)
... 5 more

flake8

Name Old New Diff Outcome
try_call_bin_op_return_type_impl 6.31kB 6.34kB +0.37% (24.00B)

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot Bot commented Apr 21, 2026

ecosystem-analyzer results

Lint rule Added Removed Changed
unused-type-ignore-comment 22 3 0
unknown-argument 12 0 0
too-many-positional-arguments 9 0 0
invalid-argument-type 4 0 1
invalid-return-type 3 0 0
invalid-assignment 1 1 0
missing-argument 1 0 0
no-matching-overload 1 0 0
possibly-missing-attribute 1 0 0
Total 54 4 1

Flaky changes detected. This PR summary excludes flaky changes; see the HTML report for details.

Raw diff (59 changes)
bokeh (https://github.com/bokeh/bokeh)
+ src/bokeh/embed/standalone.py:287:28 error[too-many-positional-arguments] Too many positional arguments to `Model.__init__`: expected 1, got 2
+ src/bokeh/embed/standalone.py:287:28 error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2

discord.py (https://github.com/Rapptz/discord.py)
+ discord/ext/commands/core.py:1183:16 warning[possibly-missing-attribute] Attribute `__cog_name__` may be missing on object of type `type[CogT@Command] & ~<class 'NoneType'>`
+ discord/ui/view.py:952:17 error[invalid-assignment] Invalid subscript assignment with key of type `Pattern[str]` and value of type `type[Item[Any]] & type[DynamicItem[object]]` on object of type `dict[Pattern[str], type[DynamicItem[Item[Any]]]]`

koda-validate (https://github.com/keithasaurus/koda-validate)
+ koda_validate/namedtuple.py:130:43 error[invalid-argument-type] Argument to function `signature` is incorrect: Expected `(...) -> Any`, found `type[_NTT@NamedTupleValidator]`

mitmproxy (https://github.com/mitmproxy/mitmproxy)
- test/mitmproxy/proxy/tutils.py:354:25 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[Command, type[CommandCompleted]].__getitem__(key: Command, /) -> type[CommandCompleted]` cannot be called with key of type `type[@Todo]` on object of type `dict[Command, type[CommandCompleted]]`
+ test/mitmproxy/proxy/tutils.py:354:25 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[Command, type[CommandCompleted]].__getitem__(key: Command, /) -> type[CommandCompleted]` cannot be called with key of type `type[Command]` on object of type `dict[Command, type[CommandCompleted]]`

operator (https://github.com/canonical/operator)
- ops/model.py:1908:69 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive

pandas (https://github.com/pandas-dev/pandas)
+ pandas/core/dtypes/concat.py:126:17 error[unknown-argument] Argument `axis` does not match any known parameter of bound method `ExtensionArray._concat_same_type`
+ pandas/core/sorting.py:613:37 error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2

psycopg (https://github.com/psycopg/psycopg)
+ psycopg/psycopg/rows.py:137:16 error[invalid-return-type] Return type does not match returned value: expected `RowMaker[tuple[object, ...] & NamedTupleLike]`, found `bound method type[NamedTupleLike]._make(iterable: Iterable[Any]) -> NamedTupleLike`
+ psycopg/psycopg/rows.py:145:12 error[invalid-return-type] Return type does not match returned value: expected `type[tuple[object, ...]] & type[NamedTupleLike]`, found `<class 'Row'>`
+ psycopg/psycopg/types/composite.py:512:12 error[invalid-return-type] Return type does not match returned value: expected `type[tuple[object, ...]] & type[NamedTupleLike]`, found `<class '<unknown>'>`

rotki (https://github.com/rotki/rotki)
+ rotkehlchen/tasks/historical_balances.py:551:36 error[missing-argument] No argument provided for required parameter `group_identifier` of `HistoryBaseEntry.__init__`
+ rotkehlchen/tasks/historical_balances.py:552:17 error[unknown-argument] Argument `tx_ref` does not match any known parameter of `HistoryBaseEntry.__init__`
+ rotkehlchen/tasks/historical_balances.py:552:17 error[invalid-argument-type] Argument to `OnchainEvent.__init__` is incorrect: Expected `Never`, found `object`
+ rotkehlchen/tasks/historical_balances.py:562:17 error[unknown-argument] Argument `counterparty` does not match any known parameter of `HistoryBaseEntry.__init__`
+ rotkehlchen/tasks/historical_balances.py:563:17 error[unknown-argument] Argument `address` does not match any known parameter of `HistoryBaseEntry.__init__`
+ rotkehlchen/tasks/historical_balances.py:563:17 error[invalid-argument-type] Argument to `OnchainEvent.__init__` is incorrect: Expected `None`, found `object`

scipy (https://github.com/scipy/scipy)
+ scipy/sparse/_index.py:54:43 error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2
+ scipy/sparse/_index.py:54:43 error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2
+ scipy/sparse/_index.py:54:50 error[unknown-argument] Argument `shape` does not match any known parameter of `object.__init__`
+ scipy/sparse/_index.py:54:50 error[unknown-argument] Argument `shape` does not match any known parameter of `object.__init__`
+ scipy/sparse/_index.py:54:67 error[unknown-argument] Argument `dtype` does not match any known parameter of `object.__init__`
+ scipy/sparse/_index.py:54:67 error[unknown-argument] Argument `dtype` does not match any known parameter of `object.__init__`
+ scipy/sparse/_index.py:56:43 error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2
+ scipy/sparse/_index.py:56:43 error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2
+ scipy/sparse/_index.py:56:52 error[unknown-argument] Argument `shape` does not match any known parameter of `object.__init__`
+ scipy/sparse/_index.py:56:52 error[unknown-argument] Argument `shape` does not match any known parameter of `object.__init__`
+ scipy/sparse/_index.py:56:69 error[unknown-argument] Argument `dtype` does not match any known parameter of `object.__init__`
+ scipy/sparse/_index.py:56:69 error[unknown-argument] Argument `dtype` does not match any known parameter of `object.__init__`

scrapy (https://github.com/scrapy/scrapy)
+ scrapy/utils/python.py:268:27 error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2

sphinx (https://github.com/sphinx-doc/sphinx)
+ sphinx/domains/std/__init__.py:1392:26 error[no-matching-overload] No overload of bound method `dict.get` matches arguments

static-frame (https://github.com/static-frame/static-frame)
+ static_frame/core/container_util.py:1521:34 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/container_util.py:1523:71 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/container_util.py:1536:17 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/index.py:334:54 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/index.py:1946:34 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/index_hierarchy.py:2154:70 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/index_hierarchy.py:2155:41 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/index_hierarchy.py:2160:75 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/index_hierarchy.py:2165:60 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/index_hierarchy.py:2166:44 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/index_hierarchy.py:2169:42 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/index_hierarchy.py:2173:57 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/index_hierarchy.py:2179:31 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- static_frame/core/quilt.py:856:58 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- static_frame/core/quilt.py:862:46 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- static_frame/core/type_blocks.py:3350:34 error[invalid-assignment] Object of type `GeneratorType[(Iterable[Any] & ~TypeBlocks) | ndarray[Any, Any], None, None]` is not assignable to `Iterable[ndarray[Any, Any]]`
+ static_frame/core/type_blocks.py:3347:75 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/type_blocks.py:3351:36 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/type_blocks.py:3352:70 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/type_blocks.py:3355:86 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/type_blocks.py:3356:69 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/type_blocks.py:3360:123 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/type_blocks.py:3362:73 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/type_blocks.py:3365:43 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/type_blocks.py:3370:127 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/test/unit/test_quilt.py:2111:45 error[invalid-argument-type] Argument to bound method `Quilt._axis_tuple` is incorrect: Expected `(type[tuple[object, ...]] & type[NamedTupleLike]) | None`, found `<class 'tuple'>`

sympy (https://github.com/sympy/sympy)
+ sympy/physics/quantum/spin.py:1028:21 error[too-many-positional-arguments] Too many positional arguments to constructor `SpinState.__new__`: expected 3, got 4

Full report with detailed diff (timing results)

@AlexWaygood
Copy link
Copy Markdown
Member Author

AlexWaygood commented Apr 21, 2026

Ecosystem analysis (WIP)

Here's a minimized version of the bokeh hits

Details
from typing import Any, Sequence

class Model: ...

def f(
    models: Model | dict[str, Model],
    x: dict[object, object],
    y: list[str],
):
    dict_type: type[dict[Any, Any]] = dict
    
    if isinstance(models, dict):
        # (Model & Top[dict[Unknown, Unknown]]) | dict[str, Model]
        reveal_type(models)
        # (type[Model] & type[Top[dict[Unknown, Unknown]]]) | type[dict[str, Model]]
        reveal_type(models.__class__)
        dict_type = models.__class__
    
    # <class 'dict'> | (type[Model] & type[Top[dict[Unknown, Unknown]]]) | type[dict[str, Model]]
    reveal_type(dict_type)

    # error[no-matching-overload] No overload of `dict.__init__` matches arguments
    # error[no-matching-overload] No overload of `dict.__init__` matches arguments
    # error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2
    dict_type(zip(x.keys(), y))

On main, we emit the first diagnostic on the last line there, but not the second or the third.

This feels like yet another instance where our narrowing-via-intersections behaviour is strictly correct, but will likely be quite confusing for our users. Still, this behaviour is consistent with our behaviour elsewhere, so it's not really a bug in this PR.


A minimized version of the koda-validate hit is this:

from typing import NamedTuple, Callable, Any

def f[T: NamedTuple](x: type[T], y: NamedTuple) -> type[T]:
    reveal_type(x)  # type[T@f]
    reveal_type(y)  # tuple[object, ...] & NamedTupleLike
    # error[invalid-assignment] Object of type `type[T@f]` is not assignable to `(...) -> Any`
    z: Callable[..., Any] = x
    return x

This is weird and seems indicative of another intersection-related bug somewhere in the codebase; I'll take a look...


The pandas hits look similar to the bokeh hits, and they already have type: ignore[call-arg] commments on those lines for mypy.


The psycopg hits minimize to this:

from typing import NamedTuple, Any, Sequence, Protocol

class RowMaker[Row: tuple[Any, ...]](Protocol):
    def __call__(self, values: Sequence[Any], /) -> Row: ...

def namedtuple_row(namedtuple_cls: type[NamedTuple]) -> RowMaker[NamedTuple]:
    # error[invalid-return-type] Return type does not match returned value: expected `RowMaker[tuple[object, ...] & NamedTupleLike]`, found `bound method type[NamedTupleLike]._make(iterable: Iterable[Any]) -> NamedTupleLike`
    return namedtuple_cls._make

If this PR is rebased on #24770, we get

info: type `bound method type[NamedTupleLike]._make(iterable: Iterable[Any]) -> NamedTupleLike` is not assignable to protocol `RowMaker[tuple[object, ...] & NamedTupleLike]`
info: └── protocol member `__call__` is incompatible
info:     └── incompatible return types: `NamedTupleLike` is not assignable to `tuple[object, ...] & NamedTupleLike`

And a minimal repro of this that demonstrates the issue on main is:

from ty_extensions import Intersection
from typing import Self, reveal_type

class Bar:
    def method(self) -> Self:
        return self

class Foo: ...

def f(x: Intersection[Bar, Foo]):
    reveal_type(x.method())

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.

1 participant