| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 07804ce commit 700ec65
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -36,8 +36,10 @@ inherits from :class:`Enum` itself. | |||
| 36 | 36 | ||
| 37 | 37 | .. note:: Case of Enum Members | |
| 38 | 38 | ||
| 39 | - Because Enums are used to represent constants we recommend using | ||
| 40 | - UPPER_CASE names for members, and will be using that style in our examples. | ||
| 39 | + Because Enums are used to represent constants, and to help avoid issues | ||
| 40 | + with name clashes between mixin-class methods/attributes and enum names, | ||
| 41 | + we strongly recommend using UPPER_CASE names for members, and will be using | ||
| 42 | + that style in our examples. | ||
| 41 | 43 | ||
| 42 | 44 | Depending on the nature of the enum a member's value may or may not be | |
| 43 | 45 | important, but either way that value can be used to get the corresponding | |
@@ -490,6 +492,10 @@ the :meth:`~Enum.__repr__` omits the inherited class' name. For example:: | |||
| 490 | 492 | Use the :func:`!dataclass` argument ``repr=False`` | |
| 491 | 493 | to use the standard :func:`repr`. | |
| 492 | 494 | ||
| 495 | + .. versionchanged:: 3.12 | ||
| 496 | + Only the dataclass fields are shown in the value area, not the dataclass' | ||
| 497 | + name. | ||
| 498 | + | ||
| 493 | 499 | ||
| 494 | 500 | Pickling | |
| 495 | 501 | -------- | |
@@ -992,7 +998,9 @@ but remain normal attributes. | |||
| 992 | 998 | Enum members are instances of their enum class, and are normally accessed as | |
| 993 | 999 | ``EnumClass.member``. In certain situations, such as writing custom enum | |
| 994 | 1000 | behavior, being able to access one member directly from another is useful, | |
| 995 | - and is supported. | ||
| 1001 | + and is supported; however, in order to avoid name clashes between member names | ||
| 1002 | + and attributes/methods from mixed-in classes, upper-case names are strongly | ||
| 1003 | + recommended. | ||
| 996 | 1004 | ||
| 997 | 1005 | .. versionchanged:: 3.5 | |
| 998 | 1006 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -119,7 +119,8 @@ Module Contents | |||
| 119 | 119 | :func:`~enum.property` | |
| 120 | 120 | ||
| 121 | 121 | Allows :class:`Enum` members to have attributes without conflicting with | |
| 122 | - member names. | ||
| 122 | + member names. The ``value`` and ``name`` attributes are implemented this | ||
| 123 | + way. | ||
| 123 | 124 | ||
| 124 | 125 | :func:`unique` | |
| 125 | 126 | ||
@@ -169,7 +170,7 @@ Data Types | |||
| 169 | 170 | final *enum*, as well as creating the enum members, properly handling | |
| 170 | 171 | duplicates, providing iteration over the enum class, etc. | |
| 171 | 172 | ||
| 172 | - .. method:: EnumType.__call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None) | ||
| 173 | + .. method:: EnumType.__call__(cls, value, names=None, \*, module=None, qualname=None, type=None, start=1, boundary=None) | ||
| 173 | 174 | ||
| 174 | 175 | This method is called in two different ways: | |
| 175 | 176 | ||
@@ -317,7 +318,7 @@ Data Types | |||
| 317 | 318 | >>> PowersOfThree.SECOND.value | |
| 318 | 319 | 9 | |
| 319 | 320 | ||
| 320 | - .. method:: Enum.__init_subclass__(cls, **kwds) | ||
| 321 | + .. method:: Enum.__init_subclass__(cls, \**kwds) | ||
| 321 | 322 | ||
| 322 | 323 | A *classmethod* that is used to further configure subsequent subclasses. | |
| 323 | 324 | By default, does nothing. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -190,6 +190,8 @@ class property(DynamicClassAttribute): | |||
| 190 | 190 | """ | |
| 191 | 191 | ||
| 192 | 192 | member = None | |
| 193 | + _attr_type = None | ||
| 194 | + _cls_type = None | ||
| 193 | 195 | ||
| 194 | 196 | def __get__(self, instance, ownerclass=None): | |
| 195 | 197 | if instance is None: | |
@@ -199,33 +201,36 @@ def __get__(self, instance, ownerclass=None): | |||
| 199 | 201 | raise AttributeError( | |
| 200 | 202 | '%r has no attribute %r' % (ownerclass, self.name) | |
| 201 | 203 | ) | |
| 202 | - else: | ||
| 203 | - if self.fget is None: | ||
| 204 | - # look for a member by this name. | ||
| 205 | - try: | ||
| 206 | - return ownerclass._member_map_[self.name] | ||
| 207 | - except KeyError: | ||
| 208 | - raise AttributeError( | ||
| 209 | - '%r has no attribute %r' % (ownerclass, self.name) | ||
| 210 | - ) from None | ||
| 211 | - else: | ||
| 212 | - return self.fget(instance) | ||
| 204 | + if self.fget is not None: | ||
| 205 | + # use previous enum.property | ||
| 206 | + return self.fget(instance) | ||
| 207 | + elif self._attr_type == 'attr': | ||
| 208 | + # look up previous attibute | ||
| 209 | + return getattr(self._cls_type, self.name) | ||
| 210 | + elif self._attr_type == 'desc': | ||
| 211 | + # use previous descriptor | ||
| 212 | + return getattr(instance._value_, self.name) | ||
| 213 | + # look for a member by this name. | ||
| 214 | + try: | ||
| 215 | + return ownerclass._member_map_[self.name] | ||
| 216 | + except KeyError: | ||
| 217 | + raise AttributeError( | ||
| 218 | + '%r has no attribute %r' % (ownerclass, self.name) | ||
| 219 | + ) from None | ||
| 213 | 220 | ||
| 214 | 221 | def __set__(self, instance, value): | |
| 215 | - if self.fset is None: | ||
| 216 | - raise AttributeError( | ||
| 217 | - "<enum %r> cannot set attribute %r" % (self.clsname, self.name) | ||
| 218 | - ) | ||
| 219 | - else: | ||
| 222 | + if self.fset is not None: | ||
| 220 | 223 | return self.fset(instance, value) | |
| 224 | + raise AttributeError( | ||
| 225 | + "<enum %r> cannot set attribute %r" % (self.clsname, self.name) | ||
| 226 | + ) | ||
| 221 | 227 | ||
| 222 | 228 | def __delete__(self, instance): | |
| 223 | - if self.fdel is None: | ||
| 224 | - raise AttributeError( | ||
| 225 | - "<enum %r> cannot delete attribute %r" % (self.clsname, self.name) | ||
| 226 | - ) | ||
| 227 | - else: | ||
| 229 | + if self.fdel is not None: | ||
| 228 | 230 | return self.fdel(instance) | |
| 231 | + raise AttributeError( | ||
| 232 | + "<enum %r> cannot delete attribute %r" % (self.clsname, self.name) | ||
| 233 | + ) | ||
| 229 | 234 | ||
| 230 | 235 | def __set_name__(self, ownerclass, name): | |
| 231 | 236 | self.name = name | |
@@ -313,27 +318,38 @@ def __set_name__(self, enum_class, member_name): | |||
| 313 | 318 | enum_class._member_names_.append(member_name) | |
| 314 | 319 | # if necessary, get redirect in place and then add it to _member_map_ | |
| 315 | 320 | found_descriptor = None | |
| 321 | + descriptor_type = None | ||
| 322 | + class_type = None | ||
| 316 | 323 | for base in enum_class.__mro__[1:]: | |
| 317 | - descriptor = base.__dict__.get(member_name) | ||
| 318 | - if descriptor is not None: | ||
| 319 | - if isinstance(descriptor, (property, DynamicClassAttribute)): | ||
| 320 | - found_descriptor = descriptor | ||
| 324 | + attr = base.__dict__.get(member_name) | ||
| 325 | + if attr is not None: | ||
| 326 | + if isinstance(attr, (property, DynamicClassAttribute)): | ||
| 327 | + found_descriptor = attr | ||
| 328 | + class_type = base | ||
| 329 | + descriptor_type = 'enum' | ||
| 321 | 330 | break | |
| 322 | - elif ( | ||
| 323 | - hasattr(descriptor, 'fget') and | ||
| 324 | - hasattr(descriptor, 'fset') and | ||
| 325 | - hasattr(descriptor, 'fdel') | ||
| 326 | - ): | ||
| 327 | - found_descriptor = descriptor | ||
| 331 | + elif _is_descriptor(attr): | ||
| 332 | + found_descriptor = attr | ||
| 333 | + descriptor_type = descriptor_type or 'desc' | ||
| 334 | + class_type = class_type or base | ||
| 328 | 335 | continue | |
| 336 | + else: | ||
| 337 | + descriptor_type = 'attr' | ||
| 338 | + class_type = base | ||
| 329 | 339 | if found_descriptor: | |
| 330 | 340 | redirect = property() | |
| 331 | 341 | redirect.member = enum_member | |
| 332 | 342 | redirect.__set_name__(enum_class, member_name) | |
| 333 | - # earlier descriptor found; copy fget, fset, fdel to this one. | ||
| 334 | - redirect.fget = found_descriptor.fget | ||
| 335 | - redirect.fset = found_descriptor.fset | ||
| 336 | - redirect.fdel = found_descriptor.fdel | ||
| 343 | + if descriptor_type in ('enum','desc'): | ||
| 344 | + # earlier descriptor found; copy fget, fset, fdel to this one. | ||
| 345 | + redirect.fget = getattr(found_descriptor, 'fget', None) | ||
| 346 | + redirect._get = getattr(found_descriptor, '__get__', None) | ||
| 347 | + redirect.fset = getattr(found_descriptor, 'fset', None) | ||
| 348 | + redirect._set = getattr(found_descriptor, '__set__', None) | ||
| 349 | + redirect.fdel = getattr(found_descriptor, 'fdel', None) | ||
| 350 | + redirect._del = getattr(found_descriptor, '__delete__', None) | ||
| 351 | + redirect._attr_type = descriptor_type | ||
| 352 | + redirect._cls_type = class_type | ||
| 337 | 353 | setattr(enum_class, member_name, redirect) | |
| 338 | 354 | else: | |
| 339 | 355 | setattr(enum_class, member_name, enum_member) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -819,10 +819,27 @@ class TestPlainFlag(_EnumTests, _PlainOutputTests, _FlagTests, unittest.TestCase | |||
| 819 | 819 | ||
| 820 | 820 | class TestIntEnum(_EnumTests, _MinimalOutputTests, unittest.TestCase): | |
| 821 | 821 | enum_type = IntEnum | |
| 822 | + # | ||
| 823 | + def test_shadowed_attr(self): | ||
| 824 | + class Number(IntEnum): | ||
| 825 | + divisor = 1 | ||
| 826 | + numerator = 2 | ||
| 827 | + # | ||
| 828 | + self.assertEqual(Number.divisor.numerator, 1) | ||
| 829 | + self.assertIs(Number.numerator.divisor, Number.divisor) | ||
| 822 | 830 | ||
| 823 | 831 | ||
| 824 | 832 | class TestStrEnum(_EnumTests, _MinimalOutputTests, unittest.TestCase): | |
| 825 | 833 | enum_type = StrEnum | |
| 834 | + # | ||
| 835 | + def test_shadowed_attr(self): | ||
| 836 | + class Book(StrEnum): | ||
| 837 | + author = 'author' | ||
| 838 | + title = 'title' | ||
| 839 | + # | ||
| 840 | + self.assertEqual(Book.author.title(), 'Author') | ||
| 841 | + self.assertEqual(Book.title.title(), 'Title') | ||
| 842 | + self.assertIs(Book.title.author, Book.author) | ||
| 826 | 843 | ||
| 827 | 844 | ||
| 828 | 845 | class TestIntFlag(_EnumTests, _MinimalOutputTests, _FlagTests, unittest.TestCase): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + Attributes/methods are no longer shadowed by same-named enum members, | ||
| 2 | + although they may be shadowed by enum.property's. | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments