Skip to content

Add collections._tuplegetter support #19479

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
9 changes: 9 additions & 0 deletions mypy/checkmember.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
ParamSpecType,
PartialType,
ProperType,
TupleGetterType,
TupleType,
Type,
TypedDictType,
Expand Down Expand Up @@ -935,12 +936,20 @@ def analyze_var(
return result


def expand_tuplegetter_type_if_needed(typ: Type) -> Type:
proper = get_proper_type(typ)
if isinstance(proper, TupleGetterType):
return proper.typ
return typ


def expand_without_binding(
typ: Type, var: Var, itype: Instance, original_itype: Instance, mx: MemberContext
) -> Type:
if not mx.preserve_type_var_ids:
typ = freshen_all_functions_type_vars(typ)
typ = expand_self_type_if_needed(typ, mx, var, original_itype)
typ = expand_tuplegetter_type_if_needed(typ)
expanded = expand_type_by_instance(typ, itype)
freeze_all_type_vars(expanded)
return expanded
Expand Down
4 changes: 4 additions & 0 deletions mypy/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
ParamSpecType,
PartialType,
ProperType,
TupleGetterType,
TupleType,
Type,
TypeAliasType,
Expand Down Expand Up @@ -1244,6 +1245,9 @@ def infer_against_overloaded(
item = find_matching_overload_item(overloaded, template)
return infer_constraints(template, item, self.direction)

def visit_tuplegetter_type(self, template: TupleGetterType) -> list[Constraint]:
raise NotImplementedError

def visit_tuple_type(self, template: TupleType) -> list[Constraint]:
actual = self.actual
unpack_index = find_unpack_in_list(template.items)
Expand Down
4 changes: 4 additions & 0 deletions mypy/copytype.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
ParamSpecType,
PartialType,
ProperType,
TupleGetterType,
TupleType,
TypeAliasType,
TypedDictType,
Expand Down Expand Up @@ -103,6 +104,9 @@ def visit_partial_type(self, t: PartialType) -> ProperType:
def visit_callable_type(self, t: CallableType) -> ProperType:
return self.copy_common(t, t.copy_modified())

def visit_tuplegetter_type(self, t: TupleGetterType) -> ProperType:
return self.copy_common(t, TupleGetterType(t.typ))

def visit_tuple_type(self, t: TupleType) -> ProperType:
return self.copy_common(t, TupleType(t.items, t.partial_fallback, implicit=t.implicit))

Expand Down
4 changes: 4 additions & 0 deletions mypy/erasetype.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
ParamSpecType,
PartialType,
ProperType,
TupleGetterType,
TupleType,
Type,
TypeAliasType,
Expand Down Expand Up @@ -115,6 +116,9 @@ def visit_callable_type(self, t: CallableType) -> ProperType:
def visit_overloaded(self, t: Overloaded) -> ProperType:
return t.fallback.accept(self)

def visit_tuplegetter_type(self, t: TupleGetterType) -> ProperType:
raise NotImplementedError

def visit_tuple_type(self, t: TupleType) -> ProperType:
return t.partial_fallback.accept(self)

Expand Down
4 changes: 4 additions & 0 deletions mypy/expandtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
PartialType,
ProperType,
TrivialSyntheticTypeTranslator,
TupleGetterType,
TupleType,
Type,
TypeAliasType,
Expand Down Expand Up @@ -456,6 +457,9 @@ def expand_type_tuple_with_unpack(self, typs: tuple[Type, ...]) -> list[Type]:
items.append(item.accept(self))
return items

def visit_tuplegetter_type(self, t: TupleGetterType) -> Type:
return TupleGetterType(t.typ.accept(self))

def visit_tuple_type(self, t: TupleType) -> Type:
items = self.expand_type_list_with_unpack(t.items)
if len(items) == 1:
Expand Down
4 changes: 4 additions & 0 deletions mypy/fixup.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
Overloaded,
Parameters,
ParamSpecType,
TupleGetterType,
TupleType,
TypeAliasType,
TypedDictType,
Expand Down Expand Up @@ -296,6 +297,9 @@ def visit_uninhabited_type(self, o: Any) -> None:
def visit_partial_type(self, o: Any) -> None:
raise RuntimeError("Shouldn't get here", o)

def visit_tuplegetter_type(self, tgt: TupleGetterType) -> None:
tgt.typ.accept(self)

def visit_tuple_type(self, tt: TupleType) -> None:
if tt.items:
for it in tt.items:
Expand Down
3 changes: 3 additions & 0 deletions mypy/indirection.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ def visit_overloaded(self, t: types.Overloaded) -> None:
self._visit_type_list(list(t.items))
self._visit(t.fallback)

def visit_tuplegetter_type(self, t: types.TupleGetterType) -> None:
self._visit(t.typ)

def visit_tuple_type(self, t: types.TupleType) -> None:
self._visit_type_list(t.items)
self._visit(t.partial_fallback)
Expand Down
4 changes: 4 additions & 0 deletions mypy/join.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
ParamSpecType,
PartialType,
ProperType,
TupleGetterType,
TupleType,
Type,
TypeAliasType,
Expand Down Expand Up @@ -559,6 +560,9 @@ def join_tuples(self, s: TupleType, t: TupleType) -> list[Type] | None:
items.append(join_types(fi, vi))
return items

def visit_tuplegetter_type(self, t: TupleGetterType) -> ProperType:
raise NotImplementedError

def visit_tuple_type(self, t: TupleType) -> ProperType:
# When given two fixed-length tuples:
# * If they have the same length, join their subtypes item-wise:
Expand Down
4 changes: 4 additions & 0 deletions mypy/meet.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
ParamSpecType,
PartialType,
ProperType,
TupleGetterType,
TupleType,
Type,
TypeAliasType,
Expand Down Expand Up @@ -1039,6 +1040,9 @@ def meet_tuples(self, s: TupleType, t: TupleType) -> list[Type] | None:
items.append(self.meet(fi, vi))
return items

def visit_tuplegetter_type(self, t: TupleGetterType) -> ProperType:
raise NotImplementedError

def visit_tuple_type(self, t: TupleType) -> ProperType:
if isinstance(self.s, TupleType):
items = self.meet_tuples(self.s, t)
Expand Down
3 changes: 3 additions & 0 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
ParamSpecType,
PartialType,
ProperType,
TupleGetterType,
TupleType,
Type,
TypeAliasType,
Expand Down Expand Up @@ -2664,6 +2665,8 @@ def format_literal_value(typ: LiteralType) -> str:
else:
# TODO: better disambiguate ParamSpec name clashes.
return typ.name_with_suffix()
elif isinstance(typ, TupleGetterType):
return f"TupleGetterType[{typ.typ}]"
elif isinstance(typ, TupleType):
# Prefer the name of the fallback class (if not tuple), as it's more informative.
if typ.partial_fallback.type.fullname != "builtins.tuple":
Expand Down
3 changes: 2 additions & 1 deletion mypy/semanal_namedtuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
AnyType,
CallableType,
LiteralType,
TupleGetterType,
TupleType,
Type,
TypeOfAny,
Expand Down Expand Up @@ -552,7 +553,7 @@ def add_field(
var._fullname = f"{info.fullname}.{var.name}"
info.names[var.name] = SymbolTableNode(MDEF, var)

fields = [Var(item, typ) for item, typ in zip(items, types)]
fields = [Var(item, TupleGetterType(typ)) for item, typ in zip(items, types)]
for var in fields:
add_field(var, is_property=True)
# We can't share Vars between fields and method arguments, since they
Expand Down
4 changes: 4 additions & 0 deletions mypy/semanal_typeargs.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
Instance,
Parameters,
ParamSpecType,
TupleGetterType,
TupleType,
Type,
TypeAliasType,
Expand Down Expand Up @@ -103,6 +104,9 @@ def visit_type_alias_type(self, t: TypeAliasType) -> None:
# the expansion, most likely it will result in the same kind of error.
get_proper_type(t).accept(self)

def visit_tuplegetter_type(self, t: TupleGetterType) -> None:
raise NotImplementedError

def visit_tuple_type(self, t: TupleType) -> None:
t.items = flatten_nested_tuples(t.items)
# We could also normalize Tuple[*tuple[X, ...]] -> tuple[X, ...] like in
Expand Down
4 changes: 4 additions & 0 deletions mypy/server/astdiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class level -- these are handled at attribute level (say, 'mod.Cls.method'
Parameters,
ParamSpecType,
PartialType,
TupleGetterType,
TupleType,
Type,
TypeAliasType,
Expand Down Expand Up @@ -481,6 +482,9 @@ def normalize_callable_variables(self, typ: CallableType) -> CallableType:
with state.strict_optional_set(True):
return expand_type(typ, tvmap).copy_modified(variables=tvs)

def visit_tuplegetter_type(self, typ: TupleGetterType) -> SnapshotItem:
return ("TupleGetterType", snapshot_type(typ.typ))

def visit_tuple_type(self, typ: TupleType) -> SnapshotItem:
return ("TupleType", snapshot_types(typ.items))

Expand Down
4 changes: 4 additions & 0 deletions mypy/server/astmerge.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
PlaceholderType,
RawExpressionType,
SyntheticTypeVisitor,
TupleGetterType,
TupleType,
Type,
TypeAliasType,
Expand Down Expand Up @@ -470,6 +471,9 @@ def visit_deleted_type(self, typ: DeletedType) -> None:
def visit_partial_type(self, typ: PartialType) -> None:
raise RuntimeError("Cannot handle partial type")

def visit_tuplegetter_type(self, typ: TupleGetterType) -> None:
typ.typ.accept(self)

def visit_tuple_type(self, typ: TupleType) -> None:
for item in typ.items:
item.accept(self)
Expand Down
4 changes: 4 additions & 0 deletions mypy/server/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ class 'mod.Cls'. This can also refer to an attribute inherited from a
ParamSpecType,
PartialType,
ProperType,
TupleGetterType,
TupleType,
Type,
TypeAliasType,
Expand Down Expand Up @@ -1017,6 +1018,9 @@ def visit_deleted_type(self, typ: DeletedType) -> list[str]:
def visit_partial_type(self, typ: PartialType) -> list[str]:
assert False, "Should not see a partial type here"

def visit_tuplegetter_type(self, typ: TupleGetterType) -> list[str]:
return typ.typ.accept(self)

def visit_tuple_type(self, typ: TupleType) -> list[str]:
triggers = []
for item in typ.items:
Expand Down
9 changes: 9 additions & 0 deletions mypy/subtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
ParamSpecType,
PartialType,
ProperType,
TupleGetterType,
TupleType,
Type,
TypeAliasType,
Expand Down Expand Up @@ -759,6 +760,14 @@ def visit_callable_type(self, left: CallableType) -> bool:
else:
return False

def visit_tuplegetter_type(self, left: TupleGetterType) -> bool:
right = self.right
if isinstance(right, TupleGetterType):
return left.typ == right.typ
elif isinstance(right, Instance):
return self.is_top_type(right)
return False

def visit_tuple_type(self, left: TupleType) -> bool:
right = self.right
if isinstance(right, Instance):
Expand Down
14 changes: 14 additions & 0 deletions mypy/type_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
PartialType,
PlaceholderType,
RawExpressionType,
TupleGetterType,
TupleType,
Type,
TypeAliasType,
Expand Down Expand Up @@ -114,6 +115,10 @@ def visit_callable_type(self, t: CallableType, /) -> T:
def visit_overloaded(self, t: Overloaded, /) -> T:
pass

@abstractmethod
def visit_tuplegetter_type(self, t: TupleGetterType, /) -> T:
pass

@abstractmethod
def visit_tuple_type(self, t: TupleType, /) -> T:
pass
Expand Down Expand Up @@ -260,6 +265,9 @@ def visit_callable_type(self, t: CallableType, /) -> Type:
variables=self.translate_variables(t.variables),
)

def visit_tuplegetter_type(self, t: TupleGetterType, /) -> Type:
return TupleGetterType(t.typ.accept(self))

def visit_tuple_type(self, t: TupleType, /) -> Type:
return TupleType(
self.translate_types(t.items),
Expand Down Expand Up @@ -412,6 +420,9 @@ def visit_callable_type(self, t: CallableType, /) -> T:
def visit_tuple_type(self, t: TupleType, /) -> T:
return self.query_types([t.partial_fallback] + t.items)

def visit_tuplegetter_type(self, t: TupleGetterType, /) -> T:
return self.query_types([t.typ])

def visit_typeddict_type(self, t: TypedDictType, /) -> T:
return self.query_types(t.items.values())

Expand Down Expand Up @@ -549,6 +560,9 @@ def visit_callable_type(self, t: CallableType, /) -> bool:
else:
return args and ret

def visit_tuplegetter_type(self, t: TupleGetterType, /) -> bool:
return self.default

def visit_tuple_type(self, t: TupleType, /) -> bool:
return self.query_types([t.partial_fallback] + t.items)

Expand Down
7 changes: 7 additions & 0 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
RequiredType,
SyntheticTypeVisitor,
TrivialSyntheticTypeTranslator,
TupleGetterType,
TupleType,
Type,
TypeAliasType,
Expand Down Expand Up @@ -1261,6 +1262,9 @@ def visit_overloaded(self, t: Overloaded) -> Type:
# fine to just return it as-is.
return t

def visit_tuplegetter_type(self, t: TupleGetterType) -> Type:
raise NotImplementedError

def visit_tuple_type(self, t: TupleType) -> Type:
# Types such as (t1, t2, ...) only allowed in assignment statements. They'll
# generate errors elsewhere, and Tuple[t1, t2, ...] must be used instead.
Expand Down Expand Up @@ -2637,6 +2641,9 @@ def visit_callable_type(self, t: CallableType) -> None:
self.process_types(t.arg_types)
t.ret_type.accept(self)

def visit_tuplegetter_type(self, t: TupleGetterType) -> None:
raise NotImplementedError

def visit_tuple_type(self, t: TupleType) -> None:
self.process_types(t.items)

Expand Down
Loading