| from __future__ import annotations |
| |
| from typing import Callable |
| |
| from mypy.nodes import TypeInfo |
| from mypy.types import Instance |
| from mypy.typestate import type_state |
| |
| |
| def calculate_mro(info: TypeInfo, obj_type: Callable[[], Instance] | None = None) -> None: |
| """Calculate and set mro (method resolution order). |
| |
| Raise MroError if cannot determine mro. |
| """ |
| mro = linearize_hierarchy(info, obj_type) |
| assert mro, f"Could not produce a MRO at all for {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) |
| type_state.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: Callable[[], Instance] | None = 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: |
| # Probably an error, add a dummy `object` base class, |
| # otherwise MRO calculation may spuriously fail. |
| bases = [obj_type().type] |
| lin_bases = [] |
| for base in bases: |
| assert base is not None, f"Cannot linearize bases for {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.copy() for s in seqs] |
| result: 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] |