typeshed icon indicating copy to clipboard operation
typeshed copied to clipboard

fix `list.__add__` to use the expected type

Open KotlinIsland opened this issue 4 months ago • 7 comments

originally: #14756

now broken (mypy)

_: list[int | str] = list[int]() + list[str]()

this break seems extremely minor (two cases in corpus)

now fixed (mypy)

_: list[int | str] = list[str]() + list[str | int]()

now fixed (pyright):

_: list[object] = list[int]() + list[str]()

KotlinIsland avatar Sep 28 '25 04:09 KotlinIsland

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

zulip (https://github.com/zulip/zulip)
+ zerver/lib/onboarding_steps.py:65: error: Unsupported operand types for + ("list[OneTimeNotice]" and "list[OneTimeAction]")  [operator]

prefect (https://github.com/PrefectHQ/prefect)
- src/prefect/utilities/callables.py:579: error: Incompatible types in assignment (expression has type "list[None]", variable has type "list[Optional[expr]]")  [assignment]
- src/prefect/utilities/callables.py:579: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
- src/prefect/utilities/callables.py:579: note: Consider using "Sequence" instead, which is covariant
- src/prefect/utilities/callables.py:581: error: Unsupported operand types for + ("list[None]" and "list[Optional[expr]]")  [operator]

pydantic (https://github.com/pydantic/pydantic)
- pydantic/aliases.py:29: error: Incompatible types in assignment (expression has type "list[str]", variable has type "list[int | str]")  [assignment]
- pydantic/aliases.py:29: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
- pydantic/aliases.py:29: note: Consider using "Sequence" instead, which is covariant
- pydantic/aliases.py:29: error: Argument 1 to "list" has incompatible type "tuple[str | int, ...]"; expected "Iterable[str]"  [arg-type]

meson (https://github.com/mesonbuild/meson)
+ mesonbuild/modules/i18n.py:165:43: error: Unsupported operand types for + ("list[File]" and "list[CustomTarget]")  [operator]

github-actions[bot] avatar Sep 28 '25 04:09 github-actions[bot]

This has the same problem as #14755: _T1 has the same meaning as Any, so this is what we should use (with an appropriate comment). Also, the tests are not successful.

srittau avatar Sep 30 '25 09:09 srittau

that's just not true, a type var and Any are different. if we make this Any then it will include Any, we don't want that

could you explain why you think it's Any? or respond to my example where I explain how they are different

KotlinIsland avatar Sep 30 '25 12:09 KotlinIsland

One of your examples relied on an incorrect behavior in your type checker (assignability involving list[B] and list[A | Any]). Another involved a TypeVar with a default, which this one doesn't have.

JelleZijlstra avatar Sep 30 '25 14:09 JelleZijlstra

One of your examples relied on an incorrect behavior in your type checker (assignability involving list[B] and list[A | Any]). Another involved a TypeVar with a default, which this one doesn't have.

yes, i already said the first example wasn't right, i've hidden it for clarity. the case with the deafult, is to explain the situation, but i can understand that it's not 1-to-1, so consider this:

def list_unsafe(*t: object) -> list[Any | int]:
    if bool():
        return ["oops", 1]  # no error
    else:
        return [*t, 1]
        
unsafe1 = list_unsafe(None)  # `unsafe1` is `list[Any | int]`
unsafe2 = list_unsafe()  # `unsafe2` is `list[Any | int]`

def list_safe[T](*t: T) -> list[T | int]:
    if bool():
        return [1, "oops"]  # error
    else:
        return [1, *t]

safe1 = list_safe(None)  # `safe1` is `list[None | int]`
safe2 = list_safe()  # `safe2` is `list[int]`

then we just remove the redundant unused *t and we get

def list_safe[T]() -> list[T | int]:
    if bool():
        return ["oops", 1]  # error
    else:
        return [1]

safe1 = list_safe()  # `safe1` is `list[int]`
safe2: list[object] = list_safe()  # `safe2` is `list[object]`, with no error

Code sample in basedpyright playground

effectivly, T is inferred as Never in these cases and is collapsed against the int

but even if Any and a type var did have the same semantics here, it would still be a mistake to use Any. Any means "not typed", just because something has the same outcome, it should still be typed correctly. print is def print(*values: object, ... why not use Any here if it has the same meaning as object?

KotlinIsland avatar Oct 01 '25 01:10 KotlinIsland

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

core (https://github.com/home-assistant/core)
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here

zulip (https://github.com/zulip/zulip)
+ zerver/lib/onboarding_steps.py:65: error: Unsupported operand types for + ("list[OneTimeNotice]" and "list[OneTimeAction]")  [operator]
- ...typeshed_to_test/stdlib/builtins.pyi:117: note: "SubTest" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:118: note: "SubTest" defined here

ibis (https://github.com/ibis-project/ibis)
- ...typeshed_to_test/stdlib/builtins.pyi:117: note: "Any" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:118: note: "Any" defined here
- ...typeshed_to_test/stdlib/builtins.pyi:117: note: "__init__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:118: note: "__init__" of "object" defined here

prefect (https://github.com/PrefectHQ/prefect)
- src/prefect/utilities/callables.py:579: error: Incompatible types in assignment (expression has type "list[None]", variable has type "list[Optional[expr]]")  [assignment]
- src/prefect/utilities/callables.py:579: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
- src/prefect/utilities/callables.py:579: note: Consider using "Sequence" instead, which is covariant
- src/prefect/utilities/callables.py:581: error: Unsupported operand types for + ("list[None]" and "list[Optional[expr]]")  [operator]
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here

pydantic (https://github.com/pydantic/pydantic)
- pydantic/aliases.py:29: error: Incompatible types in assignment (expression has type "list[str]", variable has type "list[int | str]")  [assignment]
- pydantic/aliases.py:29: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
- pydantic/aliases.py:29: note: Consider using "Sequence" instead, which is covariant
- pydantic/aliases.py:29: error: Argument 1 to "list" has incompatible type "tuple[str | int, ...]"; expected "Iterable[str]"  [arg-type]

strawberry (https://github.com/strawberry-graphql/strawberry)
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here

meson (https://github.com/mesonbuild/meson)
+ mesonbuild/modules/i18n.py:165:43: error: Unsupported operand types for + ("list[File]" and "list[CustomTarget]")  [operator]

github-actions[bot] avatar Oct 01 '25 01:10 github-actions[bot]

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

core (https://github.com/home-assistant/core)
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here

zulip (https://github.com/zulip/zulip)
+ zerver/lib/onboarding_steps.py:65: error: Unsupported operand types for + ("list[OneTimeNotice]" and "list[OneTimeAction]")  [operator]
- ...typeshed_to_test/stdlib/builtins.pyi:117: note: "SubTest" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:118: note: "SubTest" defined here

ibis (https://github.com/ibis-project/ibis)
- ...typeshed_to_test/stdlib/builtins.pyi:117: note: "Any" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:118: note: "Any" defined here
- ...typeshed_to_test/stdlib/builtins.pyi:117: note: "__init__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:118: note: "__init__" of "object" defined here

prefect (https://github.com/PrefectHQ/prefect)
- src/prefect/utilities/callables.py:579: error: Incompatible types in assignment (expression has type "list[None]", variable has type "list[Optional[expr]]")  [assignment]
- src/prefect/utilities/callables.py:579: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
- src/prefect/utilities/callables.py:579: note: Consider using "Sequence" instead, which is covariant
- src/prefect/utilities/callables.py:581: error: Unsupported operand types for + ("list[None]" and "list[Optional[expr]]")  [operator]
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here

pydantic (https://github.com/pydantic/pydantic)
- pydantic/aliases.py:29: error: Incompatible types in assignment (expression has type "list[str]", variable has type "list[int | str]")  [assignment]
- pydantic/aliases.py:29: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
- pydantic/aliases.py:29: note: Consider using "Sequence" instead, which is covariant
- pydantic/aliases.py:29: error: Argument 1 to "list" has incompatible type "tuple[str | int, ...]"; expected "Iterable[str]"  [arg-type]

strawberry (https://github.com/strawberry-graphql/strawberry)
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here
- ...typeshed_to_test/stdlib/builtins.pyi:139: note: "__init_subclass__" of "object" defined here
+ ...typeshed_to_test/stdlib/builtins.pyi:140: note: "__init_subclass__" of "object" defined here

meson (https://github.com/mesonbuild/meson)
+ mesonbuild/modules/i18n.py:165:43: error: Unsupported operand types for + ("list[File]" and "list[CustomTarget]")  [operator]

github-actions[bot] avatar Oct 01 '25 02:10 github-actions[bot]