gh-99248: [Enum] fix negative number infinite loop (GH-99256)

[Enum] fix negative number infinite loop

- _iter_bits_lsb() now raises a ValueError if a negative number
  is passed in

- verify() now skips checking negative numbers for named flags
This commit is contained in:
Ethan Furman 2022-11-08 12:00:19 -08:00 committed by GitHub
parent 52f91c642b
commit 0b4ffb08cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 23 additions and 2 deletions

View file

@ -114,9 +114,12 @@ def _break_on_call_reduce(self, proto):
setattr(obj, '__module__', '<unknown>') setattr(obj, '__module__', '<unknown>')
def _iter_bits_lsb(num): def _iter_bits_lsb(num):
# num must be an integer # num must be a positive integer
original = num
if isinstance(num, Enum): if isinstance(num, Enum):
num = num.value num = num.value
if num < 0:
raise ValueError('%r is not a positive integer' % original)
while num: while num:
b = num & (~num + 1) b = num & (~num + 1)
yield b yield b
@ -1839,6 +1842,9 @@ def __call__(self, enumeration):
if name in member_names: if name in member_names:
# not an alias # not an alias
continue continue
if alias.value < 0:
# negative numbers are not checked
continue
values = list(_iter_bits_lsb(alias.value)) values = list(_iter_bits_lsb(alias.value))
missed = [v for v in values if v not in member_values] missed = [v for v in values if v not in member_values]
if missed: if missed:

View file

@ -14,7 +14,7 @@
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, ReprEnum from enum import verify, UNIQUE, CONTINUOUS, NAMED_FLAGS, ReprEnum
from enum import member, nonmember from enum import member, nonmember, _iter_bits_lsb
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
@ -174,6 +174,10 @@ def test_is_private(self):
for name in self.sunder_names + self.dunder_names + self.random_names: for name in self.sunder_names + self.dunder_names + self.random_names:
self.assertFalse(enum._is_private('MyEnum', name), '%r is a private name?') self.assertFalse(enum._is_private('MyEnum', name), '%r is a private name?')
def test_iter_bits_lsb(self):
self.assertEqual(list(_iter_bits_lsb(7)), [1, 2, 4])
self.assertRaisesRegex(ValueError, '-8 is not a positive integer', list, _iter_bits_lsb(-8))
# for subclassing tests # for subclassing tests
@ -3960,6 +3964,16 @@ class Sillier(IntEnum):
triple = 3 triple = 3
value = 4 value = 4
def test_negative_alias(self):
@verify(NAMED_FLAGS)
class Color(Flag):
RED = 1
GREEN = 2
BLUE = 4
WHITE = -1
# no error means success
class TestInternals(unittest.TestCase): class TestInternals(unittest.TestCase):
sunder_names = '_bad_', '_good_', '_what_ho_' sunder_names = '_bad_', '_good_', '_what_ho_'

View file

@ -0,0 +1 @@
fix negative numbers failing in verify()