Skip to content

refactor visit_conditional_expr to fix ternary behavior (option 2) #19562

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

randolf-scholz
Copy link
Contributor

@randolf-scholz randolf-scholz commented Aug 1, 2025

Option ①: #19563

Option ② from #19561 (comment) which additionally copies MemberExpr elements to the nested binder, hence allows ternaries like f(x.attr) if x.attr else g(x.attr) to consider the bound type of x.attr.

The current implementation of visit_conditional_expr seemed to some rather complicated things, I found that if there is no context, we can simply use the union of the types produced when considering the branches context-free as an artificial context that leads to the desired behavior in the unification test cases.

@randolf-scholz randolf-scholz changed the title refactor visit_conditional_expr to fix ternary behavior refactor visit_conditional_expr to fix ternary behavior (option 2) Aug 1, 2025

This comment has been minimized.

Copy link
Contributor

github-actions bot commented Aug 1, 2025

Diff from mypy_primer, showing the effect of this PR on open source code:

colour (https://github.com/colour-science/colour)
+ colour/io/luts/lut.py:1366: error: Argument 1 to "tile" has incompatible type "Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str] | None"; expected "_SupportsArray[dtype[signedinteger[_8Bit] | signedinteger[_16Bit] | signedinteger[_32Bit] | signedinteger[_64Bit] | unsignedinteger[_8Bit] | unsignedinteger[_16Bit] | unsignedinteger[_32Bit] | unsignedinteger[_64Bit] | Any]] | _NestedSequence[_SupportsArray[dtype[signedinteger[_8Bit] | signedinteger[_16Bit] | signedinteger[_32Bit] | signedinteger[_64Bit] | unsignedinteger[_8Bit] | unsignedinteger[_16Bit] | unsignedinteger[_32Bit] | unsignedinteger[_64Bit] | Any]]]"  [arg-type]
- colour/io/luts/lut.py:1380: error: No overload variant of "pad" matches argument types "ndarray[Any, Any]", "tuple[int, Any | signedinteger[_8Bit] | signedinteger[_16Bit] | signedinteger[_32Bit] | signedinteger[_64Bit] | unsignedinteger[_8Bit] | unsignedinteger[_16Bit] | unsignedinteger[_32Bit] | unsignedinteger[_64Bit]]", "str", "float"  [call-overload]
- colour/io/luts/lut.py:1380: note: Possible overload variants:
- colour/io/luts/lut.py:1380: note:     def [_ScalarT: generic[Any]] pad(array: _SupportsArray[dtype[_ScalarT]] | _NestedSequence[_SupportsArray[dtype[_ScalarT]]], pad_width: _SupportsArray[dtype[integer[Any]]] | _NestedSequence[_SupportsArray[dtype[integer[Any]]]] | int | _NestedSequence[int], mode: Literal['constant', 'edge', 'linear_ramp', 'maximum', 'mean', 'median', 'minimum', 'reflect', 'symmetric', 'wrap', 'empty'] = ..., *, stat_length: _SupportsArray[dtype[integer[Any]]] | _NestedSequence[_SupportsArray[dtype[integer[Any]]]] | int | _NestedSequence[int] | None = ..., constant_values: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str] = ..., end_values: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str] = ..., reflect_type: Literal['odd', 'even'] = ...) -> ndarray[tuple[Any, ...], dtype[_ScalarT]]
- colour/io/luts/lut.py:1380: note:     def pad(array: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str], pad_width: _SupportsArray[dtype[integer[Any]]] | _NestedSequence[_SupportsArray[dtype[integer[Any]]]] | int | _NestedSequence[int], mode: Literal['constant', 'edge', 'linear_ramp', 'maximum', 'mean', 'median', 'minimum', 'reflect', 'symmetric', 'wrap', 'empty'] = ..., *, stat_length: _SupportsArray[dtype[integer[Any]]] | _NestedSequence[_SupportsArray[dtype[integer[Any]]]] | int | _NestedSequence[int] | None = ..., constant_values: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str] = ..., end_values: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str] = ..., reflect_type: Literal['odd', 'even'] = ...) -> ndarray[tuple[Any, ...], dtype[Any]]
- colour/io/luts/lut.py:1380: note:     def [_ScalarT: generic[Any]] pad(array: _SupportsArray[dtype[_ScalarT]] | _NestedSequence[_SupportsArray[dtype[_ScalarT]]], pad_width: _SupportsArray[dtype[integer[Any]]] | _NestedSequence[_SupportsArray[dtype[integer[Any]]]] | int | _NestedSequence[int], mode: _ModeFunc, **kwargs: Any) -> ndarray[tuple[Any, ...], dtype[_ScalarT]]
- colour/io/luts/lut.py:1380: note:     def pad(array: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str], pad_width: _SupportsArray[dtype[integer[Any]]] | _NestedSequence[_SupportsArray[dtype[integer[Any]]]] | int | _NestedSequence[int], mode: _ModeFunc, **kwargs: Any) -> ndarray[tuple[Any, ...], dtype[Any]]

steam.py (https://github.com/Gobot1234/steam.py)
+ steam/utils.py:883: error: Unused "type: ignore" comment  [unused-ignore]
- steam/http.py:100: error: Dict entry 0 has incompatible type "str": "str | None"; expected "str": "str"  [dict-item]
- steam/gateway.py:830: error: No overload variant of "wait_for" of "SteamWebSocket" matches argument types "int", "Callable[[UnifiedMsgT], bool]"  [call-overload]
+ steam/gateway.py:830: error: No overload variant of "wait_for" of "SteamWebSocket" matches argument types "int", "Callable[[UnifiedMsgT], bool] | Callable[[Any], Any]"  [call-overload]

pyodide (https://github.com/pyodide/pyodide)
- pyodide-build/pyodide_build/xbuildenv.py:309: error: List item 0 has incompatible type "list[str | None]"; expected "str | bytes | PathLike[str] | PathLike[bytes]"  [list-item]

prefect (https://github.com/PrefectHQ/prefect)
- src/prefect/runner/storage.py:178: error: Argument 1 to "deepcopy" has incompatible type "Union[GitCredentials, Block, dict[str, Any]]"; expected "dict[str, Any]"  [arg-type]
+ src/prefect/runner/storage.py:183: error: TypedDict key must be a string literal; expected one of ("username", "access_token")  [literal-required]
+ src/prefect/runner/storage.py:185: error: TypedDict key must be a string literal; expected one of ("username", "access_token")  [literal-required]

dd-trace-py (https://github.com/DataDog/dd-trace-py)
+ ddtrace/appsec/_iast/_taint_tracking/aspects.py:704: error: Unused "type: ignore" comment  [unused-ignore]

cwltool (https://github.com/common-workflow-language/cwltool)
+ cwltool/command_line_tool.py: note: In function "job":
+ cwltool/command_line_tool.py:1077:22: error: Redundant cast to "MutationManager"  [redundant-cast]
+ cwltool/command_line_tool.py:1082:22: error: Redundant cast to "MutationManager"  [redundant-cast]
+ cwltool/executors.py:156: error: Unused "type: ignore" comment  [unused-ignore]

discord.py (https://github.com/Rapptz/discord.py)
+ discord/utils.py:491: error: Unused "type: ignore" comment  [unused-ignore]
+ discord/utils.py:607: error: Unused "type: ignore" comment  [unused-ignore]
+ discord/message.py:2571: error: Unused "type: ignore" comment  [unused-ignore]
+ discord/message.py:2575: error: Unused "type: ignore" comment  [unused-ignore]
+ discord/message.py:2579: error: Unused "type: ignore" comment  [unused-ignore]
+ discord/client.py:2916: error: Unused "type: ignore" comment  [unused-ignore]
+ discord/client.py:2937: error: Unused "type: ignore" comment  [unused-ignore]
+ discord/ext/commands/converter.py:1032: error: Unused "type: ignore" comment  [unused-ignore]
+ discord/ext/commands/converter.py:1036: error: Unused "type: ignore" comment  [unused-ignore]
+ discord/ext/commands/converter.py:1051: error: Unused "type: ignore" comment  [unused-ignore]

spark (https://github.com/apache/spark)
+ python/pyspark/sql/connect/client/reattach.py:183: error: Unused "type: ignore" comment  [unused-ignore]
+ python/pyspark/sql/connect/client/reattach.py:183: error: No overload variant of "next" matches argument type "None"  [call-overload]
+ python/pyspark/sql/connect/client/reattach.py:183: note: Error code "call-overload" not covered by "type: ignore" comment
+ python/pyspark/sql/connect/client/reattach.py:183: note: Possible overload variants:
+ python/pyspark/sql/connect/client/reattach.py:183: note:     def [_T] next(SupportsNext[_T], /) -> _T
+ python/pyspark/sql/connect/client/reattach.py:183: note:     def [_T, _VT] next(SupportsNext[_T], _VT, /) -> _T | _VT

optuna (https://github.com/optuna/optuna)
+ optuna/visualization/_intermediate_values.py:95: error: Unused "type: ignore" comment  [unused-ignore]

core (https://github.com/home-assistant/core)
+ homeassistant/components/withings/sensor.py:767: error: Statement is unreachable  [unreachable]
+ homeassistant/components/withings/sensor.py:796: error: Statement is unreachable  [unreachable]
+ homeassistant/components/withings/sensor.py:823: error: Statement is unreachable  [unreachable]
+ homeassistant/components/withings/calendar.py:48: error: Statement is unreachable  [unreachable]

@randolf-scholz
Copy link
Contributor Author

TODO: fix is_var_redefined_in_outer_context to also consider attribute assignment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Irregular inference with ternary based on attribute. Literal list ternary behave unexpected No TypeVar variable inference in ternary operators
1 participant