|
1 | | -# Enum Library |
| 1 | +# Lightweight Python Enum (No Metaclasses) |
2 | 2 |
|
3 | | -This library provides a lightweight, memory-efficient `Enum` implementation designed for MicroPython environments. It focuses on immutability, reverse lookup capabilities, and serialization support without the complexity of metaclasses. |
| 3 | +A lightweight Python implementation of enumerations (`Enum`, `IntEnum`, `StrEnum`) created without using metaclasses. Supports class syntax, functional API, strict value type checking, and immutability. |
4 | 4 |
|
5 | | ---- |
| 5 | +## ?? Features |
6 | 6 |
|
7 | | -## Core Features |
8 | | -* **Immutability**: Enum members (`EnumValue`) are protected against modification. Any attempt to change their name or value raises an `AttributeError`. |
9 | | -* **Static Design**: Once an Enum instance is initialized, it is "frozen." You cannot add new attributes or delete existing members. |
10 | | -* **Dual Reverse Lookup**: |
11 | | - * **Class Constructor**: Retrieve a member by value using the class name (e.g., `Status(1)`). |
12 | | - * **Instance Call**: Retrieve a member by value by calling the instance (e.g., `s(1)`). |
13 | | -* **Serialization Support**: Implements `__repr__` such that `obj == eval(repr(obj))`, allowing easy restoration of Enum states. |
14 | | -* **Functional API**: Supports dynamic creation of Enums at runtime. |
| 7 | +* **No Metaclasses**: Dynamic type construction using standard `type()`. |
| 8 | +* **Types**: Base `Enum`, as well as strictly typed `IntEnum` and `StrEnum`. |
| 9 | +* **Dual Syntax**: Declaration via standard `class` statements or functional API calls. |
| 10 | +* **Immutability**: Attribute mutation guard preventing changes after member creation. |
| 11 | +* **Serialization**: `.dump()` method for easy string representation and reconstruction via `eval()`. |
15 | 12 |
|
16 | | ---- |
| 13 | +## ?? Usage |
17 | 14 |
|
18 | | -## Usage Examples |
19 | | - |
20 | | -### 1. Standard Class Definition |
21 | | -Define your enumeration by inheriting from the `Enum` class. Class-level constants are automatically converted into `EnumValue` objects upon initialization. |
| 15 | +### 1. Class Syntax Declaration |
22 | 16 |
|
23 | 17 | ```python |
24 | | -from enum import Enum |
| 18 | +from enum import Enum, IntEnum, StrEnum |
25 | 19 |
|
26 | 20 | class Color(Enum): |
27 | | - RED = 'red' |
28 | | - GREEN = 'green' |
29 | | - |
30 | | -# Initialize the enum to process attributes |
31 | | -c = Color() |
32 | | - |
33 | | -print(c.RED) # Output: RED: red |
34 | | -print(c.RED.name) # Output: RED |
35 | | -print(c.RED.value) # Output: red |
36 | | -print(c.RED()) # Output: red |
37 | | -print(c.is_value("RED")) # Output: true |
38 | | -print(c.is_value(Color.RED)) # Output: true |
39 | | -print(c.is_value('red')) # Output: true |
40 | | -print(c.list()) # Output: [Color.RED: red, Color.GREEN: green] |
41 | | -print([m for m in c]) # Output: [Color.RED: red, Color.GREEN: green] |
42 | | -print([m.name for m in c]) # Output: ['RED', 'GREEN'] |
43 | | -print([m.value for m in c]) # Output: ['red', 'green'] |
44 | | -``` |
45 | | - |
46 | | - |
47 | | -### 2. Reverse Lookup |
48 | | -The library provides two ways to find a member based on its raw value. |
49 | | - |
50 | | -```python |
51 | | -class Status(Enum): |
52 | | - IDLE = 0 |
53 | | - RUNNING = 1 |
54 | | - |
55 | | -# Method A: Via Class (Simulates interpreting hardware/network bytes) |
56 | | -# Uses __new__ logic to return the correct EnumValue |
57 | | -current_status = Status(1) |
58 | | -print(current_status.name) # Output: RUNNING |
59 | | -print(current_status.value) # Output: 1 |
60 | | -print(current_status) # Output: Status.RUNNING: 1 |
61 | | -print(current_status()) # Output: 1 |
62 | | - |
63 | | -# Method B: Via Instance Call |
64 | | -s = Status() |
65 | | -print(s(0).name) # Output: IDLE |
66 | | -print(s(0).value) # Output: 0 |
67 | | -print(s(0)) # Output: Status.IDLE: 0 |
68 | | -print(s(0)()) # Output: 0 |
69 | | -``` |
70 | | - |
| 21 | + RED = 1 |
| 22 | + GREEN = 2 |
| 23 | + BLUE = 3 |
71 | 24 |
|
72 | | -### 3. Functional API (Dynamic Creation) |
73 | | -If you need to create an Enum from external data (like a JSON config), use the functional constructor. |
| 25 | +class Status(StrEnum): |
| 26 | + PENDING = "pending" |
| 27 | + DONE = "done" |
74 | 28 |
|
75 | | -```python |
76 | | -# Create a dynamic Enum instance |
77 | | -State = Enum(name='State', names={'ON': 1, 'OFF': 2}) |
78 | | - |
79 | | -print(State) # Output: Enum(name='State', names={'ON': 1, 'OFF': 2}) |
80 | | -print(State.ON) # Output: State.ON: 1 |
81 | | -print(State.ON.name) # Output: ON |
82 | | -print(State.ON.value) # Output: 1 |
83 | | -print(State.ON()) # Output: 1 |
84 | | -assert State.ON == 1 # Comparison |
85 | | -assert State.ON() == 1 # |
86 | | -assert State.ON.value == 1 # |
87 | | -assert State.ON.name == "ON" # |
| 29 | +# Accessing members |
| 30 | +print(Color.RED) # <Color.RED: 1> |
| 31 | +print(Color.RED.name) # 'RED' |
| 32 | +print(Color.RED.value) # 1 |
| 33 | +print(Status.PENDING) # 'pending' (for StrEnum, string output equals value) |
88 | 34 | ``` |
89 | 35 |
|
90 | | - |
91 | | -### 4. Serialization (Repr / Eval) |
92 | | -The library ensures that the string representation can be used to perfectly reconstruct the object. |
| 36 | +### 2. Functional API |
93 | 37 |
|
94 | 38 | ```python |
95 | | -from enum import Enum |
96 | | - |
97 | | -class Color(Enum): |
98 | | - RED = 'red' |
99 | | - GREEN = 'green' |
100 | | - BLUE = 3 |
| 39 | +# Enum with sequential integer values |
| 40 | +Priority = Enum("Priority", "LOW MEDIUM HIGH", start=1) |
| 41 | +print(Priority.HIGH.value) # 3 |
101 | 42 |
|
102 | | -colors = Color() |
103 | | -# Get serialized string |
104 | | -serialized = repr(colors) |
105 | | -# Reconstruct object |
106 | | -restored_colors = eval(serialized) |
107 | | - |
108 | | -print(f"Original: {colors}") # Output: Original: Enum(name='Color', names={'BLUE': 3, 'RED': 'red', 'GREEN': 'green'}) |
109 | | -print(f"Restored: {restored_colors}") # Output: Restored: Enum(name='Color', names={'BLUE': 3, 'RED': 'red', 'GREEN': 'green'}) |
110 | | -print(colors == restored_colors) # Output: True |
| 43 | +# Enum from a dictionary mapping |
| 44 | +HTTPStatus = IntEnum("HTTPStatus", {"OK": 200, "NOT_FOUND": 404}) |
| 45 | +print(HTTPStatus.NOT_FOUND.value) # 404 |
111 | 46 | ``` |
112 | 47 |
|
113 | | - |
114 | | ---- |
115 | | - |
116 | | -## API Reference |
117 | | - |
118 | | -### `EnumValue` |
119 | | -The object representing a specific member of an Enum. |
120 | | -* `.name`: The string name of the member. |
121 | | -* `.value`: The raw value associated with the member. |
122 | | -* `()`: Calling the member object returns its raw value (e.g., `c.RED() -> 'red'`). |
123 | | - |
124 | | -### `Enum` |
125 | | -The base class for all enumerations. |
126 | | -* `list()`: Returns a list of all defined members. |
127 | | -* `is_value(value)`: Returns `True` if the provided raw value exists within the Enum. |
128 | | -* `__len__`: Returns the total number of members. |
129 | | -* `__iter__`: Allows looping through members (e.g., `[m.name for m in color_inst]`). |
130 | | - |
131 | | ---- |
132 | | - |
133 | | -## Error Handling |
134 | | -* **`AttributeError`**: |
135 | | - * Raised when attempting to modify an `EnumValue`. |
136 | | - * Raised when attempting to add new members to an initialized Enum. |
137 | | - * Raised when a class-level lookup (`Status(999)`) fails. |
138 | | - * Raised when an instance-level lookup (`s(999)`) fails. |
139 | | - |
140 | | -## Compare with CPython |
| 48 | +### 3. Lookup, Iteration, and Type Enforcement |
141 | 49 |
|
142 | 50 | ```python |
143 | | -# Run on MicroPython v1.28.0 on 2026-04-06; Generic ESP32 module with ESP32 |
144 | | -# Run on Python 3.12.10 |
145 | | -from enum import Enum |
| 51 | +# Lookup by value or name |
| 52 | +item = Color(1) # Color.RED |
| 53 | +item = Color("RED") # Color.RED |
146 | 54 |
|
147 | | -# class syntax |
148 | | -class Color(Enum): |
149 | | - RED = 1 |
150 | | - GREEN = 2 |
151 | | - BLUE = 3 |
| 55 | +# Iteration over members |
| 56 | +for member in Color: |
| 57 | + print(member.name, member.value) |
152 | 58 |
|
153 | | -# OR |
154 | | -# functional syntax |
155 | | -# Color = Enum('Color', {'RED': 1, 'GREEN': 2, 'BLUE': 3}) |
156 | | - |
157 | | -# List enum members |
158 | | -try: |
159 | | - print(list(Color)) |
160 | | -# [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 3>] |
161 | | -except: |
162 | | - print(Color.list()) |
163 | | -# [RED: 1, GREEN: 2, BLUE: 3] |
164 | | - |
165 | | -# Accessing enum member by name |
166 | | -print(Color.GREEN, type(Color.GREEN)) |
167 | | -# Color.GREEN <enum 'Color'> |
168 | | -# GREEN: 2 <class 'EnumValue'> |
169 | | - |
170 | | -# Accessing enum member by name |
| 59 | +# Strict type checks in IntEnum / StrEnum |
171 | 60 | try: |
172 | | - print(Color['GREEN']) |
173 | | -# Color.GREEN |
174 | | -except: |
175 | | - print(Color('GREEN')) |
176 | | -# GREEN: 2 |
177 | | - |
178 | | -# Accessing enum member by value |
179 | | -print(Color(2)) |
180 | | -# Color.GREEN |
181 | | - |
182 | | -# Accessing enum member name |
183 | | -print(Color.GREEN.name, type(Color.GREEN.name)) |
184 | | -# GREEN <class 'str'> |
185 | | - |
186 | | -# Accessing enum member value |
187 | | -print(Color.GREEN.value, type(Color.GREEN.value)) |
188 | | -# 2 <class 'int'> |
| 61 | + class BadEnum(IntEnum): |
| 62 | + INVALID = "not an int" # Raises TypeError |
| 63 | +except TypeError as e: |
| 64 | + print(e) |
189 | 65 | ``` |
190 | 66 |
|
191 | | -### Output is: |
| 67 | +### 4. Serialization & Evaluation |
192 | 68 |
|
193 | | -| MicroPython v1.28.0 | Python 3.12.10 | |
194 | | -| :--- | :--- | |
195 | | -| [Color.RED: 1, Color.GREEN: 2, Color.BLUE: 3] | [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 3>] | |
196 | | -| Color.GREEN: 2 <class 'EnumValue'> | Color.GREEN <enum 'Color'> | |
197 | | -| Color.GREEN: 2 | Color.GREEN | |
198 | | -| Color.GREEN: 2 | Color.GREEN | |
199 | | -| GREEN <class 'str'> | GREEN <class 'str'> | |
200 | | -| 2 <class 'int'> | 2 <class 'int'> | |
| 69 | +```python |
| 70 | +dump_str = Color.dump() |
| 71 | +print(dump_str) # "Enum('Color', {'RED': 1, 'GREEN': 2, 'BLUE': 3})" |
201 | 72 |
|
| 73 | +# Reconstructing the Enum class object |
| 74 | +RestoredColor = eval(dump_str) |
| 75 | +``` |
0 commit comments