conflict.py 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. from .utils import _toposort, groupby
  2. class AmbiguityWarning(Warning):
  3. pass
  4. def supercedes(a, b):
  5. """ A is consistent and strictly more specific than B """
  6. return len(a) == len(b) and all(map(issubclass, a, b))
  7. def consistent(a, b):
  8. """ It is possible for an argument list to satisfy both A and B """
  9. return (len(a) == len(b) and
  10. all(issubclass(aa, bb) or issubclass(bb, aa)
  11. for aa, bb in zip(a, b)))
  12. def ambiguous(a, b):
  13. """ A is consistent with B but neither is strictly more specific """
  14. return consistent(a, b) and not (supercedes(a, b) or supercedes(b, a))
  15. def ambiguities(signatures):
  16. """ All signature pairs such that A is ambiguous with B """
  17. signatures = list(map(tuple, signatures))
  18. return {(a, b) for a in signatures for b in signatures
  19. if hash(a) < hash(b)
  20. and ambiguous(a, b)
  21. and not any(supercedes(c, a) and supercedes(c, b)
  22. for c in signatures)}
  23. def super_signature(signatures):
  24. """ A signature that would break ambiguities """
  25. n = len(signatures[0])
  26. assert all(len(s) == n for s in signatures)
  27. return [max([type.mro(sig[i]) for sig in signatures], key=len)[0]
  28. for i in range(n)]
  29. def edge(a, b, tie_breaker=hash):
  30. """ A should be checked before B
  31. Tie broken by tie_breaker, defaults to ``hash``
  32. """
  33. if supercedes(a, b):
  34. if supercedes(b, a):
  35. return tie_breaker(a) > tie_breaker(b)
  36. else:
  37. return True
  38. return False
  39. def ordering(signatures):
  40. """ A sane ordering of signatures to check, first to last
  41. Topoological sort of edges as given by ``edge`` and ``supercedes``
  42. """
  43. signatures = list(map(tuple, signatures))
  44. edges = [(a, b) for a in signatures for b in signatures if edge(a, b)]
  45. edges = groupby(lambda x: x[0], edges)
  46. for s in signatures:
  47. if s not in edges:
  48. edges[s] = []
  49. edges = {k: [b for a, b in v] for k, v in edges.items()}
  50. return _toposort(edges)