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 _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 # 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 @@ class verify:
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 datetime import date
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 @@ class TestHelpers(unittest.TestCase):
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 TestVerify(unittest.TestCase):
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()