conflict.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. from .utils import _toposort, groupby
  2. from .variadic import isvariadic
  3. __all__ = ["AmbiguityWarning", "supercedes", "consistent", "ambiguous", "ambiguities", "super_signature",
  4. "edge", "ordering"]
  5. class AmbiguityWarning(Warning):
  6. pass
  7. def supercedes(a, b):
  8. """ A is consistent and strictly more specific than B """
  9. if len(a) < len(b):
  10. # only case is if a is empty and b is variadic
  11. return not a and len(b) == 1 and isvariadic(b[-1])
  12. elif len(a) == len(b):
  13. return all(map(issubclass, a, b))
  14. else:
  15. # len(a) > len(b)
  16. p1 = 0
  17. p2 = 0
  18. while p1 < len(a) and p2 < len(b):
  19. cur_a = a[p1]
  20. cur_b = b[p2]
  21. if not (isvariadic(cur_a) or isvariadic(cur_b)):
  22. if not issubclass(cur_a, cur_b):
  23. return False
  24. p1 += 1
  25. p2 += 1
  26. elif isvariadic(cur_a):
  27. assert p1 == len(a) - 1
  28. return p2 == len(b) - 1 and issubclass(cur_a, cur_b)
  29. elif isvariadic(cur_b):
  30. assert p2 == len(b) - 1
  31. if not issubclass(cur_a, cur_b):
  32. return False
  33. p1 += 1
  34. return p2 == len(b) - 1 and p1 == len(a)
  35. def consistent(a, b):
  36. """ It is possible for an argument list to satisfy both A and B """
  37. # Need to check for empty args
  38. if not a:
  39. return not b or isvariadic(b[0])
  40. if not b:
  41. return not a or isvariadic(a[0])
  42. # Non-empty args check for mutual subclasses
  43. if len(a) == len(b):
  44. return all(issubclass(aa, bb) or issubclass(bb, aa)
  45. for aa, bb in zip(a, b))
  46. else:
  47. p1 = 0
  48. p2 = 0
  49. while p1 < len(a) and p2 < len(b):
  50. cur_a = a[p1]
  51. cur_b = b[p2]
  52. if not issubclass(cur_b, cur_a) and not issubclass(cur_a, cur_b):
  53. return False
  54. if not (isvariadic(cur_a) or isvariadic(cur_b)):
  55. p1 += 1
  56. p2 += 1
  57. elif isvariadic(cur_a):
  58. p2 += 1
  59. elif isvariadic(cur_b):
  60. p1 += 1
  61. # We only need to check for variadic ends
  62. # Variadic types are guaranteed to be the last element
  63. return (isvariadic(cur_a) and p2 == len(b) or
  64. isvariadic(cur_b) and p1 == len(a))
  65. def ambiguous(a, b):
  66. """ A is consistent with B but neither is strictly more specific """
  67. return consistent(a, b) and not (supercedes(a, b) or supercedes(b, a))
  68. def ambiguities(signatures):
  69. """ All signature pairs such that A is ambiguous with B """
  70. signatures = list(map(tuple, signatures))
  71. return {(a, b) for a in signatures for b in signatures
  72. if hash(a) < hash(b)
  73. and ambiguous(a, b)
  74. and not any(supercedes(c, a) and supercedes(c, b)
  75. for c in signatures)}
  76. def super_signature(signatures):
  77. """ A signature that would break ambiguities """
  78. n = len(signatures[0])
  79. assert all(len(s) == n for s in signatures)
  80. return [max((type.mro(sig[i]) for sig in signatures), key=len)[0]
  81. for i in range(n)]
  82. def edge(a, b, tie_breaker=hash):
  83. """ A should be checked before B
  84. Tie broken by tie_breaker, defaults to ``hash``
  85. """
  86. # A either supercedes B and B does not supercede A or if B does then call
  87. # tie_breaker
  88. return supercedes(a, b) and (not supercedes(b, a) or tie_breaker(a) > tie_breaker(b))
  89. def ordering(signatures):
  90. """ A sane ordering of signatures to check, first to last
  91. Topoological sort of edges as given by ``edge`` and ``supercedes``
  92. """
  93. signatures = list(map(tuple, signatures))
  94. edges = [(a, b) for a in signatures for b in signatures if edge(a, b)]
  95. edges = groupby(lambda x: x[0], edges)
  96. for s in signatures:
  97. if s not in edges:
  98. edges[s] = []
  99. edges = {k: [b for a, b in v] for k, v in edges.items()} # type: ignore[assignment, attr-defined]
  100. return _toposort(edges)