From 6e709ca63b6624efadcdb062411ad3cff9670634 Mon Sep 17 00:00:00 2001 From: Ihor Nehrutsa Date: Wed, 5 Mar 2025 12:24:05 +0200 Subject: [PATCH 1/5] python-stdlib\enum\enum.py: Add Enum class. Add Enum class. tools\ci.sh: Add test_enum.py to the CI. Signed-off-by: Ihor Nehrutsa --- python-stdlib/enum/enum.py | 211 ++++++++++++++++++++++++++++++++ python-stdlib/enum/manifest.py | 3 + python-stdlib/enum/test_enum.py | 91 ++++++++++++++ tools/ci.sh | 1 + 4 files changed, 306 insertions(+) create mode 100644 python-stdlib/enum/enum.py create mode 100644 python-stdlib/enum/manifest.py create mode 100644 python-stdlib/enum/test_enum.py diff --git a/python-stdlib/enum/enum.py b/python-stdlib/enum/enum.py new file mode 100644 index 000000000..9d62faaf4 --- /dev/null +++ b/python-stdlib/enum/enum.py @@ -0,0 +1,211 @@ +# enum.py + +_Err = "no such attribute: " + + +class int_value(int): + @property + def value(self) -> int: + return self + + def __call__(self) -> int: + return self + + +class str_value(str): + @property + def value(self) -> str: + return self + + def __call__(self) -> str: + return self + + +class bool_value(bool): + @property + def value(self) -> bool: + return self + + def __call__(self) -> bool: + return self + + +class float_value(float): + @property + def value(self) -> float: + return self + + def __call__(self) -> float: + return self + + +def get_class_value(value): + if type(value) is int: + return int_value(value) + elif type(value) is bool: + return bool_value(value) + elif type(value) is float: + return float_value(value) + elif type(value) is str: + return str_value(value) + else: + return value + + +def enum(**kw_args): # `**kw_args` kept backwards compatible as in the Internet examples + return Enum(kw_args) + + +class Enum(dict): + def __init__(self, arg=None): # `arg` is dict() compatible + super().__init__() + self._arg = None + if not arg is None: + self.append(arg) + self._is_enums_from_class = False + self._get_enums_from_class() + + def _update(self, key, value): + self.update({key: get_class_value(value)}) + + def append(self, arg=None, **kw_args): + if len(kw_args): + for key, value in kw_args.items(): + self._update(key, value) + if type(arg) == type(dict()): + for key, value in arg.items(): + self._update(key, value) + else: + self._arg = arg # for __str__() + return self + + def __repr__(self): + d = self.copy() + try: + d.pop("_arg") + except: + pass + return str(d) + + def __str__(self): + value = None + try: + value = self._arg + except: + pass + if not value is None: + if self.is_value(value): + self._arg = None + return value + raise ValueError(_Err + f"{value}") + return self.__qualname__ + "(" + self.__repr__() + ")" + + def is_value(self, value): + if value in self.values(): + return True + return False + + def key_from_value(self, value): + for key in self: + if self.get(key) == value: + return self.__qualname__ + "." + key + raise ValueError(_Err + f"{value}") + + def __call__(self, value): + if self.is_value(value): + return value + raise ValueError(_Err + f"{value}") + + def __getattr__(self, key): + try: + if key in self: + return self[key] + else: + raise KeyError(_Err + f"{key}") + except: + raise KeyError(_Err + f"{key}") + + def __setattr__(self, key, value): + if key == "_arg": + self[key] = value + return + try: + self[key] = get_class_value(value) + except: + raise KeyError(_Err + f"{key}") + + def __delattr__(self, key): + try: + if key in self: + del self[key] + else: + raise KeyError(_Err + f"{key}") + except: + raise KeyError(_Err + f"{key}") + + def __len__(self): + return len(tuple(self.keys())) + + def __dir__(self): + return dir(Enum) + + def _get_enums_from_class(self): + ## Class XX(Enum): + ## X1 = 1 + ## X2 = 2 + + if not self._is_enums_from_class: + keys = dir(eval(self.__qualname__)) + + def try_remove(item): + try: + keys.remove(item) + except: + pass + + for item in dir(dict): + try_remove(item) + + _list = [ + "__init__", + "__class__init__", + "__call__", + "__Errases__", + "__module__", + "__qualname__", + "__len__", + "__lt__", + "__le__", + "__eq__", + "__ne__", + "__gt__", + "__ge__", + "__dir__", + "__delattr__", + "__getattr__", + "__setattr__", + "__str__", + "__repr__", + "_get_enums_from_class", + "_arg", + "_update", + "is_value", + "key_from_value", + "append", + ] + for item in _list: + try_remove(item) + module = "" + if self.__module__ != "__main__": + module = self.__module__ + "." + for key in keys: + try: + value = eval(f"{module}{self.__qualname__}.{key}") + except: + value = eval(f"{self.__qualname__}.{key}") + self._update(key, value) + keys.clear() + del keys + self._is_enums_from_class = True # 1 !!! + self.pop("_is_enums_from_class") # 2 !!! + return self diff --git a/python-stdlib/enum/manifest.py b/python-stdlib/enum/manifest.py new file mode 100644 index 000000000..6b6b821ca --- /dev/null +++ b/python-stdlib/enum/manifest.py @@ -0,0 +1,3 @@ +metadata(version="1.0.0") + +module("enum.py") diff --git a/python-stdlib/enum/test_enum.py b/python-stdlib/enum/test_enum.py new file mode 100644 index 000000000..1ad848f0a --- /dev/null +++ b/python-stdlib/enum/test_enum.py @@ -0,0 +1,91 @@ +# enum_test.py + +from enum import Enum, enum + + +class Direction(Enum): + CW = "CW" + CCW = "CCW" + + +class State(Direction): + Stop = 1 + Run = 2 + Ready = 3 + Disabled = False + Enabled = True + + +state = Enum() +print(state) +state = Direction() +print(state) +state = State() +print(state) +state = State({"X": 1.0, "Y": 2.0}) +print(state) +state.Idle = 10 +state.Triggered = 20 +state.Lockout = 30 +print(state) + +print("Direction(Direction.CCW):", Direction(Direction.CCW)) +print("Direction('CW'):", Direction("CW")) +print("state(10):", state(10)) + +print("state('CW'):", state("CW")) +print("type(state('CW')):", type(state("CW"))) + +print("state.key_from_value(20):", state.key_from_value(20)) +print("len(state):", len(state)) + +print("state.Idle:", state.Idle) +print("type(state.Idle):", type(state.Idle)) + +current_state = state.Idle +print("current_state:", current_state) +if current_state == state.Idle: + print(" Idle state") +if current_state != state.Triggered: + print(" Not a triggered state") + current_state = state.Idle +print("current_state:", current_state) +print("state.key_from_value(current_state):", state.key_from_value(current_state)) + +state2 = eval(str(state)) +print(state2) +print("state == state2:", state == state2) + +del state.Triggered +print(state) +print("state == state2:", state == state2) + +print("state.keys():", state.keys()) +print("state.values():", state.values()) +print("state.items():", state.items()) + +try: + del state.stop +except Exception as e: + print("Exception:", e) + +assert current_state == state.Idle +assert current_state != state.Disabled +assert state.Idle != state.Disabled +print( + "State(State.Ready):", + State(State.Ready), + "type(State.Ready):", + type(State(State.Ready)), + "type(State.Ready):", + type(State.Ready), +) +assert int(str(State(State.Ready))) == State.Ready +assert int(str(State(State.Ready))) != State.Disabled +print("will raise exception") +try: + del state.Triggered +except Exception as e: + print("Exception:", e) + +print("OK") diff --git a/tools/ci.sh b/tools/ci.sh index abe83b563..6ff914ed7 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -54,6 +54,7 @@ function ci_package_tests_run { python-stdlib/base64/test_base64.py \ python-stdlib/binascii/test_binascii.py \ python-stdlib/collections-defaultdict/test_defaultdict.py \ + python-stdlib/enum/test_enum.py \ python-stdlib/functools/test_partial.py \ python-stdlib/functools/test_reduce.py \ python-stdlib/heapq/test_heapq.py \ From 4ed3df9e69f4f589ab00e651faeb303e8f3c3d5a Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Mon, 13 Apr 2026 14:19:54 +0300 Subject: [PATCH 2/5] python-stdlib\enum\enum.py: Rewrite Enum with Gemini. Rewrite Enum with Gemini. Add enum.md. Signed-off-by: Ihor Nehrutsa --- python-stdlib/enum/enum.md | 120 ++++++++++ python-stdlib/enum/enum.py | 398 ++++++++++++++++++-------------- python-stdlib/enum/manifest.py | 2 +- python-stdlib/enum/test_enum.py | 260 +++++++++++++-------- 4 files changed, 518 insertions(+), 262 deletions(-) create mode 100644 python-stdlib/enum/enum.md diff --git a/python-stdlib/enum/enum.md b/python-stdlib/enum/enum.md new file mode 100644 index 000000000..1133abf9f --- /dev/null +++ b/python-stdlib/enum/enum.md @@ -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. diff --git a/python-stdlib/enum/enum.py b/python-stdlib/enum/enum.py index 9d62faaf4..57e825057 100644 --- a/python-stdlib/enum/enum.py +++ b/python-stdlib/enum/enum.py @@ -1,55 +1,142 @@ -# enum.py - _Err = "no such attribute: " -class int_value(int): +class ValueWrapper: + """Universal wrapper for accessing values via .value or calling ()""" + __slots__ = ('_v', ) + + def __init__(self, v): + self._v = v + @property - def value(self) -> int: - return self + def value(self): + return self._v - def __call__(self) -> int: - return self + def __call__(self): + return self._v + def __repr__(self): + return repr(self._v) -class str_value(str): - @property - def value(self) -> str: - return self + def __str__(self): + return str(self._v) - def __call__(self) -> str: - return self + # Type conversion + def __int__(self): + return int(self._v) + def __float__(self): + return float(self._v) -class bool_value(bool): - @property - def value(self) -> bool: - return self + def __index__(self): + return int(self._v) - def __call__(self) -> bool: - return self + def __bool__(self): + return bool(self._v) + # Helper function to extract the raw value + def _get_v(self, other): + return other._v if isinstance(other, ValueWrapper) else other -class float_value(float): - @property - def value(self) -> float: - return self + # Arithmetic and Bitwise operations (Forward) + def __add__(self, other): + return self._v + self._get_v(other) - def __call__(self) -> float: - return self + def __sub__(self, other): + return self._v - self._get_v(other) + + def __mul__(self, other): + return self._v * self._get_v(other) + + def __truediv__(self, other): + return self._v / self._get_v(other) + + def __floordiv__(self, other): + return self._v // self._get_v(other) + + def __mod__(self, other): + return self._v % self._get_v(other) + + def __pow__(self, other): + return self._v**self._get_v(other) + + def __and__(self, other): + return self._v & self._get_v(other) + + def __or__(self, other): + return self._v | self._get_v(other) + + def __xor__(self, other): + return self._v ^ self._get_v(other) + + def __lshift__(self, other): + return self._v << self._get_v(other) + + def __rshift__(self, other): + return self._v >> self._get_v(other) + + # Arithmetic and Bitwise operations (Reflected) + def __radd__(self, other): + return self._get_v(other) + self._v + + def __rsub__(self, other): + return self._get_v(other) - self._v + + def __rmul__(self, other): + return self._get_v(other) * self._v + + def __rtruediv__(self, other): + return self._get_v(other) / self._v + + def __rfloordiv__(self, other): + return self._get_v(other) // self._v + + def __rand__(self, other): + return self._get_v(other) & self._v + + def __ror__(self, other): + return self._get_v(other) | self._v + def __rxor__(self, other): + return self._get_v(other) ^ self._v -def get_class_value(value): - if type(value) is int: - return int_value(value) - elif type(value) is bool: - return bool_value(value) - elif type(value) is float: - return float_value(value) - elif type(value) is str: - return str_value(value) - else: - return value + def __rlshift__(self, other): + return self._get_v(other) << self._v + + def __rrshift__(self, other): + return self._get_v(other) >> self._v + + # Unary operators + def __neg__(self): + return -self._v + + def __pos__(self): + return +self._v + + def __abs__(self): + return abs(self._v) + + def __invert__(self): + return ~self._v + + # Comparison + def __eq__(self, other): + return self._v == self._get_v(other) + + def __lt__(self, other): + return self._v < self._get_v(other) + + def __le__(self, other): + return self._v <= self._get_v(other) + + def __gt__(self, other): + return self._v > self._get_v(other) + + def __ge__(self, other): + return self._v >= self._get_v(other) + + def __ne__(self, other): + return self._v != self._get_v(other) def enum(**kw_args): # `**kw_args` kept backwards compatible as in the Internet examples @@ -57,155 +144,126 @@ def enum(**kw_args): # `**kw_args` kept backwards compatible as in the Internet class Enum(dict): - def __init__(self, arg=None): # `arg` is dict() compatible + def __init__(self, arg=None, **kwargs): super().__init__() - self._arg = None - if not arg is None: - self.append(arg) - self._is_enums_from_class = False - self._get_enums_from_class() - - def _update(self, key, value): - self.update({key: get_class_value(value)}) - - def append(self, arg=None, **kw_args): - if len(kw_args): - for key, value in kw_args.items(): - self._update(key, value) - if type(arg) == type(dict()): - for key, value in arg.items(): - self._update(key, value) + # Use __dict__ directly for internal flags + # to avoid cluttering the dictionary keyspace + super().__setattr__('_is_loading', True) + + # 1. Collect class-level attributes (constants) + self._scan_class_attrs() + # 2. Add arguments from the constructor + if arg: self.append(arg) + if kwargs: self.append(kwargs) + + super().__setattr__('_is_loading', False) + + def _scan_class_attrs(self): + cls = self.__class__ + # Define attributes to skip (internal or explicitly requested) + skipped = getattr(cls, '__skipped__', ()) + + for key in dir(cls): + # Skip internal names, methods, and excluded attributes + if key.startswith('_') or key in ('append', 'is_value', 'key_from_value'): + continue + if key in skipped: + continue + + val = getattr(cls, key) + # Only wrap non-callable attributes (constants) + if not callable(val): + self[key] = ValueWrapper(val) + + def append(self, arg=None, **kwargs): + if isinstance(arg, dict): + for k, v in arg.items(): + self[k] = ValueWrapper(v) else: self._arg = arg # for __str__() + if kwargs: + for k, v in kwargs.items(): + self[k] = ValueWrapper(v) return self - def __repr__(self): - d = self.copy() - try: - d.pop("_arg") - except: - pass - return str(d) + def __getattr__(self, key): + if key in self: + return self[key] + raise AttributeError(_Err + key) - def __str__(self): - value = None - try: - value = self._arg - except: - pass - if not value is None: - if self.is_value(value): - self._arg = None - return value - raise ValueError(_Err + f"{value}") - return self.__qualname__ + "(" + self.__repr__() + ")" + def __setattr__(self, key, value): + if self._is_loading or key.startswith('_'): + # Record directly into memory as a regular variable + super().__setattr__(key, value) + else: + # Handle as an Enum element (wrap in ValueWrapper) + self[key] = ValueWrapper(value) def is_value(self, value): - if value in self.values(): - return True - return False + return any(v._v == value for v in self.values()) def key_from_value(self, value): - for key in self: - if self.get(key) == value: - return self.__qualname__ + "." + key - raise ValueError(_Err + f"{value}") + for k, v in self.items(): + if v._v == value: return f"{self.__class__.__name__}.{k}" + raise ValueError(_Err + str(value)) + + def __dir__(self): + # 1. Dictionary keys (your data: X1, X2, etc.) + data_keys = list(self.keys()) + # 2. Class attributes (your methods: append, is_value, etc.) + class_stuff = list(dir(self.__class__)) + # 3. Parent class attributes (for completeness) + parent_attrs = list(dir(super())) + # Combine and remove duplicates using set for clarity + #return list(set(data_keys + class_stuff + parent_attrs)) + return list(set(data_keys + class_stuff)) def __call__(self, value): if self.is_value(value): return value raise ValueError(_Err + f"{value}") - def __getattr__(self, key): - try: - if key in self: - return self[key] - else: - raise KeyError(_Err + f"{key}") - except: - raise KeyError(_Err + f"{key}") - def __setattr__(self, key, value): - if key == "_arg": - self[key] = value - return - try: - self[key] = get_class_value(value) - except: - raise KeyError(_Err + f"{key}") - - def __delattr__(self, key): - try: - if key in self: - del self[key] - else: - raise KeyError(_Err + f"{key}") - except: - raise KeyError(_Err + f"{key}") - - def __len__(self): - return len(tuple(self.keys())) +if __name__ == "__main__": + # --- Usage Examples --- - def __dir__(self): - return dir(Enum) - - def _get_enums_from_class(self): - ## Class XX(Enum): - ## X1 = 1 - ## X2 = 2 - - if not self._is_enums_from_class: - keys = dir(eval(self.__qualname__)) - - def try_remove(item): - try: - keys.remove(item) - except: - pass - - for item in dir(dict): - try_remove(item) - - _list = [ - "__init__", - "__class__init__", - "__call__", - "__Errases__", - "__module__", - "__qualname__", - "__len__", - "__lt__", - "__le__", - "__eq__", - "__ne__", - "__gt__", - "__ge__", - "__dir__", - "__delattr__", - "__getattr__", - "__setattr__", - "__str__", - "__repr__", - "_get_enums_from_class", - "_arg", - "_update", - "is_value", - "key_from_value", - "append", - ] - for item in _list: - try_remove(item) - module = "" - if self.__module__ != "__main__": - module = self.__module__ + "." - for key in keys: - try: - value = eval(f"{module}{self.__qualname__}.{key}") - except: - value = eval(f"{self.__qualname__}.{key}") - self._update(key, value) - keys.clear() - del keys - self._is_enums_from_class = True # 1 !!! - self.pop("_is_enums_from_class") # 2 !!! - return self + # 1. GPIO and Hardware Configuration + class Pins(Enum): + LED = 2 + BUTTON = 4 + __skipped__ = ('RESERVED_PIN', ) + RESERVED_PIN = 0 + + pins = Pins(SDA=21, SCL=22) + print(f"I2C SDA Pin: {pins.SDA}") + print(f"Is 21 a valid pin? {pins.is_value(21)}") + + # 2. Math and Logic + brightness = Enum(MIN=0, STEP=25, MAX=255) + print(f"Next level: {brightness.MIN + brightness.STEP // 2}") + print(f"Calculation: {brightness.MIN + 2 * brightness.STEP}") + + # Direct arithmetic without .value + print(f"Complex math: {100 + brightness.STEP}") + + # 3. State Machine (Dynamic Expansion) + status = Enum(IDLE=0, CONNECTING=1) + status.append(CONNECTED=2, ERROR=3) + status.DISCONNECTING = 4 + + for name, val in status.items(): + print(f"Status {name} has code {val}") + + # 4. Working with different types + commands = Enum(START="CMD_START", STOP="CMD_STOP", REBOOT_CODE=0xDEADBEEF, IS_ACTIVE=True) + + if commands.IS_ACTIVE: + print(f"Running command: {commands.START}") + + # 5. Class Config and dir() + class WebConfig(Enum): + PORT = 80 + TIMEOUT = 5.0 + + config = WebConfig({'IP': '192.168.1.1'}) + print(f"Available keys in config: {list(config.keys())}") \ No newline at end of file diff --git a/python-stdlib/enum/manifest.py b/python-stdlib/enum/manifest.py index 6b6b821ca..050ccaea0 100644 --- a/python-stdlib/enum/manifest.py +++ b/python-stdlib/enum/manifest.py @@ -1,3 +1,3 @@ -metadata(version="1.0.0") +metadata(version="1.1.0") module("enum.py") diff --git a/python-stdlib/enum/test_enum.py b/python-stdlib/enum/test_enum.py index 1ad848f0a..b927fb249 100644 --- a/python-stdlib/enum/test_enum.py +++ b/python-stdlib/enum/test_enum.py @@ -1,91 +1,169 @@ -# enum_test.py - -from enum import Enum, enum - - -class Direction(Enum): - CW = "CW" - CCW = "CCW" - - -class State(Direction): - Stop = 1 - Run = 2 - Ready = 3 - Disabled = False - Enabled = True - - -state = Enum() -print(state) -state = Direction() -print(state) -state = State() -print(state) -state = State({"X": 1.0, "Y": 2.0}) -print(state) -state.Idle = 10 -state.Triggered = 20 -state.Lockout = 30 -print(state) - -print("Direction(Direction.CCW):", Direction(Direction.CCW)) -print("Direction('CW'):", Direction("CW")) -print("state(10):", state(10)) - -print("state('CW'):", state("CW")) -print("type(state('CW')):", type(state("CW"))) - -print("state.key_from_value(20):", state.key_from_value(20)) -print("len(state):", len(state)) - -print("state.Idle:", state.Idle) -print("type(state.Idle):", type(state.Idle)) - -current_state = state.Idle -print("current_state:", current_state) -if current_state == state.Idle: - print(" Idle state") -if current_state != state.Triggered: - print(" Not a triggered state") - current_state = state.Idle -print("current_state:", current_state) -print("state.key_from_value(current_state):", state.key_from_value(current_state)) - -state2 = eval(str(state)) -print(state2) -print("state == state2:", state == state2) - -del state.Triggered -print(state) -print("state == state2:", state == state2) - -print("state.keys():", state.keys()) -print("state.values():", state.values()) -print("state.items():", state.items()) - -try: - del state.stop -except Exception as e: - print("Exception:", e) - -assert current_state == state.Idle -assert current_state != state.Disabled -assert state.Idle != state.Disabled -print( - "State(State.Ready):", - State(State.Ready), - "type(State.Ready):", - type(State(State.Ready)), - "type(State.Ready):", - type(State.Ready), -) -assert int(str(State(State.Ready))) == State.Ready -assert int(str(State(State.Ready))) != State.Disabled -print("will raise exception") -try: - del state.Triggered -except Exception as e: - print("Exception:", e) - -print("OK") +import unittest +from enum import Enum, ValueWrapper + + +class TestEnum(unittest.TestCase): + def test_class_initialization(self): + """Check Enum creation via class inheritance""" + class Pins(Enum): + TX = 1 + RX = 3 + + pins = Pins() + self.assertEqual(int(pins.TX), 1) + self.assertEqual(int(pins.RX), 3) + self.assertIn('TX', pins) + self.assertIn('RX', pins) + + def test_dict_initialization(self): + """Check Enum creation by passing a dictionary to the constructor""" + e = Enum({'A': 10, 'B': 'test'}, C='C') + self.assertEqual(e.A.value, 10) + self.assertEqual(e.B(), 'test') + self.assertEqual(e.C, 'C') + + def test_value_wrapper_behaviors(self): + """Check ValueWrapper properties (calling, types, comparison)""" + v = ValueWrapper(100) + self.assertEqual(v.value, 100) # .value + self.assertEqual(v(), 100) # __call__ + self.assertEqual(int(v), 100) # __int__ + self.assertTrue(v == 100) # __eq__ + self.assertEqual(str(v), "100") # __str__ + + def test_append_and_dynamic_attrs(self): + """Check dynamic addition of values""" + e = Enum() + e.append(C=30) + e.append({'D': 40}, E=50) + e.F = 60 + + self.assertEqual(int(e.C), 30) + self.assertEqual(int(e.D), 40) + self.assertEqual(int(e.E), 50) + self.assertEqual(int(e.F), 60) + self.assertIsInstance(e.E, ValueWrapper) + self.assertIsInstance(e.F, ValueWrapper) + + def test_getattr_error(self): + """Check that an error is raised when an attribute is missing""" + e = Enum(A=1) + with self.assertRaises(AttributeError): + _ = e.NON_EXISTENT + + def test_is_value_and_key_lookup(self): + """Check key lookup by value and value validation""" + class Status(Enum): + IDLE = 0 + BUSY = 1 + + s = Status() + self.assertTrue(s.is_value(0)) + self.assertTrue(s.is_value(1)) + self.assertFalse(s.is_value(99)) + self.assertEqual(s.key_from_value(1), "Status.X2" if "X2" in dir(s) else "Status.BUSY") + self.assertEqual(s.key_from_value(1), "Status.BUSY") + + def test_is_loading_protection(self): + """Check that _is_loading does not end up in dictionary keys""" + e = Enum(A=1) + self.assertNotIn('_is_loading', e.keys()) + # Check that the flag is False after initialization + self.assertFalse(e._is_loading) + + def test_dir_visibility(self): + """Check for the presence of keys and methods in dir()""" + e = Enum(DATA=123) + directory = dir(e) + self.assertIn('DATA', directory) # Dynamic data + self.assertIn('append', directory) # Enum class method + self.assertIn('keys', directory) # Base dict method + + def test_math_and_indexing(self): + """Check usage in mathematics and as an index""" + e = Enum(VAL=10) + # Mathematics + self.assertEqual(e.VAL + 5, 15) + # Usage as an index (e.g., in a list) + ls = [0] * 20 + ls[e.VAL] = 1 + self.assertEqual(ls[10], 1) + + def test_various_types(self): + """Check operation with various data types""" + e = Enum(STR="test", FLT=1.5, BL=True) + self.assertEqual(str(e.STR), "test") + self.assertEqual(float(e.FLT), 1.5) + self.assertTrue(e.BL) + + def test_skipped_attributes(self): + """Check ignoring attributes via __skipped__""" + class MyEnum(Enum): + __skipped__ = ('SECRET', ) + PUBLIC = 1 + SECRET = 2 + + e = MyEnum() + self.assertIn('PUBLIC', e) + self.assertNotIn('SECRET', e) + + def test_post_loading_setattr(self): + """Check setting attributes after initialization""" + e = Enum(A=1) + # Regular attribute (starts with _) + e._internal = 100 + self.assertEqual(e._internal, 100) + self.assertNotIn('_internal', e.keys()) # Should not be in data + + # New Enum element + e.B = 2 + self.assertIsInstance(e.B, ValueWrapper) + self.assertIn('B', e.keys()) + + def test_key_from_value_not_found(self): + """Check for an error when searching for a non-existent value""" + e = Enum(A=1) + with self.assertRaises(ValueError): + e.key_from_value(999) + + def test_math_division(self): + """Check floor division and true division""" + e = Enum(STEP=25) + # Floor division + self.assertEqual(e.STEP // 2, 12) + # True division + self.assertEqual(e.STEP / 2, 12.5) + + def test_full_arithmetic(self): + """Check all new arithmetic operations""" + v = ValueWrapper(10) + self.assertEqual(v + 5, 15) + self.assertEqual(20 - v, 10) # Check __rsub__ + self.assertEqual(v * 2, 20) + self.assertEqual(30 // v, 3) # Check __rfloordiv__ + self.assertEqual(v % 3, 1) + self.assertTrue(v > 5) # Check comparison + + def test_bitmask_operations(self): + """Test bitwise operations (important for registers)""" + flags = Enum(BIT_0=0x01, BIT_1=0x02) + + # Check OR and AND + combined = flags.BIT_0 | flags.BIT_1 + self.assertEqual(combined, 0x03) + self.assertEqual(combined & flags.BIT_1, 0x02) + + # Check shifts + self.assertEqual(flags.BIT_0 << 2, 4) + self.assertEqual(8 >> flags.BIT_0, 4) # Check __rrshift__ + + def test_unary_operations(self): + """Test unary operators""" + e = Enum(VAL=10, NEG_VAL=-5) + self.assertEqual(-e.VAL, -10) + self.assertEqual(abs(e.NEG_VAL), 5) + self.assertEqual(~e.VAL, -11) # Bitwise NOT + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From 0aaf8418a0289bcf8b5bdecfb3456f3ccc802b3e Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Thu, 16 Apr 2026 18:26:23 +0300 Subject: [PATCH 3/5] Save version 1.0.0 Signed-off-by: Ihor Nehrutsa --- python-stdlib/enum/enum_0.py | 212 ++++++++++++++++++++++++++++++ python-stdlib/enum/test_enum_0.py | 92 +++++++++++++ 2 files changed, 304 insertions(+) create mode 100644 python-stdlib/enum/enum_0.py create mode 100644 python-stdlib/enum/test_enum_0.py diff --git a/python-stdlib/enum/enum_0.py b/python-stdlib/enum/enum_0.py new file mode 100644 index 000000000..234498ec5 --- /dev/null +++ b/python-stdlib/enum/enum_0.py @@ -0,0 +1,212 @@ +# enum.py +# version="1.0.0" + +_Err = "no such attribute: " + + +class int_value(int): + @property + def value(self) -> int: + return self + + def __call__(self) -> int: + return self + + +class str_value(str): + @property + def value(self) -> str: + return self + + def __call__(self) -> str: + return self + + +class bool_value(bool): + @property + def value(self) -> bool: + return self + + def __call__(self) -> bool: + return self + + +class float_value(float): + @property + def value(self) -> float: + return self + + def __call__(self) -> float: + return self + + +def get_class_value(value): + if type(value) is int: + return int_value(value) + elif type(value) is bool: + return bool_value(value) + elif type(value) is float: + return float_value(value) + elif type(value) is str: + return str_value(value) + else: + return value + + +def enum(**kw_args): # `**kw_args` kept backwards compatible as in the Internet examples + return Enum(kw_args) + + +class Enum(dict): + def __init__(self, arg=None): # `arg` is dict() compatible + super().__init__() + self._arg = None + if not arg is None: + self.append(arg) + self._is_enums_from_class = False + self._get_enums_from_class() + + def _update(self, key, value): + self.update({key: get_class_value(value)}) + + def append(self, arg=None, **kw_args): + if len(kw_args): + for key, value in kw_args.items(): + self._update(key, value) + if type(arg) == type(dict()): + for key, value in arg.items(): + self._update(key, value) + else: + self._arg = arg # for __str__() + return self + + def __repr__(self): + d = self.copy() + try: + d.pop("_arg") + except: + pass + return str(d) + + def __str__(self): + value = None + try: + value = self._arg + except: + pass + if not value is None: + if self.is_value(value): + self._arg = None + return value + raise ValueError(_Err + f"{value}") + return self.__qualname__ + "(" + self.__repr__() + ")" + + def is_value(self, value): + if value in self.values(): + return True + return False + + def key_from_value(self, value): + for key in self: + if self.get(key) == value: + return self.__qualname__ + "." + key + raise ValueError(_Err + f"{value}") + + def __call__(self, value): + if self.is_value(value): + return value + raise ValueError(_Err + f"{value}") + + def __getattr__(self, key): + try: + if key in self: + return self[key] + else: + raise KeyError(_Err + f"{key}") + except: + raise KeyError(_Err + f"{key}") + + def __setattr__(self, key, value): + if key == "_arg": + self[key] = value + return + try: + self[key] = get_class_value(value) + except: + raise KeyError(_Err + f"{key}") + + def __delattr__(self, key): + try: + if key in self: + del self[key] + else: + raise KeyError(_Err + f"{key}") + except: + raise KeyError(_Err + f"{key}") + + def __len__(self): + return len(tuple(self.keys())) + + def __dir__(self): + return dir(Enum) + + def _get_enums_from_class(self): + ## Class XX(Enum): + ## X1 = 1 + ## X2 = 2 + + if not self._is_enums_from_class: + keys = dir(eval(self.__qualname__)) + + def try_remove(item): + try: + keys.remove(item) + except: + pass + + for item in dir(dict): + try_remove(item) + + _list = [ + "__init__", + "__class__init__", + "__call__", + "__Errases__", + "__module__", + "__qualname__", + "__len__", + "__lt__", + "__le__", + "__eq__", + "__ne__", + "__gt__", + "__ge__", + "__dir__", + "__delattr__", + "__getattr__", + "__setattr__", + "__str__", + "__repr__", + "_get_enums_from_class", + "_arg", + "_update", + "is_value", + "key_from_value", + "append", + ] + for item in _list: + try_remove(item) + module = "" + if self.__module__ != "__main__": + module = self.__module__ + "." + for key in keys: + try: + value = eval(f"{module}{self.__qualname__}.{key}") + except: + value = eval(f"{self.__qualname__}.{key}") + self._update(key, value) + keys.clear() + del keys + self._is_enums_from_class = True # 1 !!! + self.pop("_is_enums_from_class") # 2 !!! + return self \ No newline at end of file diff --git a/python-stdlib/enum/test_enum_0.py b/python-stdlib/enum/test_enum_0.py new file mode 100644 index 000000000..4a7363d6f --- /dev/null +++ b/python-stdlib/enum/test_enum_0.py @@ -0,0 +1,92 @@ +# test_enum.py +# version="1.0.0" + +from enum import Enum, enum + + +class Direction(Enum): + CW = "CW" + CCW = "CCW" + + +class State(Direction): + Stop = 1 + Run = 2 + Ready = 3 + Disabled = False + Enabled = True + + +state = Enum() +print(state) +state = Direction() +print(state) +state = State() +print(state) +state = State({"X": 1.0, "Y": 2.0}) +print(state) +state.Idle = 10 +state.Triggered = 20 +state.Lockout = 30 +print(state) + +print("Direction(Direction.CCW):", Direction(Direction.CCW)) +print("Direction('CW'):", Direction("CW")) +print("state(10):", state(10)) + +print("state('CW'):", state("CW")) +print("type(state('CW')):", type(state("CW"))) + +print("state.key_from_value(20):", state.key_from_value(20)) +print("len(state):", len(state)) + +print("state.Idle:", state.Idle) +print("type(state.Idle):", type(state.Idle)) + +current_state = state.Idle +print("current_state:", current_state) +if current_state == state.Idle: + print(" Idle state") +if current_state != state.Triggered: + print(" Not a triggered state") + current_state = state.Idle +print("current_state:", current_state) +print("state.key_from_value(current_state):", state.key_from_value(current_state)) + +state2 = eval(str(state)) +print(state2) +print("state == state2:", state == state2) + +del state.Triggered +print(state) +print("state == state2:", state == state2) + +print("state.keys():", state.keys()) +print("state.values():", state.values()) +print("state.items():", state.items()) + +try: + del state.stop +except Exception as e: + print("Exception:", e) + +assert current_state == state.Idle +assert current_state != state.Disabled +assert state.Idle != state.Disabled +print( + "State(State.Ready):", + State(State.Ready), + "type(State.Ready):", + type(State(State.Ready)), + "type(State.Ready):", + type(State.Ready), +) +assert int(str(State(State.Ready))) == State.Ready +assert int(str(State(State.Ready))) != State.Disabled +print("will raise exception") +try: + del state.Triggered +except Exception as e: + print("Exception:", e) + +print("OK") From 559ff60ef3ffcca1fbc4846091bb409547494a90 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Thu, 16 Apr 2026 18:27:11 +0300 Subject: [PATCH 4/5] Save version 1.1.0 Signed-off-by: Ihor Nehrutsa --- python-stdlib/enum/enum_1.py | 272 ++++++++++++++++++++++++++++++ python-stdlib/enum/test_enum_1.py | 172 +++++++++++++++++++ 2 files changed, 444 insertions(+) create mode 100644 python-stdlib/enum/enum_1.py create mode 100644 python-stdlib/enum/test_enum_1.py diff --git a/python-stdlib/enum/enum_1.py b/python-stdlib/enum/enum_1.py new file mode 100644 index 000000000..c7baf2629 --- /dev/null +++ b/python-stdlib/enum/enum_1.py @@ -0,0 +1,272 @@ +# enum.py +# version="1.1.0" + +_Err = "no such attribute: " + + +class ValueWrapper: + """Universal wrapper for accessing values via .value or calling ()""" + __slots__ = ('_v', ) + + def __init__(self, v): + self._v = v + + @property + def value(self): + return self._v + + def __call__(self): + return self._v + + def __repr__(self): + return repr(self._v) + + def __str__(self): + return str(self._v) + + # Type conversion + def __int__(self): + return int(self._v) + + def __float__(self): + return float(self._v) + + def __index__(self): + return int(self._v) + + def __bool__(self): + return bool(self._v) + + # Helper function to extract the raw value + def _get_v(self, other): + return other._v if isinstance(other, ValueWrapper) else other + + # Arithmetic and Bitwise operations (Forward) + def __add__(self, other): + return self._v + self._get_v(other) + + def __sub__(self, other): + return self._v - self._get_v(other) + + def __mul__(self, other): + return self._v * self._get_v(other) + + def __truediv__(self, other): + return self._v / self._get_v(other) + + def __floordiv__(self, other): + return self._v // self._get_v(other) + + def __mod__(self, other): + return self._v % self._get_v(other) + + def __pow__(self, other): + return self._v**self._get_v(other) + + def __and__(self, other): + return self._v & self._get_v(other) + + def __or__(self, other): + return self._v | self._get_v(other) + + def __xor__(self, other): + return self._v ^ self._get_v(other) + + def __lshift__(self, other): + return self._v << self._get_v(other) + + def __rshift__(self, other): + return self._v >> self._get_v(other) + + # Arithmetic and Bitwise operations (Reflected) + def __radd__(self, other): + return self._get_v(other) + self._v + + def __rsub__(self, other): + return self._get_v(other) - self._v + + def __rmul__(self, other): + return self._get_v(other) * self._v + + def __rtruediv__(self, other): + return self._get_v(other) / self._v + + def __rfloordiv__(self, other): + return self._get_v(other) // self._v + + def __rand__(self, other): + return self._get_v(other) & self._v + + def __ror__(self, other): + return self._get_v(other) | self._v + + def __rxor__(self, other): + return self._get_v(other) ^ self._v + + def __rlshift__(self, other): + return self._get_v(other) << self._v + + def __rrshift__(self, other): + return self._get_v(other) >> self._v + + # Unary operators + def __neg__(self): + return -self._v + + def __pos__(self): + return +self._v + + def __abs__(self): + return abs(self._v) + + def __invert__(self): + return ~self._v + + # Comparison + def __eq__(self, other): + return self._v == self._get_v(other) + + def __lt__(self, other): + return self._v < self._get_v(other) + + def __le__(self, other): + return self._v <= self._get_v(other) + + def __gt__(self, other): + return self._v > self._get_v(other) + + def __ge__(self, other): + return self._v >= self._get_v(other) + + def __ne__(self, other): + return self._v != self._get_v(other) + + +def enum(**kw_args): # `**kw_args` kept backwards compatible as in the Internet examples + return Enum(kw_args) + + +class Enum(dict): + def __init__(self, arg=None, **kwargs): + super().__init__() + # Use __dict__ directly for internal flags + # to avoid cluttering the dictionary keyspace + super().__setattr__('_is_loading', True) + + # 1. Collect class-level attributes (constants) + self._scan_class_attrs() + # 2. Add arguments from the constructor + if arg: self.append(arg) + if kwargs: self.append(kwargs) + + super().__setattr__('_is_loading', False) + + def _scan_class_attrs(self): + cls = self.__class__ + # Define attributes to skip (internal or explicitly requested) + skipped = getattr(cls, '__skipped__', ()) + + for key in dir(cls): + # Skip internal names, methods, and excluded attributes + if key.startswith('_') or key in ('append', 'is_value', 'key_from_value'): + continue + if key in skipped: + continue + + val = getattr(cls, key) + # Only wrap non-callable attributes (constants) + if not callable(val): + self[key] = ValueWrapper(val) + + def append(self, arg=None, **kwargs): + if isinstance(arg, dict): + for k, v in arg.items(): + self[k] = ValueWrapper(v) + else: + self._arg = arg # for __str__() + if kwargs: + for k, v in kwargs.items(): + self[k] = ValueWrapper(v) + return self + + def __getattr__(self, key): + if key in self: + return self[key] + raise AttributeError(_Err + key) + + def __setattr__(self, key, value): + if self._is_loading or key.startswith('_'): + # Record directly into memory as a regular variable + super().__setattr__(key, value) + else: + # Handle as an Enum element (wrap in ValueWrapper) + self[key] = ValueWrapper(value) + + def is_value(self, value): + return any(v._v == value for v in self.values()) + + def key_from_value(self, value): + for k, v in self.items(): + if v._v == value: return f"{self.__class__.__name__}.{k}" + raise ValueError(_Err + str(value)) + + def __dir__(self): + # 1. Dictionary keys (your data: X1, X2, etc.) + data_keys = list(self.keys()) + # 2. Class attributes (your methods: append, is_value, etc.) + class_stuff = list(dir(self.__class__)) + # 3. Parent class attributes (for completeness) + parent_attrs = list(dir(super())) + # Combine and remove duplicates using set for clarity + #return list(set(data_keys + class_stuff + parent_attrs)) + return list(set(data_keys + class_stuff)) + + def __call__(self, value): + if self.is_value(value): + return value + raise ValueError(_Err + f"{value}") + + +if __name__ == "__main__": + # --- Usage Examples --- + + # 1. GPIO and Hardware Configuration + class Pins(Enum): + LED = 2 + BUTTON = 4 + __skipped__ = ('RESERVED_PIN', ) + RESERVED_PIN = 0 + + pins = Pins(SDA=21, SCL=22) + print(f"I2C SDA Pin: {pins.SDA}") + print(f"Is 21 a valid pin? {pins.is_value(21)}") + + # 2. Math and Logic + brightness = Enum(MIN=0, STEP=25, MAX=255) + print(f"Next level: {brightness.MIN + brightness.STEP // 2}") + print(f"Calculation: {brightness.MIN + 2 * brightness.STEP}") + + # Direct arithmetic without .value + print(f"Complex math: {100 + brightness.STEP}") + + # 3. State Machine (Dynamic Expansion) + status = Enum(IDLE=0, CONNECTING=1) + status.append(CONNECTED=2, ERROR=3) + status.DISCONNECTING = 4 + + for name, val in status.items(): + print(f"Status {name} has code {val}") + + # 4. Working with different types + commands = Enum(START="CMD_START", STOP="CMD_STOP", REBOOT_CODE=0xDEADBEEF, IS_ACTIVE=True) + + if commands.IS_ACTIVE: + print(f"Running command: {commands.START}") + + # 5. Class Config and dir() + class WebConfig(Enum): + PORT = 80 + TIMEOUT = 5.0 + + config = WebConfig({'IP': '192.168.1.1'}) + print(f"Available keys in config: {list(config.keys())}") diff --git a/python-stdlib/enum/test_enum_1.py b/python-stdlib/enum/test_enum_1.py new file mode 100644 index 000000000..e181f1241 --- /dev/null +++ b/python-stdlib/enum/test_enum_1.py @@ -0,0 +1,172 @@ +# test_enum.py +# version="1.1.0" + +import unittest +from enum import Enum, ValueWrapper + + +class TestEnum(unittest.TestCase): + def test_class_initialization(self): + """Check Enum creation via class inheritance""" + class Pins(Enum): + TX = 1 + RX = 3 + + pins = Pins() + self.assertEqual(int(pins.TX), 1) + self.assertEqual(int(pins.RX), 3) + self.assertIn('TX', pins) + self.assertIn('RX', pins) + + def test_dict_initialization(self): + """Check Enum creation by passing a dictionary to the constructor""" + e = Enum({'A': 10, 'B': 'test'}, C='C') + self.assertEqual(e.A.value, 10) + self.assertEqual(e.B(), 'test') + self.assertEqual(e.C, 'C') + + def test_value_wrapper_behaviors(self): + """Check ValueWrapper properties (calling, types, comparison)""" + v = ValueWrapper(100) + self.assertEqual(v.value, 100) # .value + self.assertEqual(v(), 100) # __call__ + self.assertEqual(int(v), 100) # __int__ + self.assertTrue(v == 100) # __eq__ + self.assertEqual(str(v), "100") # __str__ + + def test_append_and_dynamic_attrs(self): + """Check dynamic addition of values""" + e = Enum() + e.append(C=30) + e.append({'D': 40}, E=50) + e.F = 60 + + self.assertEqual(int(e.C), 30) + self.assertEqual(int(e.D), 40) + self.assertEqual(int(e.E), 50) + self.assertEqual(int(e.F), 60) + self.assertIsInstance(e.E, ValueWrapper) + self.assertIsInstance(e.F, ValueWrapper) + + def test_getattr_error(self): + """Check that an error is raised when an attribute is missing""" + e = Enum(A=1) + with self.assertRaises(AttributeError): + _ = e.NON_EXISTENT + + def test_is_value_and_key_lookup(self): + """Check key lookup by value and value validation""" + class Status(Enum): + IDLE = 0 + BUSY = 1 + + s = Status() + self.assertTrue(s.is_value(0)) + self.assertTrue(s.is_value(1)) + self.assertFalse(s.is_value(99)) + self.assertEqual(s.key_from_value(1), "Status.X2" if "X2" in dir(s) else "Status.BUSY") + self.assertEqual(s.key_from_value(1), "Status.BUSY") + + def test_is_loading_protection(self): + """Check that _is_loading does not end up in dictionary keys""" + e = Enum(A=1) + self.assertNotIn('_is_loading', e.keys()) + # Check that the flag is False after initialization + self.assertFalse(e._is_loading) + + def test_dir_visibility(self): + """Check for the presence of keys and methods in dir()""" + e = Enum(DATA=123) + directory = dir(e) + self.assertIn('DATA', directory) # Dynamic data + self.assertIn('append', directory) # Enum class method + self.assertIn('keys', directory) # Base dict method + + def test_math_and_indexing(self): + """Check usage in mathematics and as an index""" + e = Enum(VAL=10) + # Mathematics + self.assertEqual(e.VAL + 5, 15) + # Usage as an index (e.g., in a list) + ls = [0] * 20 + ls[e.VAL] = 1 + self.assertEqual(ls[10], 1) + + def test_various_types(self): + """Check operation with various data types""" + e = Enum(STR="test", FLT=1.5, BL=True) + self.assertEqual(str(e.STR), "test") + self.assertEqual(float(e.FLT), 1.5) + self.assertTrue(e.BL) + + def test_skipped_attributes(self): + """Check ignoring attributes via __skipped__""" + class MyEnum(Enum): + __skipped__ = ('SECRET', ) + PUBLIC = 1 + SECRET = 2 + + e = MyEnum() + self.assertIn('PUBLIC', e) + self.assertNotIn('SECRET', e) + + def test_post_loading_setattr(self): + """Check setting attributes after initialization""" + e = Enum(A=1) + # Regular attribute (starts with _) + e._internal = 100 + self.assertEqual(e._internal, 100) + self.assertNotIn('_internal', e.keys()) # Should not be in data + + # New Enum element + e.B = 2 + self.assertIsInstance(e.B, ValueWrapper) + self.assertIn('B', e.keys()) + + def test_key_from_value_not_found(self): + """Check for an error when searching for a non-existent value""" + e = Enum(A=1) + with self.assertRaises(ValueError): + e.key_from_value(999) + + def test_math_division(self): + """Check floor division and true division""" + e = Enum(STEP=25) + # Floor division + self.assertEqual(e.STEP // 2, 12) + # True division + self.assertEqual(e.STEP / 2, 12.5) + + def test_full_arithmetic(self): + """Check all new arithmetic operations""" + v = ValueWrapper(10) + self.assertEqual(v + 5, 15) + self.assertEqual(20 - v, 10) # Check __rsub__ + self.assertEqual(v * 2, 20) + self.assertEqual(30 // v, 3) # Check __rfloordiv__ + self.assertEqual(v % 3, 1) + self.assertTrue(v > 5) # Check comparison + + def test_bitmask_operations(self): + """Test bitwise operations (important for registers)""" + flags = Enum(BIT_0=0x01, BIT_1=0x02) + + # Check OR and AND + combined = flags.BIT_0 | flags.BIT_1 + self.assertEqual(combined, 0x03) + self.assertEqual(combined & flags.BIT_1, 0x02) + + # Check shifts + self.assertEqual(flags.BIT_0 << 2, 4) + self.assertEqual(8 >> flags.BIT_0, 4) # Check __rrshift__ + + def test_unary_operations(self): + """Test unary operators""" + e = Enum(VAL=10, NEG_VAL=-5) + self.assertEqual(-e.VAL, -10) + self.assertEqual(abs(e.NEG_VAL), 5) + self.assertEqual(~e.VAL, -11) # Bitwise NOT + + +if __name__ == '__main__': + unittest.main() From 735e6b092147b58e85b9e041b7da0c3453bd6045 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Thu, 16 Apr 2026 18:33:49 +0300 Subject: [PATCH 5/5] python-stdlib\enum: version 1.2.1. Gemini was used Signed-off-by: Ihor Nehrutsa --- python-stdlib/enum/enum.py | 350 +++++++++++--------------------- python-stdlib/enum/manifest.py | 2 +- python-stdlib/enum/test_enum.py | 252 +++++++++-------------- 3 files changed, 211 insertions(+), 393 deletions(-) diff --git a/python-stdlib/enum/enum.py b/python-stdlib/enum/enum.py index 57e825057..2fb698a2d 100644 --- a/python-stdlib/enum/enum.py +++ b/python-stdlib/enum/enum.py @@ -1,269 +1,157 @@ -_Err = "no such attribute: " +# enum.py +# version="1.2.1" -class ValueWrapper: - """Universal wrapper for accessing values via .value or calling ()""" - __slots__ = ('_v', ) - - def __init__(self, v): - self._v = v - - @property - def value(self): - return self._v - - def __call__(self): - return self._v +class EnumValue: + def __init__(self, value, name): + object.__setattr__(self, 'value', value) + object.__setattr__(self, 'name', name) def __repr__(self): - return repr(self._v) - - def __str__(self): - return str(self._v) - - # Type conversion - def __int__(self): - return int(self._v) - - def __float__(self): - return float(self._v) - - def __index__(self): - return int(self._v) - - def __bool__(self): - return bool(self._v) - - # Helper function to extract the raw value - def _get_v(self, other): - return other._v if isinstance(other, ValueWrapper) else other - - # Arithmetic and Bitwise operations (Forward) - def __add__(self, other): - return self._v + self._get_v(other) - - def __sub__(self, other): - return self._v - self._get_v(other) - - def __mul__(self, other): - return self._v * self._get_v(other) - - def __truediv__(self, other): - return self._v / self._get_v(other) - - def __floordiv__(self, other): - return self._v // self._get_v(other) - - def __mod__(self, other): - return self._v % self._get_v(other) - - def __pow__(self, other): - return self._v**self._get_v(other) - - def __and__(self, other): - return self._v & self._get_v(other) - - def __or__(self, other): - return self._v | self._get_v(other) - - def __xor__(self, other): - return self._v ^ self._get_v(other) - - def __lshift__(self, other): - return self._v << self._get_v(other) - - def __rshift__(self, other): - return self._v >> self._get_v(other) - - # Arithmetic and Bitwise operations (Reflected) - def __radd__(self, other): - return self._get_v(other) + self._v - - def __rsub__(self, other): - return self._get_v(other) - self._v - - def __rmul__(self, other): - return self._get_v(other) * self._v - - def __rtruediv__(self, other): - return self._get_v(other) / self._v - - def __rfloordiv__(self, other): - return self._get_v(other) // self._v - - def __rand__(self, other): - return self._get_v(other) & self._v - - def __ror__(self, other): - return self._get_v(other) | self._v + return str(self.value) - def __rxor__(self, other): - return self._get_v(other) ^ self._v - - def __rlshift__(self, other): - return self._get_v(other) << self._v - - def __rrshift__(self, other): - return self._get_v(other) >> self._v - - # Unary operators - def __neg__(self): - return -self._v - - def __pos__(self): - return +self._v - - def __abs__(self): - return abs(self._v) - - def __invert__(self): - return ~self._v + def __call__(self): + return self.value - # Comparison def __eq__(self, other): - return self._v == self._get_v(other) + return self.value == (other.value if isinstance(other, EnumValue) else other) - def __lt__(self, other): - return self._v < self._get_v(other) - - def __le__(self, other): - return self._v <= self._get_v(other) - - def __gt__(self, other): - return self._v > self._get_v(other) - - def __ge__(self, other): - return self._v >= self._get_v(other) - - def __ne__(self, other): - return self._v != self._get_v(other) - - -def enum(**kw_args): # `**kw_args` kept backwards compatible as in the Internet examples - return Enum(kw_args) + def __setattr__(self, key, value): + raise AttributeError("EnumValue is immutable") -class Enum(dict): - def __init__(self, arg=None, **kwargs): - super().__init__() - # Use __dict__ directly for internal flags - # to avoid cluttering the dictionary keyspace - super().__setattr__('_is_loading', True) +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 arg: self.append(arg) - if kwargs: self.append(kwargs) + if kwargs: + self.append(**kwargs) - super().__setattr__('_is_loading', False) + def _update(self, key, value): + setattr(self.__class__, key, EnumValue(value, key)) def _scan_class_attrs(self): - cls = self.__class__ - # Define attributes to skip (internal or explicitly requested) - skipped = getattr(cls, '__skipped__', ()) + # 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(cls): - # Skip internal names, methods, and excluded attributes - if key.startswith('_') or key in ('append', 'is_value', 'key_from_value'): - continue - if key in skipped: + for key in dir(self.__class__): + # Skip internal names and methods + if key.startswith('_') or key in ignored: continue - val = getattr(cls, key) - # Only wrap non-callable attributes (constants) - if not callable(val): - self[key] = ValueWrapper(val) - - def append(self, arg=None, **kwargs): - if isinstance(arg, dict): - for k, v in arg.items(): - self[k] = ValueWrapper(v) - else: - self._arg = arg # for __str__() - if kwargs: - for k, v in kwargs.items(): - self[k] = ValueWrapper(v) + 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 __getattr__(self, key): - if key in self: - return self[key] - raise AttributeError(_Err + key) + 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 self._is_loading or key.startswith('_'): - # Record directly into memory as a regular variable - super().__setattr__(key, value) - else: - # Handle as an Enum element (wrap in ValueWrapper) - self[key] = ValueWrapper(value) + if hasattr(self, key) and isinstance(getattr(self, key), EnumValue): + raise AttributeError(f"Enum member '{key}' is immutable") + super().__setattr__(key, value) - def is_value(self, value): - return any(v._v == value for v in self.values()) - - def key_from_value(self, value): - for k, v in self.items(): - if v._v == value: return f"{self.__class__.__name__}.{k}" - raise ValueError(_Err + str(value)) - - def __dir__(self): - # 1. Dictionary keys (your data: X1, X2, etc.) - data_keys = list(self.keys()) - # 2. Class attributes (your methods: append, is_value, etc.) - class_stuff = list(dir(self.__class__)) - # 3. Parent class attributes (for completeness) - parent_attrs = list(dir(super())) - # Combine and remove duplicates using set for clarity - #return list(set(data_keys + class_stuff + parent_attrs)) - return list(set(data_keys + class_stuff)) + 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) - def __call__(self, value): - if self.is_value(value): - return value - raise ValueError(_Err + f"{value}") +if __name__ == '__main__': + # --- Usage Example --- -if __name__ == "__main__": - # --- Usage Examples --- + # 1. Creation via class + class Color(Enum): + RED = 1 + GREEN = 2 - # 1. GPIO and Hardware Configuration - class Pins(Enum): - LED = 2 - BUTTON = 4 - __skipped__ = ('RESERVED_PIN', ) - RESERVED_PIN = 0 + # Create instance + c = Color() + print(f"Enum repr: {c}") - pins = Pins(SDA=21, SCL=22) - print(f"I2C SDA Pin: {pins.SDA}") - print(f"Is 21 a valid pin? {pins.is_value(21)}") + # 2. Strict __init__ control check + try: + c_bad = Color('BLACK') + except TypeError as e: + print(f"\nTypeError: Strict Init Check: {e}\n") - # 2. Math and Logic - brightness = Enum(MIN=0, STEP=25, MAX=255) - print(f"Next level: {brightness.MIN + brightness.STEP // 2}") - print(f"Calculation: {brightness.MIN + 2 * brightness.STEP}") + # 3. Dynamic addition + c.append(BLUE=3) + print(f"c after append: {c}") - # Direct arithmetic without .value - print(f"Complex math: {100 + brightness.STEP}") + print('dir(c):', dir(c)) - # 3. State Machine (Dynamic Expansion) - status = Enum(IDLE=0, CONNECTING=1) - status.append(CONNECTED=2, ERROR=3) - status.DISCONNECTING = 4 + # 4. Immutability and name protection check + try: + c.append(append=True) + except NameError as e: + print(f"\nNameError: Reserved name protection: {e}\n") - for name, val in status.items(): - print(f"Status {name} has code {val}") + # 5. Basic access + print(f"RED: Name={c.RED.name}, Value={c.RED.value}, EnumValue={c.RED}, Call={c.RED()} ") - # 4. Working with different types - commands = Enum(START="CMD_START", STOP="CMD_STOP", REBOOT_CODE=0xDEADBEEF, IS_ACTIVE=True) + # 6. Assertions + assert c.RED == 1 + assert c.RED.value == 1 + assert c.RED.name == 'RED' - if commands.IS_ACTIVE: - print(f"Running command: {commands.START}") + # 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 - # 5. Class Config and dir() - class WebConfig(Enum): - PORT = 80 - TIMEOUT = 5.0 + # 8. Iteration + print("Values list:", [member.value for member in c]) + print("Names list:", [member.name for member in c]) - config = WebConfig({'IP': '192.168.1.1'}) - print(f"Available keys in config: {list(config.keys())}") \ No newline at end of file + try: + c(7) + except ValueError as e: + print(f"\nValueError: {c} {e}\n") diff --git a/python-stdlib/enum/manifest.py b/python-stdlib/enum/manifest.py index 050ccaea0..3046f9b87 100644 --- a/python-stdlib/enum/manifest.py +++ b/python-stdlib/enum/manifest.py @@ -1,3 +1,3 @@ -metadata(version="1.1.0") +metadata(version="1.2.0") module("enum.py") diff --git a/python-stdlib/enum/test_enum.py b/python-stdlib/enum/test_enum.py index b927fb249..4d3d7edc2 100644 --- a/python-stdlib/enum/test_enum.py +++ b/python-stdlib/enum/test_enum.py @@ -1,169 +1,99 @@ +# version="1.2.1" + import unittest -from enum import Enum, ValueWrapper +from enum import Enum, EnumValue, enum class TestEnum(unittest.TestCase): - def test_class_initialization(self): - """Check Enum creation via class inheritance""" - class Pins(Enum): - TX = 1 - RX = 3 - - pins = Pins() - self.assertEqual(int(pins.TX), 1) - self.assertEqual(int(pins.RX), 3) - self.assertIn('TX', pins) - self.assertIn('RX', pins) - - def test_dict_initialization(self): - """Check Enum creation by passing a dictionary to the constructor""" - e = Enum({'A': 10, 'B': 'test'}, C='C') - self.assertEqual(e.A.value, 10) - self.assertEqual(e.B(), 'test') - self.assertEqual(e.C, 'C') - - def test_value_wrapper_behaviors(self): - """Check ValueWrapper properties (calling, types, comparison)""" - v = ValueWrapper(100) - self.assertEqual(v.value, 100) # .value - self.assertEqual(v(), 100) # __call__ - self.assertEqual(int(v), 100) # __int__ - self.assertTrue(v == 100) # __eq__ - self.assertEqual(str(v), "100") # __str__ - - def test_append_and_dynamic_attrs(self): - """Check dynamic addition of values""" - e = Enum() - e.append(C=30) - e.append({'D': 40}, E=50) - e.F = 60 - - self.assertEqual(int(e.C), 30) - self.assertEqual(int(e.D), 40) - self.assertEqual(int(e.E), 50) - self.assertEqual(int(e.F), 60) - self.assertIsInstance(e.E, ValueWrapper) - self.assertIsInstance(e.F, ValueWrapper) - - def test_getattr_error(self): - """Check that an error is raised when an attribute is missing""" - e = Enum(A=1) - with self.assertRaises(AttributeError): - _ = e.NON_EXISTENT - - def test_is_value_and_key_lookup(self): - """Check key lookup by value and value validation""" - class Status(Enum): - IDLE = 0 - BUSY = 1 - - s = Status() - self.assertTrue(s.is_value(0)) - self.assertTrue(s.is_value(1)) - self.assertFalse(s.is_value(99)) - self.assertEqual(s.key_from_value(1), "Status.X2" if "X2" in dir(s) else "Status.BUSY") - self.assertEqual(s.key_from_value(1), "Status.BUSY") - - def test_is_loading_protection(self): - """Check that _is_loading does not end up in dictionary keys""" - e = Enum(A=1) - self.assertNotIn('_is_loading', e.keys()) - # Check that the flag is False after initialization - self.assertFalse(e._is_loading) - - def test_dir_visibility(self): - """Check for the presence of keys and methods in dir()""" - e = Enum(DATA=123) - directory = dir(e) - self.assertIn('DATA', directory) # Dynamic data - self.assertIn('append', directory) # Enum class method - self.assertIn('keys', directory) # Base dict method - - def test_math_and_indexing(self): - """Check usage in mathematics and as an index""" - e = Enum(VAL=10) - # Mathematics - self.assertEqual(e.VAL + 5, 15) - # Usage as an index (e.g., in a list) - ls = [0] * 20 - ls[e.VAL] = 1 - self.assertEqual(ls[10], 1) - - def test_various_types(self): - """Check operation with various data types""" - e = Enum(STR="test", FLT=1.5, BL=True) - self.assertEqual(str(e.STR), "test") - self.assertEqual(float(e.FLT), 1.5) - self.assertTrue(e.BL) - - def test_skipped_attributes(self): - """Check ignoring attributes via __skipped__""" - class MyEnum(Enum): - __skipped__ = ('SECRET', ) - PUBLIC = 1 - SECRET = 2 - - e = MyEnum() - self.assertIn('PUBLIC', e) - self.assertNotIn('SECRET', e) - - def test_post_loading_setattr(self): - """Check setting attributes after initialization""" - e = Enum(A=1) - # Regular attribute (starts with _) - e._internal = 100 - self.assertEqual(e._internal, 100) - self.assertNotIn('_internal', e.keys()) # Should not be in data - - # New Enum element - e.B = 2 - self.assertIsInstance(e.B, ValueWrapper) - self.assertIn('B', e.keys()) - - def test_key_from_value_not_found(self): - """Check for an error when searching for a non-existent value""" - e = Enum(A=1) + def setUp(self): + # Створюємо базовий клас для тестів + class Color(Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + + self.ColorClass = Color + self.color = Color() + + def test_class_attributes(self): + """Тест статичних атрибутів, визначених у класі""" + self.assertEqual(self.color.RED.value, 1) + self.assertEqual(self.color.RED.name, 'RED') + self.assertIsInstance(self.color.RED, EnumValue) + + def test_init_kwargs(self): + """Тест додавання значень через конструктор __init__(**kwargs)""" + c = self.ColorClass(YELLOW=4, BLACK=0) + self.assertEqual(c.YELLOW.value, 4) + self.assertEqual(c.BLACK.name, 'BLACK') + + def test_append(self): + """Тест методу append(**kwargs) та ланцюжкового виклику""" + self.color.append(MAGENTA=5).append(CYAN=6) + self.assertEqual(self.color.MAGENTA.value, 5) + self.assertEqual(self.color.CYAN.name, 'CYAN') + + def test_comparison(self): + """Тест порівняння EnumValue з числами та іншими об'єктами""" + self.assertTrue(self.color.RED == 1) + self.assertFalse(self.color.RED == 2) + self.assertEqual(self.color.RED, self.color.RED) + # Перевірка, що об'єкт не дорівнює значенню іншого типу + self.assertFalse(self.color.RED == "1") + + def test_call_reverse_lookup(self): + """Тест зворотного пошуку Color(1) -> RED""" + # У вашій реалізації __call__ повертає об'єкт EnumValue + result = self.color(1) + self.assertEqual(result.name, 'RED') + self.assertEqual(result.value, 1) + + # Перевірка виключення для неіснуючого значення with self.assertRaises(ValueError): - e.key_from_value(999) - - def test_math_division(self): - """Check floor division and true division""" - e = Enum(STEP=25) - # Floor division - self.assertEqual(e.STEP // 2, 12) - # True division - self.assertEqual(e.STEP / 2, 12.5) - - def test_full_arithmetic(self): - """Check all new arithmetic operations""" - v = ValueWrapper(10) - self.assertEqual(v + 5, 15) - self.assertEqual(20 - v, 10) # Check __rsub__ - self.assertEqual(v * 2, 20) - self.assertEqual(30 // v, 3) # Check __rfloordiv__ - self.assertEqual(v % 3, 1) - self.assertTrue(v > 5) # Check comparison - - def test_bitmask_operations(self): - """Test bitwise operations (important for registers)""" - flags = Enum(BIT_0=0x01, BIT_1=0x02) - - # Check OR and AND - combined = flags.BIT_0 | flags.BIT_1 - self.assertEqual(combined, 0x03) - self.assertEqual(combined & flags.BIT_1, 0x02) - - # Check shifts - self.assertEqual(flags.BIT_0 << 2, 4) - self.assertEqual(8 >> flags.BIT_0, 4) # Check __rrshift__ - - def test_unary_operations(self): - """Test unary operators""" - e = Enum(VAL=10, NEG_VAL=-5) - self.assertEqual(-e.VAL, -10) - self.assertEqual(abs(e.NEG_VAL), 5) - self.assertEqual(~e.VAL, -11) # Bitwise NOT + self.color(999) + + def test_is_value(self): + """Тест методу перевірки наявності значення is_value()""" + self.assertTrue(self.color.is_value(1)) + self.assertTrue(self.color.is_value(3)) + self.assertFalse(self.color.is_value(5)) + + def test_iteration(self): + """Тест ітерабельності Enum (магічний метод __iter__)""" + # Створюємо список імен через ітерацію + members = list(self.color) + names = [m.name for m in members] + + self.assertIn('RED', names) + self.assertIn('GREEN', names) + self.assertIn('BLUE', names) + self.assertEqual(len(members), 3) + + def test_enum_value_immutability(self): + """Тест захисту від зміни значень EnumValue""" + with self.assertRaises(AttributeError): + self.color.RED.value = 10 + + with self.assertRaises(AttributeError): + self.color.RED.name = "NEW_NAME" + + def test_len(self): + """Тест магічного методу __len__""" + self.assertEqual(len(self.color), 3) + self.color.append(WHITE=7) + self.assertEqual(len(self.color), 4) + + def test_backwards_compatible_function(self): + """Тест глобальної функції enum(**kwargs)""" + e = enum(A=10, B=20) + self.assertEqual(e.A.value, 10) + self.assertEqual(e.B.name, 'B') + + def test_call_method(self): + """Тест виклику об'єкта як функції c.RED() -> 1""" + self.assertEqual(self.color.RED(), 1) + self.assertEqual(self.color.GREEN(), 2) if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main()