Skip to content

Commit

Permalink
Report on too-convoluted interface/implementation hierarchies
Browse files Browse the repository at this point in the history
  • Loading branch information
kedder committed Jan 13, 2019
1 parent a5e6b1b commit 42181ae
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 2 deletions.
10 changes: 8 additions & 2 deletions src/mypy_zope/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
ClassDefContext, SymbolTableNode
)
from mypy.semanal import SemanticAnalyzerPass2
from mypy.mro import merge
from mypy.mro import merge, MroError
from mypy.options import Options

from mypy.nodes import (
Expand Down Expand Up @@ -329,7 +329,13 @@ def _analyze_implementation(self, cls: ClassDef, iface_exprs: List[str],
self.log(f"Adding {iface_expr} to MRO of {info.fullname()}")
seqs.append(cast(TypeInfo, stn.node).mro)

info.mro = merge(seqs)
try:
info.mro = merge(seqs)
except MroError:
hierarchies = [[i.fullname() for i in s] for s in seqs]
api.fail(f"Unable to calculate a consistent MRO: cannot merge class hierarchies:", info)
for h in hierarchies:
api.fail(f" -> {h}", info)

# XXX: Reuse abstract status checker from SemanticAnalyzerPass2.
# Ideally, implement a dedicated interface verifier.
Expand Down
36 changes: 36 additions & 0 deletions tests/samples/bad_mro.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""It is not always possible to consistently inject complicated
interface hierarchy into implementation MRO. We warn about such occurances.
By itself, both interface hierarchy and implementation hierarchies are
consistent, but combining them is impossible - resulting inheritance graph
become unsortable.
"""
import zope.interface

class ICreature(zope.interface.Interface): pass
class ISwimmable(zope.interface.Interface): pass
class IReptilia(ICreature): pass
class ICrocodile(ICreature, ISwimmable): pass

@zope.interface.implementer(ISwimmable)
class Swimmable: pass
@zope.interface.implementer(IReptilia)
class Reptilia(Swimmable): pass
@zope.interface.implementer(ICrocodile)
class Crocodile(Reptilia): pass


def main() -> None:
croc = Crocodile()
print(croc)

if __name__ == '__main__':
main()

"""
<output>
bad_mro.py:19: error: Unable to calculate a consistent MRO: cannot merge class hierarchies:
bad_mro.py:19: error: -> ['__main__.Crocodile', '__main__.Reptilia', '__main__.Swimmable', '__main__.ISwimmable', '__main__.IReptilia', '__main__.ICreature', 'zope.interface.Interface', 'builtins.object']
bad_mro.py:19: error: -> ['__main__.ICrocodile', '__main__.ICreature', '__main__.ISwimmable', 'zope.interface.Interface', 'builtins.object']
</output>
"""

0 comments on commit 42181ae

Please sign in to comment.