Skip to content

[ty] Allow equality narrowing across non-final classes - #27031

Merged
charliermarsh merged 12 commits into
mainfrom
charlie/fix-ty-3419-equality-narrowing
Jul 22, 2026
Merged

[ty] Allow equality narrowing across non-final classes#27031
charliermarsh merged 12 commits into
mainfrom
charlie/fix-ty-3419-equality-narrowing

Conversation

@charliermarsh

@charliermarsh charliermarsh commented Jul 20, 2026

Copy link
Copy Markdown
Member

Summary

Prior to this change, equality and membership narrowing preserved otherwise-incompatible union members because a subclass could override __eq__. By default, we now assume that subclasses of a non-final class do not override the inherited comparison method:

class Foo: ...

def f(value: Foo | None, other: Foo) -> None:
    reveal_type(None == other)  # Literal[False]

    if value == other:
        reveal_type(value)  # Foo

    if value in [other]:
        reveal_type(value)  # Foo

The behavior is intentionally unsound. Users who want conservative equality behavior can enable the single strict-equality-narrowing setting, which also preserves broad builtin types instead of narrowing them to literals:

[tool.ty.analysis]
strict-equality-narrowing = true

For compatibility, strict-literal-narrowing remains supported as an alias for strict-equality-narrowing.

Closes astral-sh/ty#3419.

@astral-sh-bot astral-sh-bot Bot added the ty Multi-file analysis & type inference label Jul 20, 2026
@astral-sh-bot

astral-sh-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown

Typing conformance results

No changes detected ✅

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

@astral-sh-bot

astral-sh-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown

Memory usage report

Summary

Project Old New Diff Outcome
prefect 450.04MB 450.82MB +0.17% (792.07kB)
sphinx 166.01MB 166.20MB +0.11% (190.74kB)
trio 69.92MB 70.04MB +0.16% (116.51kB)
flake8 28.73MB 28.75MB +0.06% (18.41kB)

Significant changes

Click to expand detailed breakdown

prefect

Name Old New Diff Outcome
infer_expression_types_impl 39.41MB 40.07MB +1.66% (671.83kB)
member_lookup_with_policy_inner 10.69MB 10.72MB +0.21% (22.70kB)
Type<'db>::class_member_with_policy_inner_ 9.20MB 9.22MB +0.23% (21.47kB)
infer_definition_types 50.42MB 50.44MB +0.04% (20.27kB)
all_narrowing_constraints_for_expression 5.64MB 5.65MB +0.34% (19.66kB)
infer_scope_types_impl 30.14MB 30.16MB +0.05% (16.36kB)
MemberLookupKey 6.59MB 6.60MB +0.17% (11.81kB)
analyze_non_terminal_call 1.79MB 1.80MB +0.12% (2.24kB)
loop_header_reachability 253.65kB 255.54kB +0.75% (1.89kB)
Type<'db>::apply_specialization_inner_::interned_arguments 2.95MB 2.95MB -0.05% (1.56kB) ⬇️
Type<'db>::apply_specialization_inner_ 2.02MB 2.02MB -0.07% (1.37kB) ⬇️
infer_statement_types_impl 830.95kB 831.98kB +0.12% (1.03kB)
function_known_decorators 3.07MB 3.07MB +0.03% (872.00B)
infer_expression_type_impl 229.80kB 230.59kB +0.34% (800.00B)
StaticClassLiteral<'db>::implicit_attribute_inner_ 820.09kB 820.83kB +0.09% (760.00B)
... 24 more

sphinx

Name Old New Diff Outcome
infer_expression_types_impl 15.36MB 15.52MB +1.03% (161.90kB)
all_narrowing_constraints_for_expression 2.16MB 2.17MB +0.52% (11.47kB)
infer_definition_types 13.74MB 13.75MB +0.06% (8.88kB)
infer_scope_types_impl 8.03MB 8.03MB +0.09% (7.62kB)
Type<'db>::apply_specialization_inner_::interned_arguments 1.54MB 1.54MB -0.27% (4.22kB) ⬇️
Type<'db>::apply_specialization_inner_ 1.03MB 1.03MB -0.36% (3.80kB) ⬇️
check_file_impl 4.45MB 4.45MB -0.05% (2.42kB) ⬇️
member_lookup_with_policy_inner 4.85MB 4.86MB +0.04% (2.18kB)
loop_header_reachability 230.72kB 232.20kB +0.64% (1.48kB)
StaticClassLiteral<'db>::try_mro_ 1.82MB 1.81MB -0.07% (1.38kB) ⬇️
TypePair 1.61MB 1.62MB +0.08% (1.29kB)
infer_statement_types_impl 501.70kB 502.79kB +0.22% (1.09kB)
analyze_pattern_predicate 1.64kB 2.70kB +64.76% (1.06kB)
infer_expression_type_impl 238.06kB 238.92kB +0.36% (880.00B)
type_narrowed_by_pattern 1.62kB 2.44kB +50.72% (840.00B)
... 21 more

trio

Name Old New Diff Outcome
infer_expression_types_impl 4.58MB 4.65MB +1.61% (75.42kB)
member_lookup_with_policy_inner 1.13MB 1.13MB +0.74% (8.52kB)
Type<'db>::class_member_with_policy_inner_ 981.99kB 990.30kB +0.85% (8.31kB)
all_narrowing_constraints_for_expression 512.55kB 520.53kB +1.56% (7.98kB)
infer_definition_types 3.98MB 3.99MB +0.14% (5.90kB)
MemberLookupKey 731.26kB 735.95kB +0.64% (4.69kB)
analyze_non_terminal_call 387.12kB 389.89kB +0.72% (2.77kB)
check_file_impl 1.58MB 1.57MB -0.12% (1.95kB) ⬇️
infer_expression_type_impl 40.26kB 41.62kB +3.38% (1.36kB)
infer_scope_types_impl 2.39MB 2.39MB +0.05% (1.34kB)
Type<'db>::apply_specialization_inner_::interned_arguments 501.95kB 500.78kB -0.23% (1.17kB) ⬇️
Type<'db>::apply_specialization_inner_ 344.73kB 343.70kB -0.30% (1.03kB) ⬇️
loop_header_reachability 71.13kB 72.03kB +1.26% (920.00B)
StaticClassLiteral<'db>::implicit_attribute_inner_ 95.54kB 96.37kB +0.87% (848.00B)
function_known_decorators 239.12kB 239.79kB +0.28% (680.00B)
... 18 more

flake8

Name Old New Diff Outcome
infer_expression_types_impl 678.15kB 689.84kB +1.72% (11.70kB)
Type<'db>::class_member_with_policy_inner_ 289.80kB 291.62kB +0.63% (1.82kB)
member_lookup_with_policy_inner 326.86kB 328.40kB +0.47% (1.54kB)
MemberLookupKey 206.13kB 207.17kB +0.50% (1.03kB)
FunctionType 252.54kB 253.01kB +0.19% (480.00B)
infer_definition_types 1014.24kB 1014.62kB +0.04% (392.00B)
FunctionType<'db>::signature_ 263.43kB 263.76kB +0.12% (336.00B)
all_narrowing_constraints_for_expression 81.50kB 81.72kB +0.27% (224.00B)
infer_deferred_types 282.00kB 282.20kB +0.07% (200.00B)
infer_scope_types_impl 510.30kB 510.48kB +0.03% (176.00B)
GenericContext 46.89kB 47.03kB +0.30% (144.00B)
OverloadLiteral 105.22kB 105.32kB +0.10% (104.00B)
place_by_id 135.09kB 135.17kB +0.06% (88.00B)
BoundTypeVarInstance 55.86kB 55.95kB +0.15% (88.00B)
Type<'db>::apply_specialization_inner_::interned_arguments 172.89kB 172.81kB -0.05% (80.00B) ⬇️
... 9 more

@astral-sh-bot

astral-sh-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown

ecosystem-analyzer results

Lint rule Added Removed Changed
invalid-argument-type 0 153 4
unsupported-operator 0 5 7
invalid-assignment 0 8 0
unresolved-attribute 0 7 1
missing-argument 0 4 0
invalid-return-type 0 3 0
redundant-cast 2 0 0
unused-type-ignore-comment 2 0 0
no-matching-overload 0 1 0
Total 4 181 12

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

Raw diff (197 changes)
CPython (Argument Clinic) (https://github.com/python/cpython)
- Tools/clinic/libclinic/converter.py:518:39 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, tuple[str, str]].__getitem__(key: str, /) -> tuple[str, str]` cannot be called with key of type `None` on object of type `dict[str, tuple[str, str]]`

apprise (https://github.com/caronc/apprise)
- apprise/plugins/email/base.py:469:25 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, dict[str, int]].__getitem__(key: str, /) -> dict[str, int]` cannot be called with key of type `None` on object of type `dict[str, dict[str, int]]`
- apprise/plugins/email/base.py:469:25 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, dict[str, int]].__getitem__(key: str, /) -> dict[str, int]` cannot be called with key of type `dict[str, dict[str, int]]` on object of type `dict[str, dict[str, int]]`
- apprise/plugins/email/base.py:469:25 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, dict[str, int]].__getitem__(key: str, /) -> dict[str, int]` cannot be called with key of type `int` on object of type `dict[str, dict[str, int]]`
- apprise/plugins/email/base.py:469:25 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, dict[str, int]].__getitem__(key: str, /) -> dict[str, int]` cannot be called with key of type `tuple[str, str, str]` on object of type `dict[str, dict[str, int]]`
- apprise/plugins/growl.py:386:22 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `None` on object of type `dict[int, str]`
- apprise/plugins/growl.py:386:22 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `dict[int, str]` on object of type `dict[int, str]`
- apprise/plugins/growl.py:386:22 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `str` on object of type `dict[int, str]`
- apprise/plugins/growl.py:386:22 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `tuple[int, int]` on object of type `dict[int, str]`
- apprise/plugins/irc/state.py:153:42 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `None` on object of type `dict[int, str]`
- apprise/plugins/irc/state.py:184:42 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `None` on object of type `dict[int, str]`
- apprise/plugins/jira.py:761:18 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `None` on object of type `dict[int, str]`
- apprise/plugins/jira.py:761:18 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `dict[int, str]` on object of type `dict[int, str]`
- apprise/plugins/jira.py:761:18 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `str` on object of type `dict[int, str]`
- apprise/plugins/jira.py:761:18 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `tuple[str, str, str, str, str, str]` on object of type `dict[int, str]`
- apprise/plugins/jira.py:761:18 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `tuple[str, str]` on object of type `dict[int, str]`
- apprise/plugins/opsgenie.py:765:22 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `None` on object of type `dict[int, str]`
- apprise/plugins/opsgenie.py:765:22 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `dict[int, str]` on object of type `dict[int, str]`
- apprise/plugins/opsgenie.py:765:22 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `str` on object of type `dict[int, str]`
- apprise/plugins/opsgenie.py:765:22 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `tuple[str, str, str, str, str, str]` on object of type `dict[int, str]`
- apprise/plugins/opsgenie.py:765:22 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `tuple[str, str]` on object of type `dict[int, str]`
- apprise/plugins/pagertree.py:215:18 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`
- apprise/plugins/prowl.py:283:22 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `None` on object of type `dict[int, str]`
- apprise/plugins/prowl.py:283:22 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `dict[int, str]` on object of type `dict[int, str]`
- apprise/plugins/prowl.py:283:22 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `str` on object of type `dict[int, str]`

beartype (https://github.com/beartype/beartype)
- beartype/_check/error/_pep/pep484585/errpep484585container.py:64:9 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[HintSign, range].__getitem__(key: HintSign, /) -> range` cannot be called with key of type `None` on object of type `dict[HintSign, range]`
- beartype/_check/error/_pep/pep484585/errpep484585mapping.py:58:9 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[HintSign, range].__getitem__(key: HintSign, /) -> range` cannot be called with key of type `None` on object of type `dict[HintSign, range]`

bokeh (https://github.com/bokeh/bokeh)
- src/bokeh/plotting/_renderer.py:219:28 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, Any].__getitem__(key: str, /) -> Any` cannot be called with key of type `None` on object of type `dict[str, Any]`
- src/bokeh/plotting/_renderer.py:227:28 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, Any].__getitem__(key: str, /) -> Any` cannot be called with key of type `None` on object of type `dict[str, Any]`

cloud-init (https://github.com/canonical/cloud-init)
- cloudinit/sources/DataSourceVMware.py:995:17 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, dict[str, Interface]].__getitem__(key: str, /) -> dict[str, Interface]` cannot be called with key of type `None` on object of type `dict[str, dict[str, Interface]]`
- cloudinit/sources/DataSourceVMware.py:1003:17 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, dict[str, Interface]].__getitem__(key: str, /) -> dict[str, Interface]` cannot be called with key of type `None` on object of type `dict[str, dict[str, Interface]]`

comtypes (https://github.com/enthought/comtypes)
- comtypes/_memberspec.py:292:44 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, Unknown].__getitem__(key: str, /) -> Unknown` cannot be called with key of type `None` on object of type `dict[str, Unknown]`
- comtypes/_memberspec.py:293:21 error[invalid-assignment] Invalid subscript assignment with key of type `str | None` and value of type `_SimpleCData[Any] | _Pointer[Any] | CFuncPtr | ... omitted 3 union elements` on object of type `dict[str, Unknown]`

core (https://github.com/home-assistant/core)
- homeassistant/components/alexa/capabilities.py:1228:24 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`
- homeassistant/components/deconz/device_trigger.py:715:54 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, dict[tuple[str, str], dict[str, int]]].__getitem__(key: str, /) -> dict[tuple[str, str], dict[str, int]]` cannot be called with key of type `None` on object of type `dict[str, dict[tuple[str, str], dict[str, int]]]`
- homeassistant/components/deconz/device_trigger.py:773:29 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, dict[tuple[str, str], dict[str, int]]].__getitem__(key: str, /) -> dict[tuple[str, str], dict[str, int]]` cannot be called with key of type `None` on object of type `dict[str, dict[tuple[str, str], dict[str, int]]]`
- homeassistant/components/ecobee/climate.py:831:13 error[invalid-argument-type] Argument to function `sorted` is incorrect: Argument type `str | None` does not satisfy upper bound `SupportsDunderLT[Any] | SupportsDunderGT[Any]` of type variable `SupportsRichComparisonT`
- homeassistant/components/intent/timers.py:312:13 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, TimerHandler].__getitem__(key: str, /) -> TimerHandler` cannot be called with key of type `None` on object of type `dict[str, TimerHandler]`
- homeassistant/components/intent/timers.py:352:13 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, TimerHandler].__getitem__(key: str, /) -> TimerHandler` cannot be called with key of type `None` on object of type `dict[str, TimerHandler]`
- homeassistant/components/intent/timers.py:381:13 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, TimerHandler].__getitem__(key: str, /) -> TimerHandler` cannot be called with key of type `None` on object of type `dict[str, TimerHandler]`
- homeassistant/components/intent/timers.py:419:13 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, TimerHandler].__getitem__(key: str, /) -> TimerHandler` cannot be called with key of type `None` on object of type `dict[str, TimerHandler]`
- homeassistant/components/intent/timers.py:445:13 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, TimerHandler].__getitem__(key: str, /) -> TimerHandler` cannot be called with key of type `None` on object of type `dict[str, TimerHandler]`
- homeassistant/components/intent/timers.py:478:13 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, TimerHandler].__getitem__(key: str, /) -> TimerHandler` cannot be called with key of type `None` on object of type `dict[str, TimerHandler]`
- homeassistant/components/network/__init__.py:85:12 error[invalid-return-type] Return type does not match returned value: expected `str`, found `str | None`
- homeassistant/components/opnsense/device_tracker.py:66:20 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, DeviceDetails].__getitem__(key: str, /) -> DeviceDetails` cannot be called with key of type `None` on object of type `dict[str, DeviceDetails]`
- homeassistant/components/steam_online/sensor.py:137:48 error[invalid-argument-type] Argument to `SteamEntity.__init__` is incorrect: Expected `str`, found `str | None`
- homeassistant/components/amberelectric/helpers.py:22:16 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`
- homeassistant/components/bang_olufsen/media_player.py:662:20 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`
- homeassistant/components/deconz/services.py:199:42 error[invalid-argument-type] Argument to bound method `list.remove` is incorrect: Expected `str`, found `str | None`
- homeassistant/components/deconz/services.py:206:46 error[invalid-argument-type] Argument to bound method `list.remove` is incorrect: Expected `str`, found `str | None`
- homeassistant/components/dsmr/sensor.py:601:50 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, UnitOfVolume].__getitem__(key: str, /) -> UnitOfVolume` cannot be called with key of type `None` on object of type `dict[str, UnitOfVolume]`
- homeassistant/components/homematic/entity.py:177:13 error[invalid-assignment] Invalid subscript assignment with key of type `str | None` and value of type `Unknown` on object of type `dict[str, Any]`
- homeassistant/components/homematic/entity.py:182:20 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, Any].__getitem__(key: str, /) -> Any` cannot be called with key of type `None` on object of type `dict[str, Any]`
- homeassistant/components/huawei_lte/binary_sensor.py:140:46 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`
- homeassistant/components/hue/v1/device_trigger.py:138:23 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, dict[tuple[str, str], dict[str, int]]].__getitem__(key: str, /) -> dict[tuple[str, str], dict[str, int]]` cannot be called with key of type `None` on object of type `dict[str, dict[tuple[str, str], dict[str, int]]]`
- homeassistant/components/hue/v1/device_trigger.py:189:29 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, dict[tuple[str, str], dict[str, int]]].__getitem__(key: str, /) -> dict[tuple[str, str], dict[str, int]]` cannot be called with key of type `None` on object of type `dict[str, dict[tuple[str, str], dict[str, int]]]`
- homeassistant/components/mqtt/tag.py:185:55 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, dict[str, MQTTTagScanner]].__getitem__(key: str, /) -> dict[str, MQTTTagScanner]` cannot be called with key of type `None` on object of type `dict[str, dict[str, MQTTTagScanner]]`
- homeassistant/components/mqtt/tag.py:186:17 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, dict[str, MQTTTagScanner]].__getitem__(key: str, /) -> dict[str, MQTTTagScanner]` cannot be called with key of type `None` on object of type `dict[str, dict[str, MQTTTagScanner]]`
- homeassistant/components/ps4/media_player.py:206:42 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, JsonValueType].__getitem__(key: str, /) -> JsonValueType` cannot be called with key of type `None` on object of type `dict[str, JsonValueType]`
- homeassistant/components/ps4/media_player.py:290:42 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, JsonValueType].__getitem__(key: str, /) -> JsonValueType` cannot be called with key of type `None` on object of type `dict[str, JsonValueType]`
- homeassistant/components/ps4/media_player.py:296:33 error[invalid-argument-type] Argument to bound method `dict.pop` is incorrect: Expected `str`, found `str | None`
- homeassistant/components/reolink/__init__.py:540:18 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, int].__getitem__(key: str, /) -> int` cannot be called with key of type `None` on object of type `dict[str, int]`
- homeassistant/components/screenlogic/coordinator.py:39:16 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, dict[str, Any]].__getitem__(key: str, /) -> dict[str, Any]` cannot be called with key of type `None` on object of type `dict[str, dict[str, Any]]`
- homeassistant/components/shelly/event.py:314:33 error[invalid-argument-type] Argument to bound method `EventEntity._trigger_event` is incorrect: Expected `str`, found `Any | None`
- homeassistant/components/stream/__init__.py:397:13 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, StreamOutput].__getitem__(key: str, /) -> StreamOutput` cannot be called with key of type `None` on object of type `dict[str, StreamOutput]`
- homeassistant/components/stream/__init__.py:398:17 error[invalid-argument-type] Method `__delitem__` of type `bound method dict[str, StreamOutput].__delitem__(key: str, /) -> None` cannot be called with key of type `str | None` on object of type `dict[str, StreamOutput]`
- homeassistant/components/subaru/sensor.py:335:54 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, Unknown].__getitem__(key: str, /) -> Unknown` cannot be called with key of type `None` on object of type `dict[str, Unknown]`
- homeassistant/components/subaru/sensor.py:338:62 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, Unknown].__getitem__(key: str, /) -> Unknown` cannot be called with key of type `None` on object of type `dict[str, Unknown]`
- homeassistant/components/tibber/sensor.py:936:33 error[unresolved-attribute] Attribute `replace` is not defined on `None` in union `str | None`
- homeassistant/components/tibber/sensor.py:942:33 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`
- homeassistant/components/yolink/device_trigger.py:90:24 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, set[str]].__getitem__(key: str, /) -> set[str]` cannot be called with key of type `None` on object of type `dict[str, set[str]]`
- homeassistant/components/zeroconf/discovery.py:481:13 error[invalid-argument-type] Argument to function `async_create_issue` is incorrect: Expected `dict[str, str] | None`, found `dict[str, str | Any | None]`

cwltool (https://github.com/common-workflow-language/cwltool)
- cwltool/cwlprov/ro.py:323:25 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`
- cwltool/cwlprov/ro.py:329:34 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`

dd-trace-py (https://github.com/DataDog/dd-trace-py)
- ddtrace/appsec/_iast/taint_sinks/ast_taint.py:70:31 error[invalid-argument-type] Argument to function `_iast_report_ssrf` is incorrect: Expected `str`, found `Any | None`
- tests/appsec/iast/test_overhead_control_engine.py:130:48 error[unsupported-operator] Operator `+` is not supported between objects of type `EnvVariable[int]` and `Literal[1]`
- ddtrace/internal/coverage/code.py:408:25 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, CollectInContext].__getitem__(key: str, /) -> CollectInContext` cannot be called with key of type `None` on object of type `dict[str, CollectInContext]`
- ddtrace/internal/coverage/code.py:411:16 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, CoverageLines].__getitem__(key: str, /) -> CoverageLines` cannot be called with key of type `None` on object of type `dict[str, CoverageLines]`
- ddtrace/internal/coverage/code.py:412:17 error[invalid-argument-type] Method `__getitem__` of type `bound method defaultdict[str, CoverageLines].__getitem__(key: str, /) -> CoverageLines` cannot be called with key of type `None` on object of type `defaultdict[str, CoverageLines]`
- ddtrace/internal/coverage/code.py:412:68 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, CoverageLines].__getitem__(key: str, /) -> CoverageLines` cannot be called with key of type `None` on object of type `dict[str, CoverageLines]`
- ddtrace/internal/coverage/code.py:414:17 error[invalid-argument-type] Method `__delitem__` of type `bound method dict[str, CollectInContext].__delitem__(key: str, /) -> None` cannot be called with key of type `str | None` on object of type `dict[str, CollectInContext]`
- ddtrace/internal/coverage/code.py:421:25 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, CollectInContext].__getitem__(key: str, /) -> CollectInContext` cannot be called with key of type `None` on object of type `dict[str, CollectInContext]`
- ddtrace/internal/coverage/code.py:424:16 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, CoverageLines].__getitem__(key: str, /) -> CoverageLines` cannot be called with key of type `None` on object of type `dict[str, CoverageLines]`
- ddtrace/internal/coverage/code.py:425:17 error[invalid-argument-type] Method `__getitem__` of type `bound method defaultdict[str, CoverageLines].__getitem__(key: str, /) -> CoverageLines` cannot be called with key of type `None` on object of type `defaultdict[str, CoverageLines]`
- ddtrace/internal/coverage/code.py:425:68 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, CoverageLines].__getitem__(key: str, /) -> CoverageLines` cannot be called with key of type `None` on object of type `dict[str, CoverageLines]`
- ddtrace/internal/coverage/code.py:427:17 error[invalid-argument-type] Method `__delitem__` of type `bound method dict[str, CollectInContext].__delitem__(key: str, /) -> None` cannot be called with key of type `str | None` on object of type `dict[str, CollectInContext]`
- tests/ci_visibility/test_atr.py:96:47 error[invalid-argument-type] Argument to bound method `TestVisibilityTest.atr_finish_retry` is incorrect: Expected `int`, found `int | None`
- tests/ci_visibility/test_atr.py:186:39 error[invalid-argument-type] Argument to bound method `TestVisibilityTest.atr_finish_retry` is incorrect: Expected `int`, found `int | None`
- tests/ci_visibility/test_efd.py:85:42 error[invalid-argument-type] Argument to bound method `TestVisibilityTest.efd_start_retry` is incorrect: Expected `int`, found `int | None`
- tests/ci_visibility/test_efd.py:86:43 error[invalid-argument-type] Argument to bound method `TestVisibilityTest.efd_finish_retry` is incorrect: Expected `int`, found `int | None`
- tests/ci_visibility/test_efd.py:172:43 error[invalid-argument-type] Argument to bound method `TestVisibilityTest.efd_finish_retry` is incorrect: Expected `int`, found `int | None`
- tests/contrib/patch.py:818:35 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, tuple[str, ...]].__getitem__(key: str, /) -> tuple[str, ...]` cannot be called with key of type `None` on object of type `dict[str, tuple[str, ...]]`

ibis (https://github.com/ibis-project/ibis)
- ibis/expr/datatypes/tests/test_core.py:558:29 error[invalid-argument-type] Argument is incorrect: Expected `Literal[0, 1, 2, 3, 4, ... omitted 5 literals] | None`, found `int`
- ibis/expr/datatypes/tests/test_core.py:560:29 error[invalid-argument-type] Argument is incorrect: Expected `Literal[0, 1, 2, 3, 4, ... omitted 5 literals] | None`, found `int`
- ibis/expr/datatypes/tests/test_core.py:562:29 error[invalid-argument-type] Argument is incorrect: Expected `Literal[0, 1, 2, 3, 4, ... omitted 5 literals] | None`, found `int`
- ibis/tests/expr/test_value_exprs.py:1516:44 error[missing-argument] No argument provided for required parameter `value_type`
- ibis/tests/expr/test_value_exprs.py:1516:53 error[invalid-argument-type] Argument is incorrect: Expected `bool`, found `Array[Unknown]`
- ibis/tests/expr/test_value_exprs.py:1516:53 error[missing-argument] No argument provided for required parameter `value_type`
- ibis/tests/expr/test_value_exprs.py:1516:62 error[invalid-argument-type] Argument is incorrect: Expected `bool`, found `Float64`
- ibis/tests/expr/test_value_exprs.py:1517:55 error[missing-argument] No argument provided for required parameter `value_type`
- ibis/tests/expr/test_value_exprs.py:1517:64 error[invalid-argument-type] Argument is incorrect: Expected `bool`, found `Array[Unknown]`
- ibis/tests/expr/test_value_exprs.py:1517:64 error[missing-argument] No argument provided for required parameter `value_type`
- ibis/tests/expr/test_value_exprs.py:1517:73 error[invalid-argument-type] Argument is incorrect: Expected `bool`, found `Float64`

jax (https://github.com/google/jax)
- jax/_src/pallas/mosaic/lowering.py:3276:12 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, Overload[[SupportsRichComparisonT](arg1: SupportsRichComparisonT, arg2: SupportsRichComparisonT, /, *_args: SupportsRichComparisonT, *, key: None = None) -> SupportsRichComparisonT, [_T](arg1: _T, arg2: _T, /, *_args: _T, *, key: (_T, /) -> SupportsDunderLT[Any] | SupportsDunderGT[Any]) -> _T, [SupportsRichComparisonT](iterable: Iterable[SupportsRichComparisonT], /, *, key: None = None) -> SupportsRichComparisonT, [_T](iterable: Iterable[_T], /, *, key: (_T, /) -> SupportsDunderLT[Any] | SupportsDunderGT[Any]) -> _T, [SupportsRichComparisonT, _T](iterable: Iterable[SupportsRichComparisonT], /, *, key: None = None, default: _T) -> SupportsRichComparisonT | _T, [_T1, _T2](iterable: Iterable[_T1], /, *, key: (_T1, /) -> SupportsDunderLT[Any] | SupportsDunderGT[Any], default: _T2) -> _T1 | _T2]].__getitem__(key: str, /) -> Overload[(arg1: SupportsRichComparisonT, arg2: SupportsRichComparisonT, /, *_args: SupportsRichComparisonT, *, key: None = None) -> SupportsRichComparisonT, (arg1: _T, arg2: _T, /, *_args: _T, *, key: (_T, /) -> SupportsDunderLT[Any] | SupportsDunderGT[Any]) -> _T, (iterable: Iterable[SupportsRichComparisonT], /, *, key: None = None) -> SupportsRichComparisonT, (iterable: Iterable[_T], /, *, key: (_T, /) -> SupportsDunderLT[Any] | SupportsDunderGT[Any]) -> _T, (iterable: Iterable[SupportsRichComparisonT], /, *, key: None = None, default: _T) -> SupportsRichComparisonT | _T, (iterable: Iterable[_T1], /, *, key: (_T1, /) -> SupportsDunderLT[Any] | SupportsDunderGT[Any], default: _T2) -> _T1 | _T2]` cannot be called with key of type `None` on object of type `dict[str, Overload[[SupportsRichComparisonT](arg1: SupportsRichComparisonT, arg2: SupportsRichComparisonT, /, *_args: SupportsRichComparisonT, *, key: None = None) -> SupportsRichComparisonT, [_T](arg1: _T, arg2: _T, /, *_args: _T, *, key: (_T, /) -> SupportsDunderLT[Any] | SupportsDunderGT[Any]) -> _T, [SupportsRichComparisonT](iterable: Iterable[SupportsRichComparisonT], /, *, key: None = None) -> SupportsRichComparisonT, [_T](iterable: Iterable[_T], /, *, key: (_T, /) -> SupportsDunderLT[Any] | SupportsDunderGT[Any]) -> _T, [SupportsRichComparisonT, _T](iterable: Iterable[SupportsRichComparisonT], /, *, key: None = None, default: _T) -> SupportsRichComparisonT | _T, [_T1, _T2](iterable: Iterable[_T1], /, *, key: (_T1, /) -> SupportsDunderLT[Any] | SupportsDunderGT[Any], default: _T2) -> _T1 | _T2]]`
- jax/_src/pallas/mosaic/pipeline.py:626:20 error[unresolved-attribute] Attribute `from_type` is not defined on `CoreMemorySpace` in union `Literal[MemorySpace.SMEM, MemorySpace.VMEM] | CoreMemorySpace`

manticore (https://github.com/trailofbits/manticore)
- manticore/wasm/structure.py:1474:59 error[invalid-argument-type] Argument to `ConcretizeCondition.__init__` is incorrect: Expected `Bool`, found `bool | Unknown`
+ manticore/wasm/structure.py:1474:59 error[invalid-argument-type] Argument to `ConcretizeCondition.__init__` is incorrect: Expected `Bool`, found `Literal[True] | Unknown`
- manticore/wasm/structure.py:1555:62 error[invalid-argument-type] Argument to `ConcretizeCondition.__init__` is incorrect: Expected `Bool`, found `bool | Unknown`
+ manticore/wasm/structure.py:1555:62 error[invalid-argument-type] Argument to `ConcretizeCondition.__init__` is incorrect: Expected `Bool`, found `Literal[True] | Unknown`
- manticore/wasm/structure.py:1670:12 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> FuncAddr | None, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[FuncAddr | None]]` cannot be called with key of type `Activation` on object of type `list[FuncAddr | None]`
- manticore/wasm/structure.py:1670:12 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> FuncAddr | None, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[FuncAddr | None]]` cannot be called with key of type `Label` on object of type `list[FuncAddr | None]`
- manticore/wasm/structure.py:1672:13 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> FuncAddr | None, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[FuncAddr | None]]` cannot be called with key of type `Activation` on object of type `list[FuncAddr | None]`
- manticore/wasm/structure.py:1672:13 error[invalid-argument-type] Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> FuncAddr | None, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[FuncAddr | None]]` cannot be called with key of type `Label` on object of type `list[FuncAddr | None]`

materialize (https://github.com/MaterializeInc/materialize)
- misc/python/materialize/cli/mzcompose.py:869:21 error[invalid-argument-type] Argument to bound method `Composition.workflow` is incorrect: Expected `str`, found `Any | None`

meson (https://github.com/mesonbuild/meson)
- mesonbuild/dependencies/cuda.py:263:40 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`
- mesonbuild/dependencies/cuda.py:270:20 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`
- mesonbuild/mdevenv.py:236:12 error[unsupported-operator] Operator `in` is not supported between objects of type `Literal["bash"]` and `str | None`
- mesonbuild/mdevenv.py:247:14 error[unresolved-attribute] Attribute `endswith` is not defined on `None` in union `str | None`
- mesonbuild/mdevenv.py:268:22 error[no-matching-overload] No overload of function `run` matches arguments
- mesonbuild/mdevenv.py:272:24 error[invalid-argument-type] Argument to function `execvpe` is incorrect: Expected `str | bytes | PathLike[str] | PathLike[bytes]`, found `Any | str | None`
- mesonbuild/mdevenv.py:272:33 error[invalid-argument-type] Argument to function `execvpe` is incorrect: Expected `tuple[str | bytes | PathLike[str] | PathLike[bytes], ...] | list[bytes] | list[str] | ... omitted 5 union elements`, found `(Any & ~AlwaysFalsy) | list[str] | list[str | None]`
- mesonbuild/mdevenv.py:276:52 error[invalid-argument-type] Argument to function `join_args` is incorrect: Expected `Iterable[str]`, found `(Any & ~AlwaysFalsy) | list[str] | list[str | None]`

mitmproxy (https://github.com/mitmproxy/mitmproxy)
- mitmproxy/proxy/events.py:92:21 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 `None` on object of type `dict[Command, type[CommandCompleted]]`
+ mitmproxy/addons/core.py:172:68 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive

mypy (https://github.com/python/mypy)
- mypy/stubutil.py:672:21 error[unsupported-operator] Operator `+` is not supported between objects of type `Literal["_"]` and `str | None | Unknown`

openlibrary (https://github.com/internetarchive/openlibrary)
- openlibrary/core/experiments.py:69:12 error[invalid-return-type] Return type does not match returned value: expected `dict[str, str]`, found `dict[str, str | None]`
- openlibrary/plugins/openlibrary/home.py:164:17 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`
- openlibrary/plugins/upstream/addbook.py:399:16 error[unresolved-attribute] Attribute `startswith` is not defined on `None` in union `str | None`
- openlibrary/plugins/upstream/addbook.py:401:15 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`
- openlibrary/solr/types_generator.py:43:27 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`
- openlibrary/solr/types_generator.py:45:27 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`

optuna (https://github.com/optuna/optuna)
- optuna/storages/_cached_storage.py:95:29 error[invalid-argument-type] Method `__delitem__` of type `bound method dict[int, tuple[int, int]].__delitem__(key: int, /) -> None` cannot be called with key of type `int | None` on object of type `dict[int, tuple[int, int]]`

pandas (https://github.com/pandas-dev/pandas)
- pandas/io/sas/sas_xport.py:351:50 error[unsupported-operator] Operator `<` is not supported between objects of type `int | bytes` and `Literal[2]`
- pandas/io/sas/sas_xport.py:351:62 error[unsupported-operator] Operator `>` is not supported between objects of type `int | bytes` and `Literal[8]`
- pandas/io/stata.py:1450:61 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `int` on object of type `dict[str, str]`
- pandas/io/stata.py:1809:30 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, tuple[int, int] | tuple[floating[_32Bit], floating[_32Bit]] | tuple[float64, float64]].__getitem__(key: str, /) -> tuple[int, int] | tuple[floating[_32Bit], floating[_32Bit]] | tuple[float64, float64]` cannot be called with key of type `int` on object of type `dict[str, tuple[int, int] | tuple[floating[_32Bit], floating[_32Bit]] | tuple[float64, float64]]`
- pandas/io/stata.py:1814:30 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, tuple[int, int] | tuple[floating[_32Bit], floating[_32Bit]] | tuple[float64, float64]].__getitem__(key: str, /) -> tuple[int, int] | tuple[floating[_32Bit], floating[_32Bit]] | tuple[float64, float64]` cannot be called with key of type `int` on object of type `dict[str, tuple[int, int] | tuple[floating[_32Bit], floating[_32Bit]] | tuple[float64, float64]]`
- pandas/io/stata.py:1831:35 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, int | floating[_32Bit] | float64].__getitem__(key: str, /) -> int | floating[_32Bit] | float64` cannot be called with key of type `int` on object of type `dict[str, int | floating[_32Bit] | float64]`

pip (https://github.com/pypa/pip)
- src/pip/_vendor/urllib3/util/ssl_.py:309:9 error[invalid-assignment] Object of type `int` is not assignable to attribute `verify_mode` of type `VerifyMode`

poetry (https://github.com/python-poetry/poetry)
- src/poetry/mixology/version_solver.py:383:67 error[invalid-argument-type] Argument to bound method `Term.difference` is incorrect: Expected `Term`, found `None | Term`
- tests/console/commands/self/test_show_plugins.py:125:42 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, list[Unknown]].__getitem__(key: str, /) -> list[Unknown]` cannot be called with key of type `None` on object of type `dict[str, list[Unknown]]`

porcupine (https://github.com/Akuli/porcupine)
- porcupine/plugins/urls.py:40:41 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`
- porcupine/plugins/urls.py:42:29 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`

prefect (https://github.com/PrefectHQ/prefect)
- src/integrations/prefect-gcp/tests/test_cloud_storage.py:612:20 error[unresolved-attribute] Attribute `startswith` is not defined on `Coroutine[Any, Any, str]` in union `Literal["alpha/761db2ef8cf25ad06b5203ca47f4f20d"] | Coroutine[Any, Any, str]`
- src/prefect/cli/work_pool.py:437:77 error[invalid-argument-type] Argument to function `get_default_base_job_template_for_infrastructure_type` is incorrect: Expected `str`, found `str | None | Unknown`
- src/prefect/cli/work_pool.py:446:84 error[invalid-argument-type] Argument to function `get_infrastructure_provisioner_for_work_pool_type` is incorrect: Expected `str`, found `str | None | Unknown`
- src/prefect/cli/work_pool.py:467:17 error[invalid-argument-type] Argument is incorrect: Expected `LaxStr`, found `str | None | Unknown`
- src/prefect/context.py:1063:36 error[invalid-argument-type] Method `__getitem__` of type `bound method ProfilesCollection.__getitem__(name: str) -> Profile` cannot be called with key of type `None` on object of type `ProfilesCollection`
+ src/prefect/flows.py:3549:28 warning[redundant-cast] Value is already of type `str`
+ src/prefect/flows.py:3562:28 warning[redundant-cast] Value is already of type `str`
- src/prefect/server/events/actions.py:596:33 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, tuple[type[PrefectBaseModel], list[(...) -> Coroutine[Any, Any, Unknown]]]].__getitem__(key: str, /) -> tuple[type[PrefectBaseModel], list[(...) -> Coroutine[Any, Any, Unknown]]]` cannot be called with key of type `None` on object of type `dict[str, tuple[type[PrefectBaseModel], list[(...) -> Coroutine[Any, Any, Unknown]]]]`

pylint (https://github.com/pycqa/pylint)
- pylint/checkers/refactoring/implicit_booleaness_checker.py:244:38 error[invalid-argument-type] Argument to bound method `ImplicitBooleanessChecker._get_suggestion` is incorrect: Expected `str`, found `str | NodeNG`
- pylint/checkers/refactoring/implicit_booleaness_checker.py:224:48 error[invalid-argument-type] Argument to bound method `ImplicitBooleanessChecker._get_suggestion` is incorrect: Expected `str`, found `str | NodeNG`
- pylint/checkers/variables.py:2669:21 error[unresolved-attribute] Attribute `parent_of` is not defined on `None` in union `NodeNG | None`
- pylint/checkers/variables.py:591:19 error[unresolved-attribute] Object of type `NodeNG | None` has no attribute `targets`
+ pylint/checkers/variables.py:591:19 error[unresolved-attribute] Object of type `NodeNG` has no attribute `targets`

pytest (https://github.com/pytest-dev/pytest)
- src/_pytest/config/__init__.py:566:25 error[unresolved-attribute] Attribute `replace` is not defined on `None` in union `str | None`

rotki (https://github.com/rotki/rotki)
- rotkehlchen/chain/ethereum/modules/eth2/structures.py:126:38 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, int].__getitem__(key: int, /) -> int` cannot be called with key of type `None` on object of type `dict[int, int]`
- rotkehlchen/chain/evm/decoding/quickswap/utils.py:47:30 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`
- rotkehlchen/exchanges/bitstamp.py:915:19 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`
- rotkehlchen/exchanges/kucoin.py:674:19 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `None` on object of type `dict[int, str]`

schema_salad (https://github.com/common-workflow-language/schema_salad)
- src/schema_salad/avro/schema.py:207:16 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, NamedSchema].__getitem__(key: str, /) -> NamedSchema` cannot be called with key of type `None` on object of type `dict[str, NamedSchema]`
- src/schema_salad/dotnet_codegen.py:509:45 error[invalid-argument-type] Argument to function `DotNetCodeGen.safe_name` is incorrect: Expected `str`, found `(dict[str, Any] & ~Top[MutableSequence[Unknown]]) | Literal["Expression", "https://w3id.org/cwl/cwl#Expression"]`
- src/schema_salad/java_codegen.py:571:45 error[invalid-argument-type] Argument to function `JavaCodeGen.safe_name` is incorrect: Expected `str`, found `(dict[str, Any] & ~Top[MutableSequence[Unknown]]) | Literal["Expression", "https://w3id.org/cwl/cwl#Expression"]`
- src/schema_salad/python_codegen.py:592:40 error[invalid-argument-type] Argument to function `PythonCodeGen.safe_name` is incorrect: Expected `str`, found `(dict[str, Any] & ~Top[MutableSequence[Unknown]]) | Literal["Expression", "https://w3id.org/cwl/cwl#Expression"]`
- src/schema_salad/tests/test_examples.py:396:23 error[invalid-argument-type] Argument to function `makerdf` is incorrect: Expected `CommentedMap | int | float | str | CommentedSeq`, found `int | float | str | ... omitted 3 union elements`
- src/schema_salad/tests/test_examples.py:409:23 error[invalid-argument-type] Argument to function `makerdf` is incorrect: Expected `CommentedMap | int | float | str | CommentedSeq`, found `int | float | str | ... omitted 3 union elements`
- src/schema_salad/tests/test_examples.py:422:23 error[invalid-argument-type] Argument to function `makerdf` is incorrect: Expected `CommentedMap | int | float | str | CommentedSeq`, found `int | float | str | ... omitted 3 union elements`
- src/schema_salad/tests/test_examples.py:435:23 error[invalid-argument-type] Argument to function `makerdf` is incorrect: Expected `CommentedMap | int | float | str | CommentedSeq`, found `int | float | str | ... omitted 3 union elements`
- src/schema_salad/typescript_codegen.py:444:40 error[invalid-argument-type] Argument to function `TypeScriptCodeGen.safe_name` is incorrect: Expected `str`, found `(dict[str, Any] & ~Top[MutableSequence[Unknown]]) | Literal["Expression", "https://w3id.org/cwl/cwl#Expression"]`

schemathesis (https://github.com/schemathesis/schemathesis)
- src/schemathesis/specs/openapi/coverage/_schema.py:694:45 error[invalid-argument-type] Argument to function `_supports_format_generation` is incorrect: Expected `str`, found `Any | None`
- src/schemathesis/specs/openapi/coverage/_schema.py:696:47 error[invalid-argument-type] Argument to function `_get_format_validator` is incorrect: Expected `str`, found `Any | None`

scipy (https://github.com/scipy/scipy)
- scipy/special/_basic.py:2902:13 error[unsupported-operator] Operator `>` is not supported between two objects of type `generic[Any] | int | float | ... omitted 4 union elements`
+ scipy/special/_basic.py:2902:13 error[unsupported-operator] Operator `>` is not supported between two objects of type `(generic[Any] & ~str & ~bytes) | int | float | complex | memoryview[int]`
- scipy/special/_basic.py:2902:24 error[unsupported-operator] Operator `<` is not supported between objects of type `generic[Any] | int | float | ... omitted 4 union elements` and `Literal[0]`
+ scipy/special/_basic.py:2902:24 error[unsupported-operator] Operator `<` is not supported between objects of type `(generic[Any] & ~str & ~bytes) | int | float | complex | memoryview[int]` and `Literal[0]`
- scipy/special/_basic.py:2902:35 error[unsupported-operator] Operator `<` is not supported between objects of type `generic[Any] | int | float | ... omitted 4 union elements` and `Literal[0]`
+ scipy/special/_basic.py:2902:35 error[unsupported-operator] Operator `<` is not supported between objects of type `(generic[Any] & ~str & ~bytes) | int | float | complex | memoryview[int]` and `Literal[0]`
- scipy/stats/_distribution_infrastructure.py:1864:26 error[unsupported-operator] Operator `%` is not supported between objects of type `generic[Any] | int | float | ... omitted 4 union elements` and `Literal[2]`
+ scipy/stats/_distribution_infrastructure.py:1864:26 error[unsupported-operator] Operator `%` is not supported between objects of type `(generic[Any] & ~str & ~bytes) | int | float | complex | memoryview[int]` and `Literal[2]`
- scipy/stats/_distribution_infrastructure.py:1868:59 error[unsupported-operator] Operator `/` is not supported between objects of type `Literal[1]` and `generic[Any] | int | float | ... omitted 4 union elements`
+ scipy/stats/_distribution_infrastructure.py:1868:59 error[unsupported-operator] Operator `/` is not supported between objects of type `Literal[1]` and `(generic[Any] & ~str & ~bytes) | int | float | complex | memoryview[int]`
- scipy/stats/_distribution_infrastructure.py:1869:35 error[unsupported-operator] Operator `/` is not supported between objects of type `Literal[1]` and `generic[Any] | int | float | ... omitted 4 union elements`
+ scipy/stats/_distribution_infrastructure.py:1869:35 error[unsupported-operator] Operator `/` is not supported between objects of type `Literal[1]` and `(generic[Any] & ~str & ~bytes) | int | float | complex | memoryview[int]`
- scipy/stats/_distribution_infrastructure.py:1869:57 error[unsupported-operator] Operator `/` is not supported between objects of type `Literal[1]` and `generic[Any] | int | float | ... omitted 4 union elements`
+ scipy/stats/_distribution_infrastructure.py:1869:57 error[unsupported-operator] Operator `/` is not supported between objects of type `Literal[1]` and `(generic[Any] & ~str & ~bytes) | int | float | complex | memoryview[int]`
- scipy/stats/_multivariate.py:6625:20 error[invalid-argument-type] Argument to constructor `int.__new__` is incorrect: Expected `str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc`, found `generic[Any] | int | float | ... omitted 4 union elements`
+ scipy/stats/_multivariate.py:6625:20 error[invalid-argument-type] Argument to constructor `int.__new__` is incorrect: Expected `str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc`, found `(generic[Any] & ~str & ~bytes) | int | float | complex | memoryview[int]`

scrapy (https://github.com/scrapy/scrapy)
- scrapy/utils/deprecate.py:86:34 error[invalid-argument-type] Argument to function `_clspath` is incorrect: Expected `type`, found `type | None`

setuptools (https://github.com/pypa/setuptools)
- setuptools/_distutils/compilers/C/unix.py:172:41 error[invalid-argument-type] Argument to bound method `list.remove` is incorrect: Expected `str`, found `int | str | None`

spack (https://github.com/spack/spack)
- lib/spack/spack/test/installer/ui.py:578:9 error[invalid-assignment] Object of type `str | None` is not assignable to attribute `tracked_build_id` of type `str`
- lib/spack/spack/test/installer/ui.py:583:9 error[invalid-assignment] Object of type `str | None` is not assignable to attribute `tracked_build_id` of type `str`
- lib/spack/spack/test/installer/ui.py:588:9 error[invalid-assignment] Object of type `str | None` is not assignable to attribute `tracked_build_id` of type `str`
- lib/spack/spack/test/installer/ui.py:605:9 error[invalid-assignment] Object of type `str | None` is not assignable to attribute `tracked_build_id` of type `str`

steam.py (https://github.com/Gobot1234/steam.py)
- steam/ext/csgo/state.py:180:25 error[invalid-argument-type] Argument is incorrect: Expected `Literal[0, 1, 2, 3, 4, 5] | None`, found `int | None`
+ steam/ext/csgo/state.py:180:25 error[invalid-argument-type] Argument is incorrect: Expected `Literal[0, 1, 2, 3, 4, 5] | None`, found `Literal[1, 2, 3, 4, 5, True] | None`

streamlit (https://github.com/streamlit/streamlit)
- lib/streamlit/elements/heading.py:379:20 error[invalid-return-type] Return type does not match returned value: expected `str`, found `Literal[False] | str | None`
- lib/streamlit/elements/lib/built_in_chart_utils.py:673:30 error[invalid-argument-type] Argument to bound method `list.remove` is incorrect: Expected `str`, found `str | None`

sympy (https://github.com/sympy/sympy)
+ sympy/polys/tests/test_puiseux.py:99:39 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- sympy/parsing/latex/__init__.py:121:41 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, str].__getitem__(key: str, /) -> str` cannot be called with key of type `None` on object of type `dict[str, str]`
- sympy/utilities/tests/test_lambdify.py:1234:12 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, tuple[() -> str | None] | tuple[int, int | float | None, list[str], str]].__getitem__(key: str, /) -> tuple[() -> str | None] | tuple[int, int | float | None, list[str], str]` cannot be called with key of type `None` on object of type `dict[str, tuple[() -> str | None] | tuple[int, int | float | None, list[str], str]]`

trio (https://github.com/python-trio/trio)
- src/trio/_highlevel_serve_listeners.py:52:25 error[invalid-argument-type] Method `__getitem__` of type `bound method Mapping[int, str].__getitem__(key: int, /) -> str` cannot be called with key of type `None` on object of type `Mapping[int, str]`
- src/trio/_highlevel_serve_listeners.py:53:37 error[invalid-argument-type] Argument to function `strerror` is incorrect: Expected `int`, found `int | None`

urllib3 (https://github.com/urllib3/urllib3)
- src/urllib3/util/ssl_.py:309:9 error[invalid-assignment] Object of type `int` is not assignable to attribute `verify_mode` of type `VerifyMode`

websockets (https://github.com/aaugustin/websockets)
- src/websockets/extensions/permessage_deflate.py:275:46 error[invalid-argument-type] Argument to constructor `int.__new__` is incorrect: Expected `str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc`, found `str | None`
- src/websockets/extensions/permessage_deflate.py:285:46 error[invalid-argument-type] Argument to constructor `int.__new__` is incorrect: Expected `str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc`, found `str | None`

zulip (https://github.com/zulip/zulip)
- zerver/actions/message_send.py:572:9 error[invalid-argument-type] Argument is incorrect: Expected `list[tuple[int, int]]`, found `list[tuple[int | Unknown, int | None | Unknown]]`
- zerver/lib/rest.py:105:17 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[str, object].__getitem__(key: str, /) -> object` cannot be called with key of type `None` on object of type `dict[str, object]`
- zproject/computed_settings.py:1141:35 error[invalid-argument-type] Argument to bound method `list.remove` is incorrect: Expected `str`, found `str | None`

Full report with detailed diff (timing results)

@charliermarsh
charliermarsh marked this pull request as ready for review July 20, 2026 20:56
@charliermarsh
charliermarsh requested review from a team as code owners July 20, 2026 20:56
@astral-sh-bot
astral-sh-bot Bot requested a review from carljm July 20, 2026 20:56
@charliermarsh
charliermarsh marked this pull request as draft July 20, 2026 20:57
@charliermarsh
charliermarsh marked this pull request as ready for review July 21, 2026 01:51
@AlexWaygood

Copy link
Copy Markdown
Member

This only affects narrowing; it does not change type inference for the comparison itself. Comparison-result inference remains conservative

Why? I'm not sure that makes sense; doesn't that mean that we'll have more cases where reachability analysis will be inconsistent with narrowing, similar to astral-sh/ty#4040?

One of the main objectives of adding the new equality.rs module was that we would be able to unify type inference and type narrowing for equality operations

@AlexWaygood

Copy link
Copy Markdown
Member

Users who want conservative equality narrowing can enable strict-equality-narrowing:

[tool.ty.analysis]
strict-equality-narrowing = true

This is separate from strict-literal-narrowing, which controls narrowing broad builtin types to literal types

I'm not sure it makes sense for this to be a separate setting. What sort of user is going to want to enable one of these but not the other one? Having two separate settings also means that the experience of ty becomes less consistent from user to user depending on exactly what settings they have enabled; and it potentially means that we have to have a much more extensive test suite (there are now four different combinations of settings to test instead of three)

@charliermarsh

Copy link
Copy Markdown
Member Author

Why? I'm not sure that makes sense; doesn't that mean that we'll have more cases where reachability analysis will be inconsistent with narrowing, similar to astral-sh/ty#4040?

I thought we wanted to keep unsoundness limited to narrowing (which I often hear described as "unsound anyway"), but maybe I misunderstood that request.

@charliermarsh

Copy link
Copy Markdown
Member Author

I'm not sure it makes sense for this to be a separate setting. What sort of user is going to want to enable one of these but not the other one? Having two separate settings also means that the experience of ty becomes less consistent from user to user depending on exactly what settings they have enabled; and it potentially means that we have to have a much more extensive test suite (there are now four different combinations of settings to test instead of three).

I'm fine to combine these, but you can of course make this argument about every subsequent strict- setting we introduce (and I think there will be more).

@charliermarsh
charliermarsh marked this pull request as draft July 21, 2026 11:55
@charliermarsh
charliermarsh force-pushed the charlie/fix-ty-3419-equality-narrowing branch from 71528cc to 0938619 Compare July 21, 2026 12:10
@AlexWaygood

Copy link
Copy Markdown
Member

I thought we wanted to keep unsoundness limited to narrowing (which I often hear described as "unsound anyway"), but maybe I misunderstood that request.

It's possible I missed some disucssion while I was out, but I think you end up with very confusing situations if ty has an inconsistent view of whether an expression always evaluates to True or not, e.g. astral-sh/ty#4051.

I'm fine to combine these, but you can of course make this argument about every subsequent strict- setting we introduce (and I think there will be more).

Heh, true. These changes feel very closely linked, though? They're both about unsound narrowing that more closely emulates what users are used to from other type checkers.

I agree that we'll probably have more options down the road, but I still think it's good to try to limit them as much as possible and interrogate whether they deserve to be distinct or can be combined.

@charliermarsh
charliermarsh marked this pull request as ready for review July 21, 2026 14:01
# Preserve broad builtin types instead of narrowing them to literals
strict-literal-narrowing = true
# Preserve values that could compare equal through a subclass
strict-equality-narrowing = true

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.

The effects here (as discussed) go beyond just narrowing: they include reachability and expression type inference. So having a -narrowing suffix on the option name seems misleading.

I would suggest just strict-equality, but one potential issue is that mypy also has a strict-equality option which does something quite different (warns on always-false comparisons). Another option I like is conservative-equality; I think "conservative" is actually more descriptive than "strict" of what this option actually does. "Strict" implies "emits diagnostics on more things", where this is a more subtle "is more conservative about what inference guesses it makes" kind of option.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

strict-equality-semantics?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

that SGTM

Comment thread crates/ty_project/src/metadata.rs Outdated
Comment thread crates/ty_python_semantic/src/types/equality.rs
Comment thread crates/ty_python_semantic/src/types/equality.rs Outdated
Comment thread crates/ty_python_semantic/src/types/equality.rs Outdated
db.analysis_settings(value.file(db))
.strict_literal_narrowing,
),
ComparisonSoundnessPolicy::from_analysis_settings(db.analysis_settings(value.file(db))),

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.

The updated match mdtests cover a non-final class against a literal and strict builtin/literal behavior, but they do not cover identity equality between distinct yet overlapping classes. Can we add default/strict dotted-value-pattern cases along these lines:

class Base: ...
class Child(Base): ...
class Constants:
    CHILD = Child()

def f(value: Base | None) -> None:
    match value:
        case Constants.CHILD:
            reveal_type(value)

The identity regression I mentioned above reproduces through this call site (the matched branch becomes Never), so this coverage would guard the newly threaded policy and match fallthrough behavior.

@charliermarsh
charliermarsh force-pushed the charlie/fix-ty-3419-equality-narrowing branch from ac5c109 to 1e6a20e Compare July 22, 2026 13:49
Comment on lines +1475 to +1481
/// Whether equality-based checks should account for subclasses with different equality
/// behavior.
///
/// By default, ty narrows `value` from `str` to `Literal["a"]` in the positive branch of
/// `value == "a"`. When this option is enabled, `value` remains `str`. This also applies to
/// membership tests and literal match patterns, which use equality comparisons.
/// By default, ty assumes that subclasses of a non-final class do not override `__eq__` or
/// `__ne__`. This allows equality, inequality, and equality-driven membership or match checks
/// to remove otherwise incompatible union members. This behavior is shared by type narrowing,
/// equality-result inference, and reachability:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

As we discussed in the previous PR that originally introduced this option, the behaviours disallowed by this setting when it's enabled to true can cause unsoundness even without subclasses having "different" equality behaviour. The central reason why narrowing from a string to a Literal["foo"] from an equality check is unsound is precisely because a string subclass by default has the same equality behaviour as str by default, so a custom StrEnum variant could still compare equal to "foo" even though it can never inhabit the type Literal["foo"].

I'm working on a suggestion to improve the docs here but I'm also happy to just push it as a followup PR

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Probably most efficient for you to open a follow-up PR if you're up for it :) Or post a suggestion and I'll apply it. Either is fine!

# Preserve broad builtin types instead of narrowing them to literals
strict-literal-narrowing = true
# Preserve values that could compare equal through a subclass
strict-equality-narrowing = true

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

that SGTM

@charliermarsh
charliermarsh force-pushed the charlie/fix-ty-3419-equality-narrowing branch 2 times, most recently from fe573a6 to bf32f53 Compare July 22, 2026 14:31
@AlexWaygood

Copy link
Copy Markdown
Member

Okay, here's the docs I would write for this option:

    /// Configure ty's behavior regarding type inference and narrowing of equality
    /// checks. Defaults to `false`.
    /// 
    /// By default, ty makes various assumptions about equality checks that match the
    /// intuitions of most Python programmers, but may not be fully sound in all situations.
    /// Enabling this option makes ty more conservative about these assumptions, making it
    /// less likely to infer `Literal[True]` or `Literal[False]` as the result of an
    /// equality check. This has various effects on type checking, including fewer type
    /// narrowing opportunities and more conservative assumptions regarding control flow.
    /// 
    /// One way in which ty will by default make unsound assumptions is by narrowing an
    /// object `x` of type `str` to `Literal["a"]` after an `if x == "a"` check. This is
    /// unsound because a subclass of `str` with value `"a"` will (by default) compare equal
    /// to `"a"`, but will not be of type `Literal["a"]`:
    /// 
    /// ```pycon
    /// >>> # `Literal["a"]` can only be inhabited by instances of exactly `str`, not
    /// >>> # subclasses, but str subclasses compare equal by default:
    /// >>> class StringSubclass(str): ...
    /// ...
    /// >>> StringSubclass("a") == "a"
    /// True
    /// >>>
    /// >>> # This also applies to `StrEnum`s:
    /// >>> from enum import StrEnum
    /// >>> class MyEnum(StrEnum):
    /// ...     A = "a"
    /// ...
    /// >>> MyEnum.A == "a"
    /// True
    /// ```
    /// 
    /// Enabling this option prevents the unsound narrowing of `x` to `Literal["a"]`,
    /// and instead keep it as `str`:
    /// 
    /// ```python
    /// from typing import Literal
    ///
    /// def parse(value: str) -> Literal["a"] | None:
    ///     # with `strict-equality-narrowing = true`, no narrowing will occur here,
    ///     # and an error will be emitted on the `return` statement.
    ///     if value == "a":
    ///         return value
    ///     return None
    /// ```
    /// 
    /// Another assumption ty makes by default is that subclasses will never override `__eq__` or
    /// `__ne__`. This allows ty to narrow the following union based on an equality check, despite
    /// the fact that an instance of a subclass of `Foo` could compare equal to `None`, and it's
    /// perfectly valid to pass an instance of a subclass into the `x` parameter of this function:
    /// 
    /// ```python
    /// def narrow(x: Foo | None, other: Foo) -> None:
    ///     if x == other:
    ///         # with this option enabled, `x` will still have type `Foo | None` here,
    ///         # since it is legal to subclass `Foo` and override its `__eq__` method.
    ///         reveal_type(x)
    /// ```
    ///
    /// Many operations in Python implicitly call `__eq__` under the hood; enabling this optionn
    /// will also impact those options. For example, this option will also impact narrowing from
    /// `in` checks, and narrowing in `match` statements that use value patterns:
    /// 
    /// ```python
    /// def narrow_in(x: Foo | None, other: list[Foo]) -> None:
    ///     if x in other:
    ///         # with this option enabled, `x` will still have type `Foo | None` here,
    ///         # since the `in` operator implicitly calls `__eq__` on each element of `other`.
    ///         reveal_type(x)
    /// 
    /// 
    /// def narrow_match(x: str) -> None:
    ///     match x:
    ///         case "a":
    ///             # with this option enabled, `x` will still have type `str` here,
    ///             # since this `case` branch will be taken by any object that compares
    ///             # equal to `"a"`, including subclasses of `str`.
    ///             reveal_type(x)
    /// ``` 

@charliermarsh
charliermarsh force-pushed the charlie/fix-ty-3419-equality-narrowing branch from bf32f53 to bb5456c Compare July 22, 2026 14:42
@charliermarsh
charliermarsh enabled auto-merge (squash) July 22, 2026 14:42
@charliermarsh
charliermarsh disabled auto-merge July 22, 2026 14:45
Comment thread crates/ty_python_semantic/src/types/match_pattern.rs Outdated
Comment thread crates/ty_python_semantic/src/types/narrow.rs

@AlexWaygood AlexWaygood left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks!

(Haven't reviewed the code in depth but I trust Carl's/Codex's review).

@charliermarsh
charliermarsh enabled auto-merge (squash) July 22, 2026 15:11
@charliermarsh
charliermarsh disabled auto-merge July 22, 2026 15:15
@charliermarsh
charliermarsh enabled auto-merge (squash) July 22, 2026 15:18
@charliermarsh
charliermarsh merged commit 64722a0 into main Jul 22, 2026
62 checks passed
@charliermarsh
charliermarsh deleted the charlie/fix-ty-3419-equality-narrowing branch July 22, 2026 15:23
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.

Consider allowing equality narrowing to ignore the possibility of a subclass overriding __eq__

3 participants