From 6da11dbcfcf636f02b14b178b20879d916953539 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Wed, 6 Sep 2023 19:43:10 -0700 Subject: [PATCH 1/2] [Enum] change TypeError to ValueError; require more for new enum type add guard so that ``Enum('bar')`` raises a ValueError instead of creating a new enum class called `bar`. To create the new but empty class, use: huh = Enum('bar', names=()) --- Lib/enum.py | 7 ++++++- Lib/test/test_enum.py | 13 ++++++++----- .../2023-09-06-19-33-41.gh-issue-108682.35Xnc5.rst | 3 +++ 3 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-09-06-19-33-41.gh-issue-108682.35Xnc5.rst diff --git a/Lib/enum.py b/Lib/enum.py index 4b99e7bda2cca5..ee747cb9b260a2 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -730,6 +730,11 @@ def __call__(cls, value, names=None, *values, module=None, qualname=None, type=N value = (value, names) + values return cls.__new__(cls, value) # otherwise, functional API: we're creating a new Enum type + if names is None and type is None: + # no body? no data-type? possibly wrong usage + raise ValueError( + f"{cls} has no members; specify `names=()` if you meant to create a new, empty, enum" + ) return cls._create_( class_name=value, names=names, @@ -1117,7 +1122,7 @@ def __new__(cls, value): # still not found -- verify that members exist, in-case somebody got here mistakenly # (such as via super when trying to override __new__) if not cls._member_map_: - raise TypeError("%r has no members defined" % cls) + raise ValueError("%r has no members defined" % cls) # # still not found -- try _missing_ hook try: diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index a838b93341a608..cea30f0f8a76e0 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -316,6 +316,7 @@ def __str__(self): return self.name.title() def __format__(self, spec): return ''.join(reversed(self.name)) + self.NewBaseEnum = NewBaseEnum class NewSubEnum(NewBaseEnum): first = auto() self.NewSubEnum = NewSubEnum @@ -382,10 +383,8 @@ def __str__(self): return self.name.title() def __format__(self, spec): return ''.join(reversed(self.name)) - NewBaseEnum = self.enum_type('NewBaseEnum', dict(__format__=__format__, __str__=__str__)) - class NewSubEnum(NewBaseEnum): - first = auto() - self.NewSubEnum = NewBaseEnum('NewSubEnum', 'first') + self.NewBaseEnum = self.enum_type('NewBaseEnum', dict(__format__=__format__, __str__=__str__)) + self.NewSubEnum = self.NewBaseEnum('NewSubEnum', 'first') # def _generate_next_value_(name, start, last, values): pass @@ -442,7 +441,7 @@ def spam(cls): def test_bad_new_super(self): with self.assertRaisesRegex( - TypeError, + ValueError, 'has no members defined', ): class BadSuper(self.enum_type): @@ -601,6 +600,10 @@ class SubEnum(SuperEnum): self.assertTrue('description' not in dir(SubEnum)) self.assertTrue('description' in dir(SubEnum.sample), dir(SubEnum.sample)) + def test_empty_enum_has_no_values(self): + with self.assertRaisesRegex(ValueError, "<.... 'NewBaseEnum'> has no members"): + self.NewBaseEnum(7) + def test_enum_in_enum_out(self): Main = self.MainEnum self.assertIs(Main(Main.first), Main.first) diff --git a/Misc/NEWS.d/next/Library/2023-09-06-19-33-41.gh-issue-108682.35Xnc5.rst b/Misc/NEWS.d/next/Library/2023-09-06-19-33-41.gh-issue-108682.35Xnc5.rst new file mode 100644 index 00000000000000..52fadeccc16dd0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-06-19-33-41.gh-issue-108682.35Xnc5.rst @@ -0,0 +1,3 @@ +Enum: raise :exc:`ValueError` if ``Enum.__new__`` is called when no members +exist; this could be from an incorrect ``super.__new__`` call, and other +incorrect usage. From 6f3a5ebc4590af3ee192e04339e4bd032c03ae6d Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Thu, 7 Sep 2023 12:35:40 -0700 Subject: [PATCH 2/2] revert ValueError to TypeError; update NEWS --- Lib/enum.py | 4 ++-- Lib/test/test_enum.py | 4 ++-- .../Library/2023-09-06-19-33-41.gh-issue-108682.35Xnc5.rst | 5 ++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Lib/enum.py b/Lib/enum.py index ee747cb9b260a2..994a7b9c73f9a7 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -732,7 +732,7 @@ def __call__(cls, value, names=None, *values, module=None, qualname=None, type=N # otherwise, functional API: we're creating a new Enum type if names is None and type is None: # no body? no data-type? possibly wrong usage - raise ValueError( + raise TypeError( f"{cls} has no members; specify `names=()` if you meant to create a new, empty, enum" ) return cls._create_( @@ -1122,7 +1122,7 @@ def __new__(cls, value): # still not found -- verify that members exist, in-case somebody got here mistakenly # (such as via super when trying to override __new__) if not cls._member_map_: - raise ValueError("%r has no members defined" % cls) + raise TypeError("%r has no members defined" % cls) # # still not found -- try _missing_ hook try: diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index cea30f0f8a76e0..8c1f285f7b3bc2 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -441,7 +441,7 @@ def spam(cls): def test_bad_new_super(self): with self.assertRaisesRegex( - ValueError, + TypeError, 'has no members defined', ): class BadSuper(self.enum_type): @@ -601,7 +601,7 @@ class SubEnum(SuperEnum): self.assertTrue('description' in dir(SubEnum.sample), dir(SubEnum.sample)) def test_empty_enum_has_no_values(self): - with self.assertRaisesRegex(ValueError, "<.... 'NewBaseEnum'> has no members"): + with self.assertRaisesRegex(TypeError, "<.... 'NewBaseEnum'> has no members"): self.NewBaseEnum(7) def test_enum_in_enum_out(self): diff --git a/Misc/NEWS.d/next/Library/2023-09-06-19-33-41.gh-issue-108682.35Xnc5.rst b/Misc/NEWS.d/next/Library/2023-09-06-19-33-41.gh-issue-108682.35Xnc5.rst index 52fadeccc16dd0..8c13d43ee9744b 100644 --- a/Misc/NEWS.d/next/Library/2023-09-06-19-33-41.gh-issue-108682.35Xnc5.rst +++ b/Misc/NEWS.d/next/Library/2023-09-06-19-33-41.gh-issue-108682.35Xnc5.rst @@ -1,3 +1,2 @@ -Enum: raise :exc:`ValueError` if ``Enum.__new__`` is called when no members -exist; this could be from an incorrect ``super.__new__`` call, and other -incorrect usage. +Enum: require ``names=()`` or ``type=...`` to create an empty enum using +the functional syntax.