|
from .utils import _toposort, groupby |
|
|
|
class AmbiguityWarning(Warning): |
|
pass |
|
|
|
|
|
def supercedes(a, b): |
|
""" A is consistent and strictly more specific than B """ |
|
return len(a) == len(b) and all(map(issubclass, a, b)) |
|
|
|
|
|
def consistent(a, b): |
|
""" It is possible for an argument list to satisfy both A and B """ |
|
return (len(a) == len(b) and |
|
all(issubclass(aa, bb) or issubclass(bb, aa) |
|
for aa, bb in zip(a, b))) |
|
|
|
|
|
def ambiguous(a, b): |
|
""" A is consistent with B but neither is strictly more specific """ |
|
return consistent(a, b) and not (supercedes(a, b) or supercedes(b, a)) |
|
|
|
|
|
def ambiguities(signatures): |
|
""" All signature pairs such that A is ambiguous with B """ |
|
signatures = list(map(tuple, signatures)) |
|
return {(a, b) for a in signatures for b in signatures |
|
if hash(a) < hash(b) |
|
and ambiguous(a, b) |
|
and not any(supercedes(c, a) and supercedes(c, b) |
|
for c in signatures)} |
|
|
|
|
|
def super_signature(signatures): |
|
""" A signature that would break ambiguities """ |
|
n = len(signatures[0]) |
|
assert all(len(s) == n for s in signatures) |
|
|
|
return [max([type.mro(sig[i]) for sig in signatures], key=len)[0] |
|
for i in range(n)] |
|
|
|
|
|
def edge(a, b, tie_breaker=hash): |
|
""" A should be checked before B |
|
|
|
Tie broken by tie_breaker, defaults to ``hash`` |
|
""" |
|
if supercedes(a, b): |
|
if supercedes(b, a): |
|
return tie_breaker(a) > tie_breaker(b) |
|
else: |
|
return True |
|
return False |
|
|
|
|
|
def ordering(signatures): |
|
""" A sane ordering of signatures to check, first to last |
|
|
|
Topoological sort of edges as given by ``edge`` and ``supercedes`` |
|
""" |
|
signatures = list(map(tuple, signatures)) |
|
edges = [(a, b) for a in signatures for b in signatures if edge(a, b)] |
|
edges = groupby(lambda x: x[0], edges) |
|
for s in signatures: |
|
if s not in edges: |
|
edges[s] = [] |
|
edges = {k: [b for a, b in v] for k, v in edges.items()} |
|
return _toposort(edges) |
|
|