Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions stdlib/@tests/test_cases/builtins/check_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ def asd(self) -> int:
assert_type(combined, List[Union[Foo, Bar]])
for item in combined:
assert_type(item.asd(), int)

l1: list[int] = [1]
l2: list[object] = l1.copy()
# this is an error, because a list of ints can't be a list of strs
l3: list[str] = l1.copy() # type: ignore
3 changes: 2 additions & 1 deletion stdlib/builtins.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1104,7 +1104,8 @@ class list(MutableSequence[_T]):
def __init__(self) -> None: ...
@overload
def __init__(self, iterable: Iterable[_T], /) -> None: ...
def copy(self) -> list[_T]: ...
# `copy` returns a new object, so capture the expected return type here using a type var
def copy(self) -> list[_S | _T]: ...
Copy link
Collaborator

Choose a reason for hiding this comment

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

_S has no meaning, as it's only used once in this context. It's just interpreted as Any in this context.

Copy link
Collaborator

@srittau srittau Sep 21, 2025

Choose a reason for hiding this comment

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

I see what you're trying to do, but we should probably use:

Suggested change
def copy(self) -> list[_S | _T]: ...
# The new list can allow broader types that the old one. `Any` allows that.
def copy(self) -> list[_T | Any]: ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

_S has no meaning, as it's only used once in this context. It's just interpreted as Any in this context.

that is demonstrably not true

This comment was marked as resolved.

Copy link
Member

Choose a reason for hiding this comment

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

basedpyright is incorrect there. A[int | Any] should be assignable to A[object].

Copy link
Contributor Author

@KotlinIsland KotlinIsland Sep 21, 2025

Choose a reason for hiding this comment

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

consider this example:

def empty_list[T=Never]() -> list[T]:
    return []

a = empty_list()
b: list[int] = empty_list()

if we were to use Any here, then the type of a would be list[Any]

additionally, there would be absolutely no safety within empty_list to return the correct type. i'm not really sure where this idea that type variables only in return positions being invalid came from

this is literally the meaning of a type variable, to use Any is a mistake

Copy link
Contributor Author

Choose a reason for hiding this comment

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

basedpyright is incorrect there. A[int | Any] should be assignable to A[object].

yes: microsoft/pyright#10713 (comment)

i was a little hasty with the first example, i think it's clear though that this case is the intended use of a type variable, not Any

def append(self, object: _T, /) -> None: ...
def extend(self, iterable: Iterable[_T], /) -> None: ...
def pop(self, index: SupportsIndex = -1, /) -> _T: ...
Expand Down
Loading