Is there a way to prevent specific `enum.Flag` combinations? - python

If I have an enum class like so:
class TestFlag(enum.Flag):
A = enum.auto()
B = enum.auto()
C = enum.auto()
D = A | B # valid
Is it possible to specify a certain combination, such as, say TestFlag.C | TestFlag.B as invalid? In other words, is there a way to ensure that writing TestFlag.C | TestFlag.B will raise an Error?

You could make use of the _missing_ method:
class TestFlag(enum.Flag):
A = enum.auto()
B = enum.auto()
C = enum.auto()
D = A | B # valid
#
def _missing_(self, value):
if value in (5, 7): # list of invalid values
raise ValueError('%r is not a valid combination' % value)
return super()._missing_(value)
NB: This will not catch invalid choices made during class creation, only operations that occur afterwards.
Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.

You could add to your TestFlag class:
def __or__(self, other):
result = super().__or__(other)
... throw exception if result has some problem
return result
I'm not sure I'd really recommend it, though.

Related

Translation of message dict into msg enum

I'm dealing with refactoring code which extensively uses dicts in a circumstance where enums could be used. Unfortunately, to reduce typing the dict keys were abbreviated in a cryptic fashion.
In order to have more meaningful code and fewer string literals as well as a more advanced interface I translated the message dictionary based code into an Enum based code using the same messages.
The message dictionaries looked like the following:
MsgDictionary = {'none': None,
'STJ': 'start_job',
'RPS': 'report_status',
'KLJ': 'kill_job'}
ExecStates = {'none': None,
'JCNS': 'job_could_not_start',
'JSS': 'job_successfully_started',
'JSF': 'job_successfully_finished'}
This, unfortunately lead to cluttered code:
...
self.send_message(id = MsgDictionary["stj"], some_data)
...
msg = self.receive_msg()
if msg.id in (MsgDictionary['STJ'], MsgDictionary['KLJ']):
self.toggle_job()
...
I would merely like to get rid of the string accesses, the cryptic names and the low level interface, like in the following. This send_message should send the str typed value of the Enum not the Enum instance itself.
...
self.send_message(id = MessagesEnum.START_JOB, some_data)
...
msg = self.receive_msg()
if msg.id in (MessagesEnum.START_JOB, MessagesEnum.KILL_JOB):
self.toggle_job()
...
But as in the original case, undefined execution states should still be allowed. This does currently not work. The reason is to not break existing code:
e = ExecStates(None)
-> ValueError: None is not a valid ExecutionStates
And I would like to be able to compare enum instances, e.g.:
e = ExecState[START_JOB]
if e == ExecState[START_JOB]:
pass
if e == ExecState[KILL_JOB]:
pass
Using the following definitions, I believe I'm almost there:
import enum
class _BaseEnum(str, enum.Enum):
#classmethod
def values(cls) -> DictValues:
return cls.__members__.values()
def _generate_next_value_(name: str, *args: object) -> str:
return name.lower()
def __str__(self):
return str(self.value) # Use stringification to cover the None value case
class MessageEnum(_BaseEnum):
NONE = None
START_JOB = enum.auto()
REPORT_STATUS = enum.auto()
KILL_JOB = enum.auto()
class ExecutionState(_BaseEnum):
NONE = None
JOB_COULD_NOT_START = enum.auto()
JOB_SUCCESSFULLY_STARTED = enum.auto()
JOB_SUCCESSFULLY_FINISHED = enum.auto()
However, one problem still remains. How can I deal with None value as well as strings in the enumerations? In my case, all enum items gets mapped to the lowercase of the enum item name. Which is the intended functionality. However, None gets unintendedly mapped to 'None'. This in effect leads to problems at other spots in the existing code which initializes an ExecutionState instance with None. I would like to also cover this case to not break existing code.
When I add a __new__ method to the _BaseEnum,
def __new__(cls, value):
obj = str.__new__(cls)
obj._value_ = value
return obj
I loose the possibility to compare the enumeration instances as all instances compare equal to ``.
My question is, in order to solve my problem, if I can corner case the None either in the _generate_next_value_ or the __new__ method or maybe using a proxy pattern ?
Two things that should help:
in your __new__, the creation line should read obj = str.__new__(cls, value) -- that way each instance will compare equal to its lower-cased name
export your enum members to the global namespace, and use is:
START_JOB, REPORT_STATUS, KILL_JOB = MessageEnum
...
if e is START_JOB: ...
...
if msg.id in (START_JOB, KILL_JOB): ...

Pythonic way of using enum to select and run a particular sub method?

I have a function that will be called by an end user, and this function accepts an int argument from 1 to 3 inclusive. Depending on the int, I want the function to run a seperate sub method. I would usually do this with if statements as below, but I am looking for a cleaner / more pythonic way to do this using Enum.
def user_function(user_arg: int) -> float:
if user_arg == 1:
return sub_method_1()
elif user_arg == 2:
return sub_method_2()
elif user_arg == 3:
return sub_mthod_3()
print(f"Error: user_arg must be in [1,2,3]. Supplied argument: {user_arg}")
Ideally the function would look something like
from enum import Enum
class UserMethod(Enum):
FIRST_METHOD = 1
SECOND_METHOD = 2
THIRD_METHOD = 3
def choose_method(self):
#... use enum to choose from the 3 sub methods ...
# This function is now much cleaner
def user_function(user_arg: int) -> float:
um = UserMethod(user_arg)
return um.choose_method()
I am confused on how to use Enum on how to do this, or if this is even the best way?
The cleanest way to do this at the moment is to use the aenum library1:
from aenum import Enum
class UserMethod(Enum):
#
_init_ = 'value method'
#
def __call__(self, *args, **kwds):
return self.method(*args, **kwds)
#
def first(blah):
return blah
#
def second(spam=2):
return spam
#
def third(this, that):
return this, that
#
FIRST_METHOD = 1, first
SECOND_METHOD = 2, second
THIRD_METHOD = 3, third
Each method is stored on the enum member itself, and calling the member passes the call to the appropriate function.
--> list(UserMethod)
[<UserMethod.FIRST_METHOD: 1>, <UserMethod.SECOND_METHOD: 2>,
<UserMethod.THIRD_METHOD: 3>]
--> UserMethod.THIRD_METHOD("hello", "world")
('hello', 'world')
and
# This function is now much cleaner
def user_function(user_arg: int) -> float:
return UserMethod(user_arg).method
1 Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.
You can keep it even simpler (that is, without relying on Python Enums):
def user_function(user_arg: int) -> float:
assert user_arg in [1,2,3], \
f"Error: user_arg must be in [1,2,3]. Supplied argument: {user_arg}"
return [sum_method_1,
sub_method_2,
sub_method_3][user_arg-1]()
On your method choose_method you can get the selected Enum using self like this:
def choose_method(self):
print(self) # Will print the Enum atributte
With this you can create a dict pointing to another methods;
def choose_method(self):
methods = {
UserMethod.FIRST_METHOD: self.sub_method_one,
UserMethod.SECOND_METHOD: self.sub_method_two,
UserMethod.THIRD_METHOD: self.sub_method_three,
}
return methods.get(self)()
def sub_method_one(self):
print("Method 1")
def sub_method_two(self):
print("Method 2")
def sub_method_three(self):
print("Method 3")
Using enums would not be my first impulse - I would argue that it wouldn't be pythonic to use one in this scenario, and I don't think you can justify using an enum. You'll probably just want to map the user's input to a function using a dictionary:
def first_function():
print("In first!")
def second_function():
print("In second!")
def third_function():
print("In third!")
functions = {
1: first_function,
2: second_function,
3: third_function
}
functions[1]()
Output:
In first!
>>>
In this case, the keys are integers, but they could be strings, too - or anything. I also just hardcoded the 1 in functions[1](), but you get the idea.

How to create an indexable and sliceable enum with string values in Python?

I've got a file like this:
class Level(Enum):
prerequisite_level: Optional["Level"]
dependent_level: Optional["Level"]
lower_priority_levels: List["Level"]
greater_priority_levels: List["Level"]
DATA_CHECK = "data check"
DESIGN_CHECK = "design check"
ALERT = "alert"
The enum values are in a specific order, and based on each of those levels I need to be able to get the previous one, the next one, and all the previous and next ones. I believe I need to be able to index the levels numerically to get these values, so I've added a constant to be able to do this:
INCREASING_PRIORITY_LEVELS: List[Level] = list(Level)
for priority_level_index, threshold_level in enumerate(Level):
if priority_level_index > 0:
threshold_level.prerequisite_level = Level[priority_level_index - 1]
else:
threshold_level.prerequisite_level = None
if priority_level_index < len(Level) - 1:
threshold_level.dependent_level = Level[priority_level_index + 1]
else:
threshold_level.dependent_level = None
threshold_level.lower_priority_levels = Level[:priority_level_index]
threshold_level.greater_priority_levels = Level[priority_level_index + 1:]
This is clunky, and I'd like to get rid of this constant. Do I need to implement __getitem__ or something to make this possible?
You can subclass EnumMeta to override the __getitem__ method with additional conditions to return a list of Enum values or a specific Enum value based on the given index, and create a subclass of Enum with the aforementioned subclass of EnumMeta as the metaclass, so that any subclass of this new subclass of Enum can be indexed as desired:
from itertools import islice
from enum import Enum, EnumMeta
class IndexableEnumMeta(EnumMeta):
def __getitem__(cls, index):
if isinstance(index, slice):
return [cls._member_map_[i] for i in islice(cls._member_map_, index.start, index.stop, index.step)]
if isinstance(index, int):
return cls._member_map_[next(islice(cls._member_map_, index, index + 1))]
return cls._member_map_[index]
class IndexableEnum(Enum, metaclass=IndexableEnumMeta):
pass
class Level(IndexableEnum):
DATA_CHECK = "data check"
DESIGN_CHECK = "design check"
ALERT = "alert"
so that Level[1:3] returns:
[<Level.DESIGN_CHECK: 'design check'>, <Level.ALERT: 'alert'>]
and Level[1] returns:
Level.DESIGN_CHECK
(Credit goes to #EthanFurman for pointing out the viability of subclassing EnumMeta.)
class Level(Enum):
prerequisite_level: Optional["Level"]
dependent_level: Optional["Level"]
lower_priority_levels: List["Level"]
greater_priority_levels: List["Level"]
DATA_CHECK = "data check"
DESIGN_CHECK = "design check"
ALERT = "alert"
I'm having a hard time understanding the above: ... [comments clarified that the first four should be attributes, and prequisite and dependent are the previous and following members, respectively].
The solution is to modify previous members as the current member is being initialized (the trick being that the current member isn't added to the parent Enum until after the member's creation and initialization). Here is the solution using the stdlib's Enum1 (Python 3.6 and later):
from enum import Enum, auto
class Level(str, Enum):
#
def __init__(self, name):
# create priority level lists
self.lower_priority_levels = list(self.__class__._member_map_.values())
self.greater_priority_levels = []
# update previous members' greater priority list
for member in self.lower_priority_levels:
member.greater_priority_levels.append(self)
# and link prereq and dependent
self.prerequisite = None
self.dependent = None
if self.lower_priority_levels:
self.prerequisite = self.lower_priority_levels[-1]
self.prerequisite.dependent = self
#
def _generate_next_value_(name, start, count, last_values, *args, **kwds):
return (name.lower().replace('_',' '), ) + args
#
DATA_CHECK = auto()
DESIGN_CHECK = auto()
ALERT = auto()
and in use:
>>> list(Level)
[<Level.DATA_CHECK: 'data check'>, <Level.DESIGN_CHECK: 'design check'>, <Level.ALERT: 'alert'>]
>>> Level.DATA_CHECK.prerequisite
None
>>> Level.DATA_CHECK.dependent
<Level.DESIGN_CHECK: 'design check'>
>>> Level.DESIGN_CHECK.prerequisite
<Level.DATA_CHECK: 'data check'>
>>> Level.DESIGN_CHECK.dependent
<Level.ALERT: 'alert'>
>>> Level.ALERT.prerequisite
<Level.DESIGN_CHECK: 'design check'>
>>> Level.ALERT.dependent
None
Note: If you don't want to see the name twice, a custom __repr__ can show just the enum and member names:
def __repr__(self):
return '<%s.%s>' % (self.__class__.__name__, self.name)
then you'll see:
>>> Level.DESIGN_CHECK
<Level.DESIGN_CHECK>
1If using Python 3.5 or older you need to use aenum2.
2 Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.
A possible alternative to achieve the same result in terms of usage would be to use collections.namedtuple instead:
from collections import namedtuple
LevelSequence = namedtuple('Level', ('DATA_CHECK', 'DESIGN_CHECK', 'ALERT'))
Level = LevelSequence('data check', 'design check', 'alert')
So that:
Level.DESIGN_CHECK and Level[1] both return 'design check', and
Level[1:3] returns ('design check', 'alert')

Convert string to Enum in Python

What's the correct way to convert a string to a corresponding instance of an Enum subclass? Seems like getattr(YourEnumType, str) does the job, but I'm not sure if it's safe enough.
As an example, suppose I have an enum like
class BuildType(Enum):
debug = 200
release = 400
Given the string 'debug', how can I get BuildType.debug as a result?
This functionality is already built in to Enum:
>>> from enum import Enum
>>> class Build(Enum):
... debug = 200
... build = 400
...
>>> Build['debug']
<Build.debug: 200>
The member names are case sensitive, so if user-input is being converted you need to make sure case matches:
an_enum = input('Which type of build?')
build_type = Build[an_enum.lower()]
Another alternative (especially useful if your strings don't map 1-1 to your enum cases) is to add a staticmethod to your Enum, e.g.:
class QuestionType(enum.Enum):
MULTI_SELECT = "multi"
SINGLE_SELECT = "single"
#staticmethod
def from_str(label):
if label in ('single', 'singleSelect'):
return QuestionType.SINGLE_SELECT
elif label in ('multi', 'multiSelect'):
return QuestionType.MULTI_SELECT
else:
raise NotImplementedError
Then you can do question_type = QuestionType.from_str('singleSelect')
def custom_enum(typename, items_dict):
class_definition = """
from enum import Enum
class {}(Enum):
{}""".format(typename, '\n '.join(['{} = {}'.format(k, v) for k, v in items_dict.items()]))
namespace = dict(__name__='enum_%s' % typename)
exec(class_definition, namespace)
result = namespace[typename]
result._source = class_definition
return result
MyEnum = custom_enum('MyEnum', {'a': 123, 'b': 321})
print(MyEnum.a, MyEnum.b)
Or do you need to convert string to known Enum?
class MyEnum(Enum):
a = 'aaa'
b = 123
print(MyEnum('aaa'), MyEnum(123))
Or:
class BuildType(Enum):
debug = 200
release = 400
print(BuildType.__dict__['debug'])
print(eval('BuildType.debug'))
print(type(eval('BuildType.debug')))
print(eval(BuildType.__name__ + '.debug')) # for work with code refactoring
My Java-like solution to the problem. Hope it helps someone...
from enum import Enum, auto
class SignInMethod(Enum):
EMAIL = auto(),
GOOGLE = auto()
#classmethod
def value_of(cls, value):
for k, v in cls.__members__.items():
if k == value:
return v
else:
raise ValueError(f"'{cls.__name__}' enum not found for '{value}'")
sim = SignInMethod.value_of('EMAIL')
assert sim == SignInMethod.EMAIL
assert sim.name == 'EMAIL'
assert isinstance(sim, SignInMethod)
# SignInMethod.value_of("invalid sign-in method") # should raise `ValueError`
An improvement to the answer of #rogueleaderr :
class QuestionType(enum.Enum):
MULTI_SELECT = "multi"
SINGLE_SELECT = "single"
#classmethod
def from_str(cls, label):
if label in ('single', 'singleSelect'):
return cls.SINGLE_SELECT
elif label in ('multi', 'multiSelect'):
return cls.MULTI_SELECT
else:
raise NotImplementedError
Change your class signature to this:
class BuildType(str, Enum):
Since MyEnum['dontexist'] will result in error KeyError: 'dontexist', you might like to fail silently (eg. return None). In such case you can use the following static method:
class Statuses(enum.Enum):
Unassigned = 1
Assigned = 2
#staticmethod
def from_str(text):
statuses = [status for status in dir(
Statuses) if not status.startswith('_')]
if text in statuses:
return getattr(Statuses, text)
return None
Statuses.from_str('Unassigned')
class LogLevel(IntEnum):
critical = logging.CRITICAL
fatal = logging.FATAL
error = logging.ERROR
warning = logging.WARNING
info = logging.INFO
debug = logging.DEBUG
notset = logging.NOTSET
def __str__(self):
return f'{self.__class__.__name__}.{self.name}'
#classmethod
def _missing_(cls, value):
if type(value) is str:
value = value.lower()
if value in dir(cls):
return cls[value]
raise ValueError("%r is not a valid %s" % (value, cls.__name__))
Example:
print(LogLevel('Info'))
print(LogLevel(logging.WARNING))
print(LogLevel(10)) # logging.DEBUG
print(LogLevel.fatal)
print(LogLevel(550))
Output:
LogLevel.info
LogLevel.warning
LogLevel.debug
LogLevel.critical
ValueError: 550 is not a valid LogLevel
I just want to notify this does not work in python 3.6
class MyEnum(Enum):
a = 'aaa'
b = 123
print(MyEnum('aaa'), MyEnum(123))
You will have to give the data as a tuple like this
MyEnum(('aaa',))
EDIT:
This turns out to be false. Credits to a commenter for pointing out my mistake

Is there a better way to get a named series of constants (enumeration) in Python? [duplicate]

This question already has answers here:
How can I represent an 'Enum' in Python?
(43 answers)
Closed 9 years ago.
Just looking at ways of getting named constants in python.
class constant_list:
(A_CONSTANT, B_CONSTANT, C_CONSTANT) = range(3)
Then of course you can refer to it like so:
constant_list.A_CONSTANT
I suppose you could use a dictionary, using strings:
constant_dic = {
"A_CONSTANT" : 1,
"B_CONSTANT" : 2,
"C_CONSTANT" : 3,}
and refer to it like this:
constant_dic["A_CONSTANT"]
My question, then, is simple. Is there any better ways of doing this? Not saying that these are inadequate or anything, just curious - any other common idioms that I've missed?
Thanks in advance.
For 2.3 or after:
class Enumerate(object):
def __init__(self, names):
for number, name in enumerate(names.split()):
setattr(self, name, number)
To use:
codes = Enumerate('FOO BAR BAZ')
codes.BAZ will be 2 and so on.
If you only have 2.2, precede this with:
from __future__ import generators
def enumerate(iterable):
number = 0
for name in iterable:
yield number, name
number += 1
(This was taken from here)
I find the enumeration class recipe (Active State, Python Cookbook) to be very effective.
Plus it has a lookup function which is nice.
Pev
An alternative construction for constant_dic:
constants = ["A_CONSTANT", "B_CONSTANT", "C_CONSTANT"]
constant_dic = dict([(c,i) for i, c in enumerate(constants)])
The following acts like a classisc "written in stone" C enum -- once defined, you can't change it, you can only read its values. Neither can you instantiate it. All you have to do is "import enum.py" and derive from class Enum.
# this is enum.py
class EnumException( Exception ):
pass
class Enum( object ):
class __metaclass__( type ):
def __setattr__( cls, name, value ):
raise EnumException("Can't set Enum class attribute!")
def __delattr__( cls, name ):
raise EnumException("Can't delete Enum class attribute!")
def __init__( self ):
raise EnumException("Enum cannot be instantiated!")
This is the test code:
# this is testenum.py
from enum import *
class ExampleEnum( Enum ):
A=1
B=22
C=333
if __name__ == '__main__' :
print "ExampleEnum.A |%s|" % ExampleEnum.A
print "ExampleEnum.B |%s|" % ExampleEnum.B
print "ExampleEnum.C |%s|" % ExampleEnum.C
z = ExampleEnum.A
if z == ExampleEnum.A:
print "z is A"
try:
ExampleEnum.A = 4
print "ExampleEnum.A |%s| FAIL!" % ExampleEnum.A
except EnumException:
print "Can't change Enum.A (pass...)"
try:
del ExampleEnum.A
except EnumException:
print "Can't delete Enum.A (pass...)"
try:
bad = ExampleEnum()
except EnumException:
print "Can't instantiate Enum (pass...)"
This is the best one I have seen: "First Class Enums in Python"
http://code.activestate.com/recipes/413486/
It gives you a class, and the class contains all the enums. The enums can be compared to each other, but don't have any particular value; you can't use them as an integer value. (I resisted this at first because I am used to C enums, which are integer values. But if you can't use it as an integer, you can't use it as an integer by mistake so overall I think it is a win.) Each enum is a unique object. You can print enums, you can iterate over them, you can test that an enum value is "in" the enum. It's pretty complete and slick.
In Python, strings are immutable and so they are better for constants than numbers. The best approach, in my opinion, is to make an object that keeps constants as strings:
class Enumeration(object):
def __init__(self, possibilities):
self.possibilities = set(possibilities.split())
def all(self):
return sorted(self.possibilities)
def __getattr__(self, name):
if name in self.possibilities:
return name
raise AttributeError("Invalid constant: %s" % name)
You could then use it like this:
>>> enum = Enumeration("FOO BAR")
>>> print enum.all()
['BAR', 'FOO']
>>> print enum.FOO
FOO
>>> print enum.FOOBAR
Traceback (most recent call last):
File "enum.py", line 17, in <module>
print enum.FOOBAR
File "enum.py", line 11, in __getattr__
raise AttributeError("Invalid constant: %s" % name)
AttributeError: Invalid constant: FOOBAR

Categories

Resources