|
19 | 19 | from mypy.checkmember import analyze_member_access, has_operator |
20 | 20 | from mypy.checkstrformat import StringFormatterChecker |
21 | 21 | from mypy.constant_fold import constant_fold_expr |
| 22 | +from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, infer_constraints |
22 | 23 | from mypy.erasetype import erase_type, remove_instance_last_known_values, replace_meta_vars |
23 | 24 | from mypy.errors import ErrorInfo, ErrorWatcher, report_internal_error |
24 | 25 | from mypy.expandtype import ( |
|
118 | 119 | Plugin, |
119 | 120 | ) |
120 | 121 | from mypy.semanal_enum import ENUM_BASES |
| 122 | +from mypy.solve import solve_constraints |
121 | 123 | from mypy.state import state |
122 | 124 | from mypy.subtypes import ( |
123 | 125 | covers_at_runtime, |
@@ -6143,28 +6145,41 @@ def is_valid_var_arg(self, typ: Type) -> bool: |
6143 | 6145 | def is_valid_keyword_var_arg(self, typ: Type) -> bool: |
6144 | 6146 | """Is a type valid as a **kwargs argument?""" |
6145 | 6147 | typ = get_proper_type(typ) |
6146 | | - return ( |
6147 | | - ( |
6148 | | - # This is a little ad hoc, ideally we would have a map_instance_to_supertype |
6149 | | - # that worked for protocols |
6150 | | - isinstance(typ, Instance) |
6151 | | - and typ.type.fullname == "builtins.dict" |
6152 | | - and is_subtype(typ.args[0], self.named_type("builtins.str")) |
6153 | | - ) |
6154 | | - or isinstance(typ, ParamSpecType) |
6155 | | - or is_subtype( |
6156 | | - typ, |
6157 | | - self.chk.named_generic_type( |
6158 | | - "_typeshed.SupportsKeysAndGetItem", |
6159 | | - [self.named_type("builtins.str"), AnyType(TypeOfAny.special_form)], |
6160 | | - ), |
6161 | | - ) |
6162 | | - or is_subtype( |
6163 | | - typ, |
6164 | | - self.chk.named_generic_type( |
6165 | | - "_typeshed.SupportsKeysAndGetItem", [UninhabitedType(), UninhabitedType()] |
6166 | | - ), |
6167 | | - ) |
| 6148 | + if isinstance(typ, ParamSpecType | AnyType): |
| 6149 | + return True |
| 6150 | + |
| 6151 | + # Check if 'typ' is a SupportsKeysAndGetItem[T, Any] for some T <: str |
| 6152 | + # Note: is_subtype(typ, SupportsKeysAndGetItem[str, Any])` is too harsh |
| 6153 | + # since SupportsKeysAndGetItem is invariant in the key type parameter. |
| 6154 | + |
| 6155 | + # create a TypeVar and template type |
| 6156 | + T = TypeVarType( |
| 6157 | + "T", |
| 6158 | + "T", |
| 6159 | + id=TypeVarId(-1, namespace="<kwargs>"), |
| 6160 | + values=[], |
| 6161 | + upper_bound=self.named_type("builtins.str"), |
| 6162 | + default=AnyType(TypeOfAny.from_omitted_generics), |
| 6163 | + ) |
| 6164 | + template = self.chk.named_generic_type( |
| 6165 | + "_typeshed.SupportsKeysAndGetItem", [T, AnyType(TypeOfAny.special_form)] |
| 6166 | + ) |
| 6167 | + |
| 6168 | + # infer constraints and solve |
| 6169 | + constraints: list[Constraint] = [ |
| 6170 | + # solve_constraints seems to completely ignore upper bounds. |
| 6171 | + # So we need to include it manually. |
| 6172 | + Constraint(T, SUBTYPE_OF, T.upper_bound), |
| 6173 | + *infer_constraints(template, typ, SUPERTYPE_OF), |
| 6174 | + ] |
| 6175 | + solution, _ = solve_constraints([T], constraints) |
| 6176 | + assert len(solution) == 1 |
| 6177 | + |
| 6178 | + return solution[0] is not None and is_subtype( |
| 6179 | + typ, |
| 6180 | + self.chk.named_generic_type( |
| 6181 | + "_typeshed.SupportsKeysAndGetItem", [solution[0], AnyType(TypeOfAny.special_form)] |
| 6182 | + ), |
6168 | 6183 | ) |
6169 | 6184 |
|
6170 | 6185 | def not_ready_callback(self, name: str, context: Context) -> None: |
|
0 commit comments