Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions python-stdlib/enum/enum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
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.

---

# Custom Enum Library for Python & MicroPython

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.

## Core Features
* **Transparent Math**: Supports arithmetic (`+`, `-`, `*`, `/`) and bitwise (`&`, `|`, `^`, `<<`, `>>`) operations directly on enum members.
* **Dynamic Expansion**: Add new members at runtime via `.append()` or direct attribute assignment.
* **Memory Efficient**: Uses `__slots__` in the `ValueWrapper` to minimize RAM usage on platforms like the ESP32.
* **Flexible Initialization**: Can be initialized via class inheritance, dictionaries, or keyword arguments.

---

## Usage Examples

### 1. Hardware Pin Configuration (ESP32)
Define your hardware pins using class inheritance. You can skip internal or reserved pins using the `__skipped__` attribute.

```python
from enum import Enum

class Pins(Enum):
# Members defined at class level
LED = 2
BUTTON = 4
# Members to exclude from the enum mapping
__skipped__ = ('RESERVED_PIN',)
RESERVED_PIN = 0

# You can also add pins during instantiation
pins = Pins(SDA=21, SCL=22)

print(f"I2C SDA Pin: {pins.SDA}") # Output: 21
print(f"Is pin 21 valid? {pins.is_value(21)}") # Output: True
```

### 2. Math and Register Logic
The `ValueWrapper` allows you to perform calculations directly. This is particularly useful for bitmasks and step-based logic.

```python
# Initialize with key-value pairs
brightness = Enum(MIN=0, STEP=25, MAX=255)

# Direct arithmetic (Forward and Reflected)
next_level = brightness.MIN + brightness.STEP // 2
complex_math = 100 + brightness.STEP

print(f"Next Level: {next_level}") # Output: 12
print(f"Complex Math: {complex_math}") # Output: 125

# Bitwise operations for register control
flags = Enum(BIT_0=0x01, BIT_1=0x02)
combined = flags.BIT_0 | flags.BIT_1
print(f"Combined Flags: {hex(combined)}") # Output: 0x03
```

### 3. Dynamic State Machines
You can expand an `Enum` as your program logic progresses, such as adding states to a connection manager.

```python
status = Enum(IDLE=0, CONNECTING=1)

# Add multiple members via append()
status.append(CONNECTED=2, ERROR=3)

# Add a single member via direct assignment
status.DISCONNECTING = 4

for name, val in status.items():
print(f"Status {name} has code {val}")
```

### 4. Working with Different Data Types
Enums are not restricted to integers; they can wrap strings, floats, and booleans.

```python
commands = Enum(
START="CMD_START",
STOP="CMD_STOP",
TIMEOUT=5.5,
IS_ACTIVE=True
)

if commands.IS_ACTIVE:
# Use str() to get the wrapped string value
print(f"Executing: {commands.START}")
```

### 5. Introspection and Utilities
The library provides helper methods to validate values or find keys based on their values.

```python
class ErrorCodes(Enum):
NOT_FOUND = 404
SERVER_ERROR = 500

# Check if a value exists in the Enum
exists = ErrorCodes.is_value(404) # True

# Get the formatted string name from a value
name = ErrorCodes.key_from_value(500)
print(name) # Output: ErrorCodes.SERVER_ERROR
```

---

## API Reference

### `ValueWrapper`
The internal class that wraps values to enable mathematical transparency.
* `.value`: Access the raw value.
* `()`: Calling the object returns the raw value.

### `Enum` (Inherits from `dict`)
* `append(arg=None, **kwargs)`: Adds new members to the Enum.
* `is_value(value)`: Returns `True` if the value exists in the Enum.
* `key_from_value(value)`: Returns the string representation (e.g., `ClassName.KEY`) for a given value.
157 changes: 157 additions & 0 deletions python-stdlib/enum/enum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# enum.py
# version="1.2.1"


class EnumValue:
def __init__(self, value, name):
object.__setattr__(self, 'value', value)
object.__setattr__(self, 'name', name)

def __repr__(self):
return str(self.value)

def __call__(self):
return self.value

def __eq__(self, other):
return self.value == (other.value if isinstance(other, EnumValue) else other)

def __setattr__(self, key, value):
raise AttributeError("EnumValue is immutable")


class Enum:
def __new__(cls, *args, **kwargs):
if len(args) > 0:
raise TypeError(f"{cls.__name__}() kwargs allowed only, not {args} args")
return super(Enum, cls).__new__(cls)

def __init__(self, **kwargs):
# 1. Collect class-level attributes (constants)
self._scan_class_attrs()
# 2. Add arguments from the constructor
if kwargs:
self.append(**kwargs)

def _update(self, key, value):
setattr(self.__class__, key, EnumValue(value, key))

def _scan_class_attrs(self):
# Converts static class attributes into EnumValue objects
# List of methods and internal names that should not be converted
ignored = ('is_value', 'append')

for key in dir(self.__class__):
# Skip internal names and methods
if key.startswith('_') or key in ignored:
continue

value = getattr(self.__class__, key)
# Convert only constants, not methods
if not callable(value) and not isinstance(value, EnumValue):
self._update(key, value)

def is_value(self, value):
# Оптимізація: ітеруємося по self (де вже є __iter__), а не через dir()
return any(member.value == value for member in self)

def append(self, **kwargs):
forbidden = ('is_value', 'append', '_update', '_scan_class_attrs')
for key, value in kwargs.items():
if key in forbidden or key.startswith('_'):
raise NameError(f"Cannot add enum member with reserved name: {key}")
if hasattr(self.__class__, key):
existing = getattr(self.__class__, key)
if isinstance(existing, EnumValue):
raise AttributeError(f"Enum member '{key}' already exists and is immutable")
self._update(key, value)
return self

def __repr__(self):
# Implementation of the principle: obj == eval(repr(obj))
# Use !r to correctly represent values ​​(e.g., quotes for strings)
members = [f"{k}={getattr(self.__class__, k).value!r}" for k in dir(self.__class__) if not k.startswith('_') and isinstance(getattr(self.__class__, k), EnumValue)]
# Return a string like: Color(RED=1, GREEN=2, BLUE=3)
return f"{type(self).__name__}({', '.join(members)})"

def __call__(self, value):
for member in self:
if member.value == value:
return member
raise ValueError(f"no such value: {value}")

def __setattr__(self, key, value):
if hasattr(self, key) and isinstance(getattr(self, key), EnumValue):
raise AttributeError(f"Enum member '{key}' is immutable")
super().__setattr__(key, value)

def __delattr__(self, key):
if hasattr(self, key) and isinstance(getattr(self, key), EnumValue):
raise AttributeError("Enum members cannot be deleted")
super().__delattr__(key)

def __len__(self):
return sum(1 for _ in self)

def __iter__(self):
for key in dir(self.__class__):
attr = getattr(self.__class__, key)
if isinstance(attr, EnumValue):
yield attr


def enum(**kwargs): # `**kwargs` kept backwards compatible as in the Internet examples
return Enum(**kwargs)


if __name__ == '__main__':
# --- Usage Example ---

# 1. Creation via class
class Color(Enum):
RED = 1
GREEN = 2

# Create instance
c = Color()
print(f"Enum repr: {c}")

# 2. Strict __init__ control check
try:
c_bad = Color('BLACK')
except TypeError as e:
print(f"\nTypeError: Strict Init Check: {e}\n")

# 3. Dynamic addition
c.append(BLUE=3)
print(f"c after append: {c}")

print('dir(c):', dir(c))

# 4. Immutability and name protection check
try:
c.append(append=True)
except NameError as e:
print(f"\nNameError: Reserved name protection: {e}\n")

# 5. Basic access
print(f"RED: Name={c.RED.name}, Value={c.RED.value}, EnumValue={c.RED}, Call={c.RED()} ")

# 6. Assertions
assert c.RED == 1
assert c.RED.value == 1
assert c.RED.name == 'RED'

# 7. Reverse lookup
print(f"c(1) lookup object: {c(1)}, Name={c(1).name}") # RED
assert c(1).name == 'RED'
assert c(1) == 1

# 8. Iteration
print("Values list:", [member.value for member in c])
print("Names list:", [member.name for member in c])

try:
c(7)
except ValueError as e:
print(f"\nValueError: {c} {e}\n")
Loading
Loading