Skip to content

[ty] Support basic narrowing with aliased conditional expressions#24302

Merged
mtshiba merged 13 commits intomainfrom
narrow-by-aliased-conditional-expr-minimal
Apr 25, 2026
Merged

[ty] Support basic narrowing with aliased conditional expressions#24302
mtshiba merged 13 commits intomainfrom
narrow-by-aliased-conditional-expr-minimal

Conversation

@mtshiba
Copy link
Copy Markdown
Collaborator

@mtshiba mtshiba commented Mar 30, 2026

Summary

Closes astral-sh/ty#719 (almost)

From #23937

A complete implementation is already available in #23937. However, due to the complexity of the mechanism, I decided to extract only the basic support. As explicitly stated in mdtest, this PR does not yet implement support for cross-scope alias narrowing and chained aliases.

Test Plan

new mdtest: narrow/aliased_conditions.md

@mtshiba mtshiba added ty Multi-file analysis & type inference ecosystem-analyzer labels Mar 30, 2026
@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot Bot commented Mar 30, 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 Mar 30, 2026

Memory usage report

Summary

Project Old New Diff Outcome
prefect 709.34MB 710.01MB +0.09% (678.49kB)
sphinx 259.63MB 260.04MB +0.16% (421.96kB)
trio 116.89MB 116.93MB +0.03% (36.17kB)
flake8 47.89MB 47.91MB +0.04% (19.56kB)

Significant changes

Click to expand detailed breakdown

prefect

Name Old New Diff Outcome
infer_expression_types_impl 63.31MB 63.62MB +0.49% (317.99kB)
all_narrowing_constraints_for_expression 7.24MB 7.38MB +1.94% (143.61kB)
semantic_index 175.59MB 175.70MB +0.06% (113.90kB)
Expression 8.98MB 9.03MB +0.57% (52.80kB)
all_negative_narrowing_constraints_for_expression 2.64MB 2.69MB +1.64% (44.47kB)
infer_definition_types 91.20MB 91.21MB +0.01% (4.91kB)
check_file_impl 17.11MB 17.10MB -0.02% (2.90kB)
infer_expression_type_impl 13.56MB 13.57MB +0.01% (2.08kB)
infer_scope_types_impl 55.59MB 55.59MB +0.00% (1000.00B)
Type<'db>::member_lookup_with_policy_ 17.29MB 17.29MB -0.00% (392.00B)
StaticClassLiteral<'db>::implicit_attribute_inner_::interned_arguments 5.40MB 5.40MB -0.01% (384.00B)
Type<'db>::class_member_with_policy_ 17.70MB 17.70MB -0.00% (360.00B)
StaticClassLiteral<'db>::implicit_attribute_inner_ 10.08MB 10.08MB -0.00% (352.00B)
place_table 763.77kB 764.06kB +0.04% (300.00B)
is_redundant_with_impl::interned_arguments 2.13MB 2.13MB +0.01% (264.00B)
... 9 more

sphinx

Name Old New Diff Outcome
infer_expression_types_impl 20.61MB 20.81MB +0.95% (201.16kB)
semantic_index 61.20MB 61.27MB +0.11% (68.81kB)
all_narrowing_constraints_for_expression 2.34MB 2.39MB +2.10% (50.19kB)
all_negative_narrowing_constraints_for_expression 1.00MB 1.05MB +4.56% (46.85kB)
Expression 3.18MB 3.22MB +1.22% (39.73kB)
infer_definition_types 23.64MB 23.65MB +0.01% (3.60kB)
infer_expression_type_impl 2.90MB 2.90MB +0.05% (1.44kB)
Type<'db>::class_member_with_policy_ 7.62MB 7.62MB +0.02% (1.22kB)
Type<'db>::member_lookup_with_policy_ 6.84MB 6.84MB +0.02% (1.17kB)
IntersectionType 463.96kB 465.07kB +0.24% (1.11kB)
infer_scope_types_impl 15.46MB 15.46MB +0.00% (792.00B)
Type<'db>::class_member_with_policy_::interned_arguments 4.02MB 4.03MB +0.02% (728.00B)
StaticClassLiteral<'db>::implicit_attribute_inner_ 2.36MB 2.36MB +0.03% (672.00B)
StaticClassLiteral<'db>::implicit_attribute_inner_::interned_arguments 1.93MB 1.93MB +0.03% (576.00B)
is_redundant_with_impl::interned_arguments 1.08MB 1.08MB +0.05% (528.00B)
... 13 more

trio

Name Old New Diff Outcome
infer_expression_types_impl 6.99MB 7.00MB +0.21% (14.91kB)
semantic_index 30.02MB 30.03MB +0.04% (11.87kB)
Expression 1.42MB 1.42MB +0.27% (3.94kB)
all_narrowing_constraints_for_expression 592.99kB 596.38kB +0.57% (3.39kB)
all_negative_narrowing_constraints_for_expression 184.62kB 185.45kB +0.45% (844.00B)
infer_definition_types 7.57MB 7.57MB +0.01% (696.00B)
IntersectionType 131.94kB 132.10kB +0.12% (168.00B)
IntersectionType<'db>::from_two_elements_ 17.50kB 17.61kB +0.65% (116.00B)
infer_expression_type_impl 1.31MB 1.31MB +0.01% (96.00B)
IntersectionType<'db>::from_two_elements_::interned_arguments 13.06kB 13.15kB +0.66% (88.00B)
infer_scope_types_impl 4.71MB 4.71MB +0.00% (60.00B)
loop_header_reachability 130.43kB 130.47kB +0.04% (48.00B)

flake8

Name Old New Diff Outcome
semantic_index 13.74MB 13.74MB +0.04% (6.01kB)
infer_expression_types_impl 1.03MB 1.04MB +0.45% (4.74kB)
all_narrowing_constraints_for_expression 83.60kB 86.86kB +3.89% (3.25kB)
Expression 366.68kB 368.58kB +0.52% (1.90kB)
all_negative_narrowing_constraints_for_expression 39.68kB 40.59kB +2.31% (940.00B)
IntersectionType 45.23kB 45.77kB +1.19% (552.00B)
infer_definition_types 1.86MB 1.86MB +0.02% (448.00B)
is_redundant_with_impl::interned_arguments 86.11kB 86.54kB +0.50% (440.00B)
infer_expression_type_impl 118.45kB 118.72kB +0.23% (284.00B)
is_redundant_with_impl 83.48kB 83.72kB +0.28% (240.00B)
enum_metadata 65.03kB 65.21kB +0.28% (184.00B)
UnionType 52.38kB 52.48kB +0.21% (112.00B)
Type<'db>::member_lookup_with_policy_::interned_arguments 230.45kB 230.55kB +0.04% (104.00B)
loop_header_reachability 12.75kB 12.84kB +0.77% (100.00B)
Type<'db>::apply_specialization_ 208.81kB 208.90kB +0.04% (92.00B)
... 3 more

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot Bot commented Mar 30, 2026

ecosystem-analyzer results

Lint rule Added Removed Changed
unresolved-attribute 1 53 3
invalid-argument-type 1 25 0
not-subscriptable 5 10 0
unused-type-ignore-comment 12 0 0
invalid-assignment 3 4 3
no-matching-overload 1 9 0
call-non-callable 0 7 0
redundant-cast 4 0 0
invalid-return-type 1 2 0
not-iterable 0 3 0
unsupported-operator 0 2 1
call-top-callable 1 0 0
Total 29 115 7
Raw diff (151 changes)
CPython (cases_generator) (https://github.com/python/cpython)
- Tools/cases_generator/stack.py:738:13 error[invalid-assignment] Object of type `PointerOffset | None` is not assignable to attribute `memory_offset` on type `None | Local`

PyGithub (https://github.com/PyGithub/PyGithub)
+ github/Repository.py:2331:35 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ github/Repository.py:4092:91 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive

apprise (https://github.com/caronc/apprise)
- apprise/plugins/webexteams.py:454:41 error[invalid-argument-type] Argument to constructor `enumerate.__new__` is incorrect: Expected `Iterable[Unknown]`, found `Unknown | None`
- apprise/plugins/webexteams.py:458:25 error[unresolved-attribute] Object of type `_T@enumerate & ~AlwaysTruthy` has no attribute `url`
- apprise/plugins/webexteams.py:464:21 error[unresolved-attribute] Object of type `_T@enumerate & ~AlwaysFalsy` has no attribute `url`
- apprise/plugins/webexteams.py:470:22 error[unresolved-attribute] Object of type `_T@enumerate & ~AlwaysFalsy` has no attribute `open`
- apprise/plugins/webexteams.py:474:33 error[unresolved-attribute] Object of type `_T@enumerate & ~AlwaysFalsy` has no attribute `name`
- apprise/plugins/webexteams.py:475:36 error[unresolved-attribute] Object of type `_T@enumerate & ~AlwaysFalsy` has no attribute `name`
- apprise/plugins/webexteams.py:479:29 error[unresolved-attribute] Object of type `_T@enumerate & ~AlwaysFalsy` has no attribute `mimetype`
- apprise/plugins/webexteams.py:511:29 error[unresolved-attribute] Object of type `_T@enumerate & ~AlwaysFalsy` has no attribute `name`
- apprise/plugins/webexteams.py:525:25 error[unresolved-attribute] Object of type `_T@enumerate & ~AlwaysFalsy` has no attribute `name`
- apprise/plugins/webexteams.py:539:25 error[unresolved-attribute] Object of type `_T@enumerate & ~AlwaysFalsy` has no attribute `name`
- apprise/plugins/webexteams.py:539:44 error[unresolved-attribute] Object of type `_T@enumerate & ~AlwaysFalsy` has no attribute `name`

bokeh (https://github.com/bokeh/bokeh)
- src/bokeh/plotting/_geo_feature.py:505:23 error[invalid-argument-type] Argument to function `len` is incorrect: Expected `Sized`, found `Unknown | None`
- src/bokeh/plotting/_geo_feature.py:521:40 error[not-subscriptable] Cannot subscript object of type `None` with no `__getitem__` method

core (https://github.com/home-assistant/core)
- homeassistant/components/tts/__init__.py:371:41 error[invalid-argument-type] Argument to bound method `StreamWriter.write` is incorrect: Expected `bytes | bytearray | memoryview[int]`, found `object`

dd-trace-py (https://github.com/DataDog/dd-trace-py)
- ddtrace/appsec/_common_module_patches.py:355:9 error[unresolved-attribute] Attribute `downstream_requests` is not defined on `None` in union `ASM_Environment | None`
- ddtrace/internal/remoteconfig/client.py:559:13 error[invalid-assignment] Invalid subscript assignment with key of type `Literal["error"]` and value of type `str | None` on object of type `dict[str, int | list[dict[str, str | int | None]]]`
+ ddtrace/internal/remoteconfig/client.py:559:13 error[invalid-assignment] Invalid subscript assignment with key of type `Literal["error"]` and value of type `str` on object of type `dict[str, int | list[dict[str, str | int | None]]]`
+ ddtrace/testing/internal/pytest/plugin.py:576:39 warning[redundant-cast] Value is already of type `RetryHandler`
- ddtrace/vendor/psutil/__init__.py:1071:24 error[invalid-argument-type] Argument to function `sleep` is incorrect: Expected `SupportsIndex | float`, found `Unknown | None`
- ddtrace/vendor/psutil/__init__.py:1829:24 error[invalid-argument-type] Argument to function `sleep` is incorrect: Expected `SupportsIndex | float`, found `Unknown | None`
- ddtrace/vendor/psutil/__init__.py:1839:24 error[invalid-argument-type] Argument to function `sleep` is incorrect: Expected `SupportsIndex | float`, found `Unknown | None`
- ddtrace/vendor/psutil/__init__.py:1894:24 error[invalid-argument-type] Argument to function `sleep` is incorrect: Expected `SupportsIndex | float`, found `Unknown | None`
- ddtrace/vendor/psutil/__init__.py:1904:24 error[invalid-argument-type] Argument to function `sleep` is incorrect: Expected `SupportsIndex | float`, found `Unknown | None`

discord.py (https://github.com/Rapptz/discord.py)
- discord/app_commands/transformers.py:112:67 error[invalid-argument-type] Argument to bound method `Translator._checked_translate` is incorrect: Expected `locale_str`, found `str | locale_str`
- discord/app_commands/transformers.py:117:67 error[invalid-argument-type] Argument to bound method `Translator._checked_translate` is incorrect: Expected `locale_str`, found `str | locale_str`
- discord/ext/commands/view.py:151:53 error[invalid-argument-type] Argument to `ExpectedClosingQuoteError.__init__` is incorrect: Expected `str`, found `str | None`
- discord/ext/commands/view.py:162:57 error[invalid-argument-type] Argument to `ExpectedClosingQuoteError.__init__` is incorrect: Expected `str`, found `str | None`
+ discord/ext/commands/view.py:184:69 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive

ibis (https://github.com/ibis-project/ibis)
- ibis/backends/materialize/__init__.py:837:24 error[unresolved-attribute] Attribute `upper` is not defined on `None` in union `str | None`
- ibis/expr/api.py:1891:30 error[not-iterable] Object of type `Unknown | None` may not be iterable
- ibis/expr/api.py:1893:31 error[not-iterable] Object of type `Unknown | None` may not be iterable
- ibis/expr/api.py:1895:33 error[not-iterable] Object of type `Unknown | None` may not be iterable

jax (https://github.com/google/jax)
- jax/_src/lax/lax.py:3754:18 error[unresolved-attribute] Object of type `DuckTypedArray | int | float | complex` has no attribute `sharding`
+ jax/_src/lax/lax.py:3754:18 error[invalid-assignment] Object of type `~None` is not assignable to `Sharding | None`
- jax/_src/pallas/mosaic_gpu/primitives.py:617:12 error[invalid-argument-type] Argument to function `len` is incorrect: Expected `Sized`, found `None | tuple[Unknown, ...]`
- jax/_src/pallas/mosaic_gpu/primitives.py:646:36 error[not-subscriptable] Cannot subscript object of type `None` with no `__getitem__` method
- jax/_src/pallas/mosaic_gpu/primitives.py:662:36 error[not-subscriptable] Cannot subscript object of type `None` with no `__getitem__` method
- jax/_src/pallas/mosaic_gpu/primitives.py:715:32 error[not-subscriptable] Cannot subscript object of type `None` with no `__getitem__` method
- jax/experimental/mosaic/gpu/tcgen05.py:454:8 error[unresolved-attribute] Attribute `shape` is not defined on `None` in union `TMEMRef | None`
- jax/experimental/mosaic/gpu/tcgen05.py:457:19 error[unresolved-attribute] Attribute `shape` is not defined on `None` in union `TMEMRef | None`
- jax/experimental/mosaic/gpu/tcgen05.py:459:8 error[unresolved-attribute] Attribute `dtype` is not defined on `None` in union `TMEMRef | None`
- jax/experimental/mosaic/gpu/tcgen05.py:462:15 error[unresolved-attribute] Attribute `dtype` is not defined on `None` in union `TMEMRef | None`
- jax/experimental/mosaic/gpu/tcgen05.py:519:24 error[unresolved-attribute] Attribute `address` is not defined on `None` in union `TMEMRef | None`

meson (https://github.com/mesonbuild/meson)
- mesonbuild/compilers/detect.py:1166:73 error[invalid-argument-type] Argument to `DynamicLinker.__init__` is incorrect: Expected `str | list[str]`, found `None | str | list[str]`

mitmproxy (https://github.com/mitmproxy/mitmproxy)
- test/mitmproxy/proxy/tutils.py:187:13 error[unresolved-attribute] Object of type `Command | Event` has no attribute `data`

more-itertools (https://github.com/more-itertools/more-itertools)
- more_itertools/more.py:4692:13 error[call-non-callable] Object of type `None` is not callable
- more_itertools/more.py:4716:13 error[call-non-callable] Object of type `None` is not callable
- more_itertools/more.py:4770:13 error[call-non-callable] Object of type `None` is not callable

mypy (https://github.com/python/mypy)
+ mypy/checkmember.py:1276:31 warning[redundant-cast] Value is already of type `Decorator`
+ mypy/checkmember.py:1305:24 warning[redundant-cast] Value is already of type `Decorator`

operator (https://github.com/canonical/operator)
+ ops/model.py:3700:16 warning[redundant-cast] Value is already of type `bool`

pandas (https://github.com/pandas-dev/pandas)
+ pandas/core/arrays/categorical.py:176:53 error[invalid-argument-type] Argument to function `invalid_comparison` is incorrect: Expected `ExtensionArray | ndarray[tuple[Any, ...], dtype[Any]] | list[Unknown] | ... omitted 14 union elements`, found `Hashable`
- pandas/core/arrays/period.py:1488:40 error[unresolved-attribute] Attribute `freq` is not defined on `NaTType` in union `(Unknown & None) | Period | NaTType`
- pandas/core/arrays/period.py:1488:54 error[unresolved-attribute] Attribute `freq` is not defined on `NaTType` in union `(Unknown & None) | Period | NaTType`
- pandas/core/arrays/period.py:1495:20 error[unresolved-attribute] Attribute `freq` is not defined on `NaTType` in union `(Unknown & None) | Period | NaTType`
- pandas/core/arrays/period.py:1497:20 error[unresolved-attribute] Attribute `freq` is not defined on `NaTType` in union `(Unknown & None) | Period | NaTType`
- pandas/core/construction.py:357:31 error[unresolved-attribute] Attribute `dtype` is not defined on `Sequence[object] & ~MaskedArray[tuple[object, ...], dtype[object]]` in union `(Sequence[object] & ~MaskedArray[tuple[object, ...], dtype[object]]) | (ExtensionArray & ~MaskedArray[tuple[object, ...], dtype[object]]) | ndarray[tuple[Any, ...], dtype[Any]] | (Index & ~MaskedArray[tuple[object, ...], dtype[object]]) | (Series & ~MaskedArray[tuple[object, ...], dtype[object]])`
- pandas/core/generic.py:8303:44 error[unsupported-operator] Unary operator `~` is not supported for object of type `ExtensionArray | ndarray[tuple[Any, ...], dtype[Any]] | Unknown`

pandera (https://github.com/pandera-dev/pandera)
- pandera/inspection_utils.py:14:37 error[unresolved-attribute] Object of type `(...) -> Unknown` has no attribute `__self__`

prefect (https://github.com/PrefectHQ/prefect)
- src/integrations/prefect-bitbucket/prefect_bitbucket/repository.py:123:21 error[unresolved-attribute] Attribute `get_secret_value` is not defined on `None` in union `SecretStr | None`
- src/integrations/prefect-bitbucket/prefect_bitbucket/repository.py:123:21 error[unresolved-attribute] Attribute `token` is not defined on `None` in union `BitBucketCredentials | None`
- src/integrations/prefect-bitbucket/prefect_bitbucket/repository.py:124:24 error[unresolved-attribute] Attribute `username` is not defined on `None` in union `BitBucketCredentials | None`
- src/integrations/prefect-dbt/prefect_dbt/core/_orchestrator.py:1356:28 error[unsupported-operator] Operator `in` is not supported between objects of type `Literal["execution_time"]` and `Any | None`
- src/integrations/prefect-dbt/prefect_dbt/core/_orchestrator.py:1357:71 error[not-subscriptable] Cannot subscript object of type `None` with no `__getitem__` method
- src/prefect/utilities/pydantic.py:192:5 error[invalid-assignment] Object of type `def __init__(__pydantic_self__: M@add_type_dispatch, **data: Any) -> None` is not assignable to attribute `__init__` of type `Overload[(self, o: object, /) -> None, (self, name: str, bases: tuple[type, ...], dict: dict[str, Any], /, **kwds: Any) -> None]`
+ src/prefect/utilities/pydantic.py:192:5 error[invalid-assignment] Object of type `def __init__(__pydantic_self__: M@add_type_dispatch, **data: Any) -> None` is not assignable to attribute `__init__` on type `type[M@add_type_dispatch] & ~<Protocol with members '__dispatch_key__'>`
- src/prefect/utilities/pydantic.py:193:5 error[invalid-assignment] Object of type `def __new__(cls: type[M@add_type_dispatch], **kwargs: Any) -> M@add_type_dispatch` is not assignable to attribute `__new__` of type `def __new__(mcs, cls_name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any], __pydantic_generic_metadata__: PydanticGenericMetadata | None = None, __pydantic_reset_parent_namespace__: bool = True, _create_model_module: str | None = None, **kwargs: Any) -> type`
+ src/prefect/utilities/pydantic.py:193:5 error[invalid-assignment] Object of type `def __new__(cls: type[M@add_type_dispatch], **kwargs: Any) -> M@add_type_dispatch` is not assignable to attribute `__new__` on type `type[M@add_type_dispatch] & ~<Protocol with members '__dispatch_key__'>`

pytest (https://github.com/pytest-dev/pytest)
- src/_pytest/runner.py:127:27 error[unresolved-attribute] Object of type `Item` has no attribute `_request`
- src/_pytest/runner.py:130:9 error[unresolved-attribute] Object of type `Item` has no attribute `_initrequest`
+ src/_pytest/runner.py:130:9 error[unresolved-attribute] Object of type `Item & <Protocol with members '_request'>` has no attribute `_initrequest`
- src/_pytest/runner.py:148:13 error[unresolved-attribute] Unresolved attribute `_request` on type `Item`
+ src/_pytest/runner.py:148:13 error[invalid-assignment] Object of type `Literal[False]` is not assignable to attribute `_request` on type `Item & <Protocol with members '_request'>`
- src/_pytest/runner.py:149:13 error[unresolved-attribute] Unresolved attribute `funcargs` on type `Item`
+ src/_pytest/runner.py:149:13 error[invalid-assignment] Object of type `None` is not assignable to attribute `funcargs` on type `Item & <Protocol with members '_request'>`

rotki (https://github.com/rotki/rotki)
+ rotkehlchen/chain/evm/names.py:63:94 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ rotkehlchen/chain/evm/names.py:67:73 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- rotkehlchen/chain/zksync_lite/manager.py:646:39 error[invalid-argument-type] Argument to bound method `list.append` is incorrect: Expected `tuple[int, HistoryEventType, HistoryEventSubType, Asset, FVal, ChecksumAddress, ChecksumAddress | None, str]`, found `tuple[Literal[0], Literal[HistoryEventType.WITHDRAWAL], Literal[HistoryEventSubType.BRIDGE], Asset, FVal, ChecksumAddress | None, None, str]`
- rotkehlchen/chain/zksync_lite/manager.py:700:39 error[invalid-argument-type] Argument to bound method `list.append` is incorrect: Expected `tuple[int, HistoryEventType, HistoryEventSubType, Asset, FVal, ChecksumAddress, ChecksumAddress | None, str]`, found `tuple[Literal[0], Literal[HistoryEventType.RECEIVE], Literal[HistoryEventSubType.NONE], Asset, FVal, ChecksumAddress | None, ChecksumAddress, str]`
+ rotkehlchen/tests/api/test_ens.py:62:94 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ rotkehlchen/tests/api/test_ens.py:66:73 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive

scikit-learn (https://github.com/scikit-learn/scikit-learn)
- sklearn/compose/_column_transformer.py:1065:29 error[unresolved-attribute] Object of type `Self@transform` has no attribute `feature_names_in_`
+ sklearn/compose/_column_transformer.py:1065:29 error[not-subscriptable] Cannot subscript object of type `object` with no `__getitem__` method
- sklearn/decomposition/_dict_learning.py:2321:23 error[no-matching-overload] No overload matches arguments
- sklearn/decomposition/_dict_learning.py:2322:23 error[no-matching-overload] No overload matches arguments
- sklearn/ensemble/_hist_gradient_boosting/gradient_boosting.py:795:32 error[unresolved-attribute] Attribute `shape` is not defined on `None` in union `Unknown | None`
- sklearn/externals/array_api_extra/_lib/_utils/_helpers.py:194:16 error[invalid-return-type] Return type does not match returned value: expected `tuple[Array, Array]`, found `tuple[Array | int | float | complex, Array | int | float | complex]`
- sklearn/linear_model/_ridge.py:252:22 error[no-matching-overload] No overload of function `atleast_1d` matches arguments

scipy (https://github.com/scipy/scipy)
- scipy/_lib/_util.py:274:23 error[invalid-argument-type] Method `__getitem__` of type `Overload[(index: SupportsIndex, /) -> Unknown, (index: slice[Any, Any, Any], /) -> tuple[Unknown, ...]]` cannot be called with key of type `None` on object of type `tuple[Unknown, ...]`
- scipy/integrate/tests/test_quadrature.py:539:15 error[no-matching-overload] No overload of function `broadcast_to` matches arguments
- scipy/interpolate/_fitpack_impl.py:130:9 error[invalid-assignment] Invalid subscript assignment with key of type `Literal["u"]` and value of type `Unknown | None` on object of type `dict[str, ndarray[tuple[Any, ...], dtype[Any]] | int]`
- scipy/interpolate/_fitpack_impl.py:132:35 error[not-subscriptable] Cannot subscript object of type `None` with no `__getitem__` method
- scipy/interpolate/_fitpack_impl.py:136:35 error[not-subscriptable] Cannot subscript object of type `None` with no `__getitem__` method
- scipy/ndimage/_morphology.py:940:9 error[no-matching-overload] No overload matches arguments
- scipy/ndimage/_morphology.py:941:9 error[no-matching-overload] No overload matches arguments
- scipy/ndimage/_morphology.py:1166:9 error[no-matching-overload] No overload matches arguments
- scipy/ndimage/_morphology.py:2382:12 error[unresolved-attribute] Attribute `dtype` is not defined on `None` in union `Unknown | None`
- scipy/ndimage/_morphology.py:2384:12 error[unresolved-attribute] Attribute `shape` is not defined on `None` in union `Unknown | None`
- scipy/ndimage/_morphology.py:2387:9 error[invalid-assignment] Cannot assign to a subscript on an object of type `None`
- scipy/ndimage/_morphology.py:2391:12 error[unresolved-attribute] Attribute `ndim` is not defined on `None` in union `Unknown | None | ndarray[tuple[Any, ...], Unknown]`
- scipy/ndimage/_morphology.py:2393:24 error[unresolved-attribute] Attribute `size` is not defined on `None` in union `Unknown | None | ndarray[tuple[Any, ...], Unknown]`
- scipy/ndimage/_morphology.py:2393:57 error[unresolved-attribute] Attribute `shape` is not defined on `None` in union `Unknown | None | ndarray[tuple[Any, ...], Unknown]`
- scipy/ndimage/_morphology.py:2398:10 error[not-subscriptable] Cannot subscript object of type `None` with no `__getitem__` method
- scipy/ndimage/_morphology.py:2398:10 error[not-subscriptable] Cannot subscript object of type `None` with no `__getitem__` method
- scipy/ndimage/_morphology.py:2407:16 error[unresolved-attribute] Attribute `dtype` is not defined on `None` in union `Unknown | None`
- scipy/ndimage/_morphology.py:2409:16 error[unresolved-attribute] Attribute `shape` is not defined on `None` in union `Unknown | None`
- scipy/ndimage/_morphology.py:2414:25 error[unresolved-attribute] Attribute `shape` is not defined on `None` in union `Unknown | None`
- scipy/ndimage/_morphology.py:2415:29 error[not-subscriptable] Cannot subscript object of type `None` with no `__getitem__` method
- scipy/ndimage/_morphology.py:2417:13 error[invalid-assignment] Cannot assign to a subscript on an object of type `None`
- scipy/ndimage/_morphology.py:2577:12 error[unresolved-attribute] Attribute `shape` is not defined on `None` in union `Unknown | None`
- scipy/ndimage/_morphology.py:2579:12 error[unresolved-attribute] Attribute `dtype` is not defined on `None` in union `Unknown | None`
- scipy/ndimage/_morphology.py:2587:49 error[unresolved-attribute] Attribute `dtype` is not defined on `None` in union `Unknown | None`
- scipy/ndimage/_morphology.py:2595:16 error[unresolved-attribute] Attribute `shape` is not defined on `None` in union `Unknown | None`
- scipy/ndimage/_morphology.py:2597:16 error[unresolved-attribute] Attribute `dtype` is not defined on `None` in union `Unknown | None`
+ scipy/optimize/_lsq/trf.py:235:19 error[not-subscriptable] Cannot subscript object of type `float` with no `__getitem__` method
+ scipy/optimize/_lsq/trf.py:235:19 error[not-subscriptable] Cannot subscript object of type `int` with no `__getitem__` method
+ scipy/optimize/_lsq/trf.py:285:23 error[not-subscriptable] Cannot subscript object of type `float` with no `__getitem__` method
+ scipy/optimize/_lsq/trf.py:285:23 error[not-subscriptable] Cannot subscript object of type `int` with no `__getitem__` method
- scipy/spatial/transform/_rigid_transform.py:1400:25 error[unresolved-attribute] Attribute `dtype` is not defined on `int`, `slice[Any, Any, Any]`, `EllipsisType`, `None`, `Buffer`, `_SupportsArray[dtype[Any]]`, `_NestedSequence[_SupportsArray[dtype[Any]]]`, `float`, `complex`, `str`, `_NestedSequence[int | float | complex | bytes | str]` in union `int | slice[Any, Any, Any] | EllipsisType | None | ArrayLike`
- scipy/spatial/transform/_rigid_transform.py:1402:36 error[unresolved-attribute] Attribute `dtype` is not defined on `int`, `slice[Any, Any, Any]`, `EllipsisType`, `None`, `Buffer`, `_SupportsArray[dtype[Any]]`, `_NestedSequence[_SupportsArray[dtype[Any]]]`, `float`, `complex`, `str`, `_NestedSequence[int | float | complex | bytes | str]` in union `int | slice[Any, Any, Any] | EllipsisType | None | ArrayLike`
- scipy/spatial/transform/_rotation.py:2223:25 error[unresolved-attribute] Attribute `dtype` is not defined on `int`, `slice[Any, Any, Any]`, `EllipsisType`, `None` in union `int | slice[Any, Any, Any] | EllipsisType | Array | None`
- scipy/spatial/transform/_rotation.py:2225:42 error[unresolved-attribute] Attribute `dtype` is not defined on `int`, `slice[Any, Any, Any]`, `EllipsisType`, `None` in union `int | slice[Any, Any, Any] | EllipsisType | Array | None`
- subprojects/array_api_extra/src/array_api_extra/_lib/_utils/_helpers.py:194:16 error[invalid-return-type] Return type does not match returned value: expected `tuple[Array, Array]`, found `tuple[Array | int | float | complex, Array | int | float | complex]`

scrapy (https://github.com/scrapy/scrapy)
- scrapy/core/spidermw.py:354:29 error[invalid-argument-type] Argument to `MutableChain.__init__` is incorrect: Expected `Iterable[Unknown]`, found `Iterable[_T@_process_spider_output] | AsyncIterator[_T@_process_spider_output] | Iterable[Unknown] | AsyncIterator[Unknown]`

setuptools (https://github.com/pypa/setuptools)
- setuptools/_imp.py:42:24 error[invalid-argument-type] Argument to function `issubclass` is incorrect: Expected `type`, found `Unknown | Loader | None`
- setuptools/_imp.py:50:24 error[invalid-argument-type] Argument to function `issubclass` is incorrect: Expected `type`, found `Unknown | Loader | None`
- setuptools/_vendor/more_itertools/more.py:4671:13 error[call-non-callable] Object of type `None` is not callable
- setuptools/_vendor/more_itertools/more.py:4695:13 error[call-non-callable] Object of type `None` is not callable
- setuptools/_vendor/more_itertools/more.py:4749:13 error[call-non-callable] Object of type `None` is not callable
- setuptools/_vendor/more_itertools/recipes.py:540:13 error[call-non-callable] Object of type `None` is not callable

spack (https://github.com/spack/spack)
- lib/spack/spack/audit.py:423:25 error[no-matching-overload] No overload of bound method `str.join` matches arguments
- lib/spack/spack/llnl/util/lang.py:264:16 error[unsupported-operator] Operator `<` is not supported between two objects of type `~None`
+ lib/spack/spack/llnl/util/lang.py:264:16 error[unsupported-operator] Operator `<` is not supported between objects of type `~Top[(...) -> object] & ~None` and `~None`
- lib/spack/spack/package_base.py:1774:21 error[unresolved-attribute] Object of type `Self@do_patch` has no attribute `patch`
+ lib/spack/spack/package_base.py:1774:21 error[call-top-callable] Object of type `Top[(...) -> object]` is not safe to call; its signature is not known

sphinx (https://github.com/sphinx-doc/sphinx)
- sphinx/ext/imgmath.py:269:9 error[unresolved-attribute] Unresolved attribute `_imgmath_warned_latex` on type `StandaloneHTMLBuilder`
- sphinx/ext/imgmath.py:279:9 error[unresolved-attribute] Unresolved attribute `_imgmath_warned_image_translator` on type `StandaloneHTMLBuilder`

static-frame (https://github.com/static-frame/static-frame)
+ static_frame/core/container_util.py:1828:66 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/frame.py:7561:43 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/index_hierarchy.py:1881:77 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/loc_map.py:187:32 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- static_frame/core/util.py:1429:26 error[no-matching-overload] No overload matches arguments
+ static_frame/core/util.py:3092:65 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive

sympy (https://github.com/sympy/sympy)
+ sympy/solvers/solveset.py:3137:19 error[unresolved-attribute] Object of type `object` has no attribute `name`
+ sympy/solvers/solveset.py:3167:15 error[no-matching-overload] No overload of bound method `Set.subs` matches arguments
- sympy/solvers/solveset.py:3129:25 error[invalid-argument-type] Argument to function `next` is incorrect: Expected `SupportsNext[Unknown]`, found `tuple[Unknown, ...] | (Unknown & <Protocol with members '__iter__'>)`
- sympy/stats/drv.py:242:20 error[unresolved-attribute] Attribute `lhs` is not defined on `BooleanFalse`, `BooleanTrue` in union `Unknown | Equality | BooleanFalse | BooleanTrue`
+ sympy/stats/drv.py:242:20 error[unresolved-attribute] Attribute `lhs` is not defined on `BooleanFalse`, `BooleanTrue` in union `(Unknown & ~Unequality) | Equality | BooleanFalse | BooleanTrue`
- sympy/stats/drv.py:242:36 error[unresolved-attribute] Attribute `rhs` is not defined on `BooleanFalse`, `BooleanTrue` in union `Unknown | Equality | BooleanFalse | BooleanTrue`
+ sympy/stats/drv.py:242:36 error[unresolved-attribute] Attribute `rhs` is not defined on `BooleanFalse`, `BooleanTrue` in union `(Unknown & ~Unequality) | Equality | BooleanFalse | BooleanTrue`

xarray (https://github.com/pydata/xarray)
- xarray/core/groupby.py:770:22 error[invalid-argument-type] Argument to bound method `DataArray._to_temp_dataset` is incorrect: Expected `DataArray`, found `T_Xarray@GroupBy`
- xarray/core/groupby.py:776:20 error[invalid-argument-type] Argument to bound method `DataArray._shuffle` is incorrect: Argument type `T_Xarray@GroupBy` does not satisfy upper bound `DataArray` of type variable `Self`
- xarray/core/groupby.py:776:20 error[invalid-argument-type] Argument to bound method `Dataset._shuffle` is incorrect: Argument type `T_Xarray@GroupBy` does not satisfy upper bound `Dataset` of type variable `Self`
- xarray/core/groupby.py:781:20 error[invalid-argument-type] Argument to bound method `DataArray._from_temp_dataset` is incorrect: Argument type `T_Xarray@GroupBy` does not satisfy upper bound `DataArray` of type variable `Self`
+ xarray/core/groupby.py:781:20 error[invalid-return-type] Return type does not match returned value: expected `T_Xarray@GroupBy`, found `DataArray`

Full report with detailed diff (timing results)

@mtshiba mtshiba force-pushed the narrow-by-aliased-conditional-expr-minimal branch 2 times, most recently from 3a5a4b7 to a59b9b9 Compare March 30, 2026 09:45
@mtshiba mtshiba marked this pull request as ready for review March 30, 2026 10:34
@carljm carljm removed their request for review March 30, 2026 17:04
@sharkdp
Copy link
Copy Markdown
Contributor

sharkdp commented Mar 31, 2026

Thank you! I'm planning to take a look soon.

Copy link
Copy Markdown
Contributor

@sharkdp sharkdp left a comment

Choose a reason for hiding this comment

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

Very clean implementation, extensive tests, great ecosystem impact.

This is beautiful, thank you.

Comment thread crates/ty_python_semantic/resources/mdtest/narrow/aliased_conditions.md Outdated
Comment thread crates/ty_python_semantic/resources/mdtest/narrow/aliased_conditions.md Outdated
Comment thread crates/ty_python_semantic/resources/mdtest/narrow/aliased_conditions.md Outdated
Comment thread crates/ty_python_semantic/resources/mdtest/narrow/aliased_conditions.md Outdated
Comment thread crates/ty_python_semantic/src/types/narrow.rs
Comment thread crates/ty_python_core/src/builder.rs Outdated
Comment thread crates/ty_python_core/src/builder.rs Outdated
Comment thread crates/ty_python_core/src/builder.rs Outdated
Comment thread crates/ty_python_core/src/builder.rs Outdated
@mtshiba mtshiba force-pushed the narrow-by-aliased-conditional-expr-minimal branch from db5b0c3 to 3d483f5 Compare April 25, 2026 04:42
@mtshiba mtshiba force-pushed the narrow-by-aliased-conditional-expr-minimal branch from d681cec to 145f64a Compare April 25, 2026 06:54
@mtshiba mtshiba force-pushed the narrow-by-aliased-conditional-expr-minimal branch from f756520 to 4e614e9 Compare April 25, 2026 08:34
@mtshiba
Copy link
Copy Markdown
Collaborator Author

mtshiba commented Apr 25, 2026

I'm merging this. If you have any questions regarding the review handling, I'll address them in the full PR.

@mtshiba mtshiba merged commit d12473e into main Apr 25, 2026
56 checks passed
@mtshiba mtshiba deleted the narrow-by-aliased-conditional-expr-minimal branch April 25, 2026 09:32
carljm added a commit to tamird/ruff that referenced this pull request Apr 27, 2026
* main: (248 commits)
  [ty] bump conformance suite commit (astral-sh#24848)
  [ty] Pass unmapped type variables to `SpecializationBuilder::build_with` (astral-sh#24809)
  [ty] Avoid bookkeeping for unannotated functions (astral-sh#24842)
  [ty] Optimize signature checking based on number of arguments (astral-sh#24674)
  [ty] Avoid eagerly inferring legacy generic context (astral-sh#24841)
  [ty] Skip decorator inference for undecorated functions (astral-sh#24839)
  [ty] solve unions against generic protocols (astral-sh#24837)
  [ty] Lazily allocate parameter type builders (astral-sh#24838)
  [ty] Lazily compute call argument fallbacks (astral-sh#24836)
  [ty] Avoid collecting type context callables (astral-sh#24835)
  Bump ecosystem-analyzer to e7576e6 (astral-sh#24834)
  [ty] Support basic narrowing with aliased conditional expressions (astral-sh#24302)
  [ty] Fix project and workspace selection (astral-sh#24824)
  [ty] Fix missing memory usage attributes (astral-sh#24823)
  Fix setting selection for multi folder workspace (astral-sh#24819)
  Improve diagnostics for implicit calls to a possibly unbound unary operator. (astral-sh#24816)
  [ty] Update review pool (astral-sh#24818)
  Bump 0.15.12 (astral-sh#24815)
  [ty] Complete support for more detailed diagnostics on possibly unbound errors from implicit dunder calls against unions. (astral-sh#24676)
  Implement `#ruff:file-ignore` file-level suppressions (astral-sh#23599)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ecosystem-analyzer ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Type narrowing fails to account for reassignment in a conditional based on an intermediate variable (aliased conditional expressions)

2 participants