mirror of https://github.com/python/cpython
bpo-44242: [Enum] remove missing bits test from Flag creation (GH-26586)
Move the check for missing named flags in flag aliases from Flag creation to a new *verify* decorator.
This commit is contained in:
parent
6f84656dc1
commit
eea8148b7d
|
@ -28,8 +28,8 @@ An enumeration:
|
||||||
|
|
||||||
* is a set of symbolic names (members) bound to unique values
|
* is a set of symbolic names (members) bound to unique values
|
||||||
* can be iterated over to return its members in definition order
|
* can be iterated over to return its members in definition order
|
||||||
* uses :meth:`call` syntax to return members by value
|
* uses *call* syntax to return members by value
|
||||||
* uses :meth:`index` syntax to return members by name
|
* uses *index* syntax to return members by name
|
||||||
|
|
||||||
Enumerations are created either by using the :keyword:`class` syntax, or by
|
Enumerations are created either by using the :keyword:`class` syntax, or by
|
||||||
using function-call syntax::
|
using function-call syntax::
|
||||||
|
@ -91,6 +91,12 @@ Module Contents
|
||||||
the bitwise operators without losing their :class:`IntFlag` membership.
|
the bitwise operators without losing their :class:`IntFlag` membership.
|
||||||
:class:`IntFlag` members are also subclasses of :class:`int`.
|
:class:`IntFlag` members are also subclasses of :class:`int`.
|
||||||
|
|
||||||
|
:class:`EnumCheck`
|
||||||
|
|
||||||
|
An enumeration with the values ``CONTINUOUS``, ``NAMED_FLAGS``, and
|
||||||
|
``UNIQUE``, for use with :func:`verify` to ensure various constraints
|
||||||
|
are met by a given enumeration.
|
||||||
|
|
||||||
:class:`FlagBoundary`
|
:class:`FlagBoundary`
|
||||||
|
|
||||||
An enumeration with the values ``STRICT``, ``CONFORM``, ``EJECT``, and
|
An enumeration with the values ``STRICT``, ``CONFORM``, ``EJECT``, and
|
||||||
|
@ -117,9 +123,14 @@ Module Contents
|
||||||
|
|
||||||
Enum class decorator that ensures only one name is bound to any one value.
|
Enum class decorator that ensures only one name is bound to any one value.
|
||||||
|
|
||||||
|
:func:`verify`
|
||||||
|
|
||||||
|
Enum class decorator that checks user-selectable constraints on an
|
||||||
|
enumeration.
|
||||||
|
|
||||||
|
|
||||||
.. versionadded:: 3.6 ``Flag``, ``IntFlag``, ``auto``
|
.. versionadded:: 3.6 ``Flag``, ``IntFlag``, ``auto``
|
||||||
.. versionadded:: 3.10 ``StrEnum``
|
.. versionadded:: 3.10 ``StrEnum``, ``EnumCheck``, ``FlagBoundary``
|
||||||
|
|
||||||
|
|
||||||
Data Types
|
Data Types
|
||||||
|
@ -514,6 +525,65 @@ Data Types
|
||||||
Using :class:`auto` with :class:`IntFlag` results in integers that are powers
|
Using :class:`auto` with :class:`IntFlag` results in integers that are powers
|
||||||
of two, starting with ``1``.
|
of two, starting with ``1``.
|
||||||
|
|
||||||
|
.. class:: EnumCheck
|
||||||
|
|
||||||
|
*EnumCheck* contains the options used by the :func:`verify` decorator to ensure
|
||||||
|
various constraints; failed constraints result in a :exc:`TypeError`.
|
||||||
|
|
||||||
|
.. attribute:: UNIQUE
|
||||||
|
|
||||||
|
Ensure that each value has only one name::
|
||||||
|
|
||||||
|
>>> from enum import Enum, verify, UNIQUE
|
||||||
|
>>> @verify(UNIQUE)
|
||||||
|
... class Color(Enum):
|
||||||
|
... RED = 1
|
||||||
|
... GREEN = 2
|
||||||
|
... BLUE = 3
|
||||||
|
... CRIMSON = 1
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValueError: aliases found in <enum 'Color'>: CRIMSON -> RED
|
||||||
|
|
||||||
|
|
||||||
|
.. attribute:: CONTINUOUS
|
||||||
|
|
||||||
|
Ensure that there are no missing values between the lowest-valued member
|
||||||
|
and the highest-valued member::
|
||||||
|
|
||||||
|
>>> from enum import Enum, verify, CONTINUOUS
|
||||||
|
>>> @verify(CONTINUOUS)
|
||||||
|
... class Color(Enum):
|
||||||
|
... RED = 1
|
||||||
|
... GREEN = 2
|
||||||
|
... BLUE = 5
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValueError: invalid enum 'Color': missing values 3, 4
|
||||||
|
|
||||||
|
.. attribute:: NAMED_FLAGS
|
||||||
|
|
||||||
|
Ensure that any flag groups/masks contain only named flags -- useful when
|
||||||
|
values are specified instead of being generated by :func:`auto`
|
||||||
|
|
||||||
|
>>> from enum import Flag, verify, NAMED_FLAGS
|
||||||
|
>>> @verify(NAMED_FLAGS)
|
||||||
|
... class Color(Flag):
|
||||||
|
... RED = 1
|
||||||
|
... GREEN = 2
|
||||||
|
... BLUE = 4
|
||||||
|
... WHITE = 15
|
||||||
|
... NEON = 31
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValueError: invalid Flag 'Color': 'WHITE' is missing a named flag for value 8; 'NEON' is missing named flags for values 8, 16
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
CONTINUOUS and NAMED_FLAGS are designed to work with integer-valued members.
|
||||||
|
|
||||||
|
.. versionadded:: 3.10
|
||||||
|
|
||||||
.. class:: FlagBoundary
|
.. class:: FlagBoundary
|
||||||
|
|
||||||
*FlagBoundary* controls how out-of-range values are handled in *Flag* and its
|
*FlagBoundary* controls how out-of-range values are handled in *Flag* and its
|
||||||
|
@ -575,7 +645,7 @@ Data Types
|
||||||
>>> KeepFlag(2**2 + 2**4)
|
>>> KeepFlag(2**2 + 2**4)
|
||||||
KeepFlag.BLUE|0x10
|
KeepFlag.BLUE|0x10
|
||||||
|
|
||||||
.. versionadded:: 3.10 ``FlagBoundary``
|
.. versionadded:: 3.10
|
||||||
|
|
||||||
|
|
||||||
Utilites and Decorators
|
Utilites and Decorators
|
||||||
|
@ -632,3 +702,11 @@ Utilites and Decorators
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE
|
ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE
|
||||||
|
|
||||||
|
.. decorator:: verify
|
||||||
|
|
||||||
|
A :keyword:`class` decorator specifically for enumerations. Members from
|
||||||
|
:class:`EnumCheck` are used to specify which constraints should be checked
|
||||||
|
on the decorated enumeration.
|
||||||
|
|
||||||
|
.. versionadded:: 3.10
|
||||||
|
|
107
Lib/enum.py
107
Lib/enum.py
|
@ -6,10 +6,10 @@ from builtins import property as _bltin_property, bin as _bltin_bin
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'EnumType', 'EnumMeta',
|
'EnumType', 'EnumMeta',
|
||||||
'Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag',
|
'Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag',
|
||||||
'auto', 'unique',
|
'auto', 'unique', 'property', 'verify',
|
||||||
'property',
|
|
||||||
'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP',
|
'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP',
|
||||||
'global_flag_repr', 'global_enum_repr', 'global_enum',
|
'global_flag_repr', 'global_enum_repr', 'global_enum',
|
||||||
|
'EnumCheck', 'CONTINUOUS', 'NAMED_FLAGS', 'UNIQUE',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,6 +89,9 @@ def _make_class_unpicklable(obj):
|
||||||
setattr(obj, '__module__', '<unknown>')
|
setattr(obj, '__module__', '<unknown>')
|
||||||
|
|
||||||
def _iter_bits_lsb(num):
|
def _iter_bits_lsb(num):
|
||||||
|
# num must be an integer
|
||||||
|
if isinstance(num, Enum):
|
||||||
|
num = num.value
|
||||||
while num:
|
while num:
|
||||||
b = num & (~num + 1)
|
b = num & (~num + 1)
|
||||||
yield b
|
yield b
|
||||||
|
@ -538,13 +541,6 @@ class EnumType(type):
|
||||||
else:
|
else:
|
||||||
# multi-bit flags are considered aliases
|
# multi-bit flags are considered aliases
|
||||||
multi_bit_total |= flag_value
|
multi_bit_total |= flag_value
|
||||||
if enum_class._boundary_ is not KEEP:
|
|
||||||
missed = list(_iter_bits_lsb(multi_bit_total & ~single_bit_total))
|
|
||||||
if missed:
|
|
||||||
raise TypeError(
|
|
||||||
'invalid Flag %r -- missing values: %s'
|
|
||||||
% (cls, ', '.join((str(i) for i in missed)))
|
|
||||||
)
|
|
||||||
enum_class._flag_mask_ = single_bit_total
|
enum_class._flag_mask_ = single_bit_total
|
||||||
#
|
#
|
||||||
# set correct __iter__
|
# set correct __iter__
|
||||||
|
@ -688,7 +684,10 @@ class EnumType(type):
|
||||||
return MappingProxyType(cls._member_map_)
|
return MappingProxyType(cls._member_map_)
|
||||||
|
|
||||||
def __repr__(cls):
|
def __repr__(cls):
|
||||||
return "<enum %r>" % cls.__name__
|
if Flag is not None and issubclass(cls, Flag):
|
||||||
|
return "<flag %r>" % cls.__name__
|
||||||
|
else:
|
||||||
|
return "<enum %r>" % cls.__name__
|
||||||
|
|
||||||
def __reversed__(cls):
|
def __reversed__(cls):
|
||||||
"""
|
"""
|
||||||
|
@ -1303,7 +1302,8 @@ class Flag(Enum, boundary=STRICT):
|
||||||
else:
|
else:
|
||||||
# calculate flags not in this member
|
# calculate flags not in this member
|
||||||
self._inverted_ = self.__class__(self._flag_mask_ ^ self._value_)
|
self._inverted_ = self.__class__(self._flag_mask_ ^ self._value_)
|
||||||
self._inverted_._inverted_ = self
|
if isinstance(self._inverted_, self.__class__):
|
||||||
|
self._inverted_._inverted_ = self
|
||||||
return self._inverted_
|
return self._inverted_
|
||||||
|
|
||||||
|
|
||||||
|
@ -1561,6 +1561,91 @@ def _simple_enum(etype=Enum, *, boundary=None, use_args=None):
|
||||||
return enum_class
|
return enum_class
|
||||||
return convert_class
|
return convert_class
|
||||||
|
|
||||||
|
@_simple_enum(StrEnum)
|
||||||
|
class EnumCheck:
|
||||||
|
"""
|
||||||
|
various conditions to check an enumeration for
|
||||||
|
"""
|
||||||
|
CONTINUOUS = "no skipped integer values"
|
||||||
|
NAMED_FLAGS = "multi-flag aliases may not contain unnamed flags"
|
||||||
|
UNIQUE = "one name per value"
|
||||||
|
CONTINUOUS, NAMED_FLAGS, UNIQUE = EnumCheck
|
||||||
|
|
||||||
|
|
||||||
|
class verify:
|
||||||
|
"""
|
||||||
|
Check an enumeration for various constraints. (see EnumCheck)
|
||||||
|
"""
|
||||||
|
def __init__(self, *checks):
|
||||||
|
self.checks = checks
|
||||||
|
def __call__(self, enumeration):
|
||||||
|
checks = self.checks
|
||||||
|
cls_name = enumeration.__name__
|
||||||
|
if Flag is not None and issubclass(enumeration, Flag):
|
||||||
|
enum_type = 'flag'
|
||||||
|
elif issubclass(enumeration, Enum):
|
||||||
|
enum_type = 'enum'
|
||||||
|
else:
|
||||||
|
raise TypeError("the 'verify' decorator only works with Enum and Flag")
|
||||||
|
for check in checks:
|
||||||
|
if check is UNIQUE:
|
||||||
|
# check for duplicate names
|
||||||
|
duplicates = []
|
||||||
|
for name, member in enumeration.__members__.items():
|
||||||
|
if name != member.name:
|
||||||
|
duplicates.append((name, member.name))
|
||||||
|
if duplicates:
|
||||||
|
alias_details = ', '.join(
|
||||||
|
["%s -> %s" % (alias, name) for (alias, name) in duplicates])
|
||||||
|
raise ValueError('aliases found in %r: %s' %
|
||||||
|
(enumeration, alias_details))
|
||||||
|
elif check is CONTINUOUS:
|
||||||
|
values = set(e.value for e in enumeration)
|
||||||
|
if len(values) < 2:
|
||||||
|
continue
|
||||||
|
low, high = min(values), max(values)
|
||||||
|
missing = []
|
||||||
|
if enum_type == 'flag':
|
||||||
|
# check for powers of two
|
||||||
|
for i in range(_high_bit(low)+1, _high_bit(high)):
|
||||||
|
if 2**i not in values:
|
||||||
|
missing.append(2**i)
|
||||||
|
elif enum_type == 'enum':
|
||||||
|
# check for powers of one
|
||||||
|
for i in range(low+1, high):
|
||||||
|
if i not in values:
|
||||||
|
missing.append(i)
|
||||||
|
else:
|
||||||
|
raise Exception('verify: unknown type %r' % enum_type)
|
||||||
|
if missing:
|
||||||
|
raise ValueError('invalid %s %r: missing values %s' % (
|
||||||
|
enum_type, cls_name, ', '.join((str(m) for m in missing)))
|
||||||
|
)
|
||||||
|
elif check is NAMED_FLAGS:
|
||||||
|
# examine each alias and check for unnamed flags
|
||||||
|
member_names = enumeration._member_names_
|
||||||
|
member_values = [m.value for m in enumeration]
|
||||||
|
missing = []
|
||||||
|
for name, alias in enumeration._member_map_.items():
|
||||||
|
if name in member_names:
|
||||||
|
# not an alias
|
||||||
|
continue
|
||||||
|
values = list(_iter_bits_lsb(alias.value))
|
||||||
|
missed = [v for v in values if v not in member_values]
|
||||||
|
if missed:
|
||||||
|
plural = ('', 's')[len(missed) > 1]
|
||||||
|
a = ('a ', '')[len(missed) > 1]
|
||||||
|
missing.append('%r is missing %snamed flag%s for value%s %s' % (
|
||||||
|
name, a, plural, plural,
|
||||||
|
', '.join(str(v) for v in missed)
|
||||||
|
))
|
||||||
|
if missing:
|
||||||
|
raise ValueError(
|
||||||
|
'invalid Flag %r: %s'
|
||||||
|
% (cls_name, '; '.join(missing))
|
||||||
|
)
|
||||||
|
return enumeration
|
||||||
|
|
||||||
def _test_simple_enum(checked_enum, simple_enum):
|
def _test_simple_enum(checked_enum, simple_enum):
|
||||||
"""
|
"""
|
||||||
A function that can be used to test an enum created with :func:`_simple_enum`
|
A function that can be used to test an enum created with :func:`_simple_enum`
|
||||||
|
|
|
@ -9,6 +9,7 @@ import threading
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from enum import Enum, IntEnum, StrEnum, EnumType, Flag, IntFlag, unique, auto
|
from enum import Enum, IntEnum, StrEnum, EnumType, Flag, IntFlag, unique, auto
|
||||||
from enum import STRICT, CONFORM, EJECT, KEEP, _simple_enum, _test_simple_enum
|
from enum import STRICT, CONFORM, EJECT, KEEP, _simple_enum, _test_simple_enum
|
||||||
|
from enum import verify, UNIQUE, CONTINUOUS, NAMED_FLAGS
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL
|
from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL
|
||||||
from test import support
|
from test import support
|
||||||
|
@ -2774,13 +2775,6 @@ class TestFlag(unittest.TestCase):
|
||||||
third = auto()
|
third = auto()
|
||||||
self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes))
|
self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes))
|
||||||
|
|
||||||
def test_bizarre(self):
|
|
||||||
with self.assertRaisesRegex(TypeError, "invalid Flag 'Bizarre' -- missing values: 1, 2"):
|
|
||||||
class Bizarre(Flag):
|
|
||||||
b = 3
|
|
||||||
c = 4
|
|
||||||
d = 6
|
|
||||||
|
|
||||||
def test_multiple_mixin(self):
|
def test_multiple_mixin(self):
|
||||||
class AllMixin:
|
class AllMixin:
|
||||||
@classproperty
|
@classproperty
|
||||||
|
@ -3345,12 +3339,6 @@ class TestIntFlag(unittest.TestCase):
|
||||||
for f in Open:
|
for f in Open:
|
||||||
self.assertEqual(bool(f.value), bool(f))
|
self.assertEqual(bool(f.value), bool(f))
|
||||||
|
|
||||||
def test_bizarre(self):
|
|
||||||
with self.assertRaisesRegex(TypeError, "invalid Flag 'Bizarre' -- missing values: 1, 2"):
|
|
||||||
class Bizarre(IntFlag):
|
|
||||||
b = 3
|
|
||||||
c = 4
|
|
||||||
d = 6
|
|
||||||
|
|
||||||
def test_multiple_mixin(self):
|
def test_multiple_mixin(self):
|
||||||
class AllMixin:
|
class AllMixin:
|
||||||
|
@ -3459,6 +3447,7 @@ class TestUnique(unittest.TestCase):
|
||||||
one = 1
|
one = 1
|
||||||
two = 'dos'
|
two = 'dos'
|
||||||
tres = 4.0
|
tres = 4.0
|
||||||
|
#
|
||||||
@unique
|
@unique
|
||||||
class Cleaner(IntEnum):
|
class Cleaner(IntEnum):
|
||||||
single = 1
|
single = 1
|
||||||
|
@ -3484,12 +3473,137 @@ class TestUnique(unittest.TestCase):
|
||||||
turkey = 3
|
turkey = 3
|
||||||
|
|
||||||
def test_unique_with_name(self):
|
def test_unique_with_name(self):
|
||||||
@unique
|
@verify(UNIQUE)
|
||||||
class Silly(Enum):
|
class Silly(Enum):
|
||||||
one = 1
|
one = 1
|
||||||
two = 'dos'
|
two = 'dos'
|
||||||
name = 3
|
name = 3
|
||||||
@unique
|
#
|
||||||
|
@verify(UNIQUE)
|
||||||
|
class Sillier(IntEnum):
|
||||||
|
single = 1
|
||||||
|
name = 2
|
||||||
|
triple = 3
|
||||||
|
value = 4
|
||||||
|
|
||||||
|
class TestVerify(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_continuous(self):
|
||||||
|
@verify(CONTINUOUS)
|
||||||
|
class Auto(Enum):
|
||||||
|
FIRST = auto()
|
||||||
|
SECOND = auto()
|
||||||
|
THIRD = auto()
|
||||||
|
FORTH = auto()
|
||||||
|
#
|
||||||
|
@verify(CONTINUOUS)
|
||||||
|
class Manual(Enum):
|
||||||
|
FIRST = 3
|
||||||
|
SECOND = 4
|
||||||
|
THIRD = 5
|
||||||
|
FORTH = 6
|
||||||
|
#
|
||||||
|
with self.assertRaisesRegex(ValueError, 'invalid enum .Missing.: missing values 5, 6, 7, 8, 9, 10, 12'):
|
||||||
|
@verify(CONTINUOUS)
|
||||||
|
class Missing(Enum):
|
||||||
|
FIRST = 3
|
||||||
|
SECOND = 4
|
||||||
|
THIRD = 11
|
||||||
|
FORTH = 13
|
||||||
|
#
|
||||||
|
with self.assertRaisesRegex(ValueError, 'invalid flag .Incomplete.: missing values 32'):
|
||||||
|
@verify(CONTINUOUS)
|
||||||
|
class Incomplete(Flag):
|
||||||
|
FIRST = 4
|
||||||
|
SECOND = 8
|
||||||
|
THIRD = 16
|
||||||
|
FORTH = 64
|
||||||
|
#
|
||||||
|
with self.assertRaisesRegex(ValueError, 'invalid flag .StillIncomplete.: missing values 16'):
|
||||||
|
@verify(CONTINUOUS)
|
||||||
|
class StillIncomplete(Flag):
|
||||||
|
FIRST = 4
|
||||||
|
SECOND = 8
|
||||||
|
THIRD = 11
|
||||||
|
FORTH = 32
|
||||||
|
|
||||||
|
|
||||||
|
def test_composite(self):
|
||||||
|
class Bizarre(Flag):
|
||||||
|
b = 3
|
||||||
|
c = 4
|
||||||
|
d = 6
|
||||||
|
self.assertEqual(list(Bizarre), [Bizarre.c])
|
||||||
|
self.assertEqual(Bizarre.b.value, 3)
|
||||||
|
self.assertEqual(Bizarre.c.value, 4)
|
||||||
|
self.assertEqual(Bizarre.d.value, 6)
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
ValueError,
|
||||||
|
"invalid Flag 'Bizarre': 'b' is missing named flags for values 1, 2; 'd' is missing a named flag for value 2",
|
||||||
|
):
|
||||||
|
@verify(NAMED_FLAGS)
|
||||||
|
class Bizarre(Flag):
|
||||||
|
b = 3
|
||||||
|
c = 4
|
||||||
|
d = 6
|
||||||
|
#
|
||||||
|
class Bizarre(IntFlag):
|
||||||
|
b = 3
|
||||||
|
c = 4
|
||||||
|
d = 6
|
||||||
|
self.assertEqual(list(Bizarre), [Bizarre.c])
|
||||||
|
self.assertEqual(Bizarre.b.value, 3)
|
||||||
|
self.assertEqual(Bizarre.c.value, 4)
|
||||||
|
self.assertEqual(Bizarre.d.value, 6)
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
ValueError,
|
||||||
|
"invalid Flag 'Bizarre': 'b' is missing named flags for values 1, 2; 'd' is missing a named flag for value 2",
|
||||||
|
):
|
||||||
|
@verify(NAMED_FLAGS)
|
||||||
|
class Bizarre(IntFlag):
|
||||||
|
b = 3
|
||||||
|
c = 4
|
||||||
|
d = 6
|
||||||
|
|
||||||
|
def test_unique_clean(self):
|
||||||
|
@verify(UNIQUE)
|
||||||
|
class Clean(Enum):
|
||||||
|
one = 1
|
||||||
|
two = 'dos'
|
||||||
|
tres = 4.0
|
||||||
|
#
|
||||||
|
@verify(UNIQUE)
|
||||||
|
class Cleaner(IntEnum):
|
||||||
|
single = 1
|
||||||
|
double = 2
|
||||||
|
triple = 3
|
||||||
|
|
||||||
|
def test_unique_dirty(self):
|
||||||
|
with self.assertRaisesRegex(ValueError, 'tres.*one'):
|
||||||
|
@verify(UNIQUE)
|
||||||
|
class Dirty(Enum):
|
||||||
|
one = 1
|
||||||
|
two = 'dos'
|
||||||
|
tres = 1
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
ValueError,
|
||||||
|
'double.*single.*turkey.*triple',
|
||||||
|
):
|
||||||
|
@verify(UNIQUE)
|
||||||
|
class Dirtier(IntEnum):
|
||||||
|
single = 1
|
||||||
|
double = 1
|
||||||
|
triple = 3
|
||||||
|
turkey = 3
|
||||||
|
|
||||||
|
def test_unique_with_name(self):
|
||||||
|
@verify(UNIQUE)
|
||||||
|
class Silly(Enum):
|
||||||
|
one = 1
|
||||||
|
two = 'dos'
|
||||||
|
name = 3
|
||||||
|
#
|
||||||
|
@verify(UNIQUE)
|
||||||
class Sillier(IntEnum):
|
class Sillier(IntEnum):
|
||||||
single = 1
|
single = 1
|
||||||
name = 2
|
name = 2
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Remove missing flag check from Enum creation and move into a ``verify``
|
||||||
|
decorator.
|
Loading…
Reference in New Issue