[3.13] gh-125316: Fix using partial() as Enum member (GH-125361)

A FutureWarning with suggestion to use enum.member() is now emitted
when the partial instance is used as an enum member.
This commit is contained in:
Serhiy Storchaka 2024-10-21 16:31:42 +03:00 committed by GitHub
parent d9dafc790d
commit 65e43ca6d9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 25 additions and 1 deletions

View file

@ -1,5 +1,6 @@
import sys import sys
import builtins as bltns import builtins as bltns
from functools import partial
from types import MappingProxyType, DynamicClassAttribute from types import MappingProxyType, DynamicClassAttribute
@ -37,7 +38,7 @@ def _is_descriptor(obj):
""" """
Returns True if obj is a descriptor, False otherwise. Returns True if obj is a descriptor, False otherwise.
""" """
return ( return not isinstance(obj, partial) and (
hasattr(obj, '__get__') or hasattr(obj, '__get__') or
hasattr(obj, '__set__') or hasattr(obj, '__set__') or
hasattr(obj, '__delete__') hasattr(obj, '__delete__')
@ -402,6 +403,12 @@ class EnumDict(dict):
elif isinstance(value, nonmember): elif isinstance(value, nonmember):
# unwrap value here; it won't be processed by the below `else` # unwrap value here; it won't be processed by the below `else`
value = value.value value = value.value
elif isinstance(value, partial):
import warnings
warnings.warn('functools.partial will be a method descriptor '
'in future Python versions; wrap it in '
'enum.member() if you want to preserve the '
'old behavior', FutureWarning, stacklevel=2)
elif _is_descriptor(value): elif _is_descriptor(value):
pass pass
elif _is_internal_class(self._cls_name, value): elif _is_internal_class(self._cls_name, value):

View file

@ -11,6 +11,7 @@ import typing
import builtins as bltns import builtins as bltns
from collections import OrderedDict from collections import OrderedDict
from datetime import date from datetime import date
from functools import partial
from enum import Enum, EnumMeta, IntEnum, StrEnum, EnumType, Flag, IntFlag, unique, auto from enum import Enum, EnumMeta, 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
@ -1537,6 +1538,19 @@ class TestSpecial(unittest.TestCase):
[Outer.a, Outer.b, Outer.Inner], [Outer.a, Outer.b, Outer.Inner],
) )
def test_partial(self):
def func(a, b=5):
return a, b
with self.assertWarnsRegex(FutureWarning, r'partial.*enum\.member') as cm:
class E(Enum):
a = 1
b = partial(func)
self.assertEqual(cm.filename, __file__)
self.assertIsInstance(E.b, partial)
self.assertEqual(E.b(2), (2, 5))
with self.assertWarnsRegex(FutureWarning, 'partial'):
self.assertEqual(E.a.b(2), (2, 5))
def test_enum_with_value_name(self): def test_enum_with_value_name(self):
class Huh(Enum): class Huh(Enum):
name = 1 name = 1

View file

@ -0,0 +1,3 @@
Fix using :func:`functools.partial` as :class:`enum.Enum` member. A
FutureWarning with suggestion to use :func:`enum.member` is now emitted when
the ``partial`` instance is used as an enum member.