Package: namespaced-enums
NamespacedEnumsLibrary for defining containers withinEnumPython classes.InstallationRun$pipinstallnamespaced_enumsUsageLibrary provides two special descriptors forEnumclasses:EnumContainerStrictEnumContaineras well as an additional enum-subclass:NamespacedEnumEnumContainerThis allows for defining arbitrary containers withinEnumclasses, e.g.fromenumimportEnumfromnamespaced_enumimportEnumContainerclassColor(Enum):RED=1GREEN=2BLUE=3LIGHT_RED=4LIGHT_GREEN=5LIGHT_BLUE=6DARK_RED=7DARK_GREEN=8DARK_BLUE=9light_colors=EnumContainer([LIGHT_RED,LIGHT_GREEN,LIGHT_BLUE])dark_colors=EnumContainer([DARK_RED,DARK_GREEN,DARK_BLUE])--->>>print(Color.RED.valueinColor.light_colors)# False>>>print(Color.DARK_BLUE.valueinColor.dark_colors)# True# Caution!# Values within containers are of enum value types>>>print(Color.dark_colors)# prints [7, 8, 9] and not [DARK_RED, DARK_GREEN, DARK_BLUE]!StrictEnumContainer and NamespacedEnumStrictEnumContaineraccepts only dict containers. Using this descriptor within
a class inheriting fromNamespacedEnumwill enforce that the provided
dictionary contains all enum possible values:fromnamespaced_enumsimportNamespacedEnum,StrictEnumContainerclassFood(NamespacedEnum):spam="spam"eggs="eggs"foo="foo"reactions=StrictEnumContainer({spam:"I like it",eggs:"I don't like it...",foo:"I like?",})--->>>print(Food.reactions[Food.spam])# "I like it"# Caution!# Unlike the `EnumContainer` this one converts dict keys to enums!>>>print(list[Food.reactions.keys()])# prints [<Food.spam: spam>, <Food.eggs: eggs>, <Food.foo: foo>]Forgetting to provide all possible enum values within a strict container will
raise aRuntimeError:fromnamespaced_enumsimportNamespacedEnum,StrictEnumContainerclassFood(NamespacedEnum):spam="spam"eggs="eggs"foo="foo"reactions=StrictEnumContainer({spam:"I like it",eggs:"I don't like it...",# missing foo in the dict})--->>># Trying to start the program raises a `RuntimeError`:# The following Food fields do not contain all possible enum values: ['reactions']RationaleIt's a common practice to use enums in a project as a way to denote
"characteristics" of certain objects.Consider the following example:fromenumimportEnumclassDogBreed(Enum):BULLDOG='bulldog'PUG='pug'SHIBA='shiba'classDog:def__init__(self,breed:DogBreed)->None:self.breed=breed@propertydefsize(self)->int:"""Returns size of the dog (in centimeters)."""ifself.breed==DogBreed.BULLDOG:return40elifself.breed==DogBreed.PUG:return30elifself.breed==DogBreed.SHIBA:return35else:# pragma: no coverraiseValueError(f"Unknown dog breed:{self.breed}")TheDog.sizeproperty implementation poses a threat when it comes to further
additions to theDogBreedenum:programmers would have toremember to updateit every time they add a new
breed support. What if there are more such properties like color, weight or
tail length?theif-elif-else chain is hard to testas the lastelseclause would
require injecting some arbitrary value into theDogBreedenum during runtime
in order to ensure proper coverage. In my experience, most developers would
prefer to add# pragma: no coverinstead.the size characteristic should arguably be not part of theDogclass as it
is more specific to theDogBreedenum - theDogclass should be aware of
how to retrieve that data.That's why I decided to create aNamespacedEnumclass that allows for writing
self-contained enums, that can help in maintaining the integrity of the written
code. The example above could be rewritten in a following manner:fromnamespaced_enumsimportNamespacedEnum,StrictEnumContainerclassDogBreed(NamespacedEnum):BULLDOG='bulldog'PUG='pug'SHIBA='shiba'size=StrictEnumContainer({BULLDOG:40,PUG:30,SHIBA:35,})classDog:def__init__(self,breed:DogBreed)->None:self.breed=breed@propertydefsize(self)->int:"""Returns size of the dog (in centimeters)."""returnDogBreed.size[self.breed]