Skip to content

Commit c1cee0b

Browse files
introduced helper function to map type to protocol
1 parent dfad788 commit c1cee0b

File tree

2 files changed

+23
-35
lines changed

2 files changed

+23
-35
lines changed

mypy/checkexpr.py

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from mypy.checkmember import analyze_member_access, has_operator
2020
from mypy.checkstrformat import StringFormatterChecker
2121
from mypy.constant_fold import constant_fold_expr
22-
from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, infer_constraints
22+
from mypy.constraints import SUBTYPE_OF
2323
from mypy.erasetype import erase_type, remove_instance_last_known_values, replace_meta_vars
2424
from mypy.errors import ErrorInfo, ErrorWatcher, report_internal_error
2525
from mypy.expandtype import (
@@ -31,7 +31,7 @@
3131
from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type
3232
from mypy.infer import ArgumentInferContext, infer_function_type_arguments, infer_type_arguments
3333
from mypy.literals import literal
34-
from mypy.maptype import map_instance_to_supertype, map_type_to_instance
34+
from mypy.maptype import as_type, map_instance_to_supertype
3535
from mypy.meet import is_overlapping_types, narrow_declared_type
3636
from mypy.message_registry import ErrorMessage
3737
from mypy.messages import MessageBuilder, format_type
@@ -119,7 +119,6 @@
119119
Plugin,
120120
)
121121
from mypy.semanal_enum import ENUM_BASES
122-
from mypy.solve import solve_constraints
123122
from mypy.state import state
124123
from mypy.subtypes import (
125124
covers_at_runtime,
@@ -6165,24 +6164,7 @@ def is_valid_keyword_var_arg(self, typ: Type) -> bool:
61656164
"_typeshed.SupportsKeysAndGetItem", [T, AnyType(TypeOfAny.special_form)]
61666165
)
61676166

6168-
return map_type_to_instance(typ, template) is not None
6169-
6170-
# infer constraints and solve
6171-
constraints: list[Constraint] = [
6172-
# solve_constraints seems to completely ignore upper bounds.
6173-
# So we need to include it manually.
6174-
Constraint(T, SUBTYPE_OF, T.upper_bound),
6175-
*infer_constraints(template, typ, SUPERTYPE_OF),
6176-
]
6177-
solution, _ = solve_constraints([T], constraints)
6178-
assert len(solution) == 1
6179-
6180-
return solution[0] is not None and is_subtype(
6181-
typ,
6182-
self.chk.named_generic_type(
6183-
"_typeshed.SupportsKeysAndGetItem", [solution[0], AnyType(TypeOfAny.special_form)]
6184-
),
6185-
)
6167+
return as_type(typ, SUBTYPE_OF, template) is not None
61866168

61876169
def not_ready_callback(self, name: str, context: Context) -> None:
61886170
"""Called when we can't infer the type of a variable because it's not ready yet.

mypy/maptype.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,49 @@
11
from __future__ import annotations
22

3+
from typing import cast
4+
35
from mypy.expandtype import expand_type, expand_type_by_instance
46
from mypy.nodes import TypeInfo
57
from mypy.types import AnyType, Instance, TupleType, Type, TypeOfAny, has_type_vars
68

79

8-
def map_type_to_instance(typ: Type, target: Instance) -> Instance | None:
9-
"""Attempt to map `typ` to an Instance of the same class as `target`
10-
11-
Examples:
12-
(list[int], Iterable[T]) -> Iterable[int]
13-
(list[list[int]], Iterable[list[T]]) -> Iterable[list[int]]
14-
(dict[str, int], Mapping[K, int]) -> Mapping[str, int]
15-
(list[int], Mapping[K, V]) -> None
10+
def as_type(typ: Type, direction: int, target: Type) -> Type | None:
11+
"""Attempts to solve type variables in `target` so that `typ` is a subtype/supertype of
12+
the resulting type.
1613
1714
Args:
1815
typ: The type to map from.
16+
direction: One of SUBTYPE_OF or SUPERTYPE_OF
1917
target: The target instance type to map to.
2018
2119
Returns:
2220
None: if the mapping is not possible.
23-
Instance: the mapped instance type if the mapping is possible.
21+
Type: the mapped type if the mapping is possible.
22+
23+
Examples:
24+
(list[int], Iterable[T]) -> Iterable[int]
25+
(list[list[int]], Iterable[list[T]]) -> Iterable[list[int]]
26+
(dict[str, int], Mapping[K, int]) -> Mapping[str, int]
27+
(list[int], Mapping[K, V]) -> None
2428
"""
2529
from mypy.subtypes import is_subtype
2630
from mypy.typeops import get_all_type_vars
2731

2832
# 1. get type vars of target
2933
tvars = get_all_type_vars(target)
3034

31-
# fast path: if no type vars,
35+
# fast path: if no type vars, just check subtype
3236
if not tvars:
3337
return target if is_subtype(typ, target) else None
3438

35-
from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, infer_constraints
39+
from mypy.constraints import SUBTYPE_OF, Constraint, infer_constraints
3640
from mypy.solve import solve_constraints
3741

3842
# 2. determine constraints
39-
constraints: list[Constraint] = infer_constraints(target, typ, SUPERTYPE_OF)
43+
constraints: list[Constraint] = infer_constraints(target, typ, not direction)
4044
for tvar in tvars:
45+
# need to manually include these because solve_constraints ignores them
46+
# apparently
4147
constraints.append(Constraint(tvar, SUBTYPE_OF, tvar.upper_bound))
4248

4349
# 3. solve constraints
@@ -46,8 +52,8 @@ def map_type_to_instance(typ: Type, target: Instance) -> Instance | None:
4652
if None in solution:
4753
return None
4854

49-
# 4. build resulting Instance by substituting typevars with solution
50-
env = {tvar.id: sol for tvar, sol in zip(tvars, solution)}
55+
# 4. build resulting Type by substituting type vars with solution
56+
env = {tvar.id: s for tvar, s in zip(tvars, cast("list[Type]", solution))}
5157
target = expand_type(target, env)
5258
return target if is_subtype(typ, target) else None
5359

0 commit comments

Comments
 (0)