1class AliasDict(dict):
2 def __init__(self, *args, **kwargs):
3 super(AliasDict, self).__init__(*args, **kwargs)
4 self.aliases = {}
5
6 def alias(self, from_, to):
7 self.aliases[from_] = to
8
9 def unalias(self, from_):
10 del self.aliases[from_]
11
12 def aliases_of(self, name):
13 """
14 Returns other names for given real key or alias ``name``.
15
16 If given a real key, returns its aliases.
17
18 If given an alias, returns the real key it points to, plus any other
19 aliases of that real key. (The given alias itself is not included in
20 the return value.)
21 """
22 names = []
23 key = name
24 # self.aliases keys are aliases, not realkeys. Easy test to see if we
25 # should flip around to the POV of a realkey when given an alias.
26 if name in self.aliases:
27 key = self.aliases[name]
28 # Ensure the real key shows up in output.
29 names.append(key)
30 # 'key' is now a realkey, whose aliases are all keys whose value is
31 # itself. Filter out the original name given.
32 names.extend(
33 [k for k, v in self.aliases.items() if v == key and k != name]
34 )
35 return names
36
37 def _handle(self, key, value, single, multi, unaliased):
38 # Attribute existence test required to not blow up when deepcopy'd
39 if key in getattr(self, "aliases", {}):
40 target = self.aliases[key]
41 # Single-string targets
42 if isinstance(target, str):
43 return single(self, target, value)
44 # Multi-string targets
45 else:
46 if multi:
47 return multi(self, target, value)
48 else:
49 for subkey in target:
50 single(self, subkey, value)
51 else:
52 return unaliased(self, key, value)
53
54 def __setitem__(self, key, value):
55 def single(d, target, value):
56 d[target] = value
57
58 def unaliased(d, key, value):
59 super(AliasDict, d).__setitem__(key, value)
60
61 return self._handle(key, value, single, None, unaliased)
62
63 def __getitem__(self, key):
64 def single(d, target, value):
65 return d[target]
66
67 def unaliased(d, key, value):
68 return super(AliasDict, d).__getitem__(key)
69
70 def multi(d, target, value):
71 msg = "Multi-target aliases have no well-defined value and can't be read." # noqa
72 raise ValueError(msg)
73
74 return self._handle(key, None, single, multi, unaliased)
75
76 def __contains__(self, key):
77 def single(d, target, value):
78 return target in d
79
80 def multi(d, target, value):
81 return all(subkey in self for subkey in self.aliases[key])
82
83 def unaliased(d, key, value):
84 return super(AliasDict, d).__contains__(key)
85
86 return self._handle(key, None, single, multi, unaliased)
87
88 def __delitem__(self, key):
89 def single(d, target, value):
90 del d[target]
91
92 def unaliased(d, key, value):
93 return super(AliasDict, d).__delitem__(key)
94
95 return self._handle(key, None, single, None, unaliased)