Skip to content

Commit 916349f

Browse files
committed
enhanced EnumBase and FlagsEnumBase to support induvidual documentation for each enum value via EnumValue
1 parent b7e92c3 commit 916349f

File tree

3 files changed

+163
-3
lines changed

3 files changed

+163
-3
lines changed

construct_typed/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
ListContainer,
1919
PathType,
2020
)
21-
from .tenum import EnumBase, FlagsEnumBase, TEnum, TFlagsEnum
21+
from .tenum import EnumBase, EnumValue, FlagsEnumBase, TEnum, TFlagsEnum
2222

2323
__all__ = [
2424
"DataclassBitStruct",
@@ -32,6 +32,7 @@
3232
"csfield",
3333
"sfield",
3434
"EnumBase",
35+
"EnumValue",
3536
"FlagsEnumBase",
3637
"TEnum",
3738
"TFlagsEnum",

construct_typed/tenum.py

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,55 @@
55

66

77
# ## TEnum ############################################################################################################
8+
class EnumValue:
9+
"""
10+
This is a helper class for adding documentation to an enum value.
11+
"""
12+
13+
def __init__(self, value: int, doc: t.Optional[str] = None) -> None:
14+
self.value = value
15+
self.__doc__ = doc if doc else ""
16+
17+
def __int__(self) -> int:
18+
return self.value
19+
20+
821
class EnumBase(enum.IntEnum):
922
"""
1023
Base class for an Enum used in `construct_typed.TEnum`.
1124
12-
This class extends the standard `enum.IntEnum`, so that missing values are automatically generated.
25+
This class extends the standard `enum.IntEnum` by.
26+
- missing values are automatically generated
27+
- possibility to add documentation for each enum value (see `EnumValue`)
28+
29+
Example::
30+
31+
>>> class State(EnumBase):
32+
... Idle = 1
33+
... Running = EnumValue(2, "This is the running state.")
34+
35+
>>> State(1)
36+
<State.Idle: 1>
37+
38+
>>> State["Idle"]
39+
<State.Idle: 1>
40+
41+
>>> State.Idle
42+
<State.Idle: 1>
43+
44+
>>> State(3) # missing value
45+
<State.3: 3>
46+
47+
>>> State.Running.__doc__ # documentation
48+
'This is the running state.'
1349
"""
1450

51+
def __init__(self, val: t.Union[EnumValue, int]):
52+
if isinstance(val, EnumValue):
53+
self.__doc__ = val.__doc__
54+
else:
55+
self.__doc__ = ""
56+
1557
# Extend the enum type with _missing_ method. So if a enum value
1658
# not found in the enum, a new pseudo member is created.
1759
# The idea is taken from: https://stackoverflow.com/a/57179436
@@ -25,6 +67,7 @@ def _missing_(cls, value: t.Any) -> t.Optional[enum.Enum]:
2567
# However, new_member._name_ = value works, too
2668
new_member._name_ = str(value)
2769
new_member._value_ = value
70+
new_member.__doc__ = "missing value"
2871
pseudo_member = cls._value2member_map_.setdefault(value, new_member)
2972
return pseudo_member
3073
return None # will raise the ValueError in Enum.__new__
@@ -75,7 +118,51 @@ def _encode(
75118

76119
# ## TFlagsEnum #######################################################################################################
77120
class FlagsEnumBase(enum.IntFlag):
78-
pass
121+
"""
122+
Base class for an Enum used in `construct_typed.TFlagsEnum`.
123+
124+
This class extends the standard `enum.IntFlag` by.
125+
- possibility to add documentation for each enum value (see `EnumValue`)
126+
127+
Example::
128+
129+
>>> class Option(FlagsEnumBase):
130+
... OptOne = 1
131+
... OptTwo = EnumValue(2, "This is option two.")
132+
133+
>>> Option(1)
134+
<Option.OptOne: 1>
135+
136+
>>> Option["OptOne"]
137+
<Option.OptOne: 1>
138+
139+
>>> Option.OptOne
140+
<Option.OptOne: 1>
141+
142+
>>> Option(3)
143+
<Option.OptTwo|OptOne: 3>
144+
145+
>>> Option(4)
146+
<Option.4: 4>
147+
148+
>>> Option.OptTwo.__doc__ # documentation
149+
'This is option two.'
150+
"""
151+
152+
def __init__(self, val: t.Union[EnumValue, int]):
153+
if isinstance(val, EnumValue):
154+
self.__doc__ = val.__doc__
155+
else:
156+
self.__doc__ = ""
157+
158+
@classmethod
159+
def _missing_(cls, value: t.Any) -> t.Any:
160+
"""
161+
Returns member (possibly creating it) if one can be found for value.
162+
"""
163+
new_member = super()._missing_(value)
164+
new_member.__doc__ = "missing value"
165+
return new_member
79166

80167

81168
FlagsEnumType = t.TypeVar("FlagsEnumType", bound=FlagsEnumBase)

tests/test_typed.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,42 @@ class E(enum.Enum):
384384
assert raises(lambda: cst.TEnum(cs.Byte, cls)) == TypeError
385385

386386

387+
def test_tenum_docstring() -> None:
388+
class TestEnum(cst.EnumBase):
389+
"""
390+
This is an test enum.
391+
"""
392+
393+
Value_WithDoc = cst.EnumValue(0, doc="an enum with a documentation")
394+
Value_WithMultilineDoc = cst.EnumValue(
395+
1,
396+
"""
397+
An enum with a multiline documentation...
398+
...next line...
399+
""",
400+
)
401+
Value_NoDoc = cst.EnumValue(2)
402+
Value_NoDoc2 = 3
403+
404+
assert (
405+
TestEnum.__doc__
406+
== """
407+
This is an test enum.
408+
"""
409+
)
410+
assert TestEnum.Value_WithDoc.__doc__ == "an enum with a documentation"
411+
assert (
412+
TestEnum.Value_WithMultilineDoc.__doc__
413+
== """
414+
An enum with a multiline documentation...
415+
...next line...
416+
"""
417+
)
418+
assert TestEnum.Value_NoDoc.__doc__ == ""
419+
assert TestEnum.Value_NoDoc2.__doc__ == ""
420+
assert TestEnum(5).__doc__ == "missing value"
421+
422+
387423
def test_dataclass_struct_wrong_enumbase() -> None:
388424
class E1(cst.EnumBase):
389425
a = 1
@@ -434,3 +470,39 @@ class TestEnum(cst.FlagsEnumBase):
434470
assert d.build(TestEnum(255)) == b"\xff"
435471
assert d.build(TestEnum.eight) == b"\x08"
436472
assert raises(d.build, 2) == TypeError
473+
474+
475+
def test_tenum_flags_docstring() -> None:
476+
class TestEnum(cst.FlagsEnumBase):
477+
"""
478+
This is an test flags enum.
479+
"""
480+
481+
Value_WithDoc = cst.EnumValue(0, doc="an enum with a documentation")
482+
Value_WithMultilineDoc = cst.EnumValue(
483+
1,
484+
"""
485+
An enum with a multiline documentation...
486+
...next line...
487+
""",
488+
)
489+
Value_NoDoc = cst.EnumValue(2)
490+
Value_NoDoc2 = 4
491+
492+
assert (
493+
TestEnum.__doc__
494+
== """
495+
This is an test flags enum.
496+
"""
497+
)
498+
assert TestEnum.Value_WithDoc.__doc__ == "an enum with a documentation"
499+
assert (
500+
TestEnum.Value_WithMultilineDoc.__doc__
501+
== """
502+
An enum with a multiline documentation...
503+
...next line...
504+
"""
505+
)
506+
assert TestEnum.Value_NoDoc.__doc__ == ""
507+
assert TestEnum.Value_NoDoc2.__doc__ == ""
508+
assert TestEnum(8).__doc__ == "missing value"

0 commit comments

Comments
 (0)