Skip to content

Commit d9ea6f7

Browse files
authored
Merge pull request #130 from loggi/fgm-optimize-getattr
chore(enums) Optimize member check and dynamic creation of `is_<name>` properties
2 parents ca53e94 + 1acfa3d commit d9ea6f7

File tree

2 files changed

+22
-24
lines changed

2 files changed

+22
-24
lines changed

choicesenum/enums.py

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,24 @@
55
from enum import Enum, EnumMeta
66

77

8+
def is_member_factory(enum_member):
9+
"Return a property that checks if the current enum is the expected one"
10+
@property
11+
def is_member(self):
12+
return self == enum_member
13+
return is_member
14+
15+
816
class ChoicesMetaClass(EnumMeta):
917

18+
def __new__(metacls, cls, bases, classdict):
19+
enum_class = EnumMeta.__new__(metacls, cls, bases, classdict)
20+
for name, enum_value in enum_class._member_map_.items():
21+
prop_name = 'is_{}'.format(name.lower())
22+
setattr(enum_class, prop_name, is_member_factory(enum_value))
23+
24+
return enum_class
25+
1026
def __contains__(cls, member):
1127
if not isinstance(member, cls):
1228
try:
@@ -25,13 +41,6 @@ def __new__(cls, value, display=None):
2541
obj._display_ = display
2642
return obj
2743

28-
def __getattr__(self, item):
29-
is_attr = 'is_'
30-
if item.startswith(is_attr) and item in self._get_dynamic_property_names():
31-
search = item[len(is_attr):]
32-
return search == self._name_.lower()
33-
raise AttributeError("'{}' object has no attribute '{}'".format(type(self).__name__, item))
34-
3544
def __str__(self):
3645
return str(self.value)
3746

@@ -75,14 +84,6 @@ def __gt__(self, other):
7584
def __ge__(self, other):
7685
return self.value >= self._get_value(other)
7786

78-
def __dir__(self):
79-
return sorted(set(
80-
dir(type(self)) +
81-
list(self.__dict__.keys()) +
82-
['display', 'get_choices', ] +
83-
list(self._get_dynamic_property_names())
84-
))
85-
8687
def __json__(self):
8788
"""
8889
If you want json serialization, you have at least two options:
@@ -114,14 +115,6 @@ def description(self):
114115
"""
115116
return self.display
116117

117-
@classmethod
118-
def _get_dynamic_property_names(cls):
119-
"""
120-
Args:
121-
cls (Enum): Enum class.
122-
"""
123-
return ('is_{}'.format(x._name_.lower()) for x in cls)
124-
125118
@classmethod
126119
def choices(cls):
127120
"""

tests/test_choicesenum.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,16 @@ def test_dynamic_is_attr_of_undefined_enums_should_fail(colors):
6868
assert colors.RED.is_banana
6969

7070

71-
@pytest.mark.parametrize('attr', ['RED', 'BLUE', 'GREEN', 'is_red', 'is_blue', 'is_green'])
71+
@pytest.mark.parametrize('attr', ['is_red', 'is_blue', 'is_green'])
7272
def test_dynamic_is_attr_should_be_in_dir(colors, attr):
7373
assert attr in dir(colors.RED)
7474

7575

76+
@pytest.mark.parametrize('attr', ['RED', 'BLUE', 'GREEN'])
77+
def test_enum_type_name_should_be_in_dir(colors, attr):
78+
assert attr in dir(colors)
79+
80+
7681
@pytest.mark.skipif(sys.version_info < (3, 0), reason="requires python3")
7782
def test_in_format_python3(colors):
7883
assert '{}'.format(colors.RED) == "#f00"

0 commit comments

Comments
 (0)