Skip to content

Commit

Permalink
fix python 3.11+ usage and add member/nonmember decorator tests for #29
Browse files Browse the repository at this point in the history
  • Loading branch information
bckohan committed Feb 16, 2023
1 parent 60787e1 commit 6abc0ba
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 9 deletions.
17 changes: 9 additions & 8 deletions doc/source/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -261,15 +261,16 @@ Nested Classes
.. note::
Nested classes behave normally on enums that inherit from
In python <3.13, nested classes behave normally on enums that inherit from
:py:class:`~enum_properties.EnumProperties` and that specify at least one
property.
On enums that inherit from Enum_ nested classes become enumeration values
because types may be values and a quirk of Python makes it difficult to
determine if a type on a class is declared as a nested class during __new__.
For enums with properties we can distinguish declared classes because values
must be tuples.
property. In python 3.13 this behavior will remain unchanged in
enum-properties and normal Enum_ classes will adopt it.
On enums that inherit from Enum_ in python < 3.13 nested classes become
enumeration values because types may be values and a quirk of Python makes it
difficult to determine if a type on a class is declared as a nested class
during __new__. For enums with properties we can distinguish declared classes
because values must be tuples.
Using :py:class:`~enum_properties.EnumProperties` this is possible:
Expand Down
11 changes: 10 additions & 1 deletion enum_properties/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ def __setitem__(self, key, value):
len(class_dict._member_names) > before and
# base class lets nested classes through! see:
# https://github.com/bckohan/enum-properties/issues/29
# todo remove below when minimum python >= 3.13
not isinstance(value, type)
):
try:
Expand Down Expand Up @@ -354,9 +355,17 @@ def __setitem__(self, key, value):
super().__setitem__(key, value)

if remove:
# todo remove when minimum python >= 3.13
# base class lets nested classes through! see:
# https://github.com/bckohan/enum-properties/issues/29
self._member_names.remove(key)
if isinstance(
self._member_names,
list
): # pragma: no cover
self._member_names.remove(key)
else: # pragma: no cover
# >= python 3.11
del self._member_names[key]
else:
super().__setitem__(key, value)

Expand Down
57 changes: 57 additions & 0 deletions enum_properties/tests/tests.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pickle
import sys
from collections.abc import Hashable
from enum import Enum, auto
from io import BytesIO
Expand Down Expand Up @@ -1288,6 +1289,7 @@ class Type3:
def test_example(self):

class MyEnum(EnumProperties, p('label')):

class Type1:
pass

Expand All @@ -1309,3 +1311,58 @@ class Type3:
self.assertTrue(MyEnum.Type1().__class__ is MyEnum.Type1)
self.assertTrue(MyEnum.Type2().__class__ is MyEnum.Type2)
self.assertTrue(MyEnum.Type3().__class__ is MyEnum.Type3)

if sys.version_info >= (3, 11): # pragma: no cover
def test_nonmember_decorator(self):
from enum import nonmember

class MyEnum(EnumProperties, p('label')):

@nonmember
class Type1:
pass

@nonmember
class Type2:
pass

@nonmember
class Type3:
pass

VALUE1 = Type1, 'label1'
VALUE2 = Type2, 'label2'
VALUE3 = Type3, 'label3'
VALUE4 = nonmember((Type3, 'label4'))

# nested classes are usable like normal
self.assertEqual(MyEnum.Type1, MyEnum.VALUE1.value)
self.assertEqual(MyEnum.Type2, MyEnum.VALUE2.value)
self.assertEqual(MyEnum.Type3, MyEnum.VALUE3.value)
self.assertEqual(len(MyEnum), 3)
self.assertEqual(MyEnum.VALUE4, (MyEnum.Type3, 'label4'))
self.assertTrue(MyEnum.Type1().__class__ is MyEnum.Type1)
self.assertTrue(MyEnum.Type2().__class__ is MyEnum.Type2)
self.assertTrue(MyEnum.Type3().__class__ is MyEnum.Type3)

def test_member_decorator(self):
from enum import member

with self.assertRaises(ValueError):
class MyEnum(EnumProperties, p('label')):

@member
class Type1:
pass

@member
class Type2:
pass

@member
class Type3:
pass

VALUE1 = Type1, 'label1'
VALUE2 = Type2, 'label2'
VALUE3 = Type3, 'label3'

0 comments on commit 6abc0ba

Please sign in to comment.