|
1 | | -Below is the documentation for your `enum.py` library. This file explains the core concepts of your custom `Enum` implementation and provides practical examples for embedded development and general logic. |
| 1 | +# Enum Library |
2 | 2 |
|
3 | | ---- |
4 | | - |
5 | | -# Custom Enum Library for Python & MicroPython |
| 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. |
6 | 4 |
|
7 | | -This library provides a flexible, memory-efficient `Enum` class designed for dynamic usage and seamless mathematical integration. Unlike the standard CPython `Enum`, this version allows for runtime expansion and direct arithmetic operations without needing to access a `.value` property. |
| 5 | +--- |
8 | 6 |
|
9 | 7 | ## Core Features |
10 | | -* **Transparent Math**: Supports arithmetic (`+`, `-`, `*`, `/`) and bitwise (`&`, `|`, `^`, `<<`, `>>`) operations directly on enum members. |
11 | | -* **Dynamic Expansion**: Add new members at runtime via `.append()` or direct attribute assignment. |
12 | | -* **Memory Efficient**: Uses `__slots__` in the `ValueWrapper` to minimize RAM usage on platforms like the ESP32. |
13 | | -* **Flexible Initialization**: Can be initialized via class inheritance, dictionaries, or keyword arguments. |
| 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. |
14 | 15 |
|
15 | 16 | --- |
16 | 17 |
|
17 | 18 | ## Usage Examples |
18 | 19 |
|
19 | | -### 1. Hardware Pin Configuration (ESP32) |
20 | | -Define your hardware pins using class inheritance. You can skip internal or reserved pins using the `__skipped__` attribute. |
| 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. |
21 | 22 |
|
22 | 23 | ```python |
23 | 24 | from enum import Enum |
24 | 25 |
|
25 | | -class Pins(Enum): |
26 | | - # Members defined at class level |
27 | | - LED = 2 |
28 | | - BUTTON = 4 |
29 | | - # Members to exclude from the enum mapping |
30 | | - __skipped__ = ('RESERVED_PIN',) |
31 | | - RESERVED_PIN = 0 |
| 26 | +class Color(Enum): |
| 27 | + RED = 'red' |
| 28 | + GREEN = 'green' |
32 | 29 |
|
33 | | -# You can also add pins during instantiation |
34 | | -pins = Pins(SDA=21, SCL=22) |
| 30 | +# Initialize the enum to process attributes |
| 31 | +c = Color() |
35 | 32 |
|
36 | | -print(f"I2C SDA Pin: {pins.SDA}") # Output: 21 |
37 | | -print(f"Is pin 21 valid? {pins.is_value(21)}") # Output: True |
| 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 |
38 | 37 | ``` |
39 | 38 |
|
40 | | -### 2. Math and Register Logic |
41 | | -The `ValueWrapper` allows you to perform calculations directly. This is particularly useful for bitmasks and step-based logic. |
42 | | - |
43 | | -```python |
44 | | -# Initialize with key-value pairs |
45 | | -brightness = Enum(MIN=0, STEP=25, MAX=255) |
46 | 39 |
|
47 | | -# Direct arithmetic (Forward and Reflected) |
48 | | -next_level = brightness.MIN + brightness.STEP // 2 |
49 | | -complex_math = 100 + brightness.STEP |
| 40 | +### 2. Reverse Lookup |
| 41 | +The library provides two ways to find a member based on its raw value. |
50 | 42 |
|
51 | | -print(f"Next Level: {next_level}") # Output: 12 |
52 | | -print(f"Complex Math: {complex_math}") # Output: 125 |
53 | | - |
54 | | -# Bitwise operations for register control |
55 | | -flags = Enum(BIT_0=0x01, BIT_1=0x02) |
56 | | -combined = flags.BIT_0 | flags.BIT_1 |
57 | | -print(f"Combined Flags: {hex(combined)}") # Output: 0x03 |
| 43 | +```python |
| 44 | +class Status(Enum): |
| 45 | + IDLE = 0 |
| 46 | + RUNNING = 1 |
| 47 | + |
| 48 | +# Method A: Via Class (Simulates interpreting hardware/network bytes) |
| 49 | +# Uses __new__ logic to return the correct EnumValue |
| 50 | +current_status = Status(1) |
| 51 | +print(current_status.name) # Output: RUNNING |
| 52 | +print(current_status) # Output: RUNNING: 1 |
| 53 | +print(current_status()) # Output: 1 |
| 54 | + |
| 55 | +# Method B: Via Instance Call |
| 56 | +s = Status() |
| 57 | +print(s(0).name) # Output: IDLE |
| 58 | +print(s(0)) # Output: IDLE: 0 |
| 59 | +print(s(0)()) # Output: 0 |
58 | 60 | ``` |
59 | 61 |
|
60 | | -### 3. Dynamic State Machines |
61 | | -You can expand an `Enum` as your program logic progresses, such as adding states to a connection manager. |
62 | | - |
63 | | -```python |
64 | | -status = Enum(IDLE=0, CONNECTING=1) |
65 | 62 |
|
66 | | -# Add multiple members via append() |
67 | | -status.append(CONNECTED=2, ERROR=3) |
| 63 | +### 3. Functional API (Dynamic Creation) |
| 64 | +If you need to create an Enum from external data (like a JSON config), use the functional constructor. |
68 | 65 |
|
69 | | -# Add a single member via direct assignment |
70 | | -status.DISCONNECTING = 4 |
| 66 | +```python |
| 67 | +# Create a dynamic Enum instance |
| 68 | +State = Enum(name='State', names={'ON': 1, 'OFF': 2}) |
71 | 69 |
|
72 | | -for name, val in status.items(): |
73 | | - print(f"Status {name} has code {val}") |
| 70 | +print(State.ON) # Output: ON: 1 |
| 71 | +assert State.ON == 1 # Comparison with raw value |
74 | 72 | ``` |
75 | 73 |
|
76 | | -### 4. Working with Different Data Types |
77 | | -Enums are not restricted to integers; they can wrap strings, floats, and booleans. |
78 | | - |
79 | | -```python |
80 | | -commands = Enum( |
81 | | - START="CMD_START", |
82 | | - STOP="CMD_STOP", |
83 | | - TIMEOUT=5.5, |
84 | | - IS_ACTIVE=True |
85 | | -) |
86 | | - |
87 | | -if commands.IS_ACTIVE: |
88 | | - # Use str() to get the wrapped string value |
89 | | - print(f"Executing: {commands.START}") |
90 | | -``` |
91 | 74 |
|
92 | | -### 5. Introspection and Utilities |
93 | | -The library provides helper methods to validate values or find keys based on their values. |
| 75 | +### 4. Serialization (Repr / Eval) |
| 76 | +The library ensures that the string representation can be used to perfectly reconstruct the object. |
94 | 77 |
|
95 | 78 | ```python |
96 | | -class ErrorCodes(Enum): |
97 | | - NOT_FOUND = 404 |
98 | | - SERVER_ERROR = 500 |
| 79 | +colors = Color() |
| 80 | +# Get serialized string |
| 81 | +serialized = repr(colors) |
| 82 | +# Reconstruct object |
| 83 | +restored_colors = eval(serialized) |
99 | 84 |
|
100 | | -# Check if a value exists in the Enum |
101 | | -exists = ErrorCodes.is_value(404) # True |
| 85 | +print(f"Original: {colors}") # Output: Original: Color(names={'ON': 1, 'OFF': 2, 'GREEN': 'green', 'RED': 'red'}) |
| 86 | +print(f"Restored: {restored_colors}") # Output: Restored: Color(names={'ON': 1, 'OFF': 2, 'GREEN': 'green', 'RED': 'red'}) |
| 87 | +print(colors == restored_colors) # Output: True |
102 | 88 |
|
103 | | -# Get the formatted string name from a value |
104 | | -name = ErrorCodes.key_from_value(500) |
105 | | -print(name) # Output: ErrorCodes.SERVER_ERROR |
106 | 89 | ``` |
107 | 90 |
|
| 91 | + |
108 | 92 | --- |
109 | 93 |
|
110 | 94 | ## API Reference |
111 | 95 |
|
112 | | -### `ValueWrapper` |
113 | | -The internal class that wraps values to enable mathematical transparency. |
114 | | -* `.value`: Access the raw value. |
115 | | -* `()`: Calling the object returns the raw value. |
| 96 | +### `EnumValue` |
| 97 | +The object representing a specific member of an Enum. |
| 98 | +* `.name`: The string name of the member. |
| 99 | +* `.value`: The raw value associated with the member. |
| 100 | +* `()`: Calling the member object returns its raw value (e.g., `c.RED() -> 'red'`). |
| 101 | + |
| 102 | +### `Enum` |
| 103 | +The base class for all enumerations. |
| 104 | +* `list_members()`: Returns a list of `(name, value)` tuples for all defined members. |
| 105 | +* `is_value(value)`: Returns `True` if the provided raw value exists within the Enum. |
| 106 | +* `__len__`: Returns the total number of members. |
| 107 | +* `__iter__`: Allows looping through members (e.g., `[m.name for m in color_inst]`). |
| 108 | + |
| 109 | +--- |
116 | 110 |
|
117 | | -### `Enum` (Inherits from `dict`) |
118 | | -* `append(arg=None, **kwargs)`: Adds new members to the Enum. |
119 | | -* `is_value(value)`: Returns `True` if the value exists in the Enum. |
120 | | -* `key_from_value(value)`: Returns the string representation (e.g., `ClassName.KEY`) for a given value. |
| 111 | +## Error Handling |
| 112 | +* **`AttributeError`**: |
| 113 | + * Raised when attempting to modify an `EnumValue`. |
| 114 | + * Raised when attempting to add new members to an initialized Enum. |
| 115 | + * Raised when a class-level lookup (`Status(999)`) fails. |
| 116 | +* **`ValueError`**: |
| 117 | + * Raised when an instance-level lookup (`s(999)`) fails. |
0 commit comments