Skip to content

refactor visit_conditional_expr to fix ternary behavior (option 1) - #19563

Merged
hauntsaninja merged 5 commits into
python:masterfrom
randolf-scholz:fix_ternary_option1
Dec 14, 2025
Merged

refactor visit_conditional_expr to fix ternary behavior (option 1)#19563
hauntsaninja merged 5 commits into
python:masterfrom
randolf-scholz:fix_ternary_option1

Conversation

@randolf-scholz

@randolf-scholzrandolf-scholz commented Aug 1, 2025

Copy link
Copy Markdown
Contributor

Option ① from #19561 (comment) which does not allow MemberExpr elements to the nested binder, hence ternaries like f(x.attr) if x.attr else g(x.attr) will not consider the narrowed type of x.attr

Option ②: #19562

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-scholzrandolf-scholz changed the title refactor visit_conditional_expr to fix ternary behaviorrefactor visit_conditional_expr to fix ternary behavior (option 1)Aug 1, 2025
@github-actions

This comment has been minimized.

1 similar comment
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@randolf-scholz

Copy link
Copy Markdown
ContributorAuthor

Repro of pyodide-build:

uv_bin: str|Noneinstall_prefix= (
[uv_bin, "pip", "install"]
ifbool()
else ["/bin/python", "-m", "pip", "install", "--no-user"]
)
reveal_type(install_prefix) # master: list[str | None], PR: list[str | None] | list[str]reveal_type([*install_prefix]) # master: list[str | None], PR: list[Any]

It seems like this is due to some overly aggressive join operation, [*install_prefix] should get inferred as list[str | None] regardless whether install_prefix is just list[str | None] or list[str | None] | list[str].

@hauntsaninja

Copy link
Copy Markdown
Collaborator

Joins don't typically result in Any's. Looks like this was some other missing case: #19650

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

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

optuna (https://github.com/optuna/optuna)
+ optuna/visualization/_intermediate_values.py:95: error: Unused "type: ignore" comment [unused-ignore]
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]
colour (https://github.com/colour-science/colour)
+ colour/io/luts/lut.py:1418: 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:1432: 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:1432: note: Possible overload variants:- colour/io/luts/lut.py:1432: 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:1432: 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:1432: 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:1432: 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]]
mypy (https://github.com/python/mypy)
+ mypyc/irbuild/statement.py:602: error: Argument 1 to "accept" of "IRBuilder" has incompatible type "Optional[Block]"; expected "Statement" [arg-type]
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]
discord.py (https://github.com/Rapptz/discord.py)
+ discord/utils.py:485: error: Unused "type: ignore" comment [unused-ignore]+ discord/utils.py:599: error: Unused "type: ignore" comment [unused-ignore]
dd-trace-py (https://github.com/DataDog/dd-trace-py)
+ ddtrace/appsec/_iast/_taint_tracking/aspects.py:706: error: Unused "type: ignore" comment [unused-ignore]
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]

@hauntsaninjahauntsaninja left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks, this seems like a really great change!

@hauntsaninja
hauntsaninja merged commit bc2481a into python:masterDec 14, 2025
20 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

3 participants

@randolf-scholz@hauntsaninja@RiyaPrabhakar-git