96 lines
3.1 KiB
Python
96 lines
3.1 KiB
Python
class AliasDict(dict):
|
|
def __init__(self, *args, **kwargs):
|
|
super(AliasDict, self).__init__(*args, **kwargs)
|
|
self.aliases = {}
|
|
|
|
def alias(self, from_, to):
|
|
self.aliases[from_] = to
|
|
|
|
def unalias(self, from_):
|
|
del self.aliases[from_]
|
|
|
|
def aliases_of(self, name):
|
|
"""
|
|
Returns other names for given real key or alias ``name``.
|
|
|
|
If given a real key, returns its aliases.
|
|
|
|
If given an alias, returns the real key it points to, plus any other
|
|
aliases of that real key. (The given alias itself is not included in
|
|
the return value.)
|
|
"""
|
|
names = []
|
|
key = name
|
|
# self.aliases keys are aliases, not realkeys. Easy test to see if we
|
|
# should flip around to the POV of a realkey when given an alias.
|
|
if name in self.aliases:
|
|
key = self.aliases[name]
|
|
# Ensure the real key shows up in output.
|
|
names.append(key)
|
|
# 'key' is now a realkey, whose aliases are all keys whose value is
|
|
# itself. Filter out the original name given.
|
|
names.extend(
|
|
[k for k, v in self.aliases.items() if v == key and k != name]
|
|
)
|
|
return names
|
|
|
|
def _handle(self, key, value, single, multi, unaliased):
|
|
# Attribute existence test required to not blow up when deepcopy'd
|
|
if key in getattr(self, "aliases", {}):
|
|
target = self.aliases[key]
|
|
# Single-string targets
|
|
if isinstance(target, str):
|
|
return single(self, target, value)
|
|
# Multi-string targets
|
|
else:
|
|
if multi:
|
|
return multi(self, target, value)
|
|
else:
|
|
for subkey in target:
|
|
single(self, subkey, value)
|
|
else:
|
|
return unaliased(self, key, value)
|
|
|
|
def __setitem__(self, key, value):
|
|
def single(d, target, value):
|
|
d[target] = value
|
|
|
|
def unaliased(d, key, value):
|
|
super(AliasDict, d).__setitem__(key, value)
|
|
|
|
return self._handle(key, value, single, None, unaliased)
|
|
|
|
def __getitem__(self, key):
|
|
def single(d, target, value):
|
|
return d[target]
|
|
|
|
def unaliased(d, key, value):
|
|
return super(AliasDict, d).__getitem__(key)
|
|
|
|
def multi(d, target, value):
|
|
msg = "Multi-target aliases have no well-defined value and can't be read." # noqa
|
|
raise ValueError(msg)
|
|
|
|
return self._handle(key, None, single, multi, unaliased)
|
|
|
|
def __contains__(self, key):
|
|
def single(d, target, value):
|
|
return target in d
|
|
|
|
def multi(d, target, value):
|
|
return all(subkey in self for subkey in self.aliases[key])
|
|
|
|
def unaliased(d, key, value):
|
|
return super(AliasDict, d).__contains__(key)
|
|
|
|
return self._handle(key, None, single, multi, unaliased)
|
|
|
|
def __delitem__(self, key):
|
|
def single(d, target, value):
|
|
del d[target]
|
|
|
|
def unaliased(d, key, value):
|
|
return super(AliasDict, d).__delitem__(key)
|
|
|
|
return self._handle(key, None, single, None, unaliased)
|