Spaces:
Sleeping
Sleeping
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) | |