@@ -185,19 +185,35 @@ class property(DynamicClassAttribute):
185
185
a corresponding enum member.
186
186
"""
187
187
188
+ member = None
189
+
188
190
def __get__ (self , instance , ownerclass = None ):
189
191
if instance is None :
190
- try :
191
- return ownerclass . _member_map_ [ self .name ]
192
- except KeyError :
192
+ if self . member is not None :
193
+ return self .member
194
+ else :
193
195
raise AttributeError (
194
196
'%r has no attribute %r' % (ownerclass , self .name )
195
197
)
196
198
else :
197
199
if self .fget is None :
198
- raise AttributeError (
199
- '%r member has no attribute %r' % (ownerclass , self .name )
200
+ if self .member is None : # not sure this can happen, but just in case
201
+ raise AttributeError (
202
+ '%r has no attribute %r' % (ownerclass , self .name )
203
+ )
204
+ # issue warning deprecating this behavior
205
+ import warnings
206
+ warnings .warn (
207
+ "`member.member` access (e.g. `Color.RED.BLUE`) is "
208
+ "deprecated and will be removed in 3.14." ,
209
+ DeprecationWarning ,
210
+ stacklevel = 2 ,
200
211
)
212
+ return self .member
213
+ # XXX: uncomment in 3.14 and remove warning above
214
+ # raise AttributeError(
215
+ # '%r member has no attribute %r' % (ownerclass, self.name)
216
+ # )
201
217
else :
202
218
return self .fget (instance )
203
219
@@ -299,30 +315,20 @@ def __set_name__(self, enum_class, member_name):
299
315
enum_class ._member_names_ .append (member_name )
300
316
# get redirect in place before adding to _member_map_
301
317
# but check for other instances in parent classes first
302
- need_override = False
303
318
descriptor = None
304
319
for base in enum_class .__mro__ [1 :]:
305
320
descriptor = base .__dict__ .get (member_name )
306
321
if descriptor is not None :
307
322
if isinstance (descriptor , (property , DynamicClassAttribute )):
308
323
break
309
- else :
310
- need_override = True
311
- # keep looking for an enum.property
312
- if descriptor and not need_override :
313
- # previous enum.property found, no further action needed
314
- pass
315
- elif descriptor and need_override :
316
- redirect = property ()
317
- redirect .__set_name__ (enum_class , member_name )
318
- # Previous enum.property found, but some other inherited attribute
319
- # is in the way; copy fget, fset, fdel to this one.
320
- redirect .fget = descriptor .fget
321
- redirect .fset = descriptor .fset
322
- redirect .fdel = descriptor .fdel
323
- setattr (enum_class , member_name , redirect )
324
- else :
325
- setattr (enum_class , member_name , enum_member )
324
+ redirect = property ()
325
+ redirect .member = enum_member
326
+ redirect .__set_name__ (enum_class , member_name )
327
+ if descriptor :
328
+ redirect .fget = getattr (descriptor , 'fget' , None )
329
+ redirect .fset = getattr (descriptor , 'fset' , None )
330
+ redirect .fdel = getattr (descriptor , 'fdel' , None )
331
+ setattr (enum_class , member_name , redirect )
326
332
# now add to _member_map_ (even aliases)
327
333
enum_class ._member_map_ [member_name ] = enum_member
328
334
try :
@@ -740,22 +746,6 @@ def __dir__(cls):
740
746
# return whatever mixed-in data type has
741
747
return sorted (set (dir (cls ._member_type_ )) | interesting )
742
748
743
- def __getattr__ (cls , name ):
744
- """
745
- Return the enum member matching `name`
746
-
747
- We use __getattr__ instead of descriptors or inserting into the enum
748
- class' __dict__ in order to support `name` and `value` being both
749
- properties for enum members (which live in the class' __dict__) and
750
- enum members themselves.
751
- """
752
- if _is_dunder (name ):
753
- raise AttributeError (name )
754
- try :
755
- return cls ._member_map_ [name ]
756
- except KeyError :
757
- raise AttributeError (name ) from None
758
-
759
749
def __getitem__ (cls , name ):
760
750
"""
761
751
Return the member matching `name`.
@@ -1200,10 +1190,10 @@ def __reduce_ex__(self, proto):
1200
1190
# enum.property is used to provide access to the `name` and
1201
1191
# `value` attributes of enum members while keeping some measure of
1202
1192
# protection from modification, while still allowing for an enumeration
1203
- # to have members named `name` and `value`. This works because enumeration
1204
- # members are not set directly on the enum class; they are kept in a
1205
- # separate structure, _member_map_, which is where enum.property looks for
1206
- # them
1193
+ # to have members named `name` and `value`. This works because each
1194
+ # instance of enum.property saves its companion member, which it returns
1195
+ # on class lookup; on instance lookup it either executes a provided function
1196
+ # or raises an AttributeError.
1207
1197
1208
1198
@property
1209
1199
def name (self ):
@@ -1677,10 +1667,12 @@ def convert_class(cls):
1677
1667
value = gnv (name , 1 , len (member_names ), gnv_last_values )
1678
1668
if value in value2member_map :
1679
1669
# an alias to an existing member
1670
+ member = value2member_map [value ]
1680
1671
redirect = property ()
1672
+ redirect .member = member
1681
1673
redirect .__set_name__ (enum_class , name )
1682
1674
setattr (enum_class , name , redirect )
1683
- member_map [name ] = value2member_map [ value ]
1675
+ member_map [name ] = member
1684
1676
else :
1685
1677
# create the member
1686
1678
if use_args :
@@ -1696,6 +1688,7 @@ def convert_class(cls):
1696
1688
member .__objclass__ = enum_class
1697
1689
member .__init__ (value )
1698
1690
redirect = property ()
1691
+ redirect .member = member
1699
1692
redirect .__set_name__ (enum_class , name )
1700
1693
setattr (enum_class , name , redirect )
1701
1694
member_map [name ] = member
@@ -1723,10 +1716,12 @@ def convert_class(cls):
1723
1716
value = value .value
1724
1717
if value in value2member_map :
1725
1718
# an alias to an existing member
1719
+ member = value2member_map [value ]
1726
1720
redirect = property ()
1721
+ redirect .member = member
1727
1722
redirect .__set_name__ (enum_class , name )
1728
1723
setattr (enum_class , name , redirect )
1729
- member_map [name ] = value2member_map [ value ]
1724
+ member_map [name ] = member
1730
1725
else :
1731
1726
# create the member
1732
1727
if use_args :
@@ -1743,6 +1738,7 @@ def convert_class(cls):
1743
1738
member .__init__ (value )
1744
1739
member ._sort_order_ = len (member_names )
1745
1740
redirect = property ()
1741
+ redirect .member = member
1746
1742
redirect .__set_name__ (enum_class , name )
1747
1743
setattr (enum_class , name , redirect )
1748
1744
member_map [name ] = member
0 commit comments