I have an enum for which some of the members are deprecated:
from enum import Enum
class Foo(Enum):
BAR = "bar"
BAZ = "baz" # deprecated
How do it get the following behavior:
Foo.BAR
, everything behaves normallyFoo.BAZ
, a DeprecationWarning
is issued using warnings.warn("BAZ is deprecated", DeprecationWarning)
. Afterwards everything behaves normally.Foo("baz")
and Foo["BAZ"]
should raise a DeprecationWarning
.Things I have tried, but failed:
_missing_
and don't define BAZ
. Does not work, because in the end I still need to return an existing member for a while (until our DB is cleaned of the deprecated value).
But I can not dynamically add members to an enum. If I define it, _missing_
is not called.__getattr__
, __getattribute__
. These are called when accessing attributes of a member, e.g. Foo.BAZ.boo
, not when accessing Foo.BAZ
. I guess this could work if I could overwrite __getattr__
of EnumMeta
and then make Enum
use the child meta class. However, I don't see how that can be done either__class_getitem__
: Reserved for static typing and not called anyways._generate_next_value_
. This function is only called on class creation, so I can get a deprecation warning when the class is called once, regardless of whether the deprecated member is called or not. But that is not what I want.TLDR: How can I detect and invoke a function when an enum member is accessed?
I am working with python 3.8, so new features are fine.
Printing enum as an iterable We can print the enum as an iterable list. In the below code we use a for loop to print all enum members.
Syntax : enum.auto() Automatically assign the integer value to the values of enum class attributes. Example #1 : In this example we can see that by using enum. auto() method, we are able to assign the numerical values automatically to the class attributes by using this method.
Python enums are useful to represent data that represent a finite set of states such as days of the week, months of the year, etc. They were added to Python 3.4 via PEP 435. However, it is available all the way back to 2.4 via pypy. As such, you can expect them to be a staple as you explore Python programming.
This appears to be one of those times when subclassing EnumMeta
is the right thing to do.
The new metaclass will run an _on_access
method, if it exists, whenever a member is accessed:
class OnAccess(EnumMeta):
"""
runs a user-specified function whenever member is accessed
"""
#
def __getattribute__(cls, name):
obj = super().__getattribute__(name)
if isinstance(obj, Enum) and obj._on_access:
obj._on_access()
return obj
#
def __getitem__(cls, name):
member = super().__getitem__(name)
if member._on_access:
member._on_access()
return member
#
def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1):
obj = super().__call__(value, names, module=module, qualname=qualname, type=type, start=start)
if isinstance(obj, Enum) and obj._on_access:
obj._on_access()
return obj
The new base Enum
treats any extra arguments on member creation as arguments for a deprecate
function, and sets the _on_access
attribute to that function only if extra arguments are given:
class DeprecatedEnum(Enum, metaclass=OnAccess):
#
def __new__(cls, value, *args):
member = object.__new__(cls)
member._value_ = value
member._args = args
member._on_access = member.deprecate if args else None
return member
#
def deprecate(self):
args = (self.name, ) + self._args
import warnings
warnings.warn(
"member %r is deprecated; %s" % args,
DeprecationWarning,
stacklevel=3,
)
And our example Enum
with deprecated members:
class Foo(DeprecatedEnum):
BAR = "bar"
BAZ = "baz", "use something else"
And the warnings (from a test script):
# no warning here
list(Foo)
# nor for non-deprecated members
Foo.BAR
# but direct use of deprecated members does generate warnings
Foo.BAZ
/home/ethan/test:74: DeprecationWarning: member 'BAZ' is deprecated; use something else
Foo.BAZ
Foo('baz')
/home/ethan/test:75: DeprecationWarning: member 'BAZ' is deprecated; use something else
Foo('baz')
Foo['BAZ']
/home/ethan/test:76: DeprecationWarning: member 'BAZ' is deprecated; use something else
Foo['BAZ']
And all the deprecated members in Foo
:
>>> print([m.name for m in Foo if m._args])
['BAZ']
Disclosure: I am the author of the Python stdlib Enum
, the enum34
backport, and the Advanced Enumeration (aenum
) library.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With