xloft 0.1.19__py3-none-any.whl → 0.10.10__py3-none-any.whl

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.
@@ -0,0 +1,359 @@
1
+ # XLOFT - X-Library of tools.
2
+ # Copyright (c) 2025 Gennady Kostyunin
3
+ # SPDX-License-Identifier: MIT
4
+ """`AliasDict` - Pseudo dictionary with supports aliases for keys.
5
+
6
+ Class `AliasDict` contains the following methods:
7
+
8
+ - `get` - Get value by alias.
9
+ - `add` - Add a new key and value pair.
10
+ - `update` - Update the value of an existing key.
11
+ - `delete` - Delete the value associated with the key and all its aliases.
12
+ - `add_alias` - Add a new alias to an existing set.
13
+ - `delete_alias` - Remove the alias from the existing set.
14
+ - `has_key` - Check if the alias exists.
15
+ - `has_value` - Check if the value exists.
16
+ - `items` - Returns a generator of list of `AliasDict` elements grouped into tuples.
17
+ - `keys` - Get a generator of list of all aliases.
18
+ - `values` - Get a generator of list of all values.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ __all__ = ("AliasDict",)
24
+
25
+ import copy
26
+ import logging
27
+ from collections.abc import Generator
28
+ from typing import Any
29
+
30
+ from xloft.errors import (
31
+ AttributeCannotBeDeleteError,
32
+ AttributeDoesNotGetValueError,
33
+ AttributeDoesNotSetValueError,
34
+ )
35
+
36
+
37
+ class AliasDict:
38
+ """Pseudo dictionary with supports aliases for keys."""
39
+
40
+ def __init__(self, data: list[tuple[set[str | int | float], Any]] | None = None) -> None: # noqa: D107
41
+ self.__dict__["_store"] = []
42
+ self.__dict__["all_alias_set"] = set() # for uniqueness check
43
+ if data is not None:
44
+ for item in data:
45
+ if not self.all_alias_set.isdisjoint(item[0]):
46
+ err_msg = "In some keys, aliases are repeated!"
47
+ logging.error(err_msg)
48
+ raise KeyError(err_msg)
49
+ self.all_alias_set.update(item[0])
50
+ self._store.append(list(item))
51
+
52
+ def __len__(self) -> int:
53
+ """Get the number of elements in the dictionary.
54
+
55
+ Examples:
56
+ >>> from xloft import AliasDict
57
+ >>> ad = AliasDict([({"English", "en"}, "lemmatize_en_all")])
58
+ >>> len(ad)
59
+ 1
60
+
61
+ Returns:
62
+ The number of elements in the dictionary.
63
+ """
64
+ return len(self.__dict__["_store"])
65
+
66
+ def __getattr__(self, name: str) -> None:
67
+ """Blocked Getter."""
68
+ raise AttributeDoesNotGetValueError(name)
69
+
70
+ def __setattr__(self, name: str, value: Any) -> None:
71
+ """Blocked Setter."""
72
+ raise AttributeDoesNotSetValueError(name)
73
+
74
+ def __delattr__(self, name: str) -> None:
75
+ """Blocked Deleter."""
76
+ raise AttributeCannotBeDeleteError(name)
77
+
78
+ def __getitem__(self, alias: str | int | float) -> Any:
79
+ """Get value by [key_name].
80
+
81
+ Examples:
82
+ >>> from xloft import AliasDict
83
+ >>> ad = AliasDict([({"English", "en"}, "lemmatize_en_all")])
84
+ >>> ad["en"]
85
+ "lemmatize_en_all"
86
+
87
+ Args:
88
+ alias (str | int | float): Alias of key.
89
+
90
+ Returns:
91
+ Deep copy of the value associated with the alias.
92
+ """
93
+ for item in self.__dict__["_store"]:
94
+ if alias in item[0]:
95
+ return copy.deepcopy(item[1])
96
+ raise KeyError(f"Alias `{alias}` is missing!")
97
+
98
+ def get(self, alias: str | int | float, default: Any = None) -> Any:
99
+ """Get value by alias.
100
+
101
+ If there is no alias, return the default value.
102
+
103
+ Examples:
104
+ >>> from xloft import AliasDict
105
+ >>> ad = AliasDict([({"English", "en"}, "lemmatize_en_all")])
106
+ >>> ad.get("en")
107
+ "lemmatize_en_all"
108
+
109
+ Args:
110
+ alias (str | int | float): Alias of key.
111
+ default (Any): Value by default.
112
+
113
+ Returns:
114
+ Deep copy of the value associated with the alias or value by default.
115
+ """
116
+ for item in self.__dict__["_store"]:
117
+ if alias in item[0]:
118
+ return copy.deepcopy(item[1])
119
+
120
+ return default
121
+
122
+ def add(self, aliases: set[str | int | float], value: Any) -> None:
123
+ """Add a new key and value pair.
124
+
125
+ Examples:
126
+ >>> from xloft import AliasDict
127
+ >>> ad = AliasDict([({"English", "en"}, "lemmatize_en_all")])
128
+ >>> ad.add({"Russian", "ru"}, "lemmatize_ru_all")
129
+ >>> ad.get("ru")
130
+ "lemmatize_ru_all"
131
+
132
+ Args:
133
+ aliases (set[str | int | float]): List (set) aliases of key.
134
+ value (Any): Value associated with key.
135
+
136
+ Returns:
137
+ `None` or `KeyError` is missing.
138
+ """
139
+ if not self.all_alias_set.isdisjoint(aliases):
140
+ err_msg = "In some keys, aliases are repeated."
141
+ logging.error(err_msg)
142
+
143
+ self._store.append([aliases, value])
144
+ self.all_alias_set.update(aliases)
145
+
146
+ def update(self, alias: str | int | float, value: Any) -> None:
147
+ """Update the value of an existing key.
148
+
149
+ Examples:
150
+ >>> from xloft import AliasDict
151
+ >>> ad = AliasDict([({"English", "en"}, "lemmatize_en_all")])
152
+ >>> ad.update("en", "Hello world!")
153
+ >>> ad.get("English")
154
+ "Hello world!"
155
+
156
+ Args:
157
+ alias (str | int | float): Alias of key.
158
+ value (Any): Value associated with key.
159
+
160
+ Returns:
161
+ `None` or `KeyError` if alias is missing.
162
+ """
163
+ for item in self.__dict__["_store"]:
164
+ if alias in item[0]:
165
+ item[1] = value
166
+ return
167
+
168
+ err_msg = f"Alias `{alias}` is missing!"
169
+ logging.error(err_msg)
170
+ raise KeyError(err_msg)
171
+
172
+ def delete(self, alias: str | int | float) -> None:
173
+ """Delete the value associated with the key and all its aliases.
174
+
175
+ Examples:
176
+ >>> from xloft import AliasDict
177
+ >>> ad = AliasDict([({"English", "en"}, "lemmatize_en_all")])
178
+ >>> ad.delete("en")
179
+ >>> ad.get("English")
180
+ None
181
+
182
+ Args:
183
+ alias (str | int | float): Alias of key.
184
+
185
+ Returns:
186
+ `None` or `KeyError` if alias is missing.
187
+ """
188
+ for item in self.__dict__["_store"]:
189
+ if alias in item[0]:
190
+ self.__dict__["all_alias_set"] = {
191
+ alias for alias in self.__dict__["all_alias_set"] if alias not in item[0]
192
+ }
193
+ self.__dict__["_store"] = [item for item in self.__dict__["_store"] if alias not in item[0]]
194
+ return
195
+
196
+ err_msg = f"Alias `{alias}` is missing!"
197
+ logging.error(err_msg)
198
+ raise KeyError(err_msg)
199
+
200
+ def add_alias(
201
+ self,
202
+ alias: str | int | float,
203
+ new_alias: str | int | float,
204
+ ) -> None:
205
+ """Add a new alias to an existing set.
206
+
207
+ Examples:
208
+ >>> from xloft import AliasDict
209
+ >>> ad = AliasDict([({"English"}, "lemmatize_en_all")])
210
+ >>> ad.add_alias("English", "en")
211
+ >>> ad.get("en")
212
+ "lemmatize_en_all"
213
+
214
+ Args:
215
+ alias (str | int | float): Existing alias.
216
+ new_alias (str | int | float): The alias that needs to be added to the existing set.
217
+
218
+ Returns:
219
+ `None` or `KeyError` if new alias is already exists.
220
+ """
221
+ if new_alias in self.__dict__["all_alias_set"]:
222
+ err_msg = f"New Alias `{new_alias}` is already exists!"
223
+ logging.error(err_msg)
224
+ raise KeyError(err_msg)
225
+
226
+ for item in self.__dict__["_store"]:
227
+ if alias in item[0]:
228
+ item[0].add(new_alias)
229
+ self.all_alias_set.add(new_alias)
230
+ return
231
+
232
+ err_msg = f"Alias `{alias}` is missing!"
233
+ logging.error(err_msg)
234
+ raise KeyError(err_msg)
235
+
236
+ def delete_alias(self, alias: str | int | float) -> None:
237
+ """Remove the alias from the existing set.
238
+
239
+ If the alias was the last one, then the value associated with it is deleted.
240
+
241
+ Examples:
242
+ >>> from xloft import AliasDict
243
+ >>> ad = AliasDict([({"English", "en"}, "lemmatize_en_all")])
244
+ >>> ad.delete_alias("en")
245
+ >>> ad.keys()
246
+ ["English"]
247
+
248
+ Args:
249
+ alias (str | int | float): Existing alias.
250
+
251
+ Returns:
252
+ `None` or `KeyError` if alias is missing.
253
+ """
254
+ for item in self.__dict__["_store"]:
255
+ if alias in item[0]:
256
+ if len(item[0]) == 1:
257
+ self._store = [item for item in self._store if alias not in item[0]]
258
+ else:
259
+ item[0].remove(alias)
260
+ self.all_alias_set.remove(alias)
261
+ return
262
+
263
+ err_msg = f"Alias `{alias}` is missing!"
264
+ logging.error(err_msg)
265
+ raise KeyError(err_msg)
266
+
267
+ def has_key(self, alias: str | int | float) -> bool:
268
+ """Check if the alias exists.
269
+
270
+ Examples:
271
+ >>> from xloft import AliasDict
272
+ >>> ad = AliasDict([({"English", "en"}, "lemmatize_en_all")])
273
+ >>> ad.has_key("en")
274
+ True
275
+
276
+ Args:
277
+ alias (str | int | float): Some alias.
278
+
279
+ Returns:
280
+ True if the key exists, otherwise False.
281
+ """
282
+ return alias in self.__dict__["all_alias_set"]
283
+
284
+ def has_value(self, value: Any) -> bool:
285
+ """Check if the value exists.
286
+
287
+ Examples:
288
+ >>> from xloft import AliasDict
289
+ >>> ad = AliasDict([({"English", "en"}, "lemmatize_en_all")])
290
+ >>> ad.has_value("lemmatize_en_all")
291
+ True
292
+
293
+ Args:
294
+ value (Any): Value associated with key.
295
+
296
+ Returns:
297
+ True if the value exists, otherwise False.
298
+ """
299
+ is_exists = False
300
+ for item in self.__dict__["_store"]:
301
+ if value == item[1]:
302
+ is_exists = True
303
+ break
304
+
305
+ return is_exists
306
+
307
+ def items(self) -> Generator[tuple[list[str | int | float], Any]]:
308
+ """Returns a generator of list containing a tuple for each key-value pair.
309
+
310
+ This is convenient for use in a `for` loop.
311
+ If you need to get a list, do it list(instance.items()).
312
+
313
+ Examples:
314
+ >>> from xloft import AliasDict
315
+ >>> ad = AliasDict([({"English", "en"}, "lemmatize_en_all")])
316
+ >>> for aliases, value in ad.items():
317
+ ... print(f"Aliases: {aliases}, Value: {value}")
318
+ "Key: ['English', 'en'], Value: lemmatize_en_all"
319
+
320
+ Returns:
321
+ Returns a list containing a tuple for each key-value pair.
322
+ Type: `list[tuple[list[str | int | float], Any]]` or `[]`.
323
+ """
324
+ store = self.__dict__["_store"]
325
+ return ((list(item[0]), item[1]) for item in store)
326
+
327
+ def keys(self) -> Generator[str | int | float]:
328
+ """Get a generator of list of all aliases.
329
+
330
+ If you need to get a list, do it list(instance.keys()).
331
+
332
+ Examples:
333
+ >>> from xloft import AliasDict
334
+ >>> ad = AliasDict([({"English", "en"}, "lemmatize_en_all")])
335
+ >>> list(ad.keys())
336
+ ["English", "en"]
337
+
338
+ Returns:
339
+ List of all aliases.
340
+ """
341
+ all_alias_set = self.__dict__["all_alias_set"]
342
+ return (item for item in all_alias_set)
343
+
344
+ def values(self) -> Generator[Any]:
345
+ """Get a generator of list of all values.
346
+
347
+ If you need to get a list, do it list(instance.values()).
348
+
349
+ Examples:
350
+ >>> from xloft import AliasDict
351
+ >>> ad = AliasDict([({"English", "en"}, "lemmatize_en_all")])
352
+ >>> list(ad.values())
353
+ ["lemmatize_en_all"]
354
+
355
+ Returns:
356
+ List of all values.
357
+ """
358
+ store = self.__dict__["_store"]
359
+ return (item[1] for item in store)