enumerific 1.0.1__py3-none-any.whl → 1.0.3__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.
- enumerific/__init__.py +9 -1
- enumerific/exceptions.py +4 -0
- enumerific/extensible.py +501 -60
- enumerific/version.txt +1 -1
- enumerific-1.0.3.dist-info/METADATA +862 -0
- enumerific-1.0.3.dist-info/RECORD +12 -0
- enumerific-1.0.1.dist-info/METADATA +0 -198
- enumerific-1.0.1.dist-info/RECORD +0 -12
- {enumerific-1.0.1.dist-info → enumerific-1.0.3.dist-info}/WHEEL +0 -0
- {enumerific-1.0.1.dist-info → enumerific-1.0.3.dist-info}/licenses/LICENSE.md +0 -0
- {enumerific-1.0.1.dist-info → enumerific-1.0.3.dist-info}/top_level.txt +0 -0
- {enumerific-1.0.1.dist-info → enumerific-1.0.3.dist-info}/zip-safe +0 -0
enumerific/extensible.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import typing
|
|
4
|
+
import collections
|
|
4
5
|
|
|
5
6
|
from enumerific.logging import logger
|
|
6
7
|
|
|
@@ -8,6 +9,7 @@ from enumerific.exceptions import (
|
|
|
8
9
|
EnumerationError,
|
|
9
10
|
EnumerationOptionError,
|
|
10
11
|
EnumerationSubclassingError,
|
|
12
|
+
EnumerationExtensibilityError,
|
|
11
13
|
EnumerationNonUniqueError,
|
|
12
14
|
)
|
|
13
15
|
|
|
@@ -17,7 +19,71 @@ from types import MappingProxyType
|
|
|
17
19
|
logger = logger.getChild(__name__)
|
|
18
20
|
|
|
19
21
|
|
|
20
|
-
class
|
|
22
|
+
class anno(collections.abc.Mapping):
|
|
23
|
+
"""The annotations class supports adding annotations to an Enumeration option."""
|
|
24
|
+
|
|
25
|
+
_value: object = None
|
|
26
|
+
_annotations: dict[str, object] = None
|
|
27
|
+
|
|
28
|
+
def __init__(self, value: object, **annotations: dict[str, object]):
|
|
29
|
+
self._value: object = value
|
|
30
|
+
|
|
31
|
+
for key, value in annotations.items():
|
|
32
|
+
if not isinstance(key, str):
|
|
33
|
+
raise TypeError("All annotation values must have string keys!")
|
|
34
|
+
|
|
35
|
+
self._annotations: dict[str, object] = annotations
|
|
36
|
+
|
|
37
|
+
def __len__(self) -> int:
|
|
38
|
+
return len(self._annotations)
|
|
39
|
+
|
|
40
|
+
def __iter__(self) -> str:
|
|
41
|
+
for key in self._annotations.keys():
|
|
42
|
+
yield key
|
|
43
|
+
|
|
44
|
+
def __contains__(self, other: object) -> bool:
|
|
45
|
+
return other in self._annotations
|
|
46
|
+
|
|
47
|
+
def __getitem__(self, key: str) -> object | None:
|
|
48
|
+
if key in self._annotations:
|
|
49
|
+
return self._annotations[key]
|
|
50
|
+
else:
|
|
51
|
+
raise KeyError(f"The annotation does not have an '{key}' item!")
|
|
52
|
+
|
|
53
|
+
def __setitem__(self, key: str, value: object):
|
|
54
|
+
raise NotImplementedError
|
|
55
|
+
|
|
56
|
+
def __delitem__(self, key: str, value: object):
|
|
57
|
+
raise NotImplementedError
|
|
58
|
+
|
|
59
|
+
def __getattr__(self, name: str) -> object | None:
|
|
60
|
+
if name.startswith("_"):
|
|
61
|
+
return super().__getattr__(name)
|
|
62
|
+
elif name in self._annotations:
|
|
63
|
+
return self._annotations[name]
|
|
64
|
+
else:
|
|
65
|
+
raise AttributeError(f"The annotation does not have an '{name}' attribute!")
|
|
66
|
+
|
|
67
|
+
def __setattr__(self, name: str, value: object):
|
|
68
|
+
if name.startswith("_"):
|
|
69
|
+
return super().__setattr__(name, value)
|
|
70
|
+
else:
|
|
71
|
+
raise NotImplementedError
|
|
72
|
+
|
|
73
|
+
def __detattr__(self, name: str):
|
|
74
|
+
raise NotImplementedError
|
|
75
|
+
|
|
76
|
+
def get(self, name: str, default: object = None) -> object | None:
|
|
77
|
+
if name in self._annotations:
|
|
78
|
+
return self._annotations[name]
|
|
79
|
+
else:
|
|
80
|
+
return default
|
|
81
|
+
|
|
82
|
+
def unwrap(self) -> object:
|
|
83
|
+
return self._value
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class auto(int, anno):
|
|
21
87
|
"""Generate an automatically inrementing integer each time the class is instantiated
|
|
22
88
|
based on the previously supplied configuration, which allows the start and steps to
|
|
23
89
|
be configured as well as if the integers should be generated as powers/flags."""
|
|
@@ -32,6 +98,7 @@ class auto(int):
|
|
|
32
98
|
cls,
|
|
33
99
|
start: int = None,
|
|
34
100
|
steps: int = None,
|
|
101
|
+
times: int = None,
|
|
35
102
|
power: int | bool = None,
|
|
36
103
|
flags: bool = None,
|
|
37
104
|
):
|
|
@@ -57,6 +124,15 @@ class auto(int):
|
|
|
57
124
|
"The 'steps' argument, if specified, must have a positive integer value!"
|
|
58
125
|
)
|
|
59
126
|
|
|
127
|
+
if times is None:
|
|
128
|
+
times = 0
|
|
129
|
+
elif isinstance(times, int) and times >= 0:
|
|
130
|
+
pass
|
|
131
|
+
else:
|
|
132
|
+
raise TypeError(
|
|
133
|
+
"The 'times' argument, if specified, must have a positive integer value!"
|
|
134
|
+
)
|
|
135
|
+
|
|
60
136
|
if power is None:
|
|
61
137
|
power = 0
|
|
62
138
|
elif isinstance(power, bool):
|
|
@@ -90,17 +166,21 @@ class auto(int):
|
|
|
90
166
|
|
|
91
167
|
cls.steps = steps
|
|
92
168
|
|
|
169
|
+
cls.times = times
|
|
170
|
+
|
|
93
171
|
cls.power = power
|
|
94
172
|
|
|
95
173
|
cls.value = cls.start
|
|
96
174
|
|
|
97
|
-
def __new__(cls):
|
|
175
|
+
def __new__(cls, **annotations: dict[str, object]):
|
|
98
176
|
"""Create a new integer (int) instance upon each call, incrementing the value as
|
|
99
177
|
per the configuration defined before this method is called; the configuration
|
|
100
178
|
can be changed at any time and the next call to this method will generate the
|
|
101
179
|
next value based on the most recently specified configuration options."""
|
|
102
180
|
|
|
103
|
-
if cls.
|
|
181
|
+
if cls.times > 0:
|
|
182
|
+
value = cls.value * cls.times
|
|
183
|
+
elif cls.power > 0:
|
|
104
184
|
value = pow(cls.power, (cls.value - 1))
|
|
105
185
|
else:
|
|
106
186
|
value = cls.value
|
|
@@ -109,52 +189,71 @@ class auto(int):
|
|
|
109
189
|
|
|
110
190
|
return super().__new__(cls, value)
|
|
111
191
|
|
|
192
|
+
def __init__(self, **annotations: dict[str, object]):
|
|
193
|
+
super().__init__(self.value, **annotations)
|
|
194
|
+
|
|
112
195
|
|
|
113
196
|
class EnumerationConfiguration(object):
|
|
114
197
|
"""The EnumerationConfiguration class holds the Enumeration configuration options"""
|
|
115
198
|
|
|
116
199
|
_unique: bool = None
|
|
117
|
-
_aliased: bool =
|
|
200
|
+
_aliased: bool = None
|
|
201
|
+
_backfill: bool = None
|
|
118
202
|
_overwritable: bool = None
|
|
119
203
|
_removable: bool = None
|
|
120
204
|
_subclassable: bool = None
|
|
205
|
+
_extensible: bool = None
|
|
121
206
|
_raises: bool = None
|
|
122
207
|
_flags: bool = None
|
|
123
208
|
_start: int = None
|
|
209
|
+
_steps: int = None
|
|
210
|
+
_times: int = None
|
|
124
211
|
_typecast: bool = None
|
|
125
212
|
|
|
126
213
|
def __init__(
|
|
127
214
|
self,
|
|
128
215
|
unique: bool = None,
|
|
129
216
|
aliased: bool = None,
|
|
217
|
+
backfill: bool = None,
|
|
130
218
|
overwritable: bool = None,
|
|
131
219
|
removable: bool = None,
|
|
132
220
|
subclassable: bool = None,
|
|
221
|
+
extensible: bool = None,
|
|
133
222
|
raises: bool = None,
|
|
134
223
|
flags: bool = None,
|
|
135
224
|
start: int = None,
|
|
225
|
+
steps: int = None,
|
|
226
|
+
times: int = None,
|
|
136
227
|
typecast: bool = None,
|
|
137
228
|
):
|
|
138
229
|
self.unique = unique
|
|
139
230
|
self.aliased = aliased
|
|
231
|
+
self.backfill = backfill
|
|
140
232
|
self.overwritable = overwritable
|
|
141
233
|
self.removable = removable
|
|
142
234
|
self.subclassable = subclassable
|
|
235
|
+
self.extensible = extensible
|
|
143
236
|
self.raises = raises
|
|
144
237
|
self.flags = flags
|
|
145
238
|
self.start = start
|
|
239
|
+
self.steps = steps
|
|
240
|
+
self.times = times
|
|
146
241
|
self.typecast = typecast
|
|
147
242
|
|
|
148
243
|
def __dir__(self) -> list[str]:
|
|
149
244
|
return [
|
|
150
245
|
"unique",
|
|
151
246
|
"aliased",
|
|
247
|
+
"backfill",
|
|
152
248
|
"overwritable",
|
|
153
249
|
"removable",
|
|
154
250
|
"subclassable",
|
|
251
|
+
"extensible",
|
|
155
252
|
"raises",
|
|
156
253
|
"flags",
|
|
157
254
|
"start",
|
|
255
|
+
"steps",
|
|
256
|
+
"times",
|
|
158
257
|
"typecast",
|
|
159
258
|
]
|
|
160
259
|
|
|
@@ -220,6 +319,20 @@ class EnumerationConfiguration(object):
|
|
|
220
319
|
)
|
|
221
320
|
self._aliased = aliased
|
|
222
321
|
|
|
322
|
+
@property
|
|
323
|
+
def backfill(self) -> bool | None:
|
|
324
|
+
return self._backfill
|
|
325
|
+
|
|
326
|
+
@backfill.setter
|
|
327
|
+
def backfill(self, backfill: bool | None):
|
|
328
|
+
if backfill is None:
|
|
329
|
+
pass
|
|
330
|
+
elif not isinstance(backfill, bool):
|
|
331
|
+
raise TypeError(
|
|
332
|
+
"The 'backfill' argument, if specified, must have a boolean value!"
|
|
333
|
+
)
|
|
334
|
+
self._backfill = backfill
|
|
335
|
+
|
|
223
336
|
@property
|
|
224
337
|
def overwritable(self) -> bool | None:
|
|
225
338
|
return self._overwritable
|
|
@@ -262,6 +375,20 @@ class EnumerationConfiguration(object):
|
|
|
262
375
|
)
|
|
263
376
|
self._subclassable = subclassable
|
|
264
377
|
|
|
378
|
+
@property
|
|
379
|
+
def extensible(self) -> bool | None:
|
|
380
|
+
return self._extensible
|
|
381
|
+
|
|
382
|
+
@extensible.setter
|
|
383
|
+
def extensible(self, extensible: bool | None):
|
|
384
|
+
if extensible is None:
|
|
385
|
+
pass
|
|
386
|
+
elif not isinstance(extensible, bool):
|
|
387
|
+
raise TypeError(
|
|
388
|
+
"The 'extensible' argument, if specified, must have a boolean value!"
|
|
389
|
+
)
|
|
390
|
+
self._extensible = extensible
|
|
391
|
+
|
|
265
392
|
@property
|
|
266
393
|
def raises(self) -> bool | None:
|
|
267
394
|
return self._raises
|
|
@@ -291,7 +418,7 @@ class EnumerationConfiguration(object):
|
|
|
291
418
|
self._flags = flags
|
|
292
419
|
|
|
293
420
|
@property
|
|
294
|
-
def start(self) ->
|
|
421
|
+
def start(self) -> int | None:
|
|
295
422
|
return self._start
|
|
296
423
|
|
|
297
424
|
@start.setter
|
|
@@ -304,6 +431,34 @@ class EnumerationConfiguration(object):
|
|
|
304
431
|
)
|
|
305
432
|
self._start = start
|
|
306
433
|
|
|
434
|
+
@property
|
|
435
|
+
def steps(self) -> int | None:
|
|
436
|
+
return self._steps
|
|
437
|
+
|
|
438
|
+
@steps.setter
|
|
439
|
+
def steps(self, steps: int | None):
|
|
440
|
+
if steps is None:
|
|
441
|
+
pass
|
|
442
|
+
elif not (isinstance(steps, int) and steps >= 0):
|
|
443
|
+
raise TypeError(
|
|
444
|
+
"The 'steps' argument, if specified, must have a positive integer value!"
|
|
445
|
+
)
|
|
446
|
+
self._steps = steps
|
|
447
|
+
|
|
448
|
+
@property
|
|
449
|
+
def times(self) -> int | None:
|
|
450
|
+
return self._times
|
|
451
|
+
|
|
452
|
+
@times.setter
|
|
453
|
+
def times(self, times: int | None):
|
|
454
|
+
if times is None:
|
|
455
|
+
pass
|
|
456
|
+
elif not (isinstance(times, int) and times >= 0):
|
|
457
|
+
raise TypeError(
|
|
458
|
+
"The 'times' argument, if specified, must have a positive integer value!"
|
|
459
|
+
)
|
|
460
|
+
self._times = times
|
|
461
|
+
|
|
307
462
|
@property
|
|
308
463
|
def typecast(self) -> bool | None:
|
|
309
464
|
return self._typecast
|
|
@@ -345,12 +500,16 @@ class EnumerationMetaClass(type):
|
|
|
345
500
|
bases: tuple[type],
|
|
346
501
|
unique: bool = None,
|
|
347
502
|
aliased: bool = None,
|
|
503
|
+
backfill: bool = None,
|
|
348
504
|
overwritable: bool = None,
|
|
349
505
|
subclassable: bool = None,
|
|
506
|
+
extensible: bool = None,
|
|
350
507
|
removable: bool = None,
|
|
351
508
|
raises: bool = None,
|
|
352
509
|
flags: bool = None,
|
|
353
510
|
start: int = None,
|
|
511
|
+
steps: int = None,
|
|
512
|
+
times: int = None,
|
|
354
513
|
typecast: bool = None,
|
|
355
514
|
**kwargs,
|
|
356
515
|
) -> dict:
|
|
@@ -361,18 +520,22 @@ class EnumerationMetaClass(type):
|
|
|
361
520
|
any other keyword arguments that are included in the class signature call."""
|
|
362
521
|
|
|
363
522
|
logger.debug(
|
|
364
|
-
"[EnumerationMetaClass] %s.__prepare__(name: %s, bases: %s, unique: %s, aliased: %s, overwritable: %s, subclassable: %s, removable: %s, raises: %s, flags: %s, start: %s, typecast: %s, kwargs: %s)",
|
|
523
|
+
"[EnumerationMetaClass] %s.__prepare__(name: %s, bases: %s, unique: %s, aliased: %s, backfill: %s, overwritable: %s, subclassable: %s, extensible: %s, removable: %s, raises: %s, flags: %s, start: %s, steps: %s, times: %s, typecast: %s, kwargs: %s)",
|
|
365
524
|
name,
|
|
366
525
|
name,
|
|
367
526
|
bases,
|
|
368
527
|
unique,
|
|
369
528
|
aliased,
|
|
529
|
+
backfill,
|
|
370
530
|
overwritable,
|
|
371
531
|
subclassable,
|
|
532
|
+
extensible,
|
|
372
533
|
removable,
|
|
373
534
|
raises,
|
|
374
535
|
flags,
|
|
375
536
|
start,
|
|
537
|
+
steps,
|
|
538
|
+
times,
|
|
376
539
|
typecast,
|
|
377
540
|
kwargs,
|
|
378
541
|
)
|
|
@@ -430,9 +593,27 @@ class EnumerationMetaClass(type):
|
|
|
430
593
|
"The 'start' argument, if specified, must have a positive integer value!"
|
|
431
594
|
)
|
|
432
595
|
|
|
596
|
+
if steps is None:
|
|
597
|
+
pass
|
|
598
|
+
elif isinstance(steps, int) and steps >= 1:
|
|
599
|
+
pass
|
|
600
|
+
else:
|
|
601
|
+
raise TypeError(
|
|
602
|
+
"The 'steps' argument, if specified, must have a positive integer value!"
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
if times is None:
|
|
606
|
+
pass
|
|
607
|
+
elif isinstance(times, int) and times >= 1:
|
|
608
|
+
pass
|
|
609
|
+
else:
|
|
610
|
+
raise TypeError(
|
|
611
|
+
"The 'times' argument, if specified, must have a positive integer value!"
|
|
612
|
+
)
|
|
613
|
+
|
|
433
614
|
# Configure the auto() class for subsequent use, resetting the sequence, setting
|
|
434
615
|
# the new start value, and whether values should be flag values (powers of 2)
|
|
435
|
-
auto.configure(start=start, flags=flags)
|
|
616
|
+
auto.configure(start=start, steps=steps, times=times, flags=flags)
|
|
436
617
|
|
|
437
618
|
return dict()
|
|
438
619
|
|
|
@@ -441,12 +622,16 @@ class EnumerationMetaClass(type):
|
|
|
441
622
|
*args,
|
|
442
623
|
unique: bool = None, # True
|
|
443
624
|
aliased: bool = None, # False
|
|
625
|
+
backfill: bool = None, # False
|
|
444
626
|
overwritable: bool = None, # False
|
|
445
627
|
subclassable: bool = None, # True
|
|
628
|
+
extensible: bool = None, # True
|
|
446
629
|
removable: bool = None, # False
|
|
447
630
|
raises: bool = None, # False
|
|
448
631
|
flags: bool = None, # False
|
|
449
632
|
start: int = None, # None
|
|
633
|
+
steps: int = None, # None
|
|
634
|
+
times: int = None, # None
|
|
450
635
|
typecast: bool = None, # True
|
|
451
636
|
**kwargs,
|
|
452
637
|
):
|
|
@@ -471,6 +656,13 @@ class EnumerationMetaClass(type):
|
|
|
471
656
|
"The 'aliased' argument, if specified, must have a boolean value!"
|
|
472
657
|
)
|
|
473
658
|
|
|
659
|
+
if backfill is None:
|
|
660
|
+
pass
|
|
661
|
+
elif not isinstance(backfill, bool):
|
|
662
|
+
raise TypeError(
|
|
663
|
+
"The 'backfill' argument, if specified, must have a boolean value!"
|
|
664
|
+
)
|
|
665
|
+
|
|
474
666
|
if overwritable is None:
|
|
475
667
|
pass
|
|
476
668
|
elif not isinstance(overwritable, bool):
|
|
@@ -485,6 +677,13 @@ class EnumerationMetaClass(type):
|
|
|
485
677
|
"The 'subclassable' argument, if specified, must have a boolean value!"
|
|
486
678
|
)
|
|
487
679
|
|
|
680
|
+
if extensible is None:
|
|
681
|
+
pass
|
|
682
|
+
elif not isinstance(extensible, bool):
|
|
683
|
+
raise TypeError(
|
|
684
|
+
"The 'extensible' argument, if specified, must have a boolean value!"
|
|
685
|
+
)
|
|
686
|
+
|
|
488
687
|
if removable is None:
|
|
489
688
|
pass
|
|
490
689
|
elif not isinstance(removable, bool):
|
|
@@ -513,6 +712,20 @@ class EnumerationMetaClass(type):
|
|
|
513
712
|
"The 'start' argument, if specified, must have a positive integer value!"
|
|
514
713
|
)
|
|
515
714
|
|
|
715
|
+
if steps is None:
|
|
716
|
+
pass
|
|
717
|
+
elif not (isinstance(steps, int) and steps >= 0):
|
|
718
|
+
raise TypeError(
|
|
719
|
+
"The 'steps' argument, if specified, must have a positive integer value!"
|
|
720
|
+
)
|
|
721
|
+
|
|
722
|
+
if times is None:
|
|
723
|
+
pass
|
|
724
|
+
elif not (isinstance(times, int) and times >= 0):
|
|
725
|
+
raise TypeError(
|
|
726
|
+
"The 'times' argument, if specified, must have a positive integer value!"
|
|
727
|
+
)
|
|
728
|
+
|
|
516
729
|
if typecast is None:
|
|
517
730
|
pass
|
|
518
731
|
elif not isinstance(typecast, bool):
|
|
@@ -523,12 +736,16 @@ class EnumerationMetaClass(type):
|
|
|
523
736
|
configuration = EnumerationConfiguration(
|
|
524
737
|
unique=unique,
|
|
525
738
|
aliased=aliased,
|
|
739
|
+
backfill=backfill,
|
|
526
740
|
overwritable=overwritable,
|
|
527
741
|
subclassable=subclassable,
|
|
742
|
+
extensible=extensible,
|
|
528
743
|
removable=removable,
|
|
529
744
|
raises=raises,
|
|
530
745
|
flags=flags,
|
|
531
746
|
start=start,
|
|
747
|
+
steps=steps,
|
|
748
|
+
times=times,
|
|
532
749
|
typecast=typecast,
|
|
533
750
|
)
|
|
534
751
|
|
|
@@ -543,6 +760,7 @@ class EnumerationMetaClass(type):
|
|
|
543
760
|
return super().__new__(cls, *args, **kwargs)
|
|
544
761
|
|
|
545
762
|
enumerations: dict[str, object] = {} # Keep track of the enumeration options
|
|
763
|
+
annotations: dict[str, dict] = {} # Keep track of the enumeration annotations
|
|
546
764
|
|
|
547
765
|
names: list[object] = [] # Keep track of the option names to check uniqueness
|
|
548
766
|
values: list[object] = [] # Keep track of the option values to check uniqueness
|
|
@@ -587,12 +805,18 @@ class EnumerationMetaClass(type):
|
|
|
587
805
|
logger.debug(
|
|
588
806
|
" >>> aliased => %s", base_configuration.aliased
|
|
589
807
|
)
|
|
808
|
+
logger.debug(
|
|
809
|
+
" >>> backfill => %s", base_configuration.backfill
|
|
810
|
+
)
|
|
590
811
|
logger.debug(
|
|
591
812
|
" >>> overwritable => %s", base_configuration.overwritable
|
|
592
813
|
)
|
|
593
814
|
logger.debug(
|
|
594
815
|
" >>> subclassable => %s", base_configuration.subclassable
|
|
595
816
|
)
|
|
817
|
+
logger.debug(
|
|
818
|
+
" >>> extensible => %s", base_configuration.extensible
|
|
819
|
+
)
|
|
596
820
|
logger.debug(
|
|
597
821
|
" >>> removable => %s", base_configuration.removable
|
|
598
822
|
)
|
|
@@ -605,13 +829,22 @@ class EnumerationMetaClass(type):
|
|
|
605
829
|
logger.debug(
|
|
606
830
|
" >>> start => %s", base_configuration.start
|
|
607
831
|
)
|
|
832
|
+
logger.debug(
|
|
833
|
+
" >>> steps => %s", base_configuration.steps
|
|
834
|
+
)
|
|
835
|
+
logger.debug(
|
|
836
|
+
" >>> times => %s", base_configuration.times
|
|
837
|
+
)
|
|
608
838
|
logger.debug(
|
|
609
839
|
" >>> typecast => %s", base_configuration.typecast
|
|
610
840
|
)
|
|
611
841
|
|
|
612
|
-
if
|
|
842
|
+
if (
|
|
843
|
+
base_configuration.subclassable is False
|
|
844
|
+
or base_configuration.extensible is False
|
|
845
|
+
):
|
|
613
846
|
raise EnumerationSubclassingError(
|
|
614
|
-
"The '%s' enumeration class cannot be subclassed when the keyword
|
|
847
|
+
"The '%s' enumeration class cannot be subclassed when the keyword arguments 'subclassable=False' or 'extensible=False` are passed to the class constructor!"
|
|
615
848
|
% (base.__name__)
|
|
616
849
|
)
|
|
617
850
|
|
|
@@ -626,6 +859,9 @@ class EnumerationMetaClass(type):
|
|
|
626
859
|
logger.debug(
|
|
627
860
|
" >>> (updated) aliased => %s", configuration.aliased
|
|
628
861
|
)
|
|
862
|
+
logger.debug(
|
|
863
|
+
" >>> (updated) backfill => %s", configuration.backfill
|
|
864
|
+
)
|
|
629
865
|
logger.debug(
|
|
630
866
|
" >>> (updated) overwritable => %s",
|
|
631
867
|
configuration.overwritable,
|
|
@@ -634,6 +870,10 @@ class EnumerationMetaClass(type):
|
|
|
634
870
|
" >>> (updated) subclassable => %s",
|
|
635
871
|
configuration.subclassable,
|
|
636
872
|
)
|
|
873
|
+
logger.debug(
|
|
874
|
+
" >>> (updated) extensible => %s",
|
|
875
|
+
configuration.extensible,
|
|
876
|
+
)
|
|
637
877
|
logger.debug(
|
|
638
878
|
" >>> (updated) removable => %s", configuration.removable
|
|
639
879
|
)
|
|
@@ -646,6 +886,12 @@ class EnumerationMetaClass(type):
|
|
|
646
886
|
logger.debug(
|
|
647
887
|
" >>> (updated) start => %s", configuration.start
|
|
648
888
|
)
|
|
889
|
+
logger.debug(
|
|
890
|
+
" >>> (updated) steps => %s", configuration.steps
|
|
891
|
+
)
|
|
892
|
+
logger.debug(
|
|
893
|
+
" >>> (updated) times => %s", configuration.times
|
|
894
|
+
)
|
|
649
895
|
logger.debug(
|
|
650
896
|
" >>> (updated) typecast => %s", configuration.typecast
|
|
651
897
|
)
|
|
@@ -653,7 +899,6 @@ class EnumerationMetaClass(type):
|
|
|
653
899
|
# logger.debug(" >>> found base (%s) that is an instance of EnumerationMetaClass and a subclass of Enumeration" % (base))
|
|
654
900
|
|
|
655
901
|
if not (base is Enumeration or Enumeration in base.__bases__):
|
|
656
|
-
# enumerations = base._enumerations # reference to the _enumerations dictionary
|
|
657
902
|
_enumerations = base._enumerations
|
|
658
903
|
|
|
659
904
|
logger.debug(" >>> enumerations => %s" % (base._enumerations))
|
|
@@ -675,29 +920,37 @@ class EnumerationMetaClass(type):
|
|
|
675
920
|
configuration.defaults(
|
|
676
921
|
unique=True,
|
|
677
922
|
aliased=False,
|
|
923
|
+
backfill=False,
|
|
678
924
|
overwritable=False,
|
|
679
925
|
subclassable=True,
|
|
926
|
+
extensible=True,
|
|
680
927
|
removable=False,
|
|
681
928
|
raises=False,
|
|
682
929
|
flags=False,
|
|
683
930
|
start=1,
|
|
931
|
+
steps=1,
|
|
932
|
+
times=None,
|
|
684
933
|
typecast=True,
|
|
685
934
|
)
|
|
686
935
|
|
|
687
936
|
logger.debug(" >>> (after defaults) unique => %s", configuration.unique)
|
|
688
937
|
logger.debug(" >>> (after defaults) aliased => %s", configuration.aliased)
|
|
938
|
+
logger.debug(" >>> (after defaults) backfill => %s", configuration.backfill)
|
|
689
939
|
logger.debug(
|
|
690
940
|
" >>> (after defaults) overwritable => %s", configuration.overwritable
|
|
691
941
|
)
|
|
692
942
|
logger.debug(
|
|
693
943
|
" >>> (after defaults) subclassable => %s", configuration.subclassable
|
|
694
944
|
)
|
|
945
|
+
logger.debug(" >>> (after defaults) extensible => %s", configuration.extensible)
|
|
695
946
|
logger.debug(
|
|
696
947
|
" >>> (after defaults) removable => %s", configuration.removable
|
|
697
948
|
)
|
|
698
949
|
logger.debug(" >>> (after defaults) raises => %s", configuration.raises)
|
|
699
950
|
logger.debug(" >>> (after defaults) flags => %s", configuration.flags)
|
|
700
951
|
logger.debug(" >>> (after defaults) start => %s", configuration.start)
|
|
952
|
+
logger.debug(" >>> (after defaults) steps => %s", configuration.steps)
|
|
953
|
+
logger.debug(" >>> (after defaults) times => %s", configuration.times)
|
|
701
954
|
logger.debug(" >>> (after defaults) typecast => %s", configuration.typecast)
|
|
702
955
|
|
|
703
956
|
# Iterate over the class attributes, looking for any enumeration options
|
|
@@ -709,6 +962,12 @@ class EnumerationMetaClass(type):
|
|
|
709
962
|
% (index, attribute, value, type(value))
|
|
710
963
|
)
|
|
711
964
|
|
|
965
|
+
if isinstance(value, auto):
|
|
966
|
+
annotations[attribute] = value
|
|
967
|
+
elif isinstance(value, anno):
|
|
968
|
+
annotations[attribute] = value
|
|
969
|
+
value = value.unwrap() # unwrap the annotated value
|
|
970
|
+
|
|
712
971
|
if attribute.startswith("_") or attribute in cls._special:
|
|
713
972
|
continue
|
|
714
973
|
elif attribute in names:
|
|
@@ -730,8 +989,8 @@ class EnumerationMetaClass(type):
|
|
|
730
989
|
)
|
|
731
990
|
else:
|
|
732
991
|
raise EnumerationNonUniqueError(
|
|
733
|
-
"The enumeration option, '%s', has a non-unique value, %r, however, unless either the keyword argument 'unique=False' or 'aliased=True' are passed during class construction, all enumeration options must have unique values!"
|
|
734
|
-
% (attribute, value)
|
|
992
|
+
"The enumeration option, '%s', has a non-unique value, %r, however, unless either the keyword argument 'unique=False' or 'aliased=True' are passed during class construction, all enumeration options must have unique values; existing values: %s!"
|
|
993
|
+
% (attribute, value, values)
|
|
735
994
|
)
|
|
736
995
|
else:
|
|
737
996
|
logger.debug(
|
|
@@ -763,6 +1022,8 @@ class EnumerationMetaClass(type):
|
|
|
763
1022
|
if isinstance(_enumerations, dict):
|
|
764
1023
|
attributes["base_enumerations"] = _enumerations
|
|
765
1024
|
|
|
1025
|
+
attributes["annotations"] = annotations
|
|
1026
|
+
|
|
766
1027
|
# If the new enumeration class is not subclassing an existing enumeration class
|
|
767
1028
|
if configuration.typecast is True and (
|
|
768
1029
|
(baseclass is None) or (baseclass is Enumeration)
|
|
@@ -815,31 +1076,26 @@ class EnumerationMetaClass(type):
|
|
|
815
1076
|
|
|
816
1077
|
logger.debug(" >>> bases => %s", [base for base in bases])
|
|
817
1078
|
|
|
818
|
-
# if "EnumerationInteger" in globals():
|
|
819
|
-
# if EnumerationInteger in bases and EnumerationFlag in bases:
|
|
820
|
-
# bases = tuple([base for base in bases if not EnumerationInteger])
|
|
821
|
-
|
|
822
1079
|
args: tuple[object] = (name, bases, attributes)
|
|
823
1080
|
|
|
824
1081
|
# Create the new enumeration class instance
|
|
825
1082
|
instance = super().__new__(cls, *args, **kwargs)
|
|
826
1083
|
|
|
827
|
-
# logger.debug(
|
|
828
|
-
# " >>> metaclass => %s (base: %s, type: %s, bases: %s)\n"
|
|
829
|
-
# % (enumclass, instance, type(instance), instance.__bases__)
|
|
830
|
-
# )
|
|
831
|
-
# logger.debug(" >>> metaclass => %s (base: %s, type: %s, bases: %s)\n" % (enumclass, instance, type(instance), instance.__bases__))
|
|
832
|
-
|
|
833
1084
|
logger.debug(" >>> baseclass => %s", baseclass)
|
|
834
1085
|
logger.debug(" >>> instance => %s", instance)
|
|
835
1086
|
|
|
836
1087
|
logger.debug(" >>> unique => %s", configuration.unique)
|
|
837
1088
|
logger.debug(" >>> aliased => %s", configuration.aliased)
|
|
1089
|
+
logger.debug(" >>> backfill => %s", configuration.backfill)
|
|
838
1090
|
logger.debug(" >>> overwritable => %s", configuration.overwritable)
|
|
839
1091
|
logger.debug(" >>> subclassable => %s", configuration.subclassable)
|
|
1092
|
+
logger.debug(" >>> extensible => %s", configuration.extensible)
|
|
840
1093
|
logger.debug(" >>> removable => %s", configuration.removable)
|
|
841
1094
|
logger.debug(" >>> raises => %s", configuration.raises)
|
|
842
1095
|
logger.debug(" >>> flags => %s", configuration.flags)
|
|
1096
|
+
logger.debug(" >>> start => %s", configuration.start)
|
|
1097
|
+
logger.debug(" >>> steps => %s", configuration.steps)
|
|
1098
|
+
logger.debug(" >>> times => %s", configuration.times)
|
|
843
1099
|
logger.debug(" >>> typecast => %s", configuration.typecast)
|
|
844
1100
|
|
|
845
1101
|
# Store the enumeration class configuration options for future reference
|
|
@@ -862,7 +1118,16 @@ class EnumerationMetaClass(type):
|
|
|
862
1118
|
)
|
|
863
1119
|
|
|
864
1120
|
if isinstance(base_enumerations := attributes.get("base_enumerations"), dict):
|
|
865
|
-
|
|
1121
|
+
if (
|
|
1122
|
+
self._configuration.backfill is True
|
|
1123
|
+
and self._configuration.extensible is True
|
|
1124
|
+
):
|
|
1125
|
+
self._enumerations: dict[str, Enumeration] = base_enumerations
|
|
1126
|
+
else:
|
|
1127
|
+
self._enumerations: dict[str, Enumeration] = {}
|
|
1128
|
+
|
|
1129
|
+
for enumeration_name, enumeration in base_enumerations.items():
|
|
1130
|
+
self._enumerations[enumeration_name] = enumeration
|
|
866
1131
|
else:
|
|
867
1132
|
self._enumerations: dict[str, Enumeration] = {}
|
|
868
1133
|
|
|
@@ -876,6 +1141,8 @@ class EnumerationMetaClass(type):
|
|
|
876
1141
|
logger.debug("+" * 100)
|
|
877
1142
|
|
|
878
1143
|
if isinstance(enumerations := attributes.get("enumerations"), dict):
|
|
1144
|
+
annotations: dict[str, anno] = attributes.get("annotations") or {}
|
|
1145
|
+
|
|
879
1146
|
for attribute, value in enumerations.items():
|
|
880
1147
|
if attribute in self._enumerations:
|
|
881
1148
|
continue
|
|
@@ -913,6 +1180,7 @@ class EnumerationMetaClass(type):
|
|
|
913
1180
|
enumeration=self,
|
|
914
1181
|
name=attribute,
|
|
915
1182
|
value=value,
|
|
1183
|
+
annotations=annotations.get(attribute),
|
|
916
1184
|
)
|
|
917
1185
|
|
|
918
1186
|
logger.debug(
|
|
@@ -935,24 +1203,32 @@ class EnumerationMetaClass(type):
|
|
|
935
1203
|
elif self._enumerations and name in self._enumerations:
|
|
936
1204
|
return self._enumerations[name]
|
|
937
1205
|
else:
|
|
1206
|
+
# EnumerationOptionError subclasses AttributeError so we adhere to convention
|
|
938
1207
|
raise EnumerationOptionError(
|
|
939
|
-
"The '%s' enumeration class, has no '%s' enumeration option!"
|
|
1208
|
+
"The '%s' enumeration class, has no '%s' enumeration option nor annotation property!"
|
|
940
1209
|
% (self.__name__, name)
|
|
941
1210
|
)
|
|
942
1211
|
|
|
943
1212
|
def __dir__(self) -> list[str]:
|
|
944
|
-
members:
|
|
945
|
-
|
|
946
|
-
for name, enumeration in self._enumerations.items():
|
|
947
|
-
members.append(name)
|
|
1213
|
+
members: set[str] = set()
|
|
948
1214
|
|
|
949
1215
|
for member in object.__dir__(self):
|
|
950
1216
|
if member.startswith("_") or member in self._special:
|
|
951
|
-
members.
|
|
1217
|
+
members.add(member)
|
|
1218
|
+
|
|
1219
|
+
for name, enumeration in self._enumerations.items():
|
|
1220
|
+
members.add(name)
|
|
952
1221
|
|
|
953
|
-
|
|
1222
|
+
for member in vars(self):
|
|
1223
|
+
members.add(member)
|
|
1224
|
+
|
|
1225
|
+
return list(members)
|
|
954
1226
|
|
|
955
1227
|
def __contains__(self, other: Enumeration | object) -> bool:
|
|
1228
|
+
logger.debug(
|
|
1229
|
+
"%s(%s).__contains__(other: %s)", self.__class__.__name__, self, other
|
|
1230
|
+
)
|
|
1231
|
+
|
|
956
1232
|
contains: bool = False
|
|
957
1233
|
|
|
958
1234
|
for name, enumeration in self._enumerations.items():
|
|
@@ -1132,6 +1408,12 @@ class EnumerationMetaClass(type):
|
|
|
1132
1408
|
value,
|
|
1133
1409
|
)
|
|
1134
1410
|
|
|
1411
|
+
if self.configuration.extensible is False:
|
|
1412
|
+
raise EnumerationExtensibilityError(
|
|
1413
|
+
"The '%s' enumeration class has been configured to prevent extensibility, so cannot be extended with new options either through registration or subclassing, so the '%s' option cannot be registered!"
|
|
1414
|
+
% (self.__name__, name)
|
|
1415
|
+
)
|
|
1416
|
+
|
|
1135
1417
|
if self.configuration.overwritable is False and name in self._enumerations:
|
|
1136
1418
|
raise EnumerationNonUniqueError(
|
|
1137
1419
|
"The '%s' enumeration class already has an option named '%s', so a new option with the same name cannot be created unless the 'overwritable=True' argument is passed during class construction!"
|
|
@@ -1175,11 +1457,12 @@ class EnumerationMetaClass(type):
|
|
|
1175
1457
|
self,
|
|
1176
1458
|
value: Enumeration | object = None,
|
|
1177
1459
|
name: str = None,
|
|
1460
|
+
caselessly: bool = False,
|
|
1178
1461
|
) -> Enumeration | None:
|
|
1179
1462
|
"""The 'reconcile' method can be used to reconcile Enumeration type, enumeration
|
|
1180
1463
|
values, or enumeration names to their matching Enumeration type instances. If a
|
|
1181
|
-
match is found the Enumeration type instance will be returned otherwise None
|
|
1182
|
-
|
|
1464
|
+
match is found the Enumeration type instance will be returned otherwise None will
|
|
1465
|
+
be returned, unless the class is configured to raise an error for mismatches."""
|
|
1183
1466
|
|
|
1184
1467
|
if name is None and value is None:
|
|
1185
1468
|
raise ValueError(
|
|
@@ -1197,14 +1480,20 @@ class EnumerationMetaClass(type):
|
|
|
1197
1480
|
reconciled: Enumeration = None
|
|
1198
1481
|
|
|
1199
1482
|
for attribute, enumeration in self._enumerations.items():
|
|
1200
|
-
if isinstance(
|
|
1201
|
-
reconciled = enumeration
|
|
1202
|
-
break
|
|
1203
|
-
elif isinstance(value, Enumeration):
|
|
1483
|
+
if isinstance(value, Enumeration):
|
|
1204
1484
|
if enumeration is value:
|
|
1205
1485
|
reconciled = enumeration
|
|
1206
1486
|
break
|
|
1207
|
-
elif isinstance(
|
|
1487
|
+
elif isinstance(name, str) and (
|
|
1488
|
+
(enumeration.name == name)
|
|
1489
|
+
or (caselessly and (enumeration.name.casefold() == name.casefold()))
|
|
1490
|
+
):
|
|
1491
|
+
reconciled = enumeration
|
|
1492
|
+
break
|
|
1493
|
+
elif isinstance(value, str) and (
|
|
1494
|
+
(enumeration.name == value)
|
|
1495
|
+
or (caselessly and (enumeration.name.casefold() == value.casefold()))
|
|
1496
|
+
):
|
|
1208
1497
|
reconciled = enumeration
|
|
1209
1498
|
break
|
|
1210
1499
|
elif enumeration.value == value:
|
|
@@ -1229,6 +1518,14 @@ class EnumerationMetaClass(type):
|
|
|
1229
1518
|
)
|
|
1230
1519
|
)
|
|
1231
1520
|
|
|
1521
|
+
# When an enumeration option is reconciled, it may be defined in another class
|
|
1522
|
+
# but have been accessed through a subclass; in order for attribute lookups to
|
|
1523
|
+
# work within the subclass, we need to provide the current lookup context to the
|
|
1524
|
+
# reconciled enumeration option, so that any attribute access on this object can
|
|
1525
|
+
# perform their lookup in the correct part of the class hierarchy
|
|
1526
|
+
if isinstance(reconciled, Enumeration):
|
|
1527
|
+
reconciled._context = self
|
|
1528
|
+
|
|
1232
1529
|
return reconciled
|
|
1233
1530
|
|
|
1234
1531
|
def validate(self, value: Enumeration | object = None, name: str = None) -> bool:
|
|
@@ -1238,13 +1535,20 @@ class EnumerationMetaClass(type):
|
|
|
1238
1535
|
|
|
1239
1536
|
return not self.reconcile(value=value, name=name) is None
|
|
1240
1537
|
|
|
1538
|
+
def options(self) -> MappingProxyType[str, Enumeration]:
|
|
1539
|
+
"""The 'options' method returns a read-only mapping proxy of the options."""
|
|
1540
|
+
|
|
1541
|
+
return MappingProxyType(self._enumerations)
|
|
1542
|
+
|
|
1241
1543
|
|
|
1242
1544
|
class Enumeration(metaclass=EnumerationMetaClass):
|
|
1243
1545
|
"""The Enumeration class is the subclass of all enumerations and their subtypes."""
|
|
1244
1546
|
|
|
1245
1547
|
_metaclass: EnumerationMetaClass = None
|
|
1548
|
+
_context: EnumerationMetaClass = None
|
|
1246
1549
|
_enumeration: Enumeration = None
|
|
1247
1550
|
_enumerations: dict[str, Enumeration] = None
|
|
1551
|
+
_annotations: anno = None
|
|
1248
1552
|
_name: str = None
|
|
1249
1553
|
_value: object = None
|
|
1250
1554
|
_aliased: Enumeration = None
|
|
@@ -1257,6 +1561,7 @@ class Enumeration(metaclass=EnumerationMetaClass):
|
|
|
1257
1561
|
name: str = None,
|
|
1258
1562
|
value: object = None,
|
|
1259
1563
|
aliased: Enumeration = None,
|
|
1564
|
+
annotations: anno = None,
|
|
1260
1565
|
**kwargs,
|
|
1261
1566
|
) -> Enumeration | None:
|
|
1262
1567
|
# Supports reconciling enumeration options via their name/value via __new__ call
|
|
@@ -1264,12 +1569,14 @@ class Enumeration(metaclass=EnumerationMetaClass):
|
|
|
1264
1569
|
value = args[0]
|
|
1265
1570
|
|
|
1266
1571
|
logger.debug(
|
|
1267
|
-
"[Enumeration] %s.__new__(args: %s, enumeration: %s, name: %s, value: %s, kwargs: %s)",
|
|
1572
|
+
"[Enumeration] %s.__new__(args: %s, enumeration: %s, name: %s, value: %s, aliased: %s, annotations: %s, kwargs: %s)",
|
|
1268
1573
|
cls.__name__,
|
|
1269
1574
|
args,
|
|
1270
1575
|
enumeration,
|
|
1271
1576
|
name,
|
|
1272
1577
|
value,
|
|
1578
|
+
aliased,
|
|
1579
|
+
annotations,
|
|
1273
1580
|
kwargs,
|
|
1274
1581
|
)
|
|
1275
1582
|
|
|
@@ -1298,15 +1605,18 @@ class Enumeration(metaclass=EnumerationMetaClass):
|
|
|
1298
1605
|
name: str = None,
|
|
1299
1606
|
value: object = None,
|
|
1300
1607
|
aliased: Enumeration = None,
|
|
1608
|
+
annotations: anno = None,
|
|
1301
1609
|
**kwargs,
|
|
1302
1610
|
) -> None:
|
|
1303
1611
|
logger.debug(
|
|
1304
|
-
"[Enumeration] %s.__init__(args: %s, enumeration: %s, name: %s, value: %s, kwargs: %s)",
|
|
1612
|
+
"[Enumeration] %s.__init__(args: %s, enumeration: %s, name: %s, value: %s, aliased: %s, annotations: %s, kwargs: %s)",
|
|
1305
1613
|
self.__class__.__name__,
|
|
1306
1614
|
args,
|
|
1307
1615
|
enumeration,
|
|
1308
1616
|
name,
|
|
1309
1617
|
value,
|
|
1618
|
+
aliased,
|
|
1619
|
+
annotations,
|
|
1310
1620
|
kwargs,
|
|
1311
1621
|
)
|
|
1312
1622
|
|
|
@@ -1340,6 +1650,15 @@ class Enumeration(metaclass=EnumerationMetaClass):
|
|
|
1340
1650
|
"The 'aliased' argument, if specified, must reference an Enumeration class instance!"
|
|
1341
1651
|
)
|
|
1342
1652
|
|
|
1653
|
+
if annotations is None:
|
|
1654
|
+
pass
|
|
1655
|
+
elif isinstance(annotations, anno):
|
|
1656
|
+
self._annotations = annotations
|
|
1657
|
+
else:
|
|
1658
|
+
raise TypeError(
|
|
1659
|
+
"The 'annotations' argument, if specified, must reference an anno class instance!"
|
|
1660
|
+
)
|
|
1661
|
+
|
|
1343
1662
|
# NOTE: This method is only called if the instance is called via instance(..) syntax
|
|
1344
1663
|
def __call__(self, *args, **kwargs) -> Enumeration | None:
|
|
1345
1664
|
logger.debug(
|
|
@@ -1361,32 +1680,51 @@ class Enumeration(metaclass=EnumerationMetaClass):
|
|
|
1361
1680
|
return id(self)
|
|
1362
1681
|
|
|
1363
1682
|
def __eq__(self, other: Enumeration | object) -> bool:
|
|
1364
|
-
logger.debug("%s.__eq__(other: %s)"
|
|
1683
|
+
logger.debug("%s(%s).__eq__(other: %s)", self.__class__.__name__, self, other)
|
|
1365
1684
|
|
|
1366
1685
|
equals: bool = False
|
|
1367
1686
|
|
|
1368
1687
|
if isinstance(other, Enumeration):
|
|
1369
|
-
|
|
1370
|
-
|
|
1688
|
+
equals = self is other
|
|
1689
|
+
elif self.name == other:
|
|
1690
|
+
equals = True
|
|
1691
|
+
elif self.value == other:
|
|
1692
|
+
equals = True
|
|
1371
1693
|
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1694
|
+
return equals
|
|
1695
|
+
|
|
1696
|
+
def __getattr__(self, name) -> object:
|
|
1697
|
+
"""The '__getattr__' method provides support for accessing attribute values that
|
|
1698
|
+
have been assigned to the current enumeration option. If a matching attribute can
|
|
1699
|
+
be found, its value will be returned, otherwise an exception will be raised."""
|
|
1700
|
+
|
|
1701
|
+
logger.debug("%s.__getattr__(name: %s)", self.__class__.__name__, name)
|
|
1702
|
+
|
|
1703
|
+
if name.startswith("_") or name in self.__class__._special or name in dir(self):
|
|
1704
|
+
return object.__getattribute__(self, name)
|
|
1705
|
+
elif self._enumerations and name in self._enumerations:
|
|
1706
|
+
return self._enumerations[name]
|
|
1707
|
+
elif self._annotations and name in self._annotations:
|
|
1708
|
+
return self._annotations[name]
|
|
1709
|
+
elif self._context and name in dir(self._context):
|
|
1710
|
+
return object.__getattribute__(self._context, name)
|
|
1711
|
+
else:
|
|
1712
|
+
# EnumerationOptionError subclasses AttributeError so we adhere to convention
|
|
1713
|
+
raise EnumerationOptionError(
|
|
1714
|
+
"The '%s' enumeration class, has no '%s' enumeration option nor annotation property!"
|
|
1715
|
+
% (self.__class__.__name__, name)
|
|
1376
1716
|
)
|
|
1377
1717
|
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
equals = True
|
|
1384
|
-
break
|
|
1385
|
-
elif enumeration.name == other:
|
|
1386
|
-
equals = True
|
|
1387
|
-
break
|
|
1718
|
+
def get(self, name: str, default: object = None) -> object | None:
|
|
1719
|
+
"""The 'get' method provides support for accessing annotation values that may
|
|
1720
|
+
have been assigned to the current enumeration option. If a matching annotation
|
|
1721
|
+
can be found, its value will be returned, otherwise the default value will be
|
|
1722
|
+
returned, which defaults to None, but may be specified as any value."""
|
|
1388
1723
|
|
|
1389
|
-
|
|
1724
|
+
if name in self._annotations:
|
|
1725
|
+
return self._annotations[name]
|
|
1726
|
+
else:
|
|
1727
|
+
return default
|
|
1390
1728
|
|
|
1391
1729
|
@property
|
|
1392
1730
|
def enumeration(self) -> Enumeration:
|
|
@@ -1407,21 +1745,43 @@ class Enumeration(metaclass=EnumerationMetaClass):
|
|
|
1407
1745
|
@property
|
|
1408
1746
|
def aliased(self) -> bool:
|
|
1409
1747
|
logger.debug(
|
|
1410
|
-
"%s.aliased() >>> id(
|
|
1748
|
+
"%s.aliased() >>> id(%s) => %s (%s)",
|
|
1411
1749
|
self.__class__.__name__,
|
|
1750
|
+
self,
|
|
1412
1751
|
id(self._enumerations),
|
|
1413
1752
|
type(self._enumerations),
|
|
1414
1753
|
)
|
|
1415
1754
|
|
|
1416
1755
|
for name, enumeration in self._enumerations.items():
|
|
1417
|
-
logger.
|
|
1756
|
+
logger.debug(" >>> checking for alias: %s => %s", name, enumeration)
|
|
1418
1757
|
|
|
1419
1758
|
if isinstance(enumeration, Enumeration):
|
|
1420
|
-
if
|
|
1759
|
+
if self is enumeration and enumeration.name != name:
|
|
1421
1760
|
return True
|
|
1422
1761
|
|
|
1423
1762
|
return False
|
|
1424
1763
|
|
|
1764
|
+
@property
|
|
1765
|
+
def aliases(self) -> list[Enumeration]:
|
|
1766
|
+
logger.debug(
|
|
1767
|
+
"%s.aliases() >>> id(%s) => %s (%s)",
|
|
1768
|
+
self.__class__.__name__,
|
|
1769
|
+
self,
|
|
1770
|
+
id(self._enumerations),
|
|
1771
|
+
type(self._enumerations),
|
|
1772
|
+
)
|
|
1773
|
+
|
|
1774
|
+
aliases: list[Enumeration] = []
|
|
1775
|
+
|
|
1776
|
+
for name, enumeration in self._enumerations.items():
|
|
1777
|
+
logger.debug(" >>> checking for alias: %s => %s", name, enumeration)
|
|
1778
|
+
|
|
1779
|
+
if isinstance(enumeration, Enumeration):
|
|
1780
|
+
if self is enumeration and enumeration.name != name:
|
|
1781
|
+
aliases.append(enumeration)
|
|
1782
|
+
|
|
1783
|
+
return aliases
|
|
1784
|
+
|
|
1425
1785
|
|
|
1426
1786
|
class EnumerationType(Enumeration, typecast=False):
|
|
1427
1787
|
"""The EnumerationType class represents the type of value held by an enumeration."""
|
|
@@ -1462,6 +1822,15 @@ class EnumerationInteger(int, Enumeration):
|
|
|
1462
1822
|
def __repr__(self) -> str:
|
|
1463
1823
|
return Enumeration.__repr__(self)
|
|
1464
1824
|
|
|
1825
|
+
def __hash__(self) -> id:
|
|
1826
|
+
return super().__hash__()
|
|
1827
|
+
|
|
1828
|
+
def __eq__(self, other: object) -> bool:
|
|
1829
|
+
if isinstance(other, (int, self.__class__)):
|
|
1830
|
+
return super().__eq__(other)
|
|
1831
|
+
else:
|
|
1832
|
+
return Enumeration.__eq__(self, other)
|
|
1833
|
+
|
|
1465
1834
|
|
|
1466
1835
|
class EnumerationFloat(float, Enumeration):
|
|
1467
1836
|
"""An Enumeration subclass where all values are float values."""
|
|
@@ -1505,6 +1874,15 @@ class EnumerationComplex(complex, Enumeration):
|
|
|
1505
1874
|
def __repr__(self) -> str:
|
|
1506
1875
|
return Enumeration.__repr__(self)
|
|
1507
1876
|
|
|
1877
|
+
def __hash__(self) -> id:
|
|
1878
|
+
return super().__hash__()
|
|
1879
|
+
|
|
1880
|
+
def __eq__(self, other: object) -> bool:
|
|
1881
|
+
if isinstance(other, (float, self.__class__)):
|
|
1882
|
+
return super().__eq__(other)
|
|
1883
|
+
else:
|
|
1884
|
+
return Enumeration.__eq__(self, other)
|
|
1885
|
+
|
|
1508
1886
|
|
|
1509
1887
|
class EnumerationString(str, Enumeration):
|
|
1510
1888
|
"""An Enumeration subclass where all values are string values."""
|
|
@@ -1528,6 +1906,15 @@ class EnumerationString(str, Enumeration):
|
|
|
1528
1906
|
def __repr__(self) -> str:
|
|
1529
1907
|
return Enumeration.__repr__(self)
|
|
1530
1908
|
|
|
1909
|
+
def __hash__(self) -> id:
|
|
1910
|
+
return super().__hash__()
|
|
1911
|
+
|
|
1912
|
+
def __eq__(self, other: object) -> bool:
|
|
1913
|
+
if isinstance(other, (str, self.__class__)):
|
|
1914
|
+
return super().__eq__(other)
|
|
1915
|
+
else:
|
|
1916
|
+
return Enumeration.__eq__(self, other)
|
|
1917
|
+
|
|
1531
1918
|
|
|
1532
1919
|
class EnumerationBytes(bytes, Enumeration):
|
|
1533
1920
|
"""An Enumeration subclass where all values are bytes values."""
|
|
@@ -1548,6 +1935,15 @@ class EnumerationBytes(bytes, Enumeration):
|
|
|
1548
1935
|
def __repr__(self) -> str:
|
|
1549
1936
|
return Enumeration.__repr__(self)
|
|
1550
1937
|
|
|
1938
|
+
def __hash__(self) -> id:
|
|
1939
|
+
return super().__hash__()
|
|
1940
|
+
|
|
1941
|
+
def __eq__(self, other: object) -> bool:
|
|
1942
|
+
if isinstance(other, (bytes, self.__class__)):
|
|
1943
|
+
return super().__eq__(other)
|
|
1944
|
+
else:
|
|
1945
|
+
return Enumeration.__eq__(self, other)
|
|
1946
|
+
|
|
1551
1947
|
|
|
1552
1948
|
class EnumerationTuple(tuple, Enumeration):
|
|
1553
1949
|
"""An Enumeration subclass where all values are tuple values."""
|
|
@@ -1568,6 +1964,15 @@ class EnumerationTuple(tuple, Enumeration):
|
|
|
1568
1964
|
def __repr__(self) -> str:
|
|
1569
1965
|
return Enumeration.__repr__(self)
|
|
1570
1966
|
|
|
1967
|
+
def __hash__(self) -> id:
|
|
1968
|
+
return super().__hash__()
|
|
1969
|
+
|
|
1970
|
+
def __eq__(self, other: object) -> bool:
|
|
1971
|
+
if isinstance(other, (tuple, self.__class__)):
|
|
1972
|
+
return super().__eq__(other)
|
|
1973
|
+
else:
|
|
1974
|
+
return Enumeration.__eq__(self, other)
|
|
1975
|
+
|
|
1571
1976
|
|
|
1572
1977
|
class EnumerationSet(set, Enumeration):
|
|
1573
1978
|
"""An Enumeration subclass where all values are set values."""
|
|
@@ -1588,6 +1993,15 @@ class EnumerationSet(set, Enumeration):
|
|
|
1588
1993
|
def __repr__(self) -> str:
|
|
1589
1994
|
return Enumeration.__repr__(self)
|
|
1590
1995
|
|
|
1996
|
+
def __hash__(self) -> id:
|
|
1997
|
+
return super().__hash__()
|
|
1998
|
+
|
|
1999
|
+
def __eq__(self, other: object) -> bool:
|
|
2000
|
+
if isinstance(other, (set, self.__class__)):
|
|
2001
|
+
return super().__eq__(other)
|
|
2002
|
+
else:
|
|
2003
|
+
return Enumeration.__eq__(self, other)
|
|
2004
|
+
|
|
1591
2005
|
|
|
1592
2006
|
class EnumerationList(list, Enumeration):
|
|
1593
2007
|
"""An Enumeration subclass where all values are list values."""
|
|
@@ -1608,6 +2022,15 @@ class EnumerationList(list, Enumeration):
|
|
|
1608
2022
|
def __repr__(self) -> str:
|
|
1609
2023
|
return Enumeration.__repr__(self)
|
|
1610
2024
|
|
|
2025
|
+
def __hash__(self) -> id:
|
|
2026
|
+
return super().__hash__()
|
|
2027
|
+
|
|
2028
|
+
def __eq__(self, other: object) -> bool:
|
|
2029
|
+
if isinstance(other, (list, self.__class__)):
|
|
2030
|
+
return super().__eq__(other)
|
|
2031
|
+
else:
|
|
2032
|
+
return Enumeration.__eq__(self, other)
|
|
2033
|
+
|
|
1611
2034
|
|
|
1612
2035
|
class EnumerationDictionary(dict, Enumeration):
|
|
1613
2036
|
"""An Enumeration subclass where all values are dictionary values."""
|
|
@@ -1631,6 +2054,15 @@ class EnumerationDictionary(dict, Enumeration):
|
|
|
1631
2054
|
def __repr__(self) -> str:
|
|
1632
2055
|
return Enumeration.__repr__(self)
|
|
1633
2056
|
|
|
2057
|
+
def __hash__(self) -> id:
|
|
2058
|
+
return super().__hash__()
|
|
2059
|
+
|
|
2060
|
+
def __eq__(self, other: object) -> bool:
|
|
2061
|
+
if isinstance(other, (dict, self.__class__)):
|
|
2062
|
+
return super().__eq__(other)
|
|
2063
|
+
else:
|
|
2064
|
+
return Enumeration.__eq__(self, other)
|
|
2065
|
+
|
|
1634
2066
|
|
|
1635
2067
|
class EnumerationFlag(int, Enumeration):
|
|
1636
2068
|
"""An Enumeration subclass where all values are integer values to the power of 2."""
|
|
@@ -1772,6 +2204,15 @@ class EnumerationFlag(int, Enumeration):
|
|
|
1772
2204
|
def __repr__(self) -> str:
|
|
1773
2205
|
return Enumeration.__repr__(self)
|
|
1774
2206
|
|
|
2207
|
+
def __hash__(self) -> id:
|
|
2208
|
+
return super().__hash__()
|
|
2209
|
+
|
|
2210
|
+
def __eq__(self, other: object) -> bool:
|
|
2211
|
+
if isinstance(other, (int, self.__class__)):
|
|
2212
|
+
return super().__eq__(other)
|
|
2213
|
+
else:
|
|
2214
|
+
return Enumeration.__eq__(self, other)
|
|
2215
|
+
|
|
1775
2216
|
def __or__(self, other: EnumerationFlag): # called for: "a | b" (bitwise or)
|
|
1776
2217
|
"""Support performing a bitwise or between the current EnumerationFlag
|
|
1777
2218
|
instance's bitmask and the 'other' provided EnumerationFlag's bitmask;
|