enumerific 1.0.1__py3-none-any.whl → 1.0.2__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,794 @@
1
+ Metadata-Version: 2.4
2
+ Name: enumerific
3
+ Version: 1.0.2
4
+ Summary: Simplifies working with Python enums.
5
+ Author: Daniel Sissman
6
+ License-Expression: MIT
7
+ Project-URL: documentation, https://github.com/bluebinary/enumerific/blob/main/README.md
8
+ Project-URL: changelog, https://github.com/bluebinary/enumerific/blob/main/CHANGELOG.md
9
+ Project-URL: repository, https://github.com/bluebinary/enumerific
10
+ Project-URL: issues, https://github.com/bluebinary/enumerific/issues
11
+ Project-URL: homepage, https://github.com/bluebinary/enumerific
12
+ Keywords: enum,enumeration,enumerations
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE.md
21
+ Provides-Extra: development
22
+ Requires-Dist: black==24.10.*; extra == "development"
23
+ Requires-Dist: pytest==8.3.*; extra == "development"
24
+ Requires-Dist: pytest-codeblocks==0.17.0; extra == "development"
25
+ Provides-Extra: distribution
26
+ Requires-Dist: build; extra == "distribution"
27
+ Requires-Dist: twine; extra == "distribution"
28
+ Requires-Dist: wheel; extra == "distribution"
29
+ Dynamic: license-file
30
+
31
+ # Enumerific Enums
32
+
33
+ The Enumerific library provides a greenfield implementation of enumerations with many unique features through the library's own `Enumeration` class, as well as separately offering several useful extra methods for the built-in standard library `enums.Enum` type.
34
+
35
+ The Enumerific library's `Enumeration` class offers the following features:
36
+
37
+ * A greenfield implementation of enumerations compatible with Python 3.10 and later versions;
38
+ * Enumerific enumerations can hold option values of the same or mixed types, including `int`, `float`, `complex`, `str`, `bytes`, `set`, `tuple`, `list`, `dict` as well as arbitrary `object` types;
39
+ * Enumerific enumeration options can be accessed directly as native data types and enumeration options can be used anywhere that the corresponding native data types can be used;
40
+ * Support for automatic typecasting of the `Enumeration` base class to support the use of enumeration option values interchangeably with native data type values;
41
+ * Enumerific enumerations options can be added after an `Enumeration` class has been created either through extending an existing enumerations class by subclassing or by registering new options directly on an existing enumerations class via the `.register()` method; this is especially useful for cases where enumeration options may not all be known prior to runtime;
42
+ * Enumerific enumerations options can be removed after an `Enumeration` class has been created via the `.unregister()` method; this specialised behaviour is prevented by default, but can be enabled for advanced use cases;
43
+ * Enforcement of unique values for all options within an enumeration, unless overridden;
44
+ * Support for aliasing enumeration options;
45
+ * Support for redefining enumeration options;
46
+ * Support for automatically generating unique number sequences for enumeration options, including powers of two for bitwise enumeration flags, as well as other sequences such as powers of other numbers and factoring;
47
+ * Support for annotating enumeration options with additional arbitrary key-value pairs, which can be particularly useful for associating additional data with a given enumeration option, which may be accessed later anywhere in code that the enumeration option is available;
48
+ * Simple one-line reconciliation of `Enumeration` class options to the corresponding `enums.Enum` class instance that represents the corresponding option; reconciliation by enumeration option name, value and enumeration class instance reference are all supported through the `.reconcile()` class method;
49
+ * Simple one-line validation of `Enumeration` class options; validation by enumeration option name, value and enumeration class instance reference are all supported through the `.validate()` class method;
50
+ * Simple access to all of the options provided by an `Enumeration` class instance through the `.options()` class method;
51
+ * Access to all of the names of the `Enumeration` class options via the `.names()` method;
52
+ * Access to all of the names/keys of the `Enumeration` class options via the `.keys()` method;
53
+ * Access to all of the values of the `Enumeration` class options via the `.values()` method;
54
+ * Access to all of the key-value pairs of the `Enumeration` class options via the `.items()` method;
55
+ * Ability to determine if an enumeration option is an alias of another option, or is an option in its own right via the `Enumeration` class' `.aliased` property.
56
+ * Ability to obtain the aliases for an enumeration option via the `.aliases` property.
57
+
58
+ Furthermore, as noted, the Enumerific library also offers extended functionality for the built-in standard library `enums.Enum` type:
59
+
60
+ * Simple one-line reconciliation of `enums.Enum` options to the corresponding `enums.Enum` class instance that represents the corresponding option; reconciliation by enumeration option name, value and enumeration class instance reference are all supported through the `.reconcile()` class method;
61
+ * Simple one-line validation of `enums.Enum` options; validation by enumeration option name, value and enumeration class instance reference are all supported through the `.validate()` class method;
62
+ * Simple access to all of the options provided by an `enums.Enum` class instance through the `.options()` class method.
63
+
64
+ ### Requirements
65
+
66
+ The Enumerific library has been tested with Python 3.10, 3.11, 3.12 and 3.13, and is not
67
+ compatible with Python 3.9 or earlier.
68
+
69
+ ### Installation
70
+
71
+ Enumerific is available from the PyPI, so may be added to a project's dependencies via
72
+ its `requirements.txt` file or similar by referencing the library's name, `enumerific`,
73
+ or the library may be installed directly into your local runtime environment using `pip`
74
+ by entering the following command, and following any prompts:
75
+
76
+ $ pip install enumerific
77
+
78
+ ### Usage
79
+
80
+ To use the Enumerific library's implementation of enumerations import the `Enumeration` class from the Enumerific library:
81
+
82
+ ```python
83
+ from enumerific import Enumeration
84
+
85
+ class Colors(Enumeration):
86
+ RED = 1
87
+ GREEN = 2
88
+ BLUE = 3
89
+
90
+ assert issubclass(Colors, Enumeration)
91
+
92
+ # Note that as all of the Colors enumeration options have integer values, the class was
93
+ # typecast to int so that its option values can be used interchangeably with integers:
94
+ assert issubclass(Colors, int)
95
+
96
+ color = Colors.RED
97
+
98
+ # Enumeration class options are instances of the class
99
+ assert isinstance(color, Colors)
100
+
101
+ # They can also be instances of the raw value that was assigned to the option
102
+ assert isinstance(color, int)
103
+
104
+ # Each enumeration class option has a name (the name used to define the option)
105
+ assert color.name == "RED"
106
+
107
+ # Each enumeration class option has a value (the value used when defining the option)
108
+ assert color.value == 1
109
+
110
+ # The identity of an enumeration option matches the option
111
+ assert color is Colors.RED
112
+
113
+ # The equality of an enumeration option can also be compared against the enumeration
114
+ # option directly, against the name of the option, or against the value:
115
+ assert color == Colors.RED
116
+ assert color == "RED"
117
+ assert color == 1
118
+ assert color != 2
119
+ ```
120
+
121
+ #### Example 1: Reconciling a Value
122
+
123
+ ```python
124
+ from enumerific import Enumeration
125
+
126
+ class Colors(Enumeration):
127
+ RED = 1
128
+ GREEN = 2
129
+ BLUE = 3
130
+
131
+ # Given a string value in this case
132
+ value = 1
133
+
134
+ # Reconcile it to the associated enumeration option
135
+ color = Colors.reconcile(value)
136
+
137
+ assert color == Colors.RED # asserts successfully
138
+ assert color is Colors.RED # asserts successfully as Enumeration class options are singletons
139
+ ```
140
+
141
+ #### Example 2: Reconciling an Enumeration Option Name
142
+
143
+ ```python
144
+ from enumerific import Enumeration
145
+
146
+ class Colors(Enumeration):
147
+ RED = 1
148
+ GREEN = 2
149
+ BLUE = 3
150
+
151
+ # Given a string value in this case
152
+ value = "RED"
153
+
154
+ # Reconcile it to the associated enumeration option
155
+ color = Colors.reconcile(value)
156
+
157
+ assert color == Colors.RED # asserts successfully
158
+ assert color is Colors.RED # asserts successfully as Enumeration class options are singletons
159
+
160
+ # Given a string value in this case
161
+ value = "red"
162
+
163
+ # Reconcile it to the associated enumeration option;
164
+ # values can be reconciled caselessly too:
165
+ color = Colors.reconcile(value, caselessly=True)
166
+
167
+ assert color == Colors.RED # asserts successfully
168
+ assert color is Colors.RED # asserts successfully as Enumeration class options are singletons
169
+ ```
170
+
171
+ #### Example 3: Validating a Value
172
+
173
+ ```python
174
+ from enumerific import Enumeration
175
+
176
+ class Colors(Enumeration):
177
+ RED = 1
178
+ GREEN = 2
179
+ BLUE = 3
180
+
181
+ # The value can be an enumeration option's name, its value, or the enumeration option
182
+ value = "RED"
183
+ value = 1
184
+ value = Colors.RED
185
+
186
+ if Colors.validate(value) is True:
187
+ # do something if the value could be validated
188
+ pass
189
+ else:
190
+ # do something else if the value could not be validated
191
+ pass
192
+ ```
193
+
194
+ #### Example 4: Iterating Over Enumeration Options
195
+
196
+ ```python
197
+ from enumerific import Enumeration
198
+
199
+ class Colors(Enumeration):
200
+ RED = 1
201
+ GREEN = 2
202
+ BLUE = 3
203
+
204
+ options = Colors.options()
205
+
206
+ for name, option in options.items():
207
+ # do something with each option
208
+ print(option.name, option.value)
209
+ ```
210
+
211
+ #### Example 5: Iterating Over Enumeration Names
212
+
213
+ ```python
214
+ from enumerific import Enumeration
215
+
216
+ class Colors(Enumeration):
217
+ RED = 1
218
+ GREEN = 2
219
+ BLUE = 3
220
+
221
+ for name in Colors.names():
222
+ # do something with each option name
223
+ print(name)
224
+ ```
225
+
226
+ #### Example 6: Iterating Over Enumeration Keys
227
+
228
+ ```python
229
+ from enumerific import Enumeration
230
+
231
+ class Colors(Enumeration):
232
+ RED = 1
233
+ GREEN = 2
234
+ BLUE = 3
235
+
236
+ for key in Colors.keys():
237
+ # do something with each option key
238
+ print(key)
239
+ ```
240
+
241
+ #### Example 7: Iterating Over Enumeration Values
242
+
243
+ ```python
244
+ from enumerific import Enumeration
245
+
246
+ class Colors(Enumeration):
247
+ RED = 1
248
+ GREEN = 2
249
+ BLUE = 3
250
+
251
+ for value in Colors.values():
252
+ # do something with each option key
253
+ print(value)
254
+ ```
255
+
256
+ #### Example 8: Registering New Option
257
+
258
+ ```python
259
+ from enumerific import Enumeration
260
+
261
+ class Colors(Enumeration):
262
+ RED = 1
263
+ GREEN = 2
264
+ BLUE = 3
265
+
266
+ Colors.register("PURPLE", 4)
267
+
268
+ assert "PURPLE" in Colors
269
+ assert Colors.PURPLE.name == "PURPLE"
270
+ assert Colors.PURPLE.value == 4
271
+ assert Colors.PURPLE == 4
272
+ ```
273
+
274
+ #### Example 9: Subclassing
275
+
276
+ ```python
277
+ from enumerific import Enumeration
278
+
279
+ class Colors(Enumeration):
280
+ RED = 1
281
+ GREEN = 2
282
+ BLUE = 3
283
+
284
+ class MoreColors(Colors):
285
+ PURPLE = 4
286
+ GOLD = 5
287
+
288
+ assert "BLUE" in Colors
289
+ assert Colors.BLUE.name == "BLUE"
290
+ assert Colors.BLUE.value == 3
291
+ assert Colors.BLUE == 3
292
+
293
+ assert "PURPLE" in MoreColors
294
+ assert MoreColors.PURPLE.name == "PURPLE"
295
+ assert MoreColors.PURPLE.value == 4
296
+ assert MoreColors.PURPLE == 4
297
+
298
+ assert "GOLD" in Colors
299
+ assert Colors.GOLD.name == "GOLD"
300
+ assert Colors.GOLD.value == 5
301
+ assert Colors.GOLD == 5
302
+ ```
303
+
304
+ #### Example 10: Subclassing Over
305
+
306
+ ```python
307
+ from enumerific import Enumeration
308
+
309
+ class Colors(Enumeration):
310
+ RED = 1
311
+ GREEN = 2
312
+ BLUE = 3
313
+
314
+ # Subclassed Enumerations can have the same name as the parent class
315
+ # The subclass will inherit the enumeration options of its parent
316
+ class Colors(Colors):
317
+ PURPLE = 4
318
+ GOLD = 5
319
+
320
+ assert "BLUE" in Colors
321
+ assert Colors.BLUE.name == "BLUE"
322
+ assert Colors.BLUE.value == 3
323
+ assert Colors.BLUE == 3
324
+
325
+ assert "PURPLE" in Colors
326
+ assert Colors.PURPLE.name == "PURPLE"
327
+ assert Colors.PURPLE.value == 4
328
+ assert Colors.PURPLE == 4
329
+
330
+ assert "GOLD" in Colors
331
+ assert Colors.GOLD.name == "GOLD"
332
+ assert Colors.GOLD.value == 5
333
+ assert Colors.GOLD == 5
334
+ ```
335
+
336
+ #### Example 11: Unregistering Existing Option
337
+
338
+ ```python
339
+ from enumerific import Enumeration
340
+
341
+ # Note that unregistering options is prevented by default; to all options to be removed
342
+ # the `removable` argument needs to be set to `True` when the class is created:
343
+ class Colors(Enumeration, removable=True):
344
+ RED = 1
345
+ GREEN = 2
346
+ BLUE = 3
347
+
348
+ Colors.unregister("GREEN")
349
+
350
+ assert "RED" in Colors
351
+ assert "GREEN" not in Colors
352
+ assert "BLUE" in Colors
353
+ ```
354
+
355
+ #### Example 12: Aliasing Options
356
+
357
+ ```python
358
+ from enumerific import Enumeration
359
+
360
+ # Note that aliasing options is prevented by default to ensure that all options have
361
+ # unique values; to allow aliasing, the `aliased` argument needs to be set to `True`
362
+ # when the class is created; aliases can be added by referencing the original option's
363
+ # name or its value as demonstrated below with the ROUGE and VERTE aliases:
364
+ class Colors(Enumeration, aliased=True):
365
+ RED = 1
366
+ GREEN = 2
367
+ BLUE = 3
368
+ ROUGE = RED
369
+ VERTE = 2
370
+
371
+ assert "RED" in Colors
372
+ assert "GREEN" in Colors
373
+ assert "BLUE" in Colors
374
+ assert "ROUGE" in Colors
375
+ assert "VERTE" in Colors
376
+
377
+ # Note that aliases are just different names for the same exact option, so the aliases
378
+ # can be used interchangeably with the original option, and they have the same identity:
379
+ assert Colors.RED is Colors.ROUGE
380
+ assert Colors.GREEN is Colors.VERTE
381
+
382
+ # All of the other properties of aliased options are also identical because the alias is
383
+ # just another reference to the same exact object in memory:
384
+ assert Colors.RED.name == Colors.ROUGE.name
385
+ assert Colors.RED.value == Colors.ROUGE.value
386
+
387
+ # Different options have their own distinct identities
388
+ assert not Colors.RED is Colors.VERTE
389
+
390
+ # Aliased options report that they have been aliased:
391
+ assert Colors.RED.aliased is True
392
+ assert Colors.GREEN.aliased is True
393
+ assert Colors.ROUGE.aliased is True
394
+ assert Colors.VERTE.aliased is True
395
+
396
+ # Non-aliased options do not report that they have been aliased:
397
+ assert Colors.BLUE.aliased is False
398
+
399
+ # The aliases for an option can be obtained via the .aliases property:
400
+ assert Colors.RED.aliases == [Colors.ROUGE]
401
+ assert Colors.GREEN.aliases == [Colors.VERTE]
402
+ assert Colors.BLUE.aliases == [] # BLUE has not been aliased
403
+ ```
404
+
405
+ #### Example 13: Non-Unique Options
406
+
407
+ ```python
408
+ from enumerific import Enumeration
409
+
410
+ # Note that non-unique options are prevented by default to ensure that all options have
411
+ # unique values; to allow non-unique option values, the `unique` argument needs to be
412
+ # set to `False` when the class is created:
413
+ class Colors(Enumeration, unique=False):
414
+ RED = 1
415
+ GREEN = 1
416
+ BLUE = 3
417
+
418
+ assert "RED" in Colors
419
+ assert Colors.RED.name == "RED"
420
+ assert Colors.RED.value == 1
421
+ assert Colors.RED == 1
422
+
423
+ assert "GREEN" in Colors
424
+ assert Colors.GREEN.name == "GREEN"
425
+ assert Colors.GREEN.value == 1
426
+ assert Colors.GREEN == 1
427
+
428
+ assert "BLUE" in Colors
429
+ assert Colors.BLUE.name == "BLUE"
430
+ assert Colors.BLUE.value == 3
431
+ assert Colors.BLUE == 3
432
+
433
+ # Note that although options can use the same values when the class has been configured
434
+ # to allow it, the enumeration options still maintain their own distinct identities:
435
+ assert not Colors.RED is Colors.GREEN
436
+ assert not Colors.BLUE is Colors.RED
437
+
438
+ # However, when enumeration options share values, options with the same values will
439
+ # compare as equal via equality checking (which is different than identity checking):
440
+ assert Colors.RED == Colors.GREEN
441
+ assert Colors.BLUE != Colors.RED
442
+ ```
443
+
444
+ #### Example 14: Bit Wise Flags
445
+
446
+ ```python
447
+ from enumerific import Enumeration
448
+
449
+ class Permissions(Enumeration, flags=True):
450
+ READ = 1
451
+ WRITE = 2
452
+ EXECUTE = 4
453
+ DELETE = 8
454
+
455
+ assert "READ" in Permissions
456
+ assert Permissions.READ.name == "READ"
457
+ assert Permissions.READ.value == 1
458
+ assert Permissions.READ == 1
459
+
460
+ assert "WRITE" in Permissions
461
+ assert Permissions.WRITE.name == "WRITE"
462
+ assert Permissions.WRITE.value == 2
463
+ assert Permissions.WRITE == 2
464
+
465
+ assert "EXECUTE" in Permissions
466
+ assert Permissions.EXECUTE.name == "EXECUTE"
467
+ assert Permissions.EXECUTE.value == 4
468
+ assert Permissions.EXECUTE == 4
469
+
470
+ # OR (add/merge) the READ and WRITE permission flags into the 'permissions' variable
471
+ permissions = Permissions.READ | Permissions.WRITE
472
+
473
+ assert str(permissions) == "Permissions.READ|WRITE"
474
+ assert Permissions.READ in permissions
475
+ assert Permissions.WRITE in permissions
476
+ assert not Permissions.EXECUTE in permissions
477
+
478
+ # Raises an exception as DELETE doesn't exist
479
+ assert not Permissions.DELETE in permissions
480
+
481
+ assert (permissions & Permissions.READ) == Permissions.READ
482
+ assert (permissions & Permissions.WRITE) == Permissions.WRITE
483
+
484
+ # XOR (remove) the WRITE permission from the 'permissions' variable
485
+ permissions = permissions ^ Permissions.WRITE
486
+
487
+ assert Permissions.READ in permissions
488
+ assert not Permissions.WRITE in permissions
489
+ assert not Permissions.EXECUTE in permissions
490
+
491
+ assert (permissions & Permissions.READ) == Permissions.READ
492
+ assert not (permissions & Permissions.WRITE) == Permissions.WRITE
493
+
494
+ assert not Permissions.WRITE in permissions
495
+ assert str(permissions) == "Permissions.READ"
496
+
497
+ # The order of the name components follows the order the underlaying flags were declared
498
+ assert str(Permissions.READ | Permissions.WRITE) == "Permissions.READ|WRITE"
499
+ assert str(Permissions.WRITE | Permissions.READ) == "Permissions.READ|WRITE"
500
+ assert (
501
+ str(Permissions.WRITE | Permissions.READ | Permissions.EXECUTE)
502
+ == "Permissions.READ|WRITE|EXECUTE"
503
+ )
504
+
505
+ # Assign 'permissions' to the (~) inverse (opposite) of EXECUTE,
506
+ # i.e. all Permissions options except EXECUTE
507
+ permissions = ~Permissions.EXECUTE
508
+
509
+ assert Permissions.READ in permissions
510
+ assert Permissions.WRITE in permissions
511
+ assert not Permissions.EXECUTE in permissions
512
+ assert Permissions.DELETE in permissions
513
+ assert str(permissions) == "Permissions.READ|WRITE|DELETE"
514
+ ```
515
+
516
+ #### Example 15: Annotating Enumeration Option Values
517
+
518
+ ```python
519
+ from enumerific import Enumeration, anno
520
+
521
+ # The 'anno' (annotation) class can be used to add annotations to enumeration options;
522
+ # these are arbitrary key-value pairs that can be used to hold any additional data that
523
+ # is useful to keep associated with the enumeration option; the annotation values are
524
+ # then accessible anywhere that the enumeration is, and can be accessed as attributes:
525
+ class Colors(Enumeration):
526
+ RED = anno(1, rgb=(255, 0, 0), primary=True)
527
+ GREEN = anno(2, rgb=(0, 255, 0), primary=True)
528
+ BLUE = anno(3, rgb=(0, 0, 255), primary=True)
529
+ PURPLE = anno(4, rgb=(255, 0, 255), primary=False)
530
+
531
+ assert "RED" in Colors
532
+ assert Colors.RED.name == "RED"
533
+ assert Colors.RED.value == 1
534
+ assert Colors.RED == 1
535
+ assert Colors.RED.rgb == (255, 0, 0)
536
+ assert Colors.RED.primary is True
537
+
538
+ assert "GREEN" in Colors
539
+ assert Colors.GREEN.name == "GREEN"
540
+ assert Colors.GREEN.value == 2
541
+ assert Colors.GREEN == 2
542
+ assert Colors.GREEN.rgb == (0, 255, 0)
543
+ assert Colors.GREEN.primary is True
544
+
545
+ assert "BLUE" in Colors
546
+ assert Colors.BLUE.name == "BLUE"
547
+ assert Colors.BLUE.value == 3
548
+ assert Colors.BLUE == 3
549
+ assert Colors.BLUE.rgb == (0, 0, 255)
550
+ assert Colors.BLUE.primary is True
551
+
552
+ assert "PURPLE" in Colors
553
+ assert Colors.PURPLE.name == "PURPLE"
554
+ assert Colors.PURPLE.value == 4
555
+ assert Colors.PURPLE == 4
556
+ assert Colors.PURPLE.rgb == (255, 0, 255)
557
+ assert Colors.PURPLE.primary is False
558
+ ```
559
+
560
+ #### Example 16: Annotating Enumeration Option Values with Automatic Sequencing
561
+
562
+ ```python
563
+ from enumerific import Enumeration, auto
564
+
565
+ # The 'auto' (automatic) class can be used to generate unique numeric sequence numbers
566
+ # for enumeration options and to optionally add annotations to those same options; the
567
+ # annotation key-value pairs can be used to hold any additional data that is useful to
568
+ # keep associated with the enumeration option; the annotation values are then accessible
569
+ # anywhere that the enumeration is, and can be accessed as attributes:
570
+ class Colors(Enumeration):
571
+ RED = auto(rgb=(255, 0, 0), primary=True)
572
+ GREEN = auto(rgb=(0, 255, 0), primary=True)
573
+ BLUE = auto(rgb=(0, 0, 255), primary=True)
574
+ PURPLE = auto(rgb=(255, 0, 255), primary=False)
575
+
576
+ assert "RED" in Colors
577
+ assert Colors.RED.name == "RED"
578
+ assert Colors.RED.value == 1
579
+ assert Colors.RED == 1
580
+ assert Colors.RED.rgb == (255, 0, 0)
581
+ assert Colors.RED.primary is True
582
+
583
+ assert "GREEN" in Colors
584
+ assert Colors.GREEN.name == "GREEN"
585
+ assert Colors.GREEN.value == 2
586
+ assert Colors.GREEN == 2
587
+ assert Colors.GREEN.rgb == (0, 255, 0)
588
+ assert Colors.GREEN.primary is True
589
+
590
+ assert "BLUE" in Colors
591
+ assert Colors.BLUE.name == "BLUE"
592
+ assert Colors.BLUE.value == 3
593
+ assert Colors.BLUE == 3
594
+ assert Colors.BLUE.rgb == (0, 0, 255)
595
+ assert Colors.BLUE.primary is True
596
+
597
+ assert "PURPLE" in Colors
598
+ assert Colors.PURPLE.name == "PURPLE"
599
+ assert Colors.PURPLE.value == 4
600
+ assert Colors.PURPLE == 4
601
+ assert Colors.PURPLE.rgb == (255, 0, 255)
602
+ assert Colors.PURPLE.primary is False
603
+ ```
604
+
605
+ # Enumerific Library Enumerations: Classes & Methods
606
+
607
+ The Enumerific library's `Enumeration` class is a greenfield implementation of enumerations
608
+ and does not inherit from any of the standard library enumeration classes, but offers equivalent
609
+ and extended functionality implemented from scratch. Enumerific library enumerations can be used
610
+ in any situation that enumerations are needed, and can replace the use of standard library
611
+ enumerations in almost every case unless some very specific functionality or underlying behaviour
612
+ of standard library enumerations are relied upon in user code. For the majority of cases, the
613
+ functionality is sufficiently equivalent from an application binary interface (ABI) perspective
614
+ that the two implementations can be used interchangeably.
615
+
616
+ The Enumerific library's extended enumerations module offers the following classes:
617
+
618
+ * `EnumerationConfiguration` – The `EnumerationConfiguration` class is used internally by the library
619
+ to hold configuration information for an `Enumeration` class instance.
620
+
621
+ * `EnumerationMetaClass` – The `EnumerationMetaClass` metaclass is responsible for creating instances of the `Enumeration` class for use, and provides an interface between the class definition and some of the special behaviours needed to facilitate enumerations, such as each enumeration option being an instance of the enumeration class.
622
+
623
+ * `Enumeration` – The `Enumeration` class is the base class for all Enumerific library extended enumerations; the `Enumeration` class defines shared functionality used by all `Enumeration` class instances.
624
+
625
+ * `EnumerationType` – The `EnumerationType` class is actually a subclass of `Enumeration` and is used internally by the library to track the data type of the options assigned to an enumeration class.
626
+
627
+ * `EnumerationInteger` – The `EnumerationInteger` class is a subclass of `int` and `Enumeration` and supports interacting with enumeration options natively as `int` (integer) data types.
628
+
629
+ * `EnumerationFloat` – The `EnumerationFloat` class is a subclass of `float` and `Enumeration` and supports interacting with enumeration options natively as `float` (floating point) data types.
630
+
631
+ * `EnumerationComplex` – The `EnumerationComplex` class is a subclass of `complex` and `Enumeration` and supports interacting with enumeration options natively as `complex` (complex number) data types.
632
+
633
+ * `EnumerationBytes` – The `EnumerationBytes` class is a subclass of `bytes` and `Enumeration` and supports interacting with enumeration options natively as `bytes` data types.
634
+
635
+ * `EnumerationString` – The `EnumerationString` class is a subclass of `str` and `Enumeration` and supports interacting with enumeration options natively as `str` (string) data types.
636
+
637
+ * `EnumerationTuple` – The `EnumerationTuple` class is a subclass of `tuple` and `Enumeration` and supports interacting with enumeration options natively as `tuple` data types.
638
+
639
+ * `EnumerationSet` – The `EnumerationSet` class is a subclass of `set` and `Enumeration` and supports interacting with enumeration options natively as `set` data types.
640
+
641
+ * `EnumerationList` – The `EnumerationList` class is a subclass of `list` and `Enumeration` and supports interacting with enumeration options natively as `list` data types.
642
+
643
+ * `EnumerationDictionary` – The `EnumerationDictionary` class is a subclass of `dict` and `Enumeration` and supports interacting with enumeration options natively as `dict` (dictionary) data types.
644
+
645
+ * `EnumerationFlag` – The `EnumerationFlag` class is a special subclass of `int` and `Enumeration` and supports interacting with enumeration options natively as `int` (integer) data types and supports bitwise operations on the enumeration options so that enumeration options can be used as bitwise flags.
646
+
647
+ The extended enumerations module also offers the following classes for annotating enumeration
648
+ options and for automatically generating sequence numbers for annotation options:
649
+
650
+ * `anno` – The `anno` class provides support for annotating an enumeration option's value, allowing an enumeration option to carry both a value of any data type, and optional additional annotations of key-value pairs that can be accessed as properties on the annotated enumeration option.
651
+
652
+ * `auto` – The `auto` class is a subclass of `anno` and provides support both for annotating and enumeration option's value as per the functionality supported by its superclass, and additionally provides support for automatically generating unique number sequences to use for enumeration options, obviating the need to manually assign unique values to each enumeration option; number sequences can be configured to suit a range of needs, including generating number sequences as powers of two, which is particularly useful for the enumeration of bitwise flags as supported by the `EnumerationFlag` subclass.
653
+
654
+ # Standard Library Enumerations: Classes & Methods
655
+
656
+ The Enumerific library's own `Enum` class is a subclass of the built-in `enum.Enum` class,
657
+ so all of the built-in functionality of `enum.Enum` is available, as well as several additional class methods:
658
+
659
+ * `reconcile(value: object, default: Enum = None, raises: bool = False)` (`Enum`) – The `reconcile()` method allows for an enumeration's value or an enumeration option's name to be reconciled against a matching enumeration option. If the provided value can be matched against one of the enumeration's available options, that option will be returned, otherwise there are two possible behaviours: if the `raises` keyword argument has been set to or left as `False` (its default), the value assigned to the `default` keyword argument will be returned, which may be `None` if no default value has been specified; if the `raises` argument has been set to `True` an `EnumValueError` exception will be raised as an alert that the provided value could not be matched. One can also provide an enumeration option as the input value to the `reconcile` method, and these will be validated and returned as-is.
660
+
661
+ * `validate(value: object)` (`bool`) – The `validate()` method takes the same range of input values as the `reconcile` method, and returns `True` when the provided value can be reconciled against an enumeration option, or `False` otherwise.
662
+
663
+ * `options()` (`list[Enum]`) – The `options()` method provides easy access to the list of the enumeration's available options.
664
+
665
+ The benefits of being able to validate and reconcile various input values against an enumeration, include allowing for a controlled vocabulary of options to be checked against, and the ability to convert enumeration values into their corresponding enumeration option. This can be especially useful when working with input data where you need to convert those values to their corresponding enumeration options, and to be able to do so without maintaining boilerplate code to perform the matching and assignment.
666
+
667
+ To make use of the extra functionality for the standard library's `Enum` class, import the `Enum` class from the Enumerific library:
668
+
669
+ ```python
670
+ from enumerific import Enum
671
+
672
+ class Colors(Enum):
673
+ RED = 1
674
+ GREEN = 2
675
+
676
+ val = Colors.RED
677
+ ```
678
+
679
+ You can also import the `Enum` class directly from the `enumerific` library and use it directly:
680
+
681
+ ```python
682
+ from enumerific import Enum
683
+
684
+ class Colors(Enum):
685
+ RED = 1
686
+ ```
687
+
688
+ Some examples of use include the following code samples, where each make use of the example `Colors` class, defined as follows:
689
+
690
+ ```python
691
+ from enumerific import Enum
692
+
693
+ class Colors(Enum):
694
+ RED = 1
695
+ GREEN = 2
696
+ ```
697
+
698
+ #### Example 1: Reconciling a Value
699
+
700
+ ```python
701
+ from enumerific import Enum
702
+
703
+ class Colors(Enum):
704
+ RED = 1
705
+ GREEN = 2
706
+
707
+ # Given a string value in this case
708
+ value = 1
709
+
710
+ # Reconcile it to the associated enumeration option
711
+ color = Colors.reconcile(value)
712
+
713
+ assert color == Colors.RED # asserts successfully
714
+ assert color is Colors.RED # asserts successfully as enums are singletons
715
+ ```
716
+
717
+ #### Example 2: Reconciling an Enumeration Option Name
718
+
719
+ ```python
720
+ from enumerific import Enum
721
+
722
+ class Colors(Enum):
723
+ RED = 1
724
+ GREEN = 2
725
+
726
+ # Given a string value in this case
727
+ value = "GREEN"
728
+
729
+ # Reconcile it to the associated enumeration option
730
+ color = Colors.reconcile(value)
731
+
732
+ assert color == Colors.GREEN # asserts successfully
733
+ assert color is Colors.GREEN # asserts successfully as enums are singletons
734
+ ```
735
+
736
+ #### Example 3: Validating a Value
737
+
738
+ ```python
739
+ from enumerific import Enum
740
+
741
+ class Colors(Enum):
742
+ RED = 1
743
+ GREEN = 2
744
+
745
+ # The value can be an enumeration option's name, its value, or the enumeration option
746
+ value = "RED"
747
+ value = 1
748
+ value = Colors.RED
749
+
750
+ if Colors.validate(value) is True:
751
+ # do something if the value could be validated
752
+ pass
753
+ else:
754
+ # do something else if the value could not be validated
755
+ pass
756
+ ```
757
+
758
+ #### Example 4: Iterating Over Enumeration Options
759
+
760
+ ```python
761
+ from enumerific import Enum
762
+
763
+ class Colors(Enum):
764
+ RED = 1
765
+ GREEN = 2
766
+
767
+ for option in Colors.options():
768
+ # do something with each option
769
+ print(option.name, option.value)
770
+ ```
771
+
772
+ ### Unit Tests
773
+
774
+ The Enumerific library includes a suite of comprehensive unit tests which ensure that the
775
+ library functionality operates as expected. The unit tests were developed with and are run via `pytest`.
776
+
777
+ To ensure that the unit tests are run within a predictable runtime environment where all of the necessary dependencies are available, a [Docker](https://www.docker.com) image is created within which the tests are run. To run the unit tests, ensure Docker and Docker Compose is [installed](https://docs.docker.com/engine/install/), and perform the following commands, which will build the Docker image via `docker compose build` and then run the tests via `docker compose run` – the output of running the tests will be displayed:
778
+
779
+ ```shell
780
+ $ docker compose build
781
+ $ docker compose run tests
782
+ ```
783
+
784
+ To run the unit tests with optional command line arguments being passed to `pytest`, append the relevant arguments to the `docker compose run tests` command, as follows, for example passing `-vv` to enable verbose output:
785
+
786
+ ```shell
787
+ $ docker compose run tests -vv
788
+ ```
789
+
790
+ See the documentation for [PyTest](https://docs.pytest.org/en/latest/) regarding available optional command line arguments.
791
+
792
+ ### Copyright & License Information
793
+
794
+ Copyright © 2024–2025 Daniel Sissman; licensed under the MIT License.