Skip to content

Commit effa2ec

Browse files
authored
[3.11] gh-108682: [Enum] raise TypeError if super().__new__ called in custom __new__ (GH-108704) (GH-108739)
When overriding the `__new__` method of an enum, the underlying data type should be created directly; i.e. . member = object.__new__(cls) member = int.__new__(cls, value) member = str.__new__(cls, value) Calling `super().__new__()` finds the lookup version of `Enum.__new__`, and will now raise an exception when detected. (cherry picked from commit d48760b)
1 parent e46be0d commit effa2ec

File tree

4 files changed

+42
-1
lines changed

4 files changed

+42
-1
lines changed

Doc/howto/enum.rst

+22-1
Original file line numberDiff line numberDiff line change
@@ -422,10 +422,17 @@ enumeration, with the exception of special methods (:meth:`__str__`,
422422
:meth:`__add__`, etc.), descriptors (methods are also descriptors), and
423423
variable names listed in :attr:`_ignore_`.
424424

425-
Note: if your enumeration defines :meth:`__new__` and/or :meth:`__init__` then
425+
Note: if your enumeration defines :meth:`__new__` and/or :meth:`__init__`,
426426
any value(s) given to the enum member will be passed into those methods.
427427
See `Planet`_ for an example.
428428

429+
.. note::
430+
431+
The :meth:`__new__` method, if defined, is used during creation of the Enum
432+
members; it is then replaced by Enum's :meth:`__new__` which is used after
433+
class creation for lookup of existing members. See :ref:`new-vs-init` for
434+
more details.
435+
429436

430437
Restricted Enum subclassing
431438
---------------------------
@@ -860,6 +867,8 @@ Some rules:
860867
:meth:`__str__` method has been reset to their data types'
861868
:meth:`__str__` method.
862869

870+
.. _new-vs-init:
871+
863872
When to use :meth:`__new__` vs. :meth:`__init__`
864873
------------------------------------------------
865874

@@ -892,6 +901,11 @@ want one of them to be the value::
892901
>>> print(Coordinate(3))
893902
Coordinate.VY
894903

904+
.. warning::
905+
906+
*Do not* call ``super().__new__()``, as the lookup-only ``__new__`` is the one
907+
that is found; instead, use the data type directly.
908+
895909

896910
Finer Points
897911
^^^^^^^^^^^^
@@ -1316,6 +1330,13 @@ to handle any extra arguments::
13161330
members; it is then replaced by Enum's :meth:`__new__` which is used after
13171331
class creation for lookup of existing members.
13181332

1333+
.. warning::
1334+
1335+
*Do not* call ``super().__new__()``, as the lookup-only ``__new__`` is the one
1336+
that is found; instead, use the data type directly -- e.g.::
1337+
1338+
obj = int.__new__(cls, value)
1339+
13191340

13201341
OrderedEnum
13211342
^^^^^^^^^^^

Lib/enum.py

+7
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,8 @@ def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, s
863863
value = first_enum._generate_next_value_(name, start, count, last_values[:])
864864
last_values.append(value)
865865
names.append((name, value))
866+
if names is None:
867+
names = ()
866868

867869
# Here, names is either an iterable of (name, value) or a mapping.
868870
for item in names:
@@ -1107,6 +1109,11 @@ def __new__(cls, value):
11071109
for member in cls._member_map_.values():
11081110
if member._value_ == value:
11091111
return member
1112+
# still not found -- verify that members exist, in-case somebody got here mistakenly
1113+
# (such as via super when trying to override __new__)
1114+
if not cls._member_map_:
1115+
raise TypeError("%r has no members defined" % cls)
1116+
#
11101117
# still not found -- try _missing_ hook
11111118
try:
11121119
exc = None

Lib/test/test_enum.py

+11
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,17 @@ def spam(cls):
322322
with self.assertRaises(AttributeError):
323323
del Season.SPRING.name
324324

325+
def test_bad_new_super(self):
326+
with self.assertRaisesRegex(
327+
TypeError,
328+
'has no members defined',
329+
):
330+
class BadSuper(self.enum_type):
331+
def __new__(cls, value):
332+
obj = super().__new__(cls, value)
333+
return obj
334+
failed = 1
335+
325336
def test_basics(self):
326337
TE = self.MainEnum
327338
if self.is_flag:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Enum: raise :exc:`TypeError` if ``super().__new__()`` is called from a
2+
custom ``__new__``.

0 commit comments

Comments
 (0)