From 65e43ca6d936304622ec8b3cd2a9b264b3a3755b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 21 Oct 2024 16:31:42 +0300 Subject: [PATCH] [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. --- Lib/enum.py | 9 ++++++++- Lib/test/test_enum.py | 14 ++++++++++++++ .../2024-10-12-15-49-17.gh-issue-125316.t15RnJ.rst | 3 +++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-10-12-15-49-17.gh-issue-125316.t15RnJ.rst diff --git a/Lib/enum.py b/Lib/enum.py index 0c2135f9040b..78df81d24a9a 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -1,5 +1,6 @@ import sys import builtins as bltns +from functools import partial from types import MappingProxyType, DynamicClassAttribute @@ -37,7 +38,7 @@ def _is_descriptor(obj): """ Returns True if obj is a descriptor, False otherwise. """ - return ( + return not isinstance(obj, partial) and ( hasattr(obj, '__get__') or hasattr(obj, '__set__') or hasattr(obj, '__delete__') @@ -402,6 +403,12 @@ class EnumDict(dict): elif isinstance(value, nonmember): # unwrap value here; it won't be processed by the below `else` 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): pass elif _is_internal_class(self._cls_name, value): diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 5b4a8070526f..46f57b2d9b64 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -11,6 +11,7 @@ import typing import builtins as bltns from collections import OrderedDict from datetime import date +from functools import partial 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 verify, UNIQUE, CONTINUOUS, NAMED_FLAGS, ReprEnum @@ -1537,6 +1538,19 @@ class TestSpecial(unittest.TestCase): [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): class Huh(Enum): name = 1 diff --git a/Misc/NEWS.d/next/Library/2024-10-12-15-49-17.gh-issue-125316.t15RnJ.rst b/Misc/NEWS.d/next/Library/2024-10-12-15-49-17.gh-issue-125316.t15RnJ.rst new file mode 100644 index 000000000000..07b642d69995 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-12-15-49-17.gh-issue-125316.t15RnJ.rst @@ -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. -- 2.47.3