Skip to content
Closed
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
cad12cc
Added class attributes access trough DynamicClassProperty (first atte…
Bobronium Aug 9, 2019
d6a48c3
Added class attributes access trough DynamicClassProperty (first atte…
Bobronium Aug 9, 2019
923902e
Replaced dynamic attributes Enum.name and .value to instance attrs
Bobronium Dec 20, 2019
8f2f90c
Improve values check speed (e.g. Color(3))
Bobronium Dec 20, 2019
ed43320
Replace tuples with sets for faster lookups
Bobronium Dec 20, 2019
2c0372a
Used cls variable instead if multiple self.__class__
Bobronium Dec 20, 2019
cdc7041
Merge remote-tracking branch 'origin/fast-enum' into fast-enum
Bobronium Dec 20, 2019
48d75d0
Fix whitespace in types
Bobronium Dec 20, 2019
ce84f10
📜🤖 Added by blurb_it.
blurb-it[bot] Dec 20, 2019
0d1996f
Rename DynamicClassAttribute.__init__(name -> alias), add tests
Bobronium Dec 21, 2019
2f6b977
DynamicClassAttribute.class_attr_name -> alias
Bobronium Dec 21, 2019
1cedc4c
Use _unique_member_map_ instead of _member_names_ to store unique enu…
Bobronium Dec 21, 2019
8304b6f
Use members dict instead of _member_names and _last_values on _EnumDict
Bobronium Dec 21, 2019
4051208
Restore original formatting
Bobronium Dec 21, 2019
a90a4aa
Fix whitespaces
Bobronium Dec 21, 2019
4436cbf
Use dict instead of list to check values in _create_pseudo_member_
Bobronium Dec 21, 2019
45be0c6
Deprecate getting values through _value_ and _name_
Bobronium Dec 22, 2019
eabb7e3
Add 'name' and 'value' to Enum.__dir__
Bobronium Dec 22, 2019
fab4c97
Remove '_member_names' and return 'list(cls._unique_member_map_)' ins…
Bobronium Dec 23, 2019
54e1eff
Use builtin dict() instead of _EnumDict for class creation
Bobronium Dec 23, 2019
378dc88
Use f-strings instead of %s formatting
Bobronium Dec 23, 2019
ecd41fe
Fix missing '>' in repr, add test for that
Bobronium Dec 24, 2019
948d3de
Add cache for repr, str and invert on Flag and IntFlag
Bobronium Dec 24, 2019
a668e2a
Remove redundant name and value setattr
Bobronium Dec 24, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
365 changes: 204 additions & 161 deletions Lib/enum.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -2420,7 +2420,7 @@ class _ParameterKind(enum.IntEnum):
VAR_KEYWORD = 4

def __str__(self):
return self._name_
return self.name

@property
def description(self):
Expand Down
12 changes: 6 additions & 6 deletions Lib/re.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,17 +153,17 @@ class RegexFlag(enum.IntFlag):
DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation

def __repr__(self):
if self._name_ is not None:
return f're.{self._name_}'
value = self._value_
if self.name is not None:
return f're.{self.name}'
value = self.value
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use _repr_ cache here

members = []
negative = value < 0
if negative:
value = ~value
for m in self.__class__:
if value & m._value_:
value &= ~m._value_
members.append(f're.{m._name_}')
if value & m.value:
value &= ~m.value
members.append(f're.{m.name}')
if value:
members.append(hex(value))
res = '|'.join(members)
Expand Down
30 changes: 30 additions & 0 deletions Lib/test/test_dynamicclassattribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,36 @@ def spam(self):
self.assertEqual(Foo.__dict__['spam'].__doc__, "a new docstring")


class TestSetClassAttr(unittest.TestCase):
def test_set_class_attr(self):
class Foo:
def __init__(self, value):
self._value = value
self._spam = 'spam'

@DynamicClassAttribute
def value(self):
return self._value

spam = DynamicClassAttribute(
lambda s: s._spam,
alias='my_shiny_spam'
)

self.assertFalse(hasattr(Foo, 'value'))
self.assertFalse(hasattr(Foo, 'name'))

foo_bar = Foo('bar')
value_desc = Foo.__dict__['value']
value_desc.set_class_attr(Foo, foo_bar)
self.assertIs(Foo.value, foo_bar)
self.assertEqual(Foo.value.value, 'bar')

foo_baz = Foo('baz')
Foo.my_shiny_spam = foo_baz
self.assertIs(Foo.spam, foo_baz)
self.assertEqual(Foo.spam.spam, 'spam')


if __name__ == '__main__':
unittest.main()
128 changes: 92 additions & 36 deletions Lib/test/test_enum.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import enum
import inspect
import pydoc
import warnings

import sys
import unittest
import threading
Expand Down Expand Up @@ -200,7 +202,7 @@ def wowser(self):
)
self.assertEqual(
set(dir(Test.this)),
set(['__class__', '__doc__', '__module__', 'name', 'value', 'wowser']),
set(['__class__', '__doc__', '__module__', 'wowser', 'name', 'value']),
)

def test_dir_on_sub_with_behavior_on_super(self):
Expand All @@ -212,7 +214,7 @@ class SubEnum(SuperEnum):
sample = 5
self.assertEqual(
set(dir(SubEnum.sample)),
set(['__class__', '__doc__', '__module__', 'name', 'value', 'invisible']),
set(['__class__', '__doc__', '__module__', 'invisible', 'name', 'value']),
)

def test_enum_in_enum_out(self):
Expand Down Expand Up @@ -326,7 +328,7 @@ class RealLogic(Enum):
true = True
false = False
def __bool__(self):
return bool(self._value_)
return bool(self.value)
self.assertTrue(RealLogic.true)
self.assertFalse(RealLogic.false)
# mixed Enums depend on mixed-in type
Expand Down Expand Up @@ -1079,9 +1081,9 @@ def test_multiple_mixin_mro(self):
class auto_enum(type(Enum)):
def __new__(metacls, cls, bases, classdict):
temp = type(classdict)()
names = set(classdict._member_names)
names = classdict.members.keys()
i = 0
for k in classdict._member_names:
for k in classdict.members:
v = classdict[k]
if v is Ellipsis:
v = i
Expand Down Expand Up @@ -1435,7 +1437,7 @@ class NEI(NamedInt, Enum):
x = ('the-x', 1)
y = ('the-y', 2)
def __reduce_ex__(self, proto):
return getattr, (self.__class__, self._name_)
return getattr, (self.__class__, self.name)

self.assertIs(NEI.__new__, Enum.__new__)
self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)")
Expand Down Expand Up @@ -1470,7 +1472,7 @@ def __new__(cls):
obj._value_ = value
return obj
def __int__(self):
return int(self._value_)
return int(self.value)
self.assertEqual(
list(AutoNumber),
[AutoNumber.first, AutoNumber.second, AutoNumber.third],
Expand All @@ -1487,7 +1489,7 @@ def __new__(cls):
obj._value_ = value
return obj
def __int__(self):
return int(self._value_)
return int(self.value)
class Color(AutoNumber):
red = ()
green = ()
Expand Down Expand Up @@ -1519,19 +1521,19 @@ def test_ordered_mixin(self):
class OrderedEnum(Enum):
def __ge__(self, other):
if self.__class__ is other.__class__:
return self._value_ >= other._value_
return self.value >= other.value
return NotImplemented
def __gt__(self, other):
if self.__class__ is other.__class__:
return self._value_ > other._value_
return self.value > other.value
return NotImplemented
def __le__(self, other):
if self.__class__ is other.__class__:
return self._value_ <= other._value_
return self.value <= other.value
return NotImplemented
def __lt__(self, other):
if self.__class__ is other.__class__:
return self._value_ < other._value_
return self.value < other.value
return NotImplemented
class Grade(OrderedEnum):
A = 5
Expand Down Expand Up @@ -1799,7 +1801,7 @@ def MAX(cls):
return max
class StrMixin:
def __str__(self):
return self._name_.lower()
return self.name.lower()
class SomeEnum(Enum):
def behavior(self):
return 'booyah'
Expand Down Expand Up @@ -2294,7 +2296,7 @@ def ALL(cls):
return all_value
class StrMixin:
def __str__(self):
return self._name_.lower()
return self.name.lower()
class Color(AllMixin, Flag):
RED = auto()
GREEN = auto()
Expand Down Expand Up @@ -2338,7 +2340,7 @@ class TestFlag(Flag):
def __eq__(self, other):
return self is other
def __hash__(self):
return hash(self._value_)
return hash(self.value)
# have multiple threads competing to complete the composite members
seen = set()
failed = False
Expand Down Expand Up @@ -2712,7 +2714,7 @@ def ALL(cls):
return all_value
class StrMixin:
def __str__(self):
return self._name_.lower()
return self.name.lower()
class Color(AllMixin, IntFlag):
RED = auto()
GREEN = auto()
Expand Down Expand Up @@ -2756,7 +2758,7 @@ class TestFlag(IntFlag):
def __eq__(self, other):
return self is other
def __hash__(self):
return hash(self._value_)
return hash(self.value)
# have multiple threads competing to complete the composite members
seen = set()
failed = False
Expand Down Expand Up @@ -2866,15 +2868,6 @@ class Color(enum.Enum)
| red = <Color.red: 1>
|\x20\x20
| ----------------------------------------------------------------------
| Data descriptors inherited from enum.Enum:
|\x20\x20
| name
| The name of the Enum member.
|\x20\x20
| value
| The value of the Enum member.
|\x20\x20
| ----------------------------------------------------------------------
| Readonly properties inherited from enum.EnumMeta:
|\x20\x20
| __members__
Expand Down Expand Up @@ -2943,9 +2936,7 @@ def test_inspect_getmembers(self):
('__module__', __name__),
('blue', self.Color.blue),
('green', self.Color.green),
('name', Enum.__dict__['name']),
('red', self.Color.red),
('value', Enum.__dict__['value']),
))
result = dict(inspect.getmembers(self.Color))
self.assertEqual(values.keys(), result.keys())
Expand Down Expand Up @@ -2977,10 +2968,6 @@ def test_inspect_classify_class_attrs(self):
defining_class=self.Color, object=self.Color.green),
Attribute(name='red', kind='data',
defining_class=self.Color, object=self.Color.red),
Attribute(name='name', kind='data',
defining_class=Enum, object=Enum.__dict__['name']),
Attribute(name='value', kind='data',
defining_class=Enum, object=Enum.__dict__['value']),
]
values.sort(key=lambda item: item.name)
result = list(inspect.classify_class_attrs(self.Color))
Expand Down Expand Up @@ -3012,7 +2999,7 @@ class TestIntEnumConvert(unittest.TestCase):
def test_convert_value_lookup_priority(self):
test_type = enum.IntEnum._convert_(
'UnittestConvert',
('test.test_enum', '__main__')[__name__=='__main__'],
__name__,
filter=lambda x: x.startswith('CONVERT_TEST_'))
# We don't want the reverse lookup value to vary when there are
# multiple possible names for a given value. It should always
Expand All @@ -3022,7 +3009,7 @@ def test_convert_value_lookup_priority(self):
def test_convert(self):
test_type = enum.IntEnum._convert_(
'UnittestConvert',
('test.test_enum', '__main__')[__name__=='__main__'],
__name__,
filter=lambda x: x.startswith('CONVERT_TEST_'))
# Ensure that test_type has all of the desired names and values.
self.assertEqual(test_type.CONVERT_TEST_NAME_F,
Expand All @@ -3042,7 +3029,7 @@ def test_convert_warn(self):
with self.assertWarns(DeprecationWarning):
enum.IntEnum._convert(
'UnittestConvert',
('test.test_enum', '__main__')[__name__=='__main__'],
__name__,
filter=lambda x: x.startswith('CONVERT_TEST_'))

@unittest.skipUnless(sys.version_info >= (3, 9),
Expand All @@ -3051,9 +3038,78 @@ def test_convert_raise(self):
with self.assertRaises(AttributeError):
enum.IntEnum._convert(
'UnittestConvert',
('test.test_enum', '__main__')[__name__=='__main__'],
__name__,
filter=lambda x: x.startswith('CONVERT_TEST_'))


class TestEnumNamesDeprecation(unittest.TestCase):
@unittest.skipUnless(sys.version_info[:2] == (3, 9),
'_member_names_ was deprecated in 3.9')
def test_member_names_warn(self):
with self.assertWarns(DeprecationWarning):
_ = enum.IntEnum._member_names_

@unittest.skipUnless(sys.version_info >= (3, 10), '_member_names_ was removed in 3.10')
def test_member_names_raise(self):
with self.assertRaises(AttributeError):
_ = enum.IntEnum._member_names_


class TestEnumDictAttrsDeprecation(unittest.TestCase):
@unittest.skipUnless(sys.version_info[:2] == (3, 9),
'_member_names was deprecated in 3.9')
def test_member_names_warn(self):
with self.assertWarns(DeprecationWarning):
_ = enum._EnumDict()._member_names

with self.assertWarns(DeprecationWarning):
_ = enum._EnumDict()._last_values

@unittest.skipUnless(sys.version_info >= (3, 10), '_member_names was removed in 3.10')
def test_member_names_raise(self):
with self.assertRaises(AttributeError):
_ = enum._EnumDict()._member_names

with self.assertRaises(AttributeError):
_ = enum._EnumDict()._last_values


class TestNameValueAttrsDeprecation(unittest.TestCase):

class SquareFoo(Enum):
a = 1
b = 2

def __new__(cls, value):
member = object.__new__(cls)
member._value_ = value ** 2
return member

@unittest.skipUnless(sys.version_info[:2] == (3, 9),
'_name_ and _value_ attr access was deprecated in 3.9')
def test_get_warns(self):
with self.assertWarns(DeprecationWarning):
self.assertEqual(self.SquareFoo.b._value_, 4)

@unittest.skipUnless(sys.version_info >= (3, 9),
'_name_ and _value_ attr access was deprecated in 3.9')
def test_set_not_warns(self):
for sunder_attr, public_attr in ('_name_', 'name'), ('_value_', 'value'):
with warnings.catch_warnings(record=True) as caught_warnings:
setattr(self.SquareFoo.b, sunder_attr, 'foo')

self.assertEqual(caught_warnings, [],
msg=f'Unexpected warnings while setting {sunder_attr}')
self.assertEqual(getattr(self.SquareFoo.b, public_attr), 'foo',
msg=f'{public_attr} and {sunder_attr} mismatch')

@unittest.skipUnless(sys.version_info >= (3, 10),
'_name_ and _value_ attr access was removed in 3.10')
def test_get_raise(self):
for attr in ('_name_', '_value_'):
with self.assertRaises(AttributeError):
getattr(self.SquareFoo.b, attr)


if __name__ == '__main__':
unittest.main()
Loading