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.
162 changes: 162 additions & 0 deletions python-stdlib/enum/enum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# enum.py
# version="1.2.0"

_Err = "no such attribute: "


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


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 __init__(self, **kwargs):
"""Ініціалізація Enum. Динамічні аргументи видалено."""
# 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):
"""Перетворює статичні атрибути класу в об'єкти EnumValue"""
# Список методів та службових імен, які не треба перетворювати
ignored = ('is_value', 'key_from_value')

for key in dir(self.__class__):
# Пропускаємо внутрішні імена та методи
if key.startswith('_') or key in ignored:
continue

value = getattr(self.__class__, key)
# Перетворюємо лише константи, не методи
if not callable(value):
self._update(key, value)
# Опціонально: видаляємо з класу оригінальне значення, щоб звільнити RAM
# try: delattr(self.__class__, key)
# except: pass


# def append(self, **kwargs):
# for key, value in kwargs.items():
# self._update(key, value)
#
# return self



def append(self, **kwargs):
forbidden = ('is_value', 'append', '_update', '_scan_class_attrs')
for key, value in kwargs.items():
if key not in forbidden:
self._update(key, value)
return self

def __repr__(self):
"""Спрощений repr, що показує лише назви констант"""
keys = [k for k in dir(self.__class__) if not k.startswith('_')]
return f"<{type(self).__name__} Enum: {keys}>"

def is_value(self, value):
"""Перевіряє, чи існує EnumValue з таким *значенням*"""
# Ітеруємо по атрибутах класу
for key in dir(self.__class__):
attr = getattr(self.__class__, key)
if isinstance(attr, EnumValue) and attr.value == value:
return True
return False

def __call__(self, value):
"""Зворотний пошук об'єкта Enum за значенням: Color(1) -> RED"""
# Ітеруємо по атрибутах класу
for key in dir(self.__class__):
attr = getattr(self.__class__, key)
if isinstance(attr, EnumValue) and attr.value == value:
# Повертаємо сам об'єкт (RED, GREEN...), щоб працював .name
return attr
raise ValueError(_Err + str(value))

def __delattr__(self, key):
"""Забороняємо видалення елементів Enum"""
raise AttributeError("Enum members cannot be deleted")

def __len__(self):
"""Повертає кількість елементів в Enum"""
count = 0
for key in dir(self.__class__):
if not key.startswith('_') and isinstance(getattr(self.__class__, key), EnumValue):
count += 1
return count

def __iter__(self):
"""Робить Enum ітерабельним. Повертає об'єкти EnumValue."""
# Ми шукаємо всі атрибути класу, які є EnumValue
for key in dir(self.__class__):
if key.startswith('_'):
continue

attr = getattr(self.__class__, key)
if isinstance(attr, EnumValue):
# Повертаємо сам об'єкт
yield attr

# --- Приклад використання ---

if __name__ == '__main__':

# Визначення Enum (тільки статично)
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3

# Створюємо екземпляр
c = Color()

# Спроба створити з аргументами (тепер ігнорується або викликає TypeError,
# якщо перевизначити __init__ зі строгим контролем)
# try:
# c_bad = Color('BLACK')
# except TypeError as e:
# print(f"Init args blocked: {e}")

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

# Базовий доступ
print(f"RED: Name={c.RED.name}, Value={c.RED.value}")

# Твердження (assertions)
assert c.RED == 1
assert c.RED.value == 1
assert c.RED.name == 'RED'

# Зворотний пошук (тепер повертає об'єкт)
print(f"c(1) lookup object: {c(1)}, Name={c(1).name}")
assert c(1).name == 'RED'
assert c(1) == 1

# Ітерація
print("Values list:", [member.value for member in c])
print("Names list:", [member.name for member in c])
print("Members:", [member for member in c])

Loading
Loading