Skip to content

Commit aba12b6

Browse files
[3.9] bpo-42517: [Enum] deprecate private name members (GH-23722) (GH-23748)
private names will raise a DeprecationWarning; in 3.10 they will become normal attributes
1 parent 6b2ed38 commit aba12b6

File tree

4 files changed

+51
-0
lines changed

4 files changed

+51
-0
lines changed

Doc/library/enum.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,15 @@ and raise an error if the two do not match::
11211121
In Python 2 code the :attr:`_order_` attribute is necessary as definition
11221122
order is lost before it can be recorded.
11231123

1124+
1125+
_Private__names
1126+
"""""""""""""""
1127+
1128+
Private names will be normal attributes in Python 3.10 instead of either an error
1129+
or a member (depending on if the name ends with an underscore). Using these names
1130+
in 3.9 will issue a :exc:`DeprecationWarning`.
1131+
1132+
11241133
``Enum`` member type
11251134
""""""""""""""""""""
11261135

Lib/enum.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,19 @@ def _is_sunder(name):
4141
name[-2:-1] != '_'
4242
)
4343

44+
def _is_private(cls_name, name):
45+
# do not use `re` as `re` imports `enum`
46+
pattern = '_%s__' % (cls_name, )
47+
if (
48+
len(name) >= 5
49+
and name.startswith(pattern)
50+
and name[len(pattern)] != '_'
51+
and (name[-1] != '_' or name[-2] != '_')
52+
):
53+
return True
54+
else:
55+
return False
56+
4457
def _make_class_unpicklable(cls):
4558
"""
4659
Make the given class un-picklable.
@@ -81,6 +94,14 @@ def __setitem__(self, key, value):
8194
8295
Single underscore (sunder) names are reserved.
8396
"""
97+
if _is_private(self._cls_name, key):
98+
import warnings
99+
warnings.warn(
100+
"private variables, such as %r, will be normal attributes in 3.10"
101+
% (key, ),
102+
DeprecationWarning,
103+
stacklevel=2,
104+
)
84105
if _is_sunder(key):
85106
if key not in (
86107
'_order_', '_create_pseudo_member_',
@@ -146,6 +167,7 @@ def __prepare__(metacls, cls, bases):
146167
metacls._check_for_existing_members(cls, bases)
147168
# create the namespace dict
148169
enum_dict = _EnumDict()
170+
enum_dict._cls_name = cls
149171
# inherit previous flags and _generate_next_value_ function
150172
member_type, first_enum = metacls._get_mixins_(cls, bases)
151173
if first_enum is not None:

Lib/test/test_enum.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,7 @@ def test_multiple_mixin_mro(self):
11471147
class auto_enum(type(Enum)):
11481148
def __new__(metacls, cls, bases, classdict):
11491149
temp = type(classdict)()
1150+
temp._cls_name = cls
11501151
names = set(classdict._member_names)
11511152
i = 0
11521153
for k in classdict._member_names:
@@ -2049,6 +2050,23 @@ def test_empty_globals(self):
20492050
exec(code, global_ns, local_ls)
20502051

20512052

2053+
@unittest.skipUnless(
2054+
sys.version_info[:2] == (3, 9),
2055+
'private variables are now normal attributes',
2056+
)
2057+
def test_warning_for_private_variables(self):
2058+
with self.assertWarns(DeprecationWarning):
2059+
class Private(Enum):
2060+
__corporal = 'Radar'
2061+
self.assertEqual(Private._Private__corporal.value, 'Radar')
2062+
try:
2063+
with self.assertWarns(DeprecationWarning):
2064+
class Private(Enum):
2065+
__major_ = 'Hoolihan'
2066+
except ValueError:
2067+
pass
2068+
2069+
20522070
class TestOrder(unittest.TestCase):
20532071

20542072
def test_same_members(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Enum: private names will raise a DeprecationWarning; in 3.10 they will
2+
become normal attributes

0 commit comments

Comments
 (0)