| from typing import Optional, Callable, List |
| |
| from mypy.nodes import TypeInfo |
| from mypy.types import Instance |
| from mypy.typestate import TypeState |
| |
| |
| def calculate_mro(info: TypeInfo, obj_type: Optional[Callable[[], Instance]] = None) -> None: |
| """Calculate and set mro (method resolution order). |
| |
| Raise MroError if cannot determine mro. |
| """ |
| mro = linearize_hierarchy(info, obj_type) |
| assert mro, "Could not produce a MRO at all for %s" % (info,) |
| info.mro = mro |
| # The property of falling back to Any is inherited. |
| info.fallback_to_any = any(baseinfo.fallback_to_any for baseinfo in info.mro) |
| TypeState.reset_all_subtype_caches_for(info) |
| |
| |
| class MroError(Exception): |
| """Raised if a consistent mro cannot be determined for a class.""" |
| |
| |
| def linearize_hierarchy(info: TypeInfo, |
| obj_type: Optional[Callable[[], Instance]] = None) -> List[TypeInfo]: |
| # TODO describe |
| if info.mro: |
| return info.mro |
| bases = info.direct_base_classes() |
| if (not bases and info.fullname() != 'builtins.object' and |
| obj_type is not None): |
| # Second pass in import cycle, add a dummy `object` base class, |
| # otherwise MRO calculation may spuriously fail. |
| # MRO will be re-calculated for real in the third pass. |
| bases = [obj_type().type] |
| lin_bases = [] |
| for base in bases: |
| assert base is not None, "Cannot linearize bases for %s %s" % (info.fullname(), bases) |
| lin_bases.append(linearize_hierarchy(base, obj_type)) |
| lin_bases.append(bases) |
| return [info] + merge(lin_bases) |
| |
| |
| def merge(seqs: List[List[TypeInfo]]) -> List[TypeInfo]: |
| seqs = [s[:] for s in seqs] |
| result = [] # type: List[TypeInfo] |
| while True: |
| seqs = [s for s in seqs if s] |
| if not seqs: |
| return result |
| for seq in seqs: |
| head = seq[0] |
| if not [s for s in seqs if head in s[1:]]: |
| break |
| else: |
| raise MroError() |
| result.append(head) |
| for s in seqs: |
| if s[0] is head: |
| del s[0] |