Package: magic-kind
magic-kindThe MagicKind type is a simpler alternative to Enum types for groups of
related magic values when order is not important.Synopsis:from magic_kind import MagicKind

class HttpCode(MagicKind):
    OK = 200
    NOT_FOUND = 404
    GATEWAY = 503
  
...

raise HttpException(HttpCode.NOT_FOUND, ...)
    
...

assert http_code in HttpCode

...

for http_code in HttpCode:
    print(http_code)MotivationThink of the kind of things you might put in a constants.py file to make
sure you use the value consistently across your application. And then think
about the times such magic values were really just one of a number of
alternatives. For example they were all different Http Code values like:HTTP_CODE_OK = 200
HTTP_CODE_NOT_FOUND = 404
HTTP_CODE_GATEWAY = 503This is the kind of case that the the MagicKind type is made for. Instead
one would do this:class HttpCode(MagicKind):
    OK = 200
    NOT_FOUND = 404
    GATEWAY = 503The class itself provides some container object like functionality. It
allows you to iterate over it and get the values, and it allows you to use theinoperator to check if a value you got somewhere else is in the Choices.But why not just use Enum instead?Enumerated types have a similar but broader functionality where they are like
containers with special objects that know their relative order and have
attributes for their "name" and "value".MagicKind types are instead like containers of the values themselves. This
makes dealing with them easier unless there is some special reason you are
interested in the order and attribute names as well as the values.To illustrate the difference, let us suppose some user entered data is stored
assome_stringand we wish to know if it is one of our kinds of soda:classSoda(str,Enum):ROOT_BEER="root beer"COLA="cola"valid_soda_values=set([_.valuefor_inSoda])ifsome_stringinvalid_soda_values:print(f"{some_string}is a soda")With MagicKind the basic elements are just the values "root beer" and "cola",
so we don't need to make avalid_soda_valuesset:classSoda(MagicKind):ROOT_BEER="root beer"COLA="cola"ifsome_stringinSoda:print(f"{some_string}is a soda")Usage Rules:To be recognized as one of the choices, attribute names must be
upper case identifiers that do not begin with an underscore. This
is by design to allow other kinds of members to be added (methods
for example, or other attributes that are not meant to be choices).The values must be hashable (they are stored in a set internally). It
is recommended that they are also immutable.The upper case choice values should never be changed nor should they
be replaced. Doing so will cause the internal metaclass data to be wrong and
trouble will likely follow. If you want some non-constant class values, don't
make them upper case, and they won't be seen as one of the magic value choices.The following pre-existing non-choice members should not be overwritten:namewhat it is already used forget_dictmethod that gets dict of magic value attribute names and valuesget_namesmethod that gets set of magic value attribute names_choices_dictinternal use by MetaMagicKind metaclass_choices_setinternal use by MetaMagicKind metaclass_pydantic_validatealong with__get_validators__provided to support use of MagicKindin pydantic modelsIn addition, all __special__ variable names should only be overloaded if you know what you are doing.Works with PydanticFor those who wish to declare the MagicKind type in apydantic model, MagicKind
has a validator method that behaves just as one expects (it will only accept
one of the "magic values" for the subclass).AcknowledgementThis package was originally developed at Rackspace Technology, which is releasing it
to the public under the Apache 2 open source license.