Skip to content

Conversation

KotlinIsland
Copy link
Contributor

@KotlinIsland KotlinIsland commented Sep 28, 2025

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]()

This comment has been minimized.

@srittau
Copy link
Collaborator

srittau commented Sep 30, 2025

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.

@KotlinIsland
Copy link
Contributor Author

KotlinIsland commented Sep 30, 2025

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

@JelleZijlstra
Copy link
Member

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.

@KotlinIsland
Copy link
Contributor Author

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?

This comment has been minimized.

Copy link
Contributor

github-actions bot commented Oct 1, 2025

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]

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.

3 participants