[Python-checkins] bpo-42517: [Enum] do not convert private names into members (GH-23722)

ethanfurman webhook-mailer at python.org
Wed Dec 9 20:12:39 EST 2020


https://github.com/python/cpython/commit/7cf0aad96d1d20f07d7f0e374885f327c2d5ff27
commit: 7cf0aad96d1d20f07d7f0e374885f327c2d5ff27
branch: master
author: Ethan Furman <ethan at stoneleaf.us>
committer: ethanfurman <ethan at stoneleaf.us>
date: 2020-12-09T17:12:11-08:00
summary:

bpo-42517: [Enum] do not convert private names into members (GH-23722)

private names, such as `_Color__hue` and `_Color__hue_` are now normal attributes, and do not become members nor raise exceptions

files:
A Misc/NEWS.d/next/Library/2020-12-09-10-59-16.bpo-42517.FKEVcZ.rst
M Doc/library/enum.rst
M Lib/enum.py
M Lib/test/test_enum.py

diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst
index 118002bef19f8..a9584b9c91083 100644
--- a/Doc/library/enum.rst
+++ b/Doc/library/enum.rst
@@ -1164,6 +1164,15 @@ and raise an error if the two do not match::
     In Python 2 code the :attr:`_order_` attribute is necessary as definition
     order is lost before it can be recorded.
 
+
+_Private__names
+"""""""""""""""
+
+Private names are not converted to Enum members, but remain normal attributes.
+
+.. versionchanged:: 3.10
+
+
 ``Enum`` member type
 """"""""""""""""""""
 
diff --git a/Lib/enum.py b/Lib/enum.py
index 83e050e1e41cd..74318c3b71deb 100644
--- a/Lib/enum.py
+++ b/Lib/enum.py
@@ -49,6 +49,19 @@ def _is_sunder(name):
             name[-2:-1] != '_'
             )
 
+def _is_private(cls_name, name):
+    # do not use `re` as `re` imports `enum`
+    pattern = '_%s__' % (cls_name, )
+    if (
+            len(name) >= 5
+            and name.startswith(pattern)
+            and name[len(pattern)] != '_'
+            and (name[-1] != '_' or name[-2] != '_')
+        ):
+        return True
+    else:
+        return False
+
 def _make_class_unpicklable(cls):
     """
     Make the given class un-picklable.
@@ -89,7 +102,10 @@ def __setitem__(self, key, value):
 
         Single underscore (sunder) names are reserved.
         """
-        if _is_sunder(key):
+        if _is_private(self._cls_name, key):
+            # do nothing, name will be a normal attribute
+            pass
+        elif _is_sunder(key):
             if key not in (
                     '_order_', '_create_pseudo_member_',
                     '_generate_next_value_', '_missing_', '_ignore_',
@@ -157,6 +173,7 @@ def __prepare__(metacls, cls, bases):
         metacls._check_for_existing_members(cls, bases)
         # create the namespace dict
         enum_dict = _EnumDict()
+        enum_dict._cls_name = cls
         # inherit previous flags and _generate_next_value_ function
         member_type, first_enum = metacls._get_mixins_(cls, bases)
         if first_enum is not None:
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py
index 20bc5b3c750d6..7ca54e9a649ca 100644
--- a/Lib/test/test_enum.py
+++ b/Lib/test/test_enum.py
@@ -1149,6 +1149,7 @@ def test_multiple_mixin_mro(self):
         class auto_enum(type(Enum)):
             def __new__(metacls, cls, bases, classdict):
                 temp = type(classdict)()
+                temp._cls_name = cls
                 names = set(classdict._member_names)
                 i = 0
                 for k in classdict._member_names:
@@ -2155,6 +2156,30 @@ class NeverEnum(WhereEnum):
         self.assertFalse(NeverEnum.__dict__.get('_test2', False))
 
 
+    @unittest.skipUnless(
+            sys.version_info[:2] == (3, 9),
+            'private variables are now normal attributes',
+            )
+    def test_warning_for_private_variables(self):
+        with self.assertWarns(DeprecationWarning):
+            class Private(Enum):
+                __corporal = 'Radar'
+        self.assertEqual(Private._Private__corporal.value, 'Radar')
+        try:
+            with self.assertWarns(DeprecationWarning):
+                class Private(Enum):
+                    __major_ = 'Hoolihan'
+        except ValueError:
+            pass
+
+    def test_private_variable_is_normal_attribute(self):
+        class Private(Enum):
+            __corporal = 'Radar'
+            __major_ = 'Hoolihan'
+        self.assertEqual(Private._Private__corporal, 'Radar')
+        self.assertEqual(Private._Private__major_, 'Hoolihan')
+
+
 class TestOrder(unittest.TestCase):
 
     def test_same_members(self):
diff --git a/Misc/NEWS.d/next/Library/2020-12-09-10-59-16.bpo-42517.FKEVcZ.rst b/Misc/NEWS.d/next/Library/2020-12-09-10-59-16.bpo-42517.FKEVcZ.rst
new file mode 100644
index 0000000000000..813139dfe5d00
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-12-09-10-59-16.bpo-42517.FKEVcZ.rst
@@ -0,0 +1,2 @@
+Enum: private names do not become members / do not generate errors -- they
+remain normal attributes



More information about the Python-checkins mailing list