Skip to content

Commit 78df9b2

Browse files
committed
support use with mixin classes
* require Python 3.12 * use explicit generic classes syntax * use get_original_bases() and get_origin() to find base class among potentially multiple bases
1 parent 94cbd83 commit 78df9b2

4 files changed

Lines changed: 45 additions & 345 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ classifiers = [
1515
"Typing :: Typed"
1616
]
1717
dynamic = ["version"]
18-
requires-python = ">=3.8"
18+
requires-python = ">=3.12"
1919
dependencies = []
2020

2121
[project.urls]
@@ -65,7 +65,7 @@ explicit_package_bases = true
6565
[tool.ruff]
6666
extend-exclude = ["src/multiprocessing_intenum/_version.py"]
6767
line-length = 119
68-
target-version = "py311"
68+
target-version = "py312"
6969

7070
#[tool.ruff.format]
7171

src/multiprocessing_intenum/__init__.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,31 @@
33
import multiprocessing
44
from enum import IntEnum
55
from multiprocessing.synchronize import Lock, RLock
6-
from typing import Any, Generic, TypeVar, get_args
6+
from types import get_original_bases
7+
from typing import Any, get_args, get_origin
78

89
from ._version import version as __version__ # noqa: F401
910

10-
T = TypeVar("T", bound=IntEnum)
1111

12-
13-
class IntEnumValue(Generic[T]):
12+
class IntEnumValue[T: IntEnum]:
1413
"""A multiprocessing safe shared object for `IntEnum` enum values."""
1514

1615
# Pre-initialize type here to avoid numerous type ignores elsewhere
1716
EnumType: type[T | IntEnum] = IntEnum
1817

1918
@classmethod
2019
def __init_subclass__(cls) -> None: # noqa: D105
20+
# find our base class among potentially multiple bases
21+
orig_base = None
22+
for base in get_original_bases(cls):
23+
origin = get_origin(base)
24+
if origin is not None and issubclass(origin, IntEnumValue):
25+
orig_base = base
26+
if orig_base is None:
27+
message = "Can not determine base class!"
28+
raise RuntimeError(message)
29+
2130
# set EnumType to the specific type specified by subclass
22-
orig_base = cls.__orig_bases__[0] # type: ignore[attr-defined]
2331
cls.EnumType = get_args(orig_base)[0]
2432

2533
def __init__(self, value: T | str, lock: None | Lock | RLock = None) -> None:

tests/test_intenum.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import multiprocessing
33
from enum import IntEnum
44
from unittest import TestCase
5+
from unittest.mock import Mock, patch
56

67
from multiprocessing_intenum import IntEnumValue
78

@@ -24,11 +25,32 @@ class Bar(IntEnumValue[BarEnum]):
2425
pass
2526

2627

28+
class TestMixin:
29+
pass
30+
31+
32+
class MixedFoo(TestMixin, IntEnumValue[FooEnum]):
33+
pass
34+
35+
2736
class IntEnumValueTestCase(TestCase):
2837
def test_init(self) -> None:
2938
foo = Foo(FooEnum.FOO)
3039
self.assertEqual(foo, FooEnum.FOO)
3140

41+
def test_init_mixin(self) -> None:
42+
foo = MixedFoo(FooEnum.FOO)
43+
self.assertEqual(foo, FooEnum.FOO)
44+
45+
@patch("multiprocessing_intenum.get_original_bases", return_value=[])
46+
def test_init_mixin_nobase(self, original_bases_mock: Mock) -> None:
47+
with self.assertRaises(RuntimeError):
48+
49+
class MixedBar(TestMixin, IntEnumValue[BarEnum]):
50+
pass
51+
52+
original_bases_mock.assert_called_once()
53+
3254
def test_init_name(self) -> None:
3355
foo = Foo(FooEnum.FOO.name)
3456
self.assertEqual(foo, FooEnum.FOO)

0 commit comments

Comments
 (0)