aldict 1.1.2__tar.gz → 1.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {aldict-1.1.2 → aldict-1.2.1}/PKG-INFO +18 -5
- {aldict-1.1.2 → aldict-1.2.1}/README.md +17 -4
- aldict-1.2.1/aldict/__init__.py +8 -0
- aldict-1.2.1/aldict/alias_dict.py +234 -0
- {aldict-1.1.2 → aldict-1.2.1}/pyproject.toml +1 -1
- aldict-1.1.2/aldict/__init__.py +0 -6
- aldict-1.1.2/aldict/alias_dict.py +0 -180
- aldict-1.1.2/aldict/exception.py +0 -6
- {aldict-1.1.2 → aldict-1.2.1}/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aldict
|
|
3
|
-
Version: 1.1
|
|
3
|
+
Version: 1.2.1
|
|
4
4
|
Summary: Multi-key dictionary, supports adding and manipulating key-aliases pointing to shared values
|
|
5
5
|
Keywords: multi-key dictionary,multidict,alias-dict
|
|
6
6
|
Author-Email: kaliv0 <kaloyan.ivanov88@gmail.com>
|
|
@@ -10,7 +10,7 @@ Requires-Python: >=3.10
|
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
13
|
-
<img src="https://github.com/kaliv0/aldict/blob/main/assets/alter-ego.jpg?raw=true" width="250" alt="Alter Ego">
|
|
13
|
+
<img src="https://github.com/kaliv0/aldict/blob/main/.github/assets/alter-ego.jpg?raw=true" width="250" alt="Alter Ego">
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
16
|
---
|
|
@@ -99,6 +99,19 @@ ad.items()
|
|
|
99
99
|
# dict_values([10, 20])
|
|
100
100
|
# dict_items([('x', 10), ('y', 20), ('Xx', 10), ('Yy', 20), ('xyz', 20)])
|
|
101
101
|
```
|
|
102
|
+
- iterkeys
|
|
103
|
+
<br>(lazy iterator over all <i>keys</i> and <i>aliases</i>)
|
|
104
|
+
```python
|
|
105
|
+
ad = AliasDict({"x": 10, "y": 20})
|
|
106
|
+
ad.add_alias("x", "Xx")
|
|
107
|
+
|
|
108
|
+
assert list(ad.iterkeys()) == ['x', 'y', 'Xx']
|
|
109
|
+
```
|
|
110
|
+
- iteritems
|
|
111
|
+
<br>(lazy iterator over all <i>items</i> including <i>alias/value</i> pairs)
|
|
112
|
+
```python
|
|
113
|
+
assert list(ad.iteritems()) == [('x', 10), ('y', 20), ('Xx', 10)]
|
|
114
|
+
```
|
|
102
115
|
- remove key and aliases
|
|
103
116
|
```python
|
|
104
117
|
ad.pop("y")
|
|
@@ -152,8 +165,8 @@ assert ad_copy is not ad
|
|
|
152
165
|
```
|
|
153
166
|
- merge with | and |= operators
|
|
154
167
|
```python
|
|
155
|
-
ad1 = AliasDict({"a": 1}, aliases={"a":
|
|
156
|
-
ad2 = AliasDict({"b": 2}, aliases={"b":
|
|
168
|
+
ad1 = AliasDict({"a": 1}, aliases={"a": "aa"})
|
|
169
|
+
ad2 = AliasDict({"b": 2}, aliases={"b": "bb"})
|
|
157
170
|
|
|
158
171
|
merged = ad1 | ad2
|
|
159
172
|
assert merged["aa"] == 1
|
|
@@ -164,6 +177,6 @@ assert ad1["c"] == 3
|
|
|
164
177
|
```
|
|
165
178
|
- fromkeys
|
|
166
179
|
```python
|
|
167
|
-
ad = AliasDict.fromkeys(["a", "b", "c"], 0, aliases={"a":
|
|
180
|
+
ad = AliasDict.fromkeys(["a", "b", "c"], 0, aliases={"a": "aa"})
|
|
168
181
|
assert ad["a"] == ad["aa"] == 0
|
|
169
182
|
```
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://github.com/kaliv0/aldict/blob/main/assets/alter-ego.jpg?raw=true" width="250" alt="Alter Ego">
|
|
2
|
+
<img src="https://github.com/kaliv0/aldict/blob/main/.github/assets/alter-ego.jpg?raw=true" width="250" alt="Alter Ego">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
---
|
|
@@ -88,6 +88,19 @@ ad.items()
|
|
|
88
88
|
# dict_values([10, 20])
|
|
89
89
|
# dict_items([('x', 10), ('y', 20), ('Xx', 10), ('Yy', 20), ('xyz', 20)])
|
|
90
90
|
```
|
|
91
|
+
- iterkeys
|
|
92
|
+
<br>(lazy iterator over all <i>keys</i> and <i>aliases</i>)
|
|
93
|
+
```python
|
|
94
|
+
ad = AliasDict({"x": 10, "y": 20})
|
|
95
|
+
ad.add_alias("x", "Xx")
|
|
96
|
+
|
|
97
|
+
assert list(ad.iterkeys()) == ['x', 'y', 'Xx']
|
|
98
|
+
```
|
|
99
|
+
- iteritems
|
|
100
|
+
<br>(lazy iterator over all <i>items</i> including <i>alias/value</i> pairs)
|
|
101
|
+
```python
|
|
102
|
+
assert list(ad.iteritems()) == [('x', 10), ('y', 20), ('Xx', 10)]
|
|
103
|
+
```
|
|
91
104
|
- remove key and aliases
|
|
92
105
|
```python
|
|
93
106
|
ad.pop("y")
|
|
@@ -141,8 +154,8 @@ assert ad_copy is not ad
|
|
|
141
154
|
```
|
|
142
155
|
- merge with | and |= operators
|
|
143
156
|
```python
|
|
144
|
-
ad1 = AliasDict({"a": 1}, aliases={"a":
|
|
145
|
-
ad2 = AliasDict({"b": 2}, aliases={"b":
|
|
157
|
+
ad1 = AliasDict({"a": 1}, aliases={"a": "aa"})
|
|
158
|
+
ad2 = AliasDict({"b": 2}, aliases={"b": "bb"})
|
|
146
159
|
|
|
147
160
|
merged = ad1 | ad2
|
|
148
161
|
assert merged["aa"] == 1
|
|
@@ -153,6 +166,6 @@ assert ad1["c"] == 3
|
|
|
153
166
|
```
|
|
154
167
|
- fromkeys
|
|
155
168
|
```python
|
|
156
|
-
ad = AliasDict.fromkeys(["a", "b", "c"], 0, aliases={"a":
|
|
169
|
+
ad = AliasDict.fromkeys(["a", "b", "c"], 0, aliases={"a": "aa"})
|
|
157
170
|
assert ad["a"] == ad["aa"] == 0
|
|
158
171
|
```
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
from collections import UserDict
|
|
2
|
+
from collections.abc import Mapping
|
|
3
|
+
from itertools import chain
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AliasError(KeyError):
|
|
7
|
+
"""Key alias not found"""
|
|
8
|
+
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AliasValueError(ValueError):
|
|
13
|
+
"""Inappropriate alias value"""
|
|
14
|
+
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AliasDict(UserDict):
|
|
19
|
+
"""Dict with key-aliases pointing to shared values."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, dict_=None, /, aliases=None):
|
|
22
|
+
self._lookup_map = {} # key -> set(aliases)
|
|
23
|
+
self._alias_map = {} # alias -> key
|
|
24
|
+
|
|
25
|
+
if isinstance(dict_, AliasDict):
|
|
26
|
+
super().__init__(dict_.data)
|
|
27
|
+
self._lookup_map = {k: v.copy() for k, v in dict_._lookup_map.items()}
|
|
28
|
+
self._alias_map = dict_._alias_map.copy()
|
|
29
|
+
else:
|
|
30
|
+
super().__init__(dict_)
|
|
31
|
+
|
|
32
|
+
if aliases:
|
|
33
|
+
for key, alias_list in aliases.items():
|
|
34
|
+
self.add_alias(key, alias_list)
|
|
35
|
+
|
|
36
|
+
def add_alias(self, key, *aliases, strict=False):
|
|
37
|
+
"""Add one or more aliases to a key. Accepts *args or a list/tuple.
|
|
38
|
+
If strict=True, raises AliasValueError when an alias is already assigned to a different key."""
|
|
39
|
+
if key not in self.data:
|
|
40
|
+
raise KeyError(key)
|
|
41
|
+
|
|
42
|
+
for alias in self._unpack(aliases):
|
|
43
|
+
if alias == key:
|
|
44
|
+
raise AliasValueError(f"Key and corresponding alias cannot be equal: '{key}'")
|
|
45
|
+
if alias in self.data:
|
|
46
|
+
raise AliasValueError(f"Alias '{alias}' already exists as a key in the dictionary")
|
|
47
|
+
|
|
48
|
+
if (old_key := self._alias_map.get(alias)) is not None and old_key != key:
|
|
49
|
+
if strict:
|
|
50
|
+
raise AliasValueError(f"Alias '{alias}' already assigned to key '{old_key}'")
|
|
51
|
+
aliases_set = self._lookup_map[old_key]
|
|
52
|
+
aliases_set.discard(alias)
|
|
53
|
+
if not aliases_set:
|
|
54
|
+
del self._lookup_map[old_key]
|
|
55
|
+
|
|
56
|
+
self._lookup_map.setdefault(key, set()).add(alias)
|
|
57
|
+
self._alias_map[alias] = key
|
|
58
|
+
|
|
59
|
+
def remove_alias(self, *aliases):
|
|
60
|
+
"""Remove one or more aliases. Accepts *args or a list/tuple."""
|
|
61
|
+
for alias in self._unpack(aliases):
|
|
62
|
+
try:
|
|
63
|
+
key = self._alias_map.pop(alias)
|
|
64
|
+
except KeyError as e:
|
|
65
|
+
raise AliasError(f"Alias '{alias}' not found") from e
|
|
66
|
+
|
|
67
|
+
aliases_set = self._lookup_map[key]
|
|
68
|
+
aliases_set.discard(alias)
|
|
69
|
+
if not aliases_set:
|
|
70
|
+
del self._lookup_map[key]
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def _unpack(args):
|
|
74
|
+
return args[0] if len(args) == 1 and isinstance(args[0], (list, tuple)) else args
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def fromkeys(cls, iterable, value=None, aliases=None):
|
|
78
|
+
"""Create an AliasDict from an iterable of keys with optional aliases."""
|
|
79
|
+
return cls(dict.fromkeys(iterable, value), aliases=aliases)
|
|
80
|
+
|
|
81
|
+
def clear(self):
|
|
82
|
+
"""Clear all data and aliases."""
|
|
83
|
+
super().clear()
|
|
84
|
+
self._lookup_map.clear()
|
|
85
|
+
self._alias_map.clear()
|
|
86
|
+
|
|
87
|
+
def clear_aliases(self):
|
|
88
|
+
"""Remove all aliases."""
|
|
89
|
+
self._lookup_map.clear()
|
|
90
|
+
self._alias_map.clear()
|
|
91
|
+
|
|
92
|
+
def aliases(self):
|
|
93
|
+
"""Return all aliases."""
|
|
94
|
+
return self._alias_map.keys()
|
|
95
|
+
|
|
96
|
+
def is_alias(self, key):
|
|
97
|
+
"""Return True if the key is an alias, False otherwise."""
|
|
98
|
+
return key in self._alias_map
|
|
99
|
+
|
|
100
|
+
def has_aliases(self, key):
|
|
101
|
+
"""Return True if the key has any aliases, False otherwise."""
|
|
102
|
+
return key in self._lookup_map
|
|
103
|
+
|
|
104
|
+
def keys_with_aliases(self):
|
|
105
|
+
"""Return keys with their aliases."""
|
|
106
|
+
return self._lookup_map.items()
|
|
107
|
+
|
|
108
|
+
def origin_keys(self):
|
|
109
|
+
"""Return original keys (without aliases)."""
|
|
110
|
+
return self.data.keys()
|
|
111
|
+
|
|
112
|
+
def origin_key(self, alias):
|
|
113
|
+
"""Return the original key for an alias, or None if not an alias."""
|
|
114
|
+
return self._alias_map.get(alias)
|
|
115
|
+
|
|
116
|
+
def keys(self):
|
|
117
|
+
"""Return all keys and aliases."""
|
|
118
|
+
return (self.data | self._alias_map).keys()
|
|
119
|
+
|
|
120
|
+
def values(self):
|
|
121
|
+
"""Return all values."""
|
|
122
|
+
return self.data.values()
|
|
123
|
+
|
|
124
|
+
def items(self):
|
|
125
|
+
"""Return all items (including alias/value pairs)."""
|
|
126
|
+
return (self.data | {k: self.data[v] for k, v in self._alias_map.items()}).items()
|
|
127
|
+
|
|
128
|
+
def iterkeys(self):
|
|
129
|
+
"""Return a lazy iterator over all keys and aliases."""
|
|
130
|
+
return iter(self)
|
|
131
|
+
|
|
132
|
+
def iteritems(self):
|
|
133
|
+
"""Return a lazy iterator over all items (including alias/value pairs)."""
|
|
134
|
+
return chain(self.data.items(), ((k, self.data[v]) for k, v in self._alias_map.items()))
|
|
135
|
+
|
|
136
|
+
def origin_len(self):
|
|
137
|
+
"""Return count of original keys (without aliases)."""
|
|
138
|
+
return len(self.data)
|
|
139
|
+
|
|
140
|
+
def __len__(self):
|
|
141
|
+
return len(self.data) + len(self._alias_map)
|
|
142
|
+
|
|
143
|
+
def __missing__(self, key):
|
|
144
|
+
try:
|
|
145
|
+
return super().__getitem__(self._alias_map[key])
|
|
146
|
+
except KeyError:
|
|
147
|
+
raise KeyError(key) from None
|
|
148
|
+
|
|
149
|
+
def __setitem__(self, key, value):
|
|
150
|
+
try:
|
|
151
|
+
key = self._alias_map[key]
|
|
152
|
+
except KeyError:
|
|
153
|
+
pass
|
|
154
|
+
super().__setitem__(key, value)
|
|
155
|
+
|
|
156
|
+
def __delitem__(self, key):
|
|
157
|
+
try:
|
|
158
|
+
del self.data[key]
|
|
159
|
+
for alias in self._lookup_map.pop(key, ()):
|
|
160
|
+
del self._alias_map[alias]
|
|
161
|
+
except KeyError:
|
|
162
|
+
return self.remove_alias(key)
|
|
163
|
+
|
|
164
|
+
def __contains__(self, item):
|
|
165
|
+
return item in self.data or item in self._alias_map
|
|
166
|
+
|
|
167
|
+
def __iter__(self):
|
|
168
|
+
return chain(self.data, self._alias_map)
|
|
169
|
+
|
|
170
|
+
def __reversed__(self):
|
|
171
|
+
return chain(reversed(self._alias_map), reversed(self.data))
|
|
172
|
+
|
|
173
|
+
def copy(self):
|
|
174
|
+
"""Return a shallow copy of the AliasDict."""
|
|
175
|
+
return type(self)(self)
|
|
176
|
+
|
|
177
|
+
def __repr__(self):
|
|
178
|
+
return f"AliasDict({dict(self.items())})"
|
|
179
|
+
|
|
180
|
+
def __eq__(self, other):
|
|
181
|
+
if not isinstance(other, AliasDict):
|
|
182
|
+
return NotImplemented
|
|
183
|
+
# _lookup_map is derived from _alias_map, so comparing it is redundant
|
|
184
|
+
return self.data == other.data and self._alias_map == other._alias_map
|
|
185
|
+
|
|
186
|
+
def __or__(self, other):
|
|
187
|
+
if not isinstance(other, Mapping):
|
|
188
|
+
return NotImplemented
|
|
189
|
+
new = self.copy()
|
|
190
|
+
if isinstance(other, AliasDict):
|
|
191
|
+
new.update(other.data)
|
|
192
|
+
self._validate_merge_aliases(new, other)
|
|
193
|
+
new._alias_map.update(other._alias_map)
|
|
194
|
+
for k, v in other._lookup_map.items():
|
|
195
|
+
new._lookup_map.setdefault(k, set()).update(v)
|
|
196
|
+
else:
|
|
197
|
+
new.update(other)
|
|
198
|
+
return new
|
|
199
|
+
|
|
200
|
+
def __ror__(self, other):
|
|
201
|
+
if not isinstance(other, Mapping):
|
|
202
|
+
return NotImplemented
|
|
203
|
+
new = type(self)(other)
|
|
204
|
+
new.update(self.data)
|
|
205
|
+
self._validate_merge_aliases(new, self)
|
|
206
|
+
new._alias_map.update(self._alias_map)
|
|
207
|
+
for k, v in self._lookup_map.items():
|
|
208
|
+
new._lookup_map.setdefault(k, set()).update(v)
|
|
209
|
+
return new
|
|
210
|
+
|
|
211
|
+
def __ior__(self, other):
|
|
212
|
+
if isinstance(other, AliasDict):
|
|
213
|
+
self._validate_merge_aliases(self, other)
|
|
214
|
+
self.update(other.data)
|
|
215
|
+
self._alias_map.update(other._alias_map)
|
|
216
|
+
for k, v in other._lookup_map.items():
|
|
217
|
+
self._lookup_map.setdefault(k, set()).update(v)
|
|
218
|
+
else:
|
|
219
|
+
self.update(other)
|
|
220
|
+
return self
|
|
221
|
+
|
|
222
|
+
@staticmethod
|
|
223
|
+
def _validate_merge_aliases(target, other):
|
|
224
|
+
"""Check that other's aliases don't collide with target's keys and vice versa."""
|
|
225
|
+
for alias, key in other._alias_map.items(): # noqa
|
|
226
|
+
if alias in target.data:
|
|
227
|
+
raise AliasValueError(f"Alias '{alias}' already exists as a key in the dictionary")
|
|
228
|
+
if (existing := target._alias_map.get(alias)) is not None and existing != key: # noqa
|
|
229
|
+
raise AliasValueError(f"Alias '{alias}' already assigned to key '{existing}'")
|
|
230
|
+
for key in other.data:
|
|
231
|
+
if key in target._alias_map: # noqa
|
|
232
|
+
raise AliasValueError(f"Key '{key}' already exists as an alias in the dictionary")
|
|
233
|
+
|
|
234
|
+
__hash__ = None
|
aldict-1.1.2/aldict/__init__.py
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
from collections import UserDict, defaultdict
|
|
2
|
-
from collections.abc import Mapping
|
|
3
|
-
from itertools import chain
|
|
4
|
-
|
|
5
|
-
from aldict.exception import AliasError, AliasValueError
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class AliasDict(UserDict):
|
|
9
|
-
"""Dict with key-aliases pointing to shared values."""
|
|
10
|
-
|
|
11
|
-
def __init__(self, dict_=None, /, aliases=None):
|
|
12
|
-
self._alias_dict = {}
|
|
13
|
-
if isinstance(dict_, AliasDict):
|
|
14
|
-
super().__init__(dict_.data)
|
|
15
|
-
self._alias_dict = dict(dict_._alias_dict)
|
|
16
|
-
else:
|
|
17
|
-
super().__init__(dict_)
|
|
18
|
-
|
|
19
|
-
if aliases:
|
|
20
|
-
for key, alias_list in aliases.items():
|
|
21
|
-
self.add_alias(key, alias_list)
|
|
22
|
-
|
|
23
|
-
def add_alias(self, key, *aliases):
|
|
24
|
-
"""Add one or more aliases to a key. Accepts *args or a list/tuple."""
|
|
25
|
-
if key not in self.data:
|
|
26
|
-
raise KeyError(key)
|
|
27
|
-
|
|
28
|
-
for alias in self._unpack(aliases):
|
|
29
|
-
if alias == key:
|
|
30
|
-
raise AliasValueError(f"Key and corresponding alias cannot be equal: '{key}'")
|
|
31
|
-
if alias in self.data:
|
|
32
|
-
raise AliasValueError(f"Alias '{alias}' already exists as a key in the dictionary")
|
|
33
|
-
self._alias_dict[alias] = key
|
|
34
|
-
|
|
35
|
-
def remove_alias(self, *aliases):
|
|
36
|
-
"""Remove one or more aliases. Accepts *args or a list/tuple."""
|
|
37
|
-
for alias in self._unpack(aliases):
|
|
38
|
-
try:
|
|
39
|
-
self._alias_dict.__delitem__(alias)
|
|
40
|
-
except KeyError as e:
|
|
41
|
-
raise AliasError(f"Alias '{alias}' not found") from e
|
|
42
|
-
|
|
43
|
-
@staticmethod
|
|
44
|
-
def _unpack(args):
|
|
45
|
-
return args[0] if len(args) == 1 and isinstance(args[0], (list, tuple)) else args
|
|
46
|
-
|
|
47
|
-
@classmethod
|
|
48
|
-
def fromkeys(cls, iterable, value=None, aliases=None):
|
|
49
|
-
"""Create an AliasDict from an iterable of keys with optional aliases."""
|
|
50
|
-
return cls(dict.fromkeys(iterable, value), aliases=aliases)
|
|
51
|
-
|
|
52
|
-
def clear(self):
|
|
53
|
-
"""Clear all data and aliases."""
|
|
54
|
-
super().clear()
|
|
55
|
-
self._alias_dict.clear()
|
|
56
|
-
|
|
57
|
-
def clear_aliases(self):
|
|
58
|
-
"""Remove all aliases."""
|
|
59
|
-
self._alias_dict.clear()
|
|
60
|
-
|
|
61
|
-
def aliases(self):
|
|
62
|
-
"""Return all aliases."""
|
|
63
|
-
return self._alias_dict.keys()
|
|
64
|
-
|
|
65
|
-
def is_alias(self, key):
|
|
66
|
-
"""Return True if the key is an alias, False otherwise."""
|
|
67
|
-
return key in self._alias_dict
|
|
68
|
-
|
|
69
|
-
def has_aliases(self, key):
|
|
70
|
-
"""Return True if the key has any aliases, False otherwise."""
|
|
71
|
-
return key in self._alias_dict.values()
|
|
72
|
-
|
|
73
|
-
def keys_with_aliases(self):
|
|
74
|
-
"""Return keys with their aliases."""
|
|
75
|
-
result = defaultdict(list)
|
|
76
|
-
for alias, key in self._alias_dict.items():
|
|
77
|
-
result[key].append(alias)
|
|
78
|
-
return result.items()
|
|
79
|
-
|
|
80
|
-
def origin_keys(self):
|
|
81
|
-
"""Return original keys (without aliases)."""
|
|
82
|
-
return self.data.keys()
|
|
83
|
-
|
|
84
|
-
def origin_key(self, alias):
|
|
85
|
-
"""Return the original key for an alias, or None if not an alias."""
|
|
86
|
-
return self._alias_dict.get(alias)
|
|
87
|
-
|
|
88
|
-
def keys(self):
|
|
89
|
-
"""Return all keys and aliases."""
|
|
90
|
-
return dict(**self.data, **self._alias_dict).keys()
|
|
91
|
-
# NB: could be optimized as 'return iter(self)' but we won't be able to call e.g. len(alias_dict.keys())
|
|
92
|
-
|
|
93
|
-
def values(self):
|
|
94
|
-
"""Return all values."""
|
|
95
|
-
return self.data.values()
|
|
96
|
-
|
|
97
|
-
def items(self):
|
|
98
|
-
"""Return all items (including alias/value pairs)."""
|
|
99
|
-
return dict(**self.data, **{k: self.data[v] for k, v in self._alias_dict.items()}).items()
|
|
100
|
-
# NB: could be optimized as
|
|
101
|
-
# 'return chain(self.data.items(), ((k, self.data[v]) for k, v in self._alias_dict.items()))'
|
|
102
|
-
# (same as .keys() above)
|
|
103
|
-
|
|
104
|
-
def origin_len(self):
|
|
105
|
-
"""Return count of original keys (without aliases)."""
|
|
106
|
-
return len(self.data)
|
|
107
|
-
|
|
108
|
-
def __len__(self):
|
|
109
|
-
return len(self.data) + len(self._alias_dict)
|
|
110
|
-
|
|
111
|
-
def __missing__(self, key):
|
|
112
|
-
try:
|
|
113
|
-
return super().__getitem__(self._alias_dict[key])
|
|
114
|
-
except KeyError:
|
|
115
|
-
raise KeyError(key) from None
|
|
116
|
-
|
|
117
|
-
def __setitem__(self, key, value):
|
|
118
|
-
try:
|
|
119
|
-
key = self._alias_dict[key]
|
|
120
|
-
except KeyError:
|
|
121
|
-
pass
|
|
122
|
-
super().__setitem__(key, value)
|
|
123
|
-
|
|
124
|
-
def __delitem__(self, key):
|
|
125
|
-
try:
|
|
126
|
-
self.data.__delitem__(key)
|
|
127
|
-
for alias in [k for k, v in self._alias_dict.items() if v == key]:
|
|
128
|
-
del self._alias_dict[alias]
|
|
129
|
-
except KeyError:
|
|
130
|
-
return self.remove_alias(key)
|
|
131
|
-
|
|
132
|
-
def __contains__(self, item):
|
|
133
|
-
return item in self.data or item in self._alias_dict
|
|
134
|
-
|
|
135
|
-
def __iter__(self):
|
|
136
|
-
return chain(self.data, self._alias_dict)
|
|
137
|
-
|
|
138
|
-
def __reversed__(self):
|
|
139
|
-
return chain(reversed(self._alias_dict), reversed(self.data))
|
|
140
|
-
|
|
141
|
-
def copy(self):
|
|
142
|
-
"""Return a shallow copy of the AliasDict."""
|
|
143
|
-
return type(self)(self)
|
|
144
|
-
|
|
145
|
-
def __repr__(self):
|
|
146
|
-
return f"AliasDict({dict(self.items())})"
|
|
147
|
-
|
|
148
|
-
def __eq__(self, other):
|
|
149
|
-
if not isinstance(other, AliasDict):
|
|
150
|
-
return NotImplemented
|
|
151
|
-
return self.data == other.data and self._alias_dict == other._alias_dict
|
|
152
|
-
|
|
153
|
-
def __or__(self, other):
|
|
154
|
-
if not isinstance(other, Mapping):
|
|
155
|
-
return NotImplemented
|
|
156
|
-
new = self.copy()
|
|
157
|
-
if isinstance(other, AliasDict):
|
|
158
|
-
new.update(other.data)
|
|
159
|
-
new._alias_dict.update(other._alias_dict)
|
|
160
|
-
else:
|
|
161
|
-
new.update(other)
|
|
162
|
-
return new
|
|
163
|
-
|
|
164
|
-
def __ror__(self, other):
|
|
165
|
-
if not isinstance(other, Mapping):
|
|
166
|
-
return NotImplemented
|
|
167
|
-
new = AliasDict(other)
|
|
168
|
-
new.update(self.data)
|
|
169
|
-
new._alias_dict.update(self._alias_dict)
|
|
170
|
-
return new
|
|
171
|
-
|
|
172
|
-
def __ior__(self, other):
|
|
173
|
-
if isinstance(other, AliasDict):
|
|
174
|
-
self.update(other.data)
|
|
175
|
-
self._alias_dict.update(other._alias_dict)
|
|
176
|
-
else:
|
|
177
|
-
self.update(other)
|
|
178
|
-
return self
|
|
179
|
-
|
|
180
|
-
__hash__ = None
|
aldict-1.1.2/aldict/exception.py
DELETED
|
File without changes
|