Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
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
3 changes: 3 additions & 0 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2960,6 +2960,9 @@ def visit_index_with_type(self, left_type: Type, e: IndexExpr,
elif (isinstance(left_type, CallableType)
and left_type.is_type_obj() and left_type.type_object().is_enum):
return self.visit_enum_index_expr(left_type.type_object(), e.index, e)
elif (isinstance(left_type, TypeVarType)
and not self.has_member(left_type.upper_bound, "__getitem__")):
return self.visit_index_with_type(left_type.upper_bound, e, original_type)

@sobolevn sobolevn Nov 3, 2021

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What if this type var has .values instead of .upper_bound? Will it still work?

@ilyalabun ilyalabun Nov 3, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

surprisingly it works. I'm not sure why, but I suspect it's related to TypeVars expansion in checker.py::check_func_def. Added test for this case as well

else:
result, method_type = self.check_method_call_by_name(
'__getitem__', left_type, [e.index], [ARG_POS], e,
Expand Down
71 changes: 71 additions & 0 deletions test-data/unit/check-typevar-values.test
Original file line number Diff line number Diff line change
Expand Up @@ -631,3 +631,74 @@ def g(s: S) -> Callable[[S], None]: ...
def f(x: S) -> None:
h = g(x)
h(x)

[case testTypeVarWithTypedDictBoundInIndexExpression]
from typing import TypeVar
from typing_extensions import TypedDict

class Data(TypedDict):
x: int


T = TypeVar("T", bound=Data)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Will the same work for bound=Union[Data, Other]?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes, it works because Union is handled in visit_index_with_type by processing Union items recursively. Added test for this case.



def f(data: T) -> None:
reveal_type(data["x"]) # N: Revealed type is "builtins.int"
[builtins fixtures/tuple.pyi]

[case testTypeVarWithUnionTypedDictBoundInIndexExpression]
from typing import TypeVar, Union, Dict
from typing_extensions import TypedDict

class Data(TypedDict):
x: int


T = TypeVar("T", bound=Union[Data, Dict[str, str]])


def f(data: T) -> None:
reveal_type(data["x"]) # N: Revealed type is "Union[builtins.int, builtins.str*]"

[builtins fixtures/tuple.pyi]
[builtins fixtures/dict.pyi]

[case testTypeVarWithTypedDictValueInIndexExpression]
from typing import TypeVar, Union, Dict
from typing_extensions import TypedDict

class Data(TypedDict):
x: int


T = TypeVar("T", Data, Dict[str, str])


def f(data: T) -> None:
_: Union[str, int] = data["x"]
[builtins fixtures/tuple.pyi]
[builtins fixtures/dict.pyi]

[case testSelfTypeVarIndexExpr]
from typing import TypeVar, Union, Type
from typing_extensions import TypedDict

T = TypeVar("T", bound="Indexable")

class Indexable:
def __init__(self, index: str) -> None:
self.index = index

def __getitem__(self: T, index: str) -> T:
return self._new_instance(index)

@classmethod
def _new_instance(cls: Type[T], index: str) -> T:
return cls("foo")

def m(self: T) -> T:
return self["foo"]

[builtins fixtures/tuple.pyi]
[builtins fixtures/classmethod.pyi]