Skip to content

Commit 2482d0b

Browse files
authored
Add a SYMBOL_FUNCBASE_TYPES constant to avoid multiple-inheritance bug (#6481)
A mypy bug (#3603) causes mypy to think that SymbolNodes cannot be FuncBases, which causes mypy-mypyc to crash in code that tests that. Currently we work around this in a couple gross ways (like turning the SymbolNode into Any). Work around it instead by testing directly against a tuple of the subtypes instead of FuncBase itself. This is the better fix promised in #6480.
1 parent d87e626 commit 2482d0b

File tree

3 files changed

+27
-17
lines changed

3 files changed

+27
-17
lines changed

mypy/checkmember.py

+6-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Type checking of attribute access"""
22

3-
from typing import cast, Callable, List, Optional, TypeVar, Any
3+
from typing import cast, Callable, List, Optional, TypeVar
44

55
from mypy.types import (
66
Type, Instance, AnyType, TupleType, TypedDictType, CallableType, FunctionLike, TypeVarDef,
@@ -10,7 +10,7 @@
1010
from mypy.nodes import (
1111
TypeInfo, FuncBase, Var, FuncDef, SymbolNode, Context, MypyFile, TypeVarExpr,
1212
ARG_POS, ARG_STAR, ARG_STAR2, Decorator, OverloadedFuncDef, TypeAlias, TempNode,
13-
is_final_node
13+
is_final_node, SYMBOL_FUNCBASE_TYPES,
1414
)
1515
from mypy.messages import MessageBuilder
1616
from mypy.maptype import map_instance_to_supertype
@@ -624,12 +624,10 @@ def analyze_class_attribute_access(itype: Instance,
624624
return mx.chk.handle_partial_var_type(t, mx.is_lvalue, symnode, mx.context)
625625

626626
# Find the class where method/variable was defined.
627-
# mypyc hack to workaround mypy misunderstanding multiple inheritance (#3603)
628-
node_node = node.node # type: Any
629-
if isinstance(node_node, Decorator):
630-
super_info = node_node.var.info # type: Optional[TypeInfo]
631-
elif isinstance(node_node, (Var, FuncBase)):
632-
super_info = node_node.info
627+
if isinstance(node.node, Decorator):
628+
super_info = node.node.var.info # type: Optional[TypeInfo]
629+
elif isinstance(node.node, (Var, SYMBOL_FUNCBASE_TYPES)):
630+
super_info = node.node.info
633631
else:
634632
super_info = None
635633

mypy/nodes.py

+18-5
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,18 @@ def __str__(self) -> str:
403403

404404

405405
class FuncBase(Node):
406-
"""Abstract base class for function-like nodes"""
406+
"""Abstract base class for function-like nodes.
407+
408+
N.B: Although this has SymbolNode subclasses (FuncDef,
409+
OverloadedFuncDef), avoid calling isinstance(..., FuncBase) on
410+
something that is typed as SymbolNode. This is to work around
411+
mypy bug #3603, in which mypy doesn't understand multiple
412+
inheritance very well, and will assume that a SymbolNode
413+
cannot be a FuncBase.
414+
415+
Instead, test against SYMBOL_FUNCBASE_TYPES, which enumerates
416+
SymbolNode subclasses that are also FuncBase subclasses.
417+
"""
407418

408419
__slots__ = ('type',
409420
'unanalyzed_type',
@@ -668,6 +679,11 @@ def deserialize(cls, data: JsonDict) -> 'FuncDef':
668679
return ret
669680

670681

682+
# All types that are both SymbolNodes and FuncBases. See the FuncBase
683+
# docstring for the rationale.
684+
SYMBOL_FUNCBASE_TYPES = (OverloadedFuncDef, FuncDef)
685+
686+
671687
class Decorator(SymbolNode, Statement):
672688
"""A decorated function.
673689
@@ -2857,10 +2873,7 @@ def fullname(self) -> Optional[str]:
28572873
@property
28582874
def type(self) -> 'Optional[mypy.types.Type]':
28592875
node = self.node
2860-
if (isinstance(node, Var) and node.type is not None):
2861-
return node.type
2862-
# mypy thinks this branch is unreachable but it is wrong (#3603)
2863-
elif (isinstance(node, FuncBase) and node.type is not None):
2876+
if isinstance(node, (Var, SYMBOL_FUNCBASE_TYPES)) and node.type is not None:
28642877
return node.type
28652878
elif isinstance(node, Decorator):
28662879
return node.var.type

mypy/plugins/common.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import List, Optional, Any
22

33
from mypy.nodes import (
4-
ARG_POS, MDEF, Argument, Block, CallExpr, Expression, FuncBase,
4+
ARG_POS, MDEF, Argument, Block, CallExpr, Expression, FuncBase, SYMBOL_FUNCBASE_TYPES,
55
FuncDef, PassStmt, RefExpr, SymbolTableNode, Var, StrExpr,
66
)
77
from mypy.plugin import ClassDefContext
@@ -51,9 +51,8 @@ def _get_argument(call: CallExpr, name: str) -> Optional[Expression]:
5151
return None
5252

5353
callee_type = None
54-
# mypyc hack to workaround mypy misunderstanding multiple inheritance (#3603)
55-
callee_node = call.callee.node # type: Any
56-
if (isinstance(callee_node, (Var, FuncBase))
54+
callee_node = call.callee.node
55+
if (isinstance(callee_node, (Var, SYMBOL_FUNCBASE_TYPES))
5756
and callee_node.type):
5857
callee_node_type = callee_node.type
5958
if isinstance(callee_node_type, Overloaded):

0 commit comments

Comments
 (0)