Skip to content

Commit a55635d

Browse files
authored
Retrieve class or static info from node instead of type (#5261)
Fixes #5259 This partially undo changes made in https://github.com/python/mypy/pull/5224/files. It is more safe to retrieve class and static flags directly from nodes, since callable types have often definition not set (especially after de-serialization). Also being class or static method is not really a property of type itself, it is rather an "access flag" for a given name in symbol table (similar to how a variable can be settable).
1 parent aa82bd5 commit a55635d

File tree

3 files changed

+35
-24
lines changed

3 files changed

+35
-24
lines changed

mypy/checker.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,22 +1221,32 @@ def check_method_override_for_base_with_name(
12211221
# Construct the type of the overriding method.
12221222
if isinstance(defn, FuncBase):
12231223
typ = self.function_type(defn) # type: Type
1224+
override_class_or_static = defn.is_class or defn.is_static
12241225
else:
12251226
assert defn.var.is_ready
12261227
assert defn.var.type is not None
12271228
typ = defn.var.type
1229+
override_class_or_static = defn.func.is_class or defn.func.is_static
12281230
if isinstance(typ, FunctionLike) and not is_static(context):
12291231
typ = bind_self(typ, self.scope.active_self_type())
12301232
# Map the overridden method type to subtype context so that
12311233
# it can be checked for compatibility.
12321234
original_type = base_attr.type
1235+
original_node = base_attr.node
12331236
if original_type is None:
1234-
if isinstance(base_attr.node, FuncDef):
1235-
original_type = self.function_type(base_attr.node)
1236-
elif isinstance(base_attr.node, Decorator):
1237-
original_type = self.function_type(base_attr.node.func)
1237+
if isinstance(original_node, FuncBase):
1238+
original_type = self.function_type(original_node)
1239+
elif isinstance(original_node, Decorator):
1240+
original_type = self.function_type(original_node.func)
12381241
else:
12391242
assert False, str(base_attr.node)
1243+
if isinstance(original_node, FuncBase):
1244+
original_class_or_static = original_node.is_class or original_node.is_static
1245+
elif isinstance(original_node, Decorator):
1246+
fdef = original_node.func
1247+
original_class_or_static = fdef.is_class or fdef.is_static
1248+
else:
1249+
original_class_or_static = False # a variable can't be class or static
12401250
if isinstance(original_type, AnyType) or isinstance(typ, AnyType):
12411251
pass
12421252
elif isinstance(original_type, FunctionLike) and isinstance(typ, FunctionLike):
@@ -1253,6 +1263,8 @@ def check_method_override_for_base_with_name(
12531263
defn.name(),
12541264
name,
12551265
base.name(),
1266+
original_class_or_static,
1267+
override_class_or_static,
12561268
context)
12571269
elif is_equivalent(original_type, typ):
12581270
# Assume invariance for a non-callable attribute here. Note
@@ -1267,6 +1279,8 @@ def check_method_override_for_base_with_name(
12671279

12681280
def check_override(self, override: FunctionLike, original: FunctionLike,
12691281
name: str, name_in_super: str, supertype: str,
1282+
original_class_or_static: bool,
1283+
override_class_or_static: bool,
12701284
node: Context) -> None:
12711285
"""Check a method override with given signatures.
12721286
@@ -1289,8 +1303,7 @@ def check_override(self, override: FunctionLike, original: FunctionLike,
12891303
fail = True
12901304

12911305
if isinstance(original, FunctionLike) and isinstance(override, FunctionLike):
1292-
if ((original.is_classmethod() or original.is_staticmethod()) and
1293-
not (override.is_classmethod() or override.is_staticmethod())):
1306+
if original_class_or_static and not override_class_or_static:
12941307
fail = True
12951308

12961309
if fail:

mypy/types.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -645,12 +645,6 @@ def with_name(self, name: str) -> 'FunctionLike': pass
645645
@abstractmethod
646646
def get_name(self) -> Optional[str]: pass
647647

648-
@abstractmethod
649-
def is_classmethod(self) -> bool: pass
650-
651-
@abstractmethod
652-
def is_staticmethod(self) -> bool: pass
653-
654648

655649
FormalArgument = NamedTuple('FormalArgument', [
656650
('name', Optional[str]),
@@ -834,12 +828,6 @@ def with_name(self, name: str) -> 'CallableType':
834828
def get_name(self) -> Optional[str]:
835829
return self.name
836830

837-
def is_classmethod(self) -> bool:
838-
return isinstance(self.definition, FuncBase) and self.definition.is_class
839-
840-
def is_staticmethod(self) -> bool:
841-
return isinstance(self.definition, FuncBase) and self.definition.is_static
842-
843831
def max_fixed_args(self) -> int:
844832
n = len(self.arg_types)
845833
if self.is_var_arg:
@@ -1058,12 +1046,6 @@ def with_name(self, name: str) -> 'Overloaded':
10581046
def get_name(self) -> Optional[str]:
10591047
return self._items[0].name
10601048

1061-
def is_classmethod(self) -> bool:
1062-
return self._items[0].is_classmethod()
1063-
1064-
def is_staticmethod(self) -> bool:
1065-
return self._items[0].is_staticmethod()
1066-
10671049
def accept(self, visitor: 'TypeVisitor[T]') -> T:
10681050
return visitor.visit_overloaded(self)
10691051

test-data/unit/check-classes.test

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4376,3 +4376,19 @@ class C1(object):
43764376
main:4: error: Revealed local types are:
43774377
main:4: error: t: builtins.str
43784378
main:4: error: y: builtins.float
4379+
4380+
[case testClassMethodOverride]
4381+
from typing import Callable, Any
4382+
4383+
def deco(f: Callable[..., Any]) -> Callable[..., Any]: ...
4384+
4385+
class B:
4386+
@classmethod
4387+
def meth(cls, x: int) -> int: ...
4388+
4389+
class C(B):
4390+
@classmethod
4391+
@deco
4392+
def meth(cls, x: int) -> int: ...
4393+
[builtins fixtures/classmethod.pyi]
4394+
[out]

0 commit comments

Comments
 (0)