Skip to content

Commit 25b83ea

Browse files
authored
Refactor: move MRO calculation to a new module (#6133)
1 parent d5e0f3f commit 25b83ea

File tree

3 files changed

+64
-58
lines changed

3 files changed

+64
-58
lines changed

mypy/checker.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@
5050
from mypy.constraints import SUPERTYPE_OF
5151
from mypy.maptype import map_instance_to_supertype
5252
from mypy.typevars import fill_typevars, has_no_typevars
53-
from mypy.semanal import set_callable_name, refers_to_fullname, calculate_mro
53+
from mypy.semanal import set_callable_name, refers_to_fullname
54+
from mypy.mro import calculate_mro
5455
from mypy.erasetype import erase_typevars
5556
from mypy.expandtype import expand_type, expand_type_by_instance
5657
from mypy.visitor import NodeVisitor

mypy/mro.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from typing import Optional, Callable, List
2+
3+
from mypy.nodes import TypeInfo
4+
from mypy.types import Instance
5+
from mypy.typestate import TypeState
6+
7+
8+
def calculate_mro(info: TypeInfo, obj_type: Optional[Callable[[], Instance]] = None) -> None:
9+
"""Calculate and set mro (method resolution order).
10+
11+
Raise MroError if cannot determine mro.
12+
"""
13+
mro = linearize_hierarchy(info, obj_type)
14+
assert mro, "Could not produce a MRO at all for %s" % (info,)
15+
info.mro = mro
16+
# The property of falling back to Any is inherited.
17+
info.fallback_to_any = any(baseinfo.fallback_to_any for baseinfo in info.mro)
18+
TypeState.reset_all_subtype_caches_for(info)
19+
20+
21+
class MroError(Exception):
22+
"""Raised if a consistent mro cannot be determined for a class."""
23+
24+
25+
def linearize_hierarchy(info: TypeInfo,
26+
obj_type: Optional[Callable[[], Instance]] = None) -> List[TypeInfo]:
27+
# TODO describe
28+
if info.mro:
29+
return info.mro
30+
bases = info.direct_base_classes()
31+
if (not bases and info.fullname() != 'builtins.object' and
32+
obj_type is not None):
33+
# Second pass in import cycle, add a dummy `object` base class,
34+
# otherwise MRO calculation may spuriously fail.
35+
# MRO will be re-calculated for real in the third pass.
36+
bases = [obj_type().type]
37+
lin_bases = []
38+
for base in bases:
39+
assert base is not None, "Cannot linearize bases for %s %s" % (info.fullname(), bases)
40+
lin_bases.append(linearize_hierarchy(base, obj_type))
41+
lin_bases.append(bases)
42+
return [info] + merge(lin_bases)
43+
44+
45+
def merge(seqs: List[List[TypeInfo]]) -> List[TypeInfo]:
46+
seqs = [s[:] for s in seqs]
47+
result = [] # type: List[TypeInfo]
48+
while True:
49+
seqs = [s for s in seqs if s]
50+
if not seqs:
51+
return result
52+
for seq in seqs:
53+
head = seq[0]
54+
if not [s for s in seqs if head in s[1:]]:
55+
break
56+
else:
57+
raise MroError()
58+
result.append(head)
59+
for s in seqs:
60+
if s[0] is head:
61+
del s[0]

mypy/semanal.py

Lines changed: 1 addition & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@
9393
infer_reachability_of_if_statement, infer_condition_value, ALWAYS_FALSE, ALWAYS_TRUE,
9494
MYPY_TRUE, MYPY_FALSE
9595
)
96-
from mypy.typestate import TypeState
96+
from mypy.mro import calculate_mro, MroError
9797

9898
MYPY = False
9999
if MYPY:
@@ -3732,62 +3732,6 @@ def refers_to_class_or_function(node: Expression) -> bool:
37323732
isinstance(node.node, (TypeInfo, FuncDef, OverloadedFuncDef)))
37333733

37343734

3735-
def calculate_mro(info: TypeInfo, obj_type: Optional[Callable[[], Instance]] = None) -> None:
3736-
"""Calculate and set mro (method resolution order).
3737-
3738-
Raise MroError if cannot determine mro.
3739-
"""
3740-
mro = linearize_hierarchy(info, obj_type)
3741-
assert mro, "Could not produce a MRO at all for %s" % (info,)
3742-
info.mro = mro
3743-
# The property of falling back to Any is inherited.
3744-
info.fallback_to_any = any(baseinfo.fallback_to_any for baseinfo in info.mro)
3745-
TypeState.reset_all_subtype_caches_for(info)
3746-
3747-
3748-
class MroError(Exception):
3749-
"""Raised if a consistent mro cannot be determined for a class."""
3750-
3751-
3752-
def linearize_hierarchy(info: TypeInfo,
3753-
obj_type: Optional[Callable[[], Instance]] = None) -> List[TypeInfo]:
3754-
# TODO describe
3755-
if info.mro:
3756-
return info.mro
3757-
bases = info.direct_base_classes()
3758-
if (not bases and info.fullname() != 'builtins.object' and
3759-
obj_type is not None):
3760-
# Second pass in import cycle, add a dummy `object` base class,
3761-
# otherwise MRO calculation may spuriously fail.
3762-
# MRO will be re-calculated for real in the third pass.
3763-
bases = [obj_type().type]
3764-
lin_bases = []
3765-
for base in bases:
3766-
assert base is not None, "Cannot linearize bases for %s %s" % (info.fullname(), bases)
3767-
lin_bases.append(linearize_hierarchy(base, obj_type))
3768-
lin_bases.append(bases)
3769-
return [info] + merge(lin_bases)
3770-
3771-
3772-
def merge(seqs: List[List[TypeInfo]]) -> List[TypeInfo]:
3773-
seqs = [s[:] for s in seqs]
3774-
result = [] # type: List[TypeInfo]
3775-
while True:
3776-
seqs = [s for s in seqs if s]
3777-
if not seqs:
3778-
return result
3779-
for seq in seqs:
3780-
head = seq[0]
3781-
if not [s for s in seqs if head in s[1:]]:
3782-
break
3783-
else:
3784-
raise MroError()
3785-
result.append(head)
3786-
for s in seqs:
3787-
if s[0] is head:
3788-
del s[0]
3789-
3790-
37913735
def find_duplicate(list: List[T]) -> Optional[T]:
37923736
"""If the list has duplicates, return one of the duplicates.
37933737

0 commit comments

Comments
 (0)