From ad669f43fc406786eab367755928b7a94c75e815 Mon Sep 17 00:00:00 2001 From: orlnub123 <30984274+orlnub123@users.noreply.github.com> Date: Sun, 2 Sep 2018 16:26:04 +0300 Subject: [PATCH 1/6] Fix Enum._convert shadowing members named _convert --- Lib/enum.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Lib/enum.py b/Lib/enum.py index 576de031805dbc..7a40ce0f352c55 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -173,7 +173,8 @@ def __new__(metacls, cls, bases, classdict): # save attributes from super classes so we know if we can take # the shortcut of storing members in the class dict - base_attributes = {a for b in enum_class.mro() for a in b.__dict__} + base_attributes = {k: v for c in reversed(enum_class.mro()) + for k, v in c.__dict__.items()} # Reverse value->name map for hashable values. enum_class._value2member_map_ = {} @@ -235,6 +236,13 @@ def __new__(metacls, cls, bases, classdict): # a DynamicClassAttribute if member_name not in base_attributes: setattr(enum_class, member_name, enum_member) + # issue34282: Members named _convert should take precedence over + # the _convert method except if the enum also has a behavior named + # _convert. In which case, it falls back to the default + # behavior > member ordering where the behavior takes precedence. + elif (member_name == '_convert' and + base_attributes['_convert'] is Enum.__dict__['_convert']): + enum_class._convert = enum_member # now add to _member_map_ enum_class._member_map_[member_name] = enum_member try: From 1aa1575aba4114d2f9a7b6e4bb04fe797047199a Mon Sep 17 00:00:00 2001 From: orlnub123 <30984274+orlnub123@users.noreply.github.com> Date: Sun, 2 Sep 2018 16:34:02 +0300 Subject: [PATCH 2/6] Add news entry --- .../NEWS.d/next/Library/2018-09-02-13-33-35.bpo-34282.ztyXH8.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2018-09-02-13-33-35.bpo-34282.ztyXH8.rst diff --git a/Misc/NEWS.d/next/Library/2018-09-02-13-33-35.bpo-34282.ztyXH8.rst b/Misc/NEWS.d/next/Library/2018-09-02-13-33-35.bpo-34282.ztyXH8.rst new file mode 100644 index 00000000000000..499baf6c2c0614 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-09-02-13-33-35.bpo-34282.ztyXH8.rst @@ -0,0 +1 @@ +Fix ``Enum._convert`` shadowing members named _convert. From 1d762635ec5cd46d5466209da0077d890466d2fd Mon Sep 17 00:00:00 2001 From: orlnub123 <30984274+orlnub123@users.noreply.github.com> Date: Wed, 5 Sep 2018 13:27:44 +0300 Subject: [PATCH 3/6] Revert "Fix Enum._convert shadowing members named _convert" This reverts commit ad669f43fc406786eab367755928b7a94c75e815. --- Lib/enum.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Lib/enum.py b/Lib/enum.py index 7a40ce0f352c55..576de031805dbc 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -173,8 +173,7 @@ def __new__(metacls, cls, bases, classdict): # save attributes from super classes so we know if we can take # the shortcut of storing members in the class dict - base_attributes = {k: v for c in reversed(enum_class.mro()) - for k, v in c.__dict__.items()} + base_attributes = {a for b in enum_class.mro() for a in b.__dict__} # Reverse value->name map for hashable values. enum_class._value2member_map_ = {} @@ -236,13 +235,6 @@ def __new__(metacls, cls, bases, classdict): # a DynamicClassAttribute if member_name not in base_attributes: setattr(enum_class, member_name, enum_member) - # issue34282: Members named _convert should take precedence over - # the _convert method except if the enum also has a behavior named - # _convert. In which case, it falls back to the default - # behavior > member ordering where the behavior takes precedence. - elif (member_name == '_convert' and - base_attributes['_convert'] is Enum.__dict__['_convert']): - enum_class._convert = enum_member # now add to _member_map_ enum_class._member_map_[member_name] = enum_member try: From c41ef6458644e1e509919131601909ed076622a4 Mon Sep 17 00:00:00 2001 From: orlnub123 <30984274+orlnub123@users.noreply.github.com> Date: Wed, 5 Sep 2018 13:48:51 +0300 Subject: [PATCH 4/6] Fix enum members getting shadowed by parent attributes --- Lib/enum.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Lib/enum.py b/Lib/enum.py index 576de031805dbc..69b41fe7cbc08c 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -171,9 +171,11 @@ def __new__(metacls, cls, bases, classdict): enum_class._member_map_ = OrderedDict() # name->value map enum_class._member_type_ = member_type - # save attributes from super classes so we know if we can take - # the shortcut of storing members in the class dict - base_attributes = {a for b in enum_class.mro() for a in b.__dict__} + # save DynamicClassAttribute attributes from super classes so we know + # if we can take the shortcut of storing members in the class dict + dynamic_attributes = {k for c in enum_class.mro() + for k, v in c.__dict__.items() + if isinstance(v, DynamicClassAttribute)} # Reverse value->name map for hashable values. enum_class._value2member_map_ = {} @@ -233,7 +235,7 @@ def __new__(metacls, cls, bases, classdict): enum_class._member_names_.append(member_name) # performance boost for any member that would not shadow # a DynamicClassAttribute - if member_name not in base_attributes: + if member_name not in dynamic_attributes: setattr(enum_class, member_name, enum_member) # now add to _member_map_ enum_class._member_map_[member_name] = enum_member From aedd0878d259e57ccad4222d46d03618a0be24ca Mon Sep 17 00:00:00 2001 From: orlnub123 <30984274+orlnub123@users.noreply.github.com> Date: Wed, 5 Sep 2018 13:55:59 +0300 Subject: [PATCH 5/6] Update news entry --- .../next/Library/2018-09-02-13-33-35.bpo-34282.ztyXH8.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2018-09-02-13-33-35.bpo-34282.ztyXH8.rst b/Misc/NEWS.d/next/Library/2018-09-02-13-33-35.bpo-34282.ztyXH8.rst index 499baf6c2c0614..c1e606ab0ce506 100644 --- a/Misc/NEWS.d/next/Library/2018-09-02-13-33-35.bpo-34282.ztyXH8.rst +++ b/Misc/NEWS.d/next/Library/2018-09-02-13-33-35.bpo-34282.ztyXH8.rst @@ -1 +1 @@ -Fix ``Enum._convert`` shadowing members named _convert. +Fix enum members getting shadowed by parent attributes. From f7f73e93065cb03510fecaf81a754dc84ecf11ae Mon Sep 17 00:00:00 2001 From: orlnub123 <30984274+orlnub123@users.noreply.github.com> Date: Fri, 7 Sep 2018 13:10:02 +0300 Subject: [PATCH 6/6] Add tests --- Lib/test/test_enum.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index ef2d1daaf9420d..4b17228946015a 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -1516,6 +1516,23 @@ class MoreColor(Color): yellow = 6 self.assertEqual(MoreColor.magenta.hex(), '5 hexlified!') + def test_subclass_duplicate_name(self): + class Base(Enum): + def test(self): + pass + class Test(Base): + test = 1 + self.assertIs(type(Test.test), Test) + + def test_subclass_duplicate_name_dynamic(self): + from types import DynamicClassAttribute + class Base(Enum): + @DynamicClassAttribute + def test(self): + return 'dynamic' + class Test(Base): + test = 1 + self.assertEqual(Test.test.test, 'dynamic') def test_no_duplicates(self): class UniqueEnum(Enum):