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
12 changes: 12 additions & 0 deletions pytype/tests/test_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,18 @@ class A(typing.Generic[_T]):
""",
)

def test_generic_and_double_typevar(self):
self.assertNoCrash(
self.Check,
"""
import typing
_T = typing.TypeVar("_T")
_S = typing.TypeVar("_S")
class A(typing.Generic[_T, _S]):
...
""",
)

def test_jump_into_class_through_annotation(self):
self.Check("""
class Foo:
Expand Down
72 changes: 72 additions & 0 deletions pytype/tests/test_typevar1.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,78 @@ def foo(a: T, b: S) -> tuple[S, T]: ...
""",
)

@test_utils.skipBeforePy((3, 12), "PEP 695 - 3.12 feature")
def test_unused_typevar_pep695_class_single_type_var(self):
ty = self.Infer("""
class A[T]: pass
""")
self.assertTypesMatchPytd(
ty,
"""
from typing import Any, Generic, TypeVar

T = TypeVar('T')

class A(Generic[T]):
__type_params__: tuple[Any]
""",
)

@test_utils.skipBeforePy((3, 12), "PEP 695 - 3.12 feature")
def test_unused_typevar_pep695_class_double_type_var(self):
ty = self.Infer("""
class A[T, S]: pass
""")
self.assertTypesMatchPytd(
ty,
"""
from typing import Any, Generic, TypeVar
S = TypeVar('S')
T = TypeVar('T')

class A(Generic[T, S]):
__type_params__: tuple[Any, Any]
""",
)

@test_utils.skipBeforePy((3, 12), "PEP 695 - 3.12 feature")
def test_unused_typevar_pep695_class_both_generic_and_base(self):
errors = self.CheckWithErrors("""
from typing import Generic, TypeVar
U = TypeVar('U')
class A[T, S](Generic[U]): pass # invalid-annotation[e1]
""")
self.assertErrorRegexes(
errors,
{
"e1": (
r"Invalid type annotation 'A' \nCannot inherit from"
r" Generic\[...\] multiple times"
),
},
)

@test_utils.skipBeforePy((3, 12), "PEP 695 - 3.12 feature")
def test_unused_typevar_pep695_class_inherit_from_base(self):
ty = self.Infer("""
class Base[T]: pass
class Derived[S, T](Base[T]): pass
""")
self.assertTypesMatchPytd(
ty,
"""
from typing import Any, Generic, TypeVar
S = TypeVar('S')
T = TypeVar('T')

class Base(Generic[T]):
__type_params__: tuple[Any]

class Derived(Base[T], Generic[S, T]):
__type_params__: tuple[Any, Any]
""",
)

def test_import_typevar(self):
with test_utils.Tempdir() as d:
d.create_file("a.pyi", """T = TypeVar("T")""")
Expand Down
14 changes: 12 additions & 2 deletions pytype/vm.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,12 @@ def _typings_paramspec(self):
self.ctx.root_node
)

@functools.cached_property
def _typings_generic(self):
return typing_overlay.Generic("Generic", self.ctx).to_variable(
self.ctx.root_node
)

def run_instruction(
self, op: opcodes.Opcode, state: frame_state.FrameState
) -> frame_state.FrameState:
Expand Down Expand Up @@ -3915,8 +3921,12 @@ def byte_INTRINSIC_TYPEVARTUPLE(self, state):
return state

def byte_INTRINSIC_SUBSCRIPT_GENERIC(self, state):
# TODO: b/350910471 - Implement to support PEP 695
return state
state, type_parameters = state.pop()
state = state.push(self._typings_generic)
# This will be a tuple of type parameters in order.
state = state.push(type_parameters)
# Returning Generic[S, T]
return self.binary_operator(state, "__getitem__")

def byte_INTRINSIC_TYPEALIAS(self, state):
"""This intrinsic creates a type alias and puts the result on the stack."""
Expand Down
Loading