Skip to content

Commit 61cfa0a

Browse files
Merge branch 'master' into patch-4
2 parents b9c8d19 + d52ce3b commit 61cfa0a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+782
-136
lines changed

mypy/checker.py

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,7 +1362,7 @@ def check_func_def(
13621362
if typ.type_is:
13631363
arg_index = 0
13641364
# For methods and classmethods, we want the second parameter
1365-
if ref_type is not None and (not defn.is_static or defn.name == "__new__"):
1365+
if ref_type is not None and defn.has_self_or_cls_argument:
13661366
arg_index = 1
13671367
if arg_index < len(typ.arg_types) and not is_subtype(
13681368
typ.type_is, typ.arg_types[arg_index]
@@ -1382,7 +1382,7 @@ def check_func_def(
13821382
isinstance(defn, FuncDef)
13831383
and ref_type is not None
13841384
and i == 0
1385-
and (not defn.is_static or defn.name == "__new__")
1385+
and defn.has_self_or_cls_argument
13861386
and typ.arg_kinds[0] not in [nodes.ARG_STAR, nodes.ARG_STAR2]
13871387
):
13881388
if defn.is_class or defn.name == "__new__":
@@ -4400,9 +4400,9 @@ def set_inferred_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
44004400
refers to the variable (lvalue). If var is None, do nothing.
44014401
"""
44024402
if var and not self.current_node_deferred:
4403-
# TODO: should we also set 'is_ready = True' here?
44044403
var.type = type
44054404
var.is_inferred = True
4405+
var.is_ready = True
44064406
if var not in self.var_decl_frames:
44074407
# Used for the hack to improve optional type inference in conditionals
44084408
self.var_decl_frames[var] = {frame.id for frame in self.binder.frames}
@@ -4412,9 +4412,23 @@ def set_inferred_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
44124412
self.inferred_attribute_types[lvalue.def_var] = type
44134413
self.store_type(lvalue, type)
44144414
p_type = get_proper_type(type)
4415-
if isinstance(p_type, CallableType) and is_node_static(p_type.definition):
4416-
# TODO: handle aliases to class methods (similarly).
4417-
var.is_staticmethod = True
4415+
definition = None
4416+
if isinstance(p_type, CallableType):
4417+
definition = p_type.definition
4418+
elif isinstance(p_type, Overloaded):
4419+
# Randomly select first item, if items are different, there will
4420+
# be an error during semantic analysis.
4421+
definition = p_type.items[0].definition
4422+
if definition:
4423+
if is_node_static(definition):
4424+
var.is_staticmethod = True
4425+
elif is_classmethod_node(definition):
4426+
var.is_classmethod = True
4427+
elif is_property(definition):
4428+
var.is_property = True
4429+
if isinstance(p_type, Overloaded):
4430+
# TODO: in theory we can have a property with a deleter only.
4431+
var.is_settable_property = True
44184432

44194433
def set_inference_error_fallback_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
44204434
"""Store best known type for variable if type inference failed.
@@ -8531,15 +8545,21 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type:
85318545
return t.copy_modified(args=[a.accept(self) for a in t.args])
85328546

85338547

8548+
def is_classmethod_node(node: Node | None) -> bool | None:
8549+
"""Find out if a node describes a classmethod."""
8550+
if isinstance(node, FuncDef):
8551+
return node.is_class
8552+
if isinstance(node, Var):
8553+
return node.is_classmethod
8554+
return None
8555+
8556+
85348557
def is_node_static(node: Node | None) -> bool | None:
85358558
"""Find out if a node describes a static function method."""
8536-
85378559
if isinstance(node, FuncDef):
85388560
return node.is_static
8539-
85408561
if isinstance(node, Var):
85418562
return node.is_staticmethod
8542-
85438563
return None
85448564

85458565

@@ -8786,6 +8806,8 @@ def is_static(func: FuncBase | Decorator) -> bool:
87868806

87878807

87888808
def is_property(defn: SymbolNode) -> bool:
8809+
if isinstance(defn, FuncDef):
8810+
return defn.is_property
87898811
if isinstance(defn, Decorator):
87908812
return defn.func.is_property
87918813
if isinstance(defn, OverloadedFuncDef):

mypy/checkexpr.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2338,10 +2338,10 @@ def apply_inferred_arguments(
23382338
# Report error if some of the variables could not be solved. In that
23392339
# case assume that all variables have type Any to avoid extra
23402340
# bogus error messages.
2341-
for i, inferred_type in enumerate(inferred_args):
2341+
for inferred_type, tv in zip(inferred_args, callee_type.variables):
23422342
if not inferred_type or has_erased_component(inferred_type):
23432343
# Could not infer a non-trivial type for a type variable.
2344-
self.msg.could_not_infer_type_arguments(callee_type, i + 1, context)
2344+
self.msg.could_not_infer_type_arguments(callee_type, tv, context)
23452345
inferred_args = [AnyType(TypeOfAny.from_error)] * len(inferred_args)
23462346
# Apply the inferred types to the function type. In this case the
23472347
# return type must be CallableType, since we give the right number of type

mypy/checkmember.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -371,8 +371,6 @@ def analyze_instance_member_access(
371371
signature, mx.self_type, method.is_class, mx.context, name, mx.msg
372372
)
373373
signature = bind_self(signature, mx.self_type, is_classmethod=method.is_class)
374-
# TODO: should we skip these steps for static methods as well?
375-
# Since generic static methods should not be allowed.
376374
typ = map_instance_to_supertype(typ, method.info)
377375
member_type = expand_type_by_instance(signature, typ)
378376
freeze_all_type_vars(member_type)
@@ -1224,8 +1222,11 @@ def analyze_class_attribute_access(
12241222
# C[int].x -> int
12251223
t = erase_typevars(expand_type_by_instance(t, isuper), {tv.id for tv in def_vars})
12261224

1227-
is_classmethod = (is_decorated and cast(Decorator, node.node).func.is_class) or (
1228-
isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_class
1225+
is_classmethod = (
1226+
(is_decorated and cast(Decorator, node.node).func.is_class)
1227+
or (isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_class)
1228+
or isinstance(node.node, Var)
1229+
and node.node.is_classmethod
12291230
)
12301231
is_staticmethod = (is_decorated and cast(Decorator, node.node).func.is_static) or (
12311232
isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_static
@@ -1237,7 +1238,12 @@ def analyze_class_attribute_access(
12371238
is_trivial_self = node.node.func.is_trivial_self and not node.node.decorators
12381239
elif isinstance(node.node, (FuncDef, OverloadedFuncDef)):
12391240
is_trivial_self = node.node.is_trivial_self
1240-
if isinstance(t, FunctionLike) and is_classmethod and not is_trivial_self:
1241+
if (
1242+
isinstance(t, FunctionLike)
1243+
and is_classmethod
1244+
and not is_trivial_self
1245+
and not t.bound()
1246+
):
12411247
t = check_self_arg(t, mx.self_type, False, mx.context, name, mx.msg)
12421248
t = add_class_tvars(
12431249
t,
@@ -1406,7 +1412,7 @@ class B(A[str]): pass
14061412
tvars = original_vars if original_vars is not None else []
14071413
if not mx.preserve_type_var_ids:
14081414
t = freshen_all_functions_type_vars(t)
1409-
if is_classmethod:
1415+
if is_classmethod and not t.is_bound:
14101416
if is_trivial_self:
14111417
t = bind_self_fast(t, mx.self_type)
14121418
else:

mypy/constraints.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,6 +1335,11 @@ def visit_tuple_type(self, template: TupleType) -> list[Constraint]:
13351335
res.extend(
13361336
infer_constraints(template_items[i], actual_items[i], self.direction)
13371337
)
1338+
res.extend(
1339+
infer_constraints(
1340+
template.partial_fallback, actual.partial_fallback, self.direction
1341+
)
1342+
)
13381343
return res
13391344
elif isinstance(actual, AnyType):
13401345
return self.infer_against_any(template.items, actual)

mypy/message_registry.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,10 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
353353
"TypeVar constraint type cannot be parametrized by type variables", codes.MISC
354354
)
355355

356+
TYPE_VAR_REDECLARED_IN_NESTED_CLASS: Final = ErrorMessage(
357+
'Type variable "{}" is bound by an outer class', codes.VALID_TYPE
358+
)
359+
356360
TYPE_ALIAS_WITH_YIELD_EXPRESSION: Final = ErrorMessage(
357361
"Yield expression cannot be used within a type alias", codes.SYNTAX
358362
)

mypy/messages.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,11 +1370,14 @@ def incompatible_type_application(
13701370
self.fail(f"Type application has too few types ({s})", context)
13711371

13721372
def could_not_infer_type_arguments(
1373-
self, callee_type: CallableType, n: int, context: Context
1373+
self, callee_type: CallableType, tv: TypeVarLikeType, context: Context
13741374
) -> None:
13751375
callee_name = callable_name(callee_type)
1376-
if callee_name is not None and n > 0:
1377-
self.fail(f"Cannot infer type argument {n} of {callee_name}", context)
1376+
if callee_name is not None:
1377+
self.fail(
1378+
f"Cannot infer value of type parameter {format_type(tv, self.options)} of {callee_name}",
1379+
context,
1380+
)
13781381
if callee_name == "<dict>":
13791382
# Invariance in key type causes more of these errors than we would want.
13801383
self.note(

mypy/nodes.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,8 @@ def __init__(self) -> None:
508508
self.info = FUNC_NO_INFO
509509
self.is_property = False
510510
self.is_class = False
511+
# Is this a `@staticmethod` (explicit or implicit)?
512+
# Note: use has_self_or_cls_argument to check if there is `self` or `cls` argument
511513
self.is_static = False
512514
self.is_final = False
513515
self.is_explicit_override = False
@@ -524,6 +526,15 @@ def name(self) -> str:
524526
def fullname(self) -> str:
525527
return self._fullname
526528

529+
@property
530+
def has_self_or_cls_argument(self) -> bool:
531+
"""If used as a method, does it have an argument for method binding (`self`, `cls`)?
532+
533+
This is true for `__new__` even though `__new__` does not undergo method binding,
534+
because we still usually assume that `cls` corresponds to the enclosing class.
535+
"""
536+
return not self.is_static or self.name == "__new__"
537+
527538

528539
OverloadPart: _TypeAlias = Union["FuncDef", "Decorator"]
529540

mypy/semanal.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,7 +1069,7 @@ def prepare_method_signature(self, func: FuncDef, info: TypeInfo, has_self_type:
10691069
functype = func.type
10701070
if func.name == "__new__":
10711071
func.is_static = True
1072-
if not func.is_static or func.name == "__new__":
1072+
if func.has_self_or_cls_argument:
10731073
if func.name in ["__init_subclass__", "__class_getitem__"]:
10741074
func.is_class = True
10751075
if not func.arguments:
@@ -1602,7 +1602,7 @@ def analyze_function_body(self, defn: FuncItem) -> None:
16021602
# The first argument of a non-static, non-class method is like 'self'
16031603
# (though the name could be different), having the enclosing class's
16041604
# instance type.
1605-
if is_method and (not defn.is_static or defn.name == "__new__") and defn.arguments:
1605+
if is_method and defn.has_self_or_cls_argument and defn.arguments:
16061606
if not defn.is_class:
16071607
defn.arguments[0].variable.is_self = True
16081608
else:
@@ -2374,6 +2374,14 @@ def tvar_defs_from_tvars(
23742374
tvar_expr.default = tvar_expr.default.accept(
23752375
TypeVarDefaultTranslator(self, tvar_expr.name, context)
23762376
)
2377+
# PEP-695 type variables that are redeclared in an inner scope are warned
2378+
# about elsewhere.
2379+
if not tvar_expr.is_new_style and not self.tvar_scope.allow_binding(
2380+
tvar_expr.fullname
2381+
):
2382+
self.fail(
2383+
message_registry.TYPE_VAR_REDECLARED_IN_NESTED_CLASS.format(name), context
2384+
)
23772385
tvar_def = self.tvar_scope.bind_new(name, tvar_expr)
23782386
if last_tvar_name_with_default is not None and not tvar_def.has_default():
23792387
self.msg.tvar_without_default_type(

mypy/suggestions.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,18 @@ def is_implicit_any(typ: Type) -> bool:
229229
return isinstance(typ, AnyType) and not is_explicit_any(typ)
230230

231231

232+
def _arg_accepts_function(typ: ProperType) -> bool:
233+
return (
234+
# TypeVar / Callable
235+
isinstance(typ, (TypeVarType, CallableType))
236+
or
237+
# Protocol with __call__
238+
isinstance(typ, Instance)
239+
and typ.type.is_protocol
240+
and typ.type.get_method("__call__") is not None
241+
)
242+
243+
232244
class SuggestionEngine:
233245
"""Engine for finding call sites and suggesting signatures."""
234246

@@ -474,7 +486,7 @@ def get_suggestion(self, mod: str, node: FuncDef) -> PyAnnotateSignature:
474486
if self.no_errors and orig_errors:
475487
raise SuggestionFailure("Function does not typecheck.")
476488

477-
is_method = bool(node.info) and not node.is_static
489+
is_method = bool(node.info) and node.has_self_or_cls_argument
478490

479491
with state.strict_optional_set(graph[mod].options.strict_optional):
480492
guesses = self.get_guesses(
@@ -658,7 +670,7 @@ def extract_from_decorator(self, node: Decorator) -> FuncDef | None:
658670
for ct in typ.items:
659671
if not (
660672
len(ct.arg_types) == 1
661-
and isinstance(ct.arg_types[0], TypeVarType)
673+
and _arg_accepts_function(get_proper_type(ct.arg_types[0]))
662674
and ct.arg_types[0] == ct.ret_type
663675
):
664676
return None

mypy/typeanal.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1844,11 +1844,8 @@ def bind_function_type_variables(
18441844
defs = []
18451845
for name, tvar in typevars:
18461846
if not self.tvar_scope.allow_binding(tvar.fullname):
1847-
self.fail(
1848-
f'Type variable "{name}" is bound by an outer class',
1849-
defn,
1850-
code=codes.VALID_TYPE,
1851-
)
1847+
err_msg = message_registry.TYPE_VAR_REDECLARED_IN_NESTED_CLASS.format(name)
1848+
self.fail(err_msg.value, defn, code=err_msg.code)
18521849
binding = self.tvar_scope.bind_new(name, tvar)
18531850
defs.append(binding)
18541851

0 commit comments

Comments
 (0)