8000 bpo-38250: [Enum] single-bit flags are canonical by ethanfurman · Pull Request #24215 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

bpo-38250: [Enum] single-bit flags are canonical #24215

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 30 commits into from
Jan 25, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5f522d3
[Enum] fix Flag iteration, repr(), and str()
ethanfurman Jan 13, 2021
f7f9e72
add boundary KEEP for Flags (default for _convert_)
ethanfurman Jan 14, 2021
d289ba3
update re.RegexFlag for new Flag implementation
ethanfurman Jan 14, 2021
72dbdd7
remove extra white space
ethanfurman Jan 14, 2021
210fae7
test that zero-valued members are empty
ethanfurman Jan 14, 2021
48bcd07
update tests to confirm CONFORM with negate
ethanfurman Jan 14, 2021
1fd7471
update aenum.rst; add doctest to test_enum
ethanfurman Jan 14, 2021
aa425e6
fix doc test
ethanfurman Jan 14, 2021
4786942
optimizations
ethanfurman Jan 14, 2021
45565b2
formatting
ethanfurman Jan 14, 2021
806c8c6
add news entry
ethanfurman Jan 14, 2021
668c9a9
fix formatting of news entry
ethanfurman Jan 15, 2021
9bd9e97
update iteration method and order
ethanfurman Jan 20, 2021
18bcbac
add John Belmonte
ethanfurman Jan 20, 2021
f1c4584
more bit-fiddling improvements
ethanfurman Jan 20, 2021
e3713aa
use pop() instead of "del"
ethanfurman Jan 20, 2021
8000
c4ec211
update DynamicClassAttribute __doc__
ethanfurman Jan 20, 2021
00b2bfe
remove formatting changes
ethanfurman Jan 20, 2021
9f432c3
remove extra parens
ethanfurman Jan 20, 2021
15c060a
remove formatting changes
ethanfurman Jan 20, 2021
86d7669
remove formatting
ethanfurman Jan 20, 2021
55915df
simplify determination of member iteration
ethanfurman Jan 22, 2021
95bf9c8
add note about next auto() value for Enum and Flag
ethanfurman Jan 25, 2021
41ac1ce
local name optimizations
ethanfurman Jan 25, 2021
3ea814e
remove commented-out code
ethanfurman Jan 25, 2021
4983558
add test for next auto() and _order_
ethanfurman Jan 25, 2021
651da18
raise TypeError if _value_ not added in custom new
ethanfurman Jan 25, 2021
6e99d48
enable doc tests, update formatting
ethanfurman Jan 25, 2021
b52c5a2
fix note
ethanfurman Jan 25, 2021
8d7b272
Update 2021-01-14-15-07-16.bpo-38250.1fvhOk.rst
ethanfurman Jan 25, 2021
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
Prev Previous commit
Next Next commit
raise TypeError if _value_ not added in custom new
also fix _order_ tests
  • Loading branch information
ethanfurman committed Jan 25, 2021
commit 651da184f1ae6ff5c067a658130473fb13e09422
42 changes: 38 additions & 4 deletions Lib/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,12 @@ def __set_name__(self, enum_class, member_name):
if enum_class._member_type_ is object:
enum_member._value_ = value
else:
enum_member._value_ = enum_class._member_type_(*args)
try:
enum_member._value_ = enum_class._member_type_(*args)
except Exception as exc:
raise TypeError(
'_value_ not set in __new__, unable to create it'
) from None
value = enum_member._value_
enum_member._name_ = member_name
enum_member.__objclass__ = enum_class
Expand Down Expand Up @@ -478,11 +483,17 @@ def __new__(metacls, cls, bases, classdict, boundary=None, **kwds):
enum_class.__new__ = Enum.__new__
#
# py3 support for definition order (helps keep py2/py3 code in sync)
#
# _order_ checking is spread out into three/four steps
# - if enum_class is a Flag:
# - remove any non-single-bit flags from _order_
# - remove any aliases from _order_
# - check that _order_ and _member_names_ match
#
# step 1: ensure we have a list
if _order_ is not None:
if isinstance(_order_, str):
_order_ = _order_.replace(',', ' ').split()
if _order_ != enum_class._member_names_:
raise TypeError('member order does not match _order_')
#
# remove Flag structures if final class is not a Flag
if (
Expand All @@ -505,7 +516,6 @@ def __new__(metacls, cls, bases, classdict, boundary=None, **kwds):
# multi-bit flags are considered aliases
multi_bit_total |= flag_value
if enum_class._boundary_ is not KEEP:
# missed_bits = multi_bit_total & ~single_bit_total
missed = list(_iter_bits_lsb(multi_bit_total & ~single_bit_total))
if missed:
raise TypeError(
Expand All @@ -518,6 +528,30 @@ def __new__(metacls, cls, bases, classdict, boundary=None, **kwds):
member_list = [m._value_ for m in enum_class]
if member_list != sorted(member_list):
enum_class._iter_member_ = enum_class._iter_member_by_def_
if _order_:
# _order_ step 2: remove any items from _order_ that are not single-bit
_order_ = [
o
for o in _order_
if o not in enum_class._member_map_ or _is_single_bit(enum_class[o]._value_)
]
#
if _order_:
# _order_ step 3: remove aliases from _order_
_order_ = [
o
for o in _order_
if (
o not in enum_class._member_map_
or
(o in enum_class._member_map_ and o in enum_class._member_names_)
)]
# _order_ step 4: verify that _order_ and _member_names_ match
if _order_ != enum_class._member_names_:
raise TypeError(
'member order does not match _order_:\n%r\n%r'
% (enum_class._member_names_, _order_)
)
#
return enum_class

Expand Down
26 changes: 24 additions & 2 deletions Lib/test/test_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -2135,7 +2135,30 @@ class ThirdFailedStrEnum(StrEnum):
one = '1'
two = b'2', 'ascii', 9


def test_missing_value_error(self):
with self.assertRaisesRegex(TypeError, "_value_ not set in __new__"):
class Combined(str, Enum):
#
def __new__(cls, value, sequence):
enum = str.__new__(cls, value)
if '(' in value:
fis_name, segment = value.split('(', 1)
segment = segment.strip(' )')
else:
fis_name = value
segment = None
enum.fis_name = fis_name
enum.segment = segment
enum.sequence = sequence
return enum
#
def __repr__(self):
return "<%s.%s>" % (self.__class__.__name__, self._name_)
#
key_type = 'An$(1,2)', 0
company_id = 'An$(3,2)', 1
code = 'An$(5,1)', 2
description = 'Bn$', 3

@unittest.skipUnless(
sys.version_info[:2] == (3, 9),
Expand Down Expand Up @@ -2580,7 +2603,6 @@ def test_member_length(self):

def test_number_reset_and_order_cleanup(self):
class Confused(Flag):
_settings_ = AutoValue
_order_ = 'ONE TWO FOUR DOS EIGHT SIXTEEN'
ONE = auto()
TWO = auto()
Expand Down
0