From 6e709ca63b6624efadcdb062411ad3cff9670634 Mon Sep 17 00:00:00 2001 From: Ihor Nehrutsa Date: Wed, 5 Mar 2025 12:24:05 +0200 Subject: [PATCH 01/14] 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 02/14] 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 e630610472844c0ad5339e310debeb08557dbf50 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Thu, 16 Apr 2026 18:33:49 +0300 Subject: [PATCH 03/14] 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() From 343c45577b35c3a7d9e4f306f60194f0d7765157 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Fri, 17 Apr 2026 14:51:47 +0300 Subject: [PATCH 04/14] enum.py: Add Reverse Lookup like Status(1). class Status(Enum): IDLE = 0 RUNNING = 1 ERROR = 2 # This simulates receiving a byte from the hardware received_byte = 1 status = Status(received_byte) print(f"Lookup check: Received {received_byte} -> {status}") assert status == Status.RUNNING assert status.name == "RUNNING" Signed-off-by: Ihor Nehrutsa --- python-stdlib/enum/enum.py | 383 ++++++++++++++++++++++--------------- 1 file changed, 226 insertions(+), 157 deletions(-) diff --git a/python-stdlib/enum/enum.py b/python-stdlib/enum/enum.py index 2fb698a2d..a1b8a43b4 100644 --- a/python-stdlib/enum/enum.py +++ b/python-stdlib/enum/enum.py @@ -1,157 +1,226 @@ -# 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") +# enum.py +# version="1.2.2" + + +class EnumValue: + # An immutable object representing a specific enum member + def __init__(self, value, name): + object.__setattr__(self, 'value', value) + object.__setattr__(self, 'name', name) + + def __repr__(self): + return f"<{self.name}: {self.value}>" + + def __str__(self): + return str(self.value) + + def __call__(self): + return self.value + + def __eq__(self, other): + if isinstance(other, EnumValue): + return self.value == other.value + return self.value == other + + def __int__(self): + return self.value + + def __setattr__(self, key, value): + raise AttributeError("EnumValue is immutable") + + +class Enum: + def __new__(cls, *args, **kwargs): + # Scenario 1: Reverse lookup by value (e.g., Status(1)) + if len(args) == 1: + if cls is not Enum: + return cls._lookup(args[0]) + return super(Enum, cls).__new__(cls) + + # Scenario 2: Restriction on multiple positional arguments + elif len(args) > 1: + raise TypeError(f"{cls.__name__}() takes at most 1 positional argument ({len(args)} given)") + + # Scenario 3: Creating an instance (e.g. Color() або Color(BLUE=3)) + return super(Enum, cls).__new__(cls) + + def __init__(self, **kwargs): + # 1. Convert class-level attributes (constants) to EnumValue objects + self._scan_class_attrs() + # 2. Add dynamic arguments from constructor + if kwargs: + self.append(**kwargs) + + @classmethod + def _lookup(cls, value): + # Finds an EnumValue by its raw value + for key in dir(cls): + if key.startswith('_'): + continue + attr = getattr(cls, key) + if isinstance(attr, EnumValue) and (attr.value == value or attr.name == value): + return attr + if not callable(attr) and attr == value: + # Wrap static numbers found in class definition + return EnumValue(attr, key) + return attr + + raise AttributeError(f"{value} is not in {cls.__name__} enum") + + def list_members(self): + # Returns a list of tuples (name, value) for all members + return [(m.name, m.value) for m in self] + + 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 = ('append', 'is_value', 'list_members') + 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): + return any(member.value == value for member in self) + + def append(self, **kwargs): + # Adds new members dynamically. + for key, value in kwargs.items(): + if hasattr(self, key): + raise AttributeError(f"Enum key '{key}' is immutable") + self._update(key, value) + + def __repr__(self): + members = [f"{m.name}={m.value}" for m in self] + # Return a string like: Color(RED=1, GREEN=2, BLUE=3) + return f"{self.__class__.__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 --- + # 1. Creation via class + class Color(Enum): + RED = 1 + GREEN = 2 + + # 2. Create instance + c = Color() + print(f"Enum repr c: {c}") + + # 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 AttributeError as e: + print(f"\nAttributeError: 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") + + # --- Usage Example 2 --- + # 1. Define an Enum class + class Status(Enum): + IDLE = 0 + RUNNING = 1 + ERROR = 2 + + # 2. Test: Reverse Lookup + # This simulates receiving a byte from the hardware + received_byte = 1 + status = Status(received_byte) + print(f"Lookup check: Received {received_byte} -> {status!r}") + assert status == Status.RUNNING + assert status.name == "RUNNING" + + # 3. Test: Comparisons + print(f"Comparison check: {status} == 1 is {status == 1}") + assert status == 1 + assert status != 0 + assert status == Status.RUNNING + + # 4. Test: Immutability + try: + Status.RUNNING.value = 99 + except AttributeError as e: + print(f"\nImmutability check: Passed (Cannot modify EnumValue): {e}\n") + + # 5. Test: Dynamic Append + powers = Enum() + powers.append(LOW=10, HIGH=100) + print(f"Dynamic Enum check: {powers}") + assert powers.LOW == 10 + + # 6. Test: Iteration + print("Iteration check: ", end="") + for m in Status(): + print(f"{m.name}, ", end="") + print("-> Passed") + + # 7. Test: Error handling for invalid lookup + try: + Status(99) + except AttributeError as e: + print(f"\nAttributeError: Invalid lookup check: Caught expected error -> {e}\n") + + print("\nAll tests passed successfully!") From 3610c16680846a424a06dc89eee7c1d1c8870275 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Tue, 21 Apr 2026 14:32:56 +0300 Subject: [PATCH 05/14] Enum: Use Functional API CPython instead of kwargs. Signed-off-by: Ihor Nehrutsa --- python-stdlib/enum/enum.py | 149 +++++++++++++++---------------- python-stdlib/enum/test_enum.py | 23 +---- python-stdlib/enum/test_enum_.py | 114 +++++++++++++++++++++++ 3 files changed, 189 insertions(+), 97 deletions(-) create mode 100644 python-stdlib/enum/test_enum_.py diff --git a/python-stdlib/enum/enum.py b/python-stdlib/enum/enum.py index a1b8a43b4..6486ab268 100644 --- a/python-stdlib/enum/enum.py +++ b/python-stdlib/enum/enum.py @@ -1,5 +1,5 @@ # enum.py -# version="1.2.2" +# version="1.2.3" class EnumValue: @@ -9,10 +9,10 @@ def __init__(self, value, name): object.__setattr__(self, 'name', name) def __repr__(self): - return f"<{self.name}: {self.value}>" + return f"{self.name}: {self.value}" - def __str__(self): - return str(self.value) +# def __str__(self): +# return str(self.value) def __call__(self): return self.value @@ -22,34 +22,35 @@ def __eq__(self, other): return self.value == other.value return self.value == other - def __int__(self): - return self.value +# def __int__(self): +# return self.value def __setattr__(self, key, value): raise AttributeError("EnumValue is immutable") class Enum: - def __new__(cls, *args, **kwargs): + def __new__(cls, name=None, names=None): # Scenario 1: Reverse lookup by value (e.g., Status(1)) - if len(args) == 1: + if name is not None and names is None: if cls is not Enum: - return cls._lookup(args[0]) - return super(Enum, cls).__new__(cls) - - # Scenario 2: Restriction on multiple positional arguments - elif len(args) > 1: - raise TypeError(f"{cls.__name__}() takes at most 1 positional argument ({len(args)} given)") + return cls._lookup(name) - # Scenario 3: Creating an instance (e.g. Color() або Color(BLUE=3)) + # Scenario 2: Functional API (e.g., Enum('Color', {'RED': 1})) return super(Enum, cls).__new__(cls) - def __init__(self, **kwargs): + def __init__(self, name=None, names=None): # 1. Convert class-level attributes (constants) to EnumValue objects self._scan_class_attrs() - # 2. Add dynamic arguments from constructor - if kwargs: - self.append(**kwargs) + + # Support Functional API: Enum('Name', {'KEY': VALUE}) + if name is not None and isinstance(names, dict): + for key, value in names.items(): + # Prevent addition if the key already exists + if not hasattr(self, key): + self._update(key, value) + + object.__setattr__(self, '_initialized', True) @classmethod def _lookup(cls, value): @@ -63,7 +64,6 @@ def _lookup(cls, value): if not callable(attr) and attr == value: # Wrap static numbers found in class definition return EnumValue(attr, key) - return attr raise AttributeError(f"{value} is not in {cls.__name__} enum") @@ -77,7 +77,7 @@ def _update(self, key, value): def _scan_class_attrs(self): # Converts static class attributes into EnumValue objects # List of methods and internal names that should not be converted - ignored = ('append', 'is_value', 'list_members') + ignored = ('is_value', 'list_members') for key in dir(self.__class__): # Skip internal names and methods if key.startswith('_') or key in ignored: @@ -91,17 +91,13 @@ def _scan_class_attrs(self): def is_value(self, value): return any(member.value == value for member in self) - def append(self, **kwargs): - # Adds new members dynamically. - for key, value in kwargs.items(): - if hasattr(self, key): - raise AttributeError(f"Enum key '{key}' is immutable") - self._update(key, value) - def __repr__(self): - members = [f"{m.name}={m.value}" for m in self] - # Return a string like: Color(RED=1, GREEN=2, BLUE=3) - return f"{self.__class__.__name__}({', '.join(members)})" + # Supports the condition: obj == eval(repr(obj)) + members = {m.name: m.value for m in self} + if self.__class__.__name__ == 'Enum': + return f"Enum(name='Enum', names={members})" + # Return a string like: Name(names={'KEY1': VALUE1, 'KEY2': VALUE2, ..}) + return f"{self.__class__.__name__}(names={members})" def __call__(self, value): for member in self: @@ -110,8 +106,8 @@ def __call__(self, value): 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") + if hasattr(self, '_initialized'): + raise AttributeError(f"Enum '{self.__class__.__name__}' is static") super().__setattr__(key, value) def __delattr__(self, key): @@ -128,58 +124,49 @@ def __iter__(self): if isinstance(attr, EnumValue): yield attr - -def enum(**kwargs): # `**kwargs` kept backwards compatible as in the Internet examples - return Enum(**kwargs) + def __eq__(self, other): + if not isinstance(other, Enum): + return False + return self.list_members() == other.list_members() if __name__ == '__main__': # --- Usage Example 1 --- - # 1. Creation via class + # Standard Class Definition class Color(Enum): - RED = 1 - GREEN = 2 + RED = 'red' + GREEN = 'green' - # 2. Create instance + # Create instance c = Color() print(f"Enum repr c: {c}") - # 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 AttributeError as e: - print(f"\nAttributeError: Reserved name protection: {e}\n") - - # 5. Basic access + # 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 + # Assertions assert c.RED.name == 'RED' + assert c.RED.value == 'red' + assert c.RED == 'red' + assert c.RED() == '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 + # Reverse Lookup via instance call + print(f"c('red') lookup object: {c('red')}, Name={c('red').name}, value={c('red').value}") # RED + assert c('red').name == 'RED' + assert c('red').value == 'red' + assert c('red') == 'red' - # 8. Iteration + # Iteration print("Values list:", [member.value for member in c]) print("Names list:", [member.name for member in c]) try: - c(7) + c(999) except ValueError as e: print(f"\nValueError: {c} {e}\n") # --- Usage Example 2 --- - # 1. Define an Enum class + # Define an Enum class class Status(Enum): IDLE = 0 RUNNING = 1 @@ -189,38 +176,48 @@ class Status(Enum): # This simulates receiving a byte from the hardware received_byte = 1 status = Status(received_byte) - print(f"Lookup check: Received {received_byte} -> {status!r}") + print(f"Lookup check: Received {received_byte} -> {status}") assert status == Status.RUNNING assert status.name == "RUNNING" - # 3. Test: Comparisons + # Test: Comparisons print(f"Comparison check: {status} == 1 is {status == 1}") assert status == 1 assert status != 0 assert status == Status.RUNNING - # 4. Test: Immutability + # Immutability Check try: - Status.RUNNING.value = 99 + Status.RUNNING.value = 999 except AttributeError as e: print(f"\nImmutability check: Passed (Cannot modify EnumValue): {e}\n") - # 5. Test: Dynamic Append - powers = Enum() - powers.append(LOW=10, HIGH=100) - print(f"Dynamic Enum check: {powers}") - assert powers.LOW == 10 - - # 6. Test: Iteration + # Test: Iteration print("Iteration check: ", end="") for m in Status(): print(f"{m.name}, ", end="") print("-> Passed") - # 7. Test: Error handling for invalid lookup + # Test: Error handling for invalid lookup try: - Status(99) + Status(999) except AttributeError as e: print(f"\nAttributeError: Invalid lookup check: Caught expected error -> {e}\n") + # --- Example 3: Functional API and serialization --- + print("\n--- Functional API and Eval Check ---") + + # Verify that eval(repr(obj)) restores the object + c2 = eval(repr(c)) + print(f"Original: {repr(c)}") + print(f"Restored: {repr(c2)}") + print(f"Objects are equal: {c == c2}") + assert c == c2 + + # Direct creation using the Enum base class + state = eval("Enum(name='State', names={'ON':1, 'OFF':2})") + print(f"Functional Enum instance (state): {state}") + assert state.ON == 1 + assert state.ON.name == 'ON' + print("\nAll tests passed successfully!") diff --git a/python-stdlib/enum/test_enum.py b/python-stdlib/enum/test_enum.py index 4d3d7edc2..1863ac949 100644 --- a/python-stdlib/enum/test_enum.py +++ b/python-stdlib/enum/test_enum.py @@ -1,7 +1,8 @@ +# test_enum.py # version="1.2.1" import unittest -from enum import Enum, EnumValue, enum +from enum import Enum, EnumValue class TestEnum(unittest.TestCase): @@ -21,18 +22,6 @@ def test_class_attributes(self): 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) @@ -80,14 +69,6 @@ def test_enum_value_immutability(self): 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""" diff --git a/python-stdlib/enum/test_enum_.py b/python-stdlib/enum/test_enum_.py new file mode 100644 index 000000000..a765e106a --- /dev/null +++ b/python-stdlib/enum/test_enum_.py @@ -0,0 +1,114 @@ +# test_enum.py +# version="1.2.3" + +import unittest +from enum import Enum, EnumValue + +class TestEnum(unittest.TestCase): + + def setUp(self): + # Define standard Enum classes for testing + class Color(Enum): + RED = 'red' + GREEN = 'green' + BLUE = 'blue' + + class Status(Enum): + IDLE = 0 + RUNNING = 1 + ERROR = 2 + + self.Color = Color + self.Status = Status + self.color_inst = Color() + self.status_inst = Status() + + def test_member_access(self): + """Test basic access to Enum members, names, and values.""" + self.assertEqual(self.color_inst.RED.name, 'RED') + self.assertEqual(self.color_inst.RED.value, 'red') + self.assertEqual(self.status_inst.IDLE.value, 0) + + def test_member_equality(self): + """Test equality between EnumValues and raw values.""" + self.assertTrue(self.color_inst.RED == 'red') + self.assertTrue(self.status_inst.RUNNING == 1) + self.assertFalse(self.status_inst.ERROR == 0) + # Test equality between two EnumValue objects + self.assertEqual(self.color_inst.RED, self.color_inst.RED) + + def test_immutability(self): + """Verify that EnumValue attributes and Enum instances are static.""" + # Test EnumValue immutability + with self.assertRaises(AttributeError): + self.color_inst.RED.value = 'blue' + + # Test Enum instance immutability (once initialized) + with self.assertRaises(AttributeError): + self.color_inst.NEW_COLOR = 'yellow' + + def test_reverse_lookup_via_constructor(self): + """Test reverse lookup by passing a value to the class constructor.""" + # Using __new__ logic for reverse lookup + member = self.Status(1) + self.assertEqual(member.name, "RUNNING") + self.assertEqual(member, self.Status.RUNNING) + + with self.assertRaises(AttributeError): + self.Status(999) + + def test_reverse_lookup_via_instance_call(self): + """Test reverse lookup by calling the instance with a value.""" + # Using __call__ logic for reverse lookup + member = self.color_inst('green') + self.assertEqual(member.name, 'GREEN') + + with self.assertRaises(ValueError): + self.color_inst('yellow') + + def test_iteration(self): + """Test that the Enum instance is iterable and returns all members.""" + members = list(self.status_inst) + names = [m.name for m in members] + values = [m.value for m in members] + + self.assertEqual(len(members), 3) + self.assertIn('IDLE', names) + self.assertIn(2, values) + +# def test_functional_api(self): +# """Test dynamic Enum creation using the Functional API.""" +# # Dynamic creation via Enum(name, names_dict) +# State = Enum(name='State', names={'ON': 1, 'OFF': 0}) +# +# self.assertTrue(hasattr(State, 'ON')) +# self.assertEqual(State.ON.value, 1) +# self.assertEqual(State.OFF.name, 'OFF') + +# def test_serialization_repr_eval(self): +# """Verify that eval(repr(obj)) restores the Enum instance correctly.""" +# # Test standard class instance +# c_repr = repr(self.color_inst) +# print('c_repr', c_repr) +# c_restored = eval(c_repr) +# self.assertEqual(self.color_inst, c_restored) +# +# # Test dynamically created instance +# s_dynamic = Enum(name='State', names={'ON': 1, 'OFF': 0}) +# s_repr = repr(s_dynamic) +# s_restored = eval(s_repr) +# self.assertEqual(s_dynamic, s_restored) + + def test_len_and_list_members(self): + """Test utility functions like __len__ and list_members.""" + self.assertEqual(len(self.color_inst), 3) + members_list = self.color_inst.list_members() + self.assertEqual(members_list, [('BLUE', 'blue'), ('GREEN', 'green'), ('RED', 'red')]) + + def test_deletion_prevention(self): + """Verify that members cannot be deleted.""" + with self.assertRaises(AttributeError): + del self.color_inst.RED + +if __name__ == '__main__': + unittest.main() From a6ed80ef512579eabb641f79af081c7b31558977 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Tue, 21 Apr 2026 15:40:36 +0300 Subject: [PATCH 06/14] enum.md: Update according to enum.py. Signed-off-by: Ihor Nehrutsa --- python-stdlib/enum/enum.md | 161 ++++++++++++++++++------------------- 1 file changed, 79 insertions(+), 82 deletions(-) diff --git a/python-stdlib/enum/enum.md b/python-stdlib/enum/enum.md index 1133abf9f..0cf4c31bf 100644 --- a/python-stdlib/enum/enum.md +++ b/python-stdlib/enum/enum.md @@ -1,120 +1,117 @@ -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. +# Enum Library ---- - -# Custom Enum Library for Python & MicroPython +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. -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. +* **Immutability**: Enum members (`EnumValue`) are protected against modification. Any attempt to change their name or value raises an `AttributeError`. +* **Static Design**: Once an Enum instance is initialized, it is "frozen." You cannot add new attributes or delete existing members. +* **Dual Reverse Lookup**: + * **Class Constructor**: Retrieve a member by value using the class name (e.g., `Status(1)`). + * **Instance Call**: Retrieve a member by value by calling the instance (e.g., `s(1)`). +* **Serialization Support**: Implements `__repr__` such that `obj == eval(repr(obj))`, allowing easy restoration of Enum states. +* **Functional API**: Supports dynamic creation of Enums at runtime. --- ## 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. +### 1. Standard Class Definition +Define your enumeration by inheriting from the `Enum` class. Class-level constants are automatically converted into `EnumValue` objects upon initialization. ```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 +class Color(Enum): + RED = 'red' + GREEN = 'green' -# You can also add pins during instantiation -pins = Pins(SDA=21, SCL=22) +# Initialize the enum to process attributes +c = Color() -print(f"I2C SDA Pin: {pins.SDA}") # Output: 21 -print(f"Is pin 21 valid? {pins.is_value(21)}") # Output: True +print(c.RED) # Output: RED: red +print(c.RED.name) # Output: RED +print(c.RED.value) # Output: red +print(c.RED()) # Output: red ``` -### 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 +### 2. Reverse Lookup +The library provides two ways to find a member based on its raw value. -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 +```python +class Status(Enum): + IDLE = 0 + RUNNING = 1 + +# Method A: Via Class (Simulates interpreting hardware/network bytes) +# Uses __new__ logic to return the correct EnumValue +current_status = Status(1) +print(current_status.name) # Output: RUNNING +print(current_status) # Output: RUNNING: 1 +print(current_status()) # Output: 1 + +# Method B: Via Instance Call +s = Status() +print(s(0).name) # Output: IDLE +print(s(0)) # Output: IDLE: 0 +print(s(0)()) # Output: 0 ``` -### 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) +### 3. Functional API (Dynamic Creation) +If you need to create an Enum from external data (like a JSON config), use the functional constructor. -# Add a single member via direct assignment -status.DISCONNECTING = 4 +```python +# Create a dynamic Enum instance +State = Enum(name='State', names={'ON': 1, 'OFF': 2}) -for name, val in status.items(): - print(f"Status {name} has code {val}") +print(State.ON) # Output: ON: 1 +assert State.ON == 1 # Comparison with raw value ``` -### 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. +### 4. Serialization (Repr / Eval) +The library ensures that the string representation can be used to perfectly reconstruct the object. ```python -class ErrorCodes(Enum): - NOT_FOUND = 404 - SERVER_ERROR = 500 +colors = Color() +# Get serialized string +serialized = repr(colors) +# Reconstruct object +restored_colors = eval(serialized) -# Check if a value exists in the Enum -exists = ErrorCodes.is_value(404) # True +print(f"Original: {colors}") # Output: Original: Color(names={'ON': 1, 'OFF': 2, 'GREEN': 'green', 'RED': 'red'}) +print(f"Restored: {restored_colors}") # Output: Restored: Color(names={'ON': 1, 'OFF': 2, 'GREEN': 'green', 'RED': 'red'}) +print(colors == restored_colors) # Output: 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. +### `EnumValue` +The object representing a specific member of an Enum. +* `.name`: The string name of the member. +* `.value`: The raw value associated with the member. +* `()`: Calling the member object returns its raw value (e.g., `c.RED() -> 'red'`). + +### `Enum` +The base class for all enumerations. +* `list_members()`: Returns a list of `(name, value)` tuples for all defined members. +* `is_value(value)`: Returns `True` if the provided raw value exists within the Enum. +* `__len__`: Returns the total number of members. +* `__iter__`: Allows looping through members (e.g., `[m.name for m in color_inst]`). + +--- -### `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. +## Error Handling +* **`AttributeError`**: + * Raised when attempting to modify an `EnumValue`. + * Raised when attempting to add new members to an initialized Enum. + * Raised when a class-level lookup (`Status(999)`) fails. +* **`ValueError`**: + * Raised when an instance-level lookup (`s(999)`) fails. From 33404875b1b76b871143f76a2a91f5ba0c2d3f85 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Tue, 21 Apr 2026 16:16:26 +0300 Subject: [PATCH 07/14] Enum: Replace ValueError to AttributeError. Signed-off-by: Ihor Nehrutsa --- python-stdlib/enum/enum.md | 1 - python-stdlib/enum/enum.py | 10 +++++----- python-stdlib/enum/test_enum.py | 2 +- python-stdlib/enum/test_enum_.py | 23 ++++++++++++----------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/python-stdlib/enum/enum.md b/python-stdlib/enum/enum.md index 0cf4c31bf..e5a6b5285 100644 --- a/python-stdlib/enum/enum.md +++ b/python-stdlib/enum/enum.md @@ -113,5 +113,4 @@ The base class for all enumerations. * Raised when attempting to modify an `EnumValue`. * Raised when attempting to add new members to an initialized Enum. * Raised when a class-level lookup (`Status(999)`) fails. -* **`ValueError`**: * Raised when an instance-level lookup (`s(999)`) fails. diff --git a/python-stdlib/enum/enum.py b/python-stdlib/enum/enum.py index 6486ab268..6d501b28f 100644 --- a/python-stdlib/enum/enum.py +++ b/python-stdlib/enum/enum.py @@ -65,7 +65,7 @@ def _lookup(cls, value): # Wrap static numbers found in class definition return EnumValue(attr, key) - raise AttributeError(f"{value} is not in {cls.__name__} enum") + raise AttributeError(f"{value} is not in {cls.__name__}") def list_members(self): # Returns a list of tuples (name, value) for all members @@ -103,7 +103,7 @@ def __call__(self, value): for member in self: if member.value == value: return member - raise ValueError(f"no such value: {value}") + raise AttributeError(f"{value} is not in {self.__class__.__name__}") def __setattr__(self, key, value): if hasattr(self, '_initialized'): @@ -139,7 +139,7 @@ class Color(Enum): # Create instance c = Color() - print(f"Enum repr c: {c}") + print(f"Enum c: {c}") # Basic access print(f"RED: Name={c.RED.name}, Value={c.RED.value}, EnumValue={c.RED}, Call={c.RED()} ") @@ -162,8 +162,8 @@ class Color(Enum): try: c(999) - except ValueError as e: - print(f"\nValueError: {c} {e}\n") + except AttributeError as e: + print(f"\nAttributeError: {e}: {c}\n") # --- Usage Example 2 --- # Define an Enum class diff --git a/python-stdlib/enum/test_enum.py b/python-stdlib/enum/test_enum.py index 1863ac949..0007172d8 100644 --- a/python-stdlib/enum/test_enum.py +++ b/python-stdlib/enum/test_enum.py @@ -38,7 +38,7 @@ def test_call_reverse_lookup(self): self.assertEqual(result.value, 1) # Перевірка виключення для неіснуючого значення - with self.assertRaises(ValueError): + with self.assertRaises(AttributeError): self.color(999) def test_is_value(self): diff --git a/python-stdlib/enum/test_enum_.py b/python-stdlib/enum/test_enum_.py index a765e106a..1ce8b21b9 100644 --- a/python-stdlib/enum/test_enum_.py +++ b/python-stdlib/enum/test_enum_.py @@ -4,15 +4,15 @@ import unittest from enum import Enum, EnumValue -class TestEnum(unittest.TestCase): +class TestEnum(unittest.TestCase): def setUp(self): # Define standard Enum classes for testing class Color(Enum): RED = 'red' GREEN = 'green' BLUE = 'blue' - + class Status(Enum): IDLE = 0 RUNNING = 1 @@ -42,7 +42,7 @@ def test_immutability(self): # Test EnumValue immutability with self.assertRaises(AttributeError): self.color_inst.RED.value = 'blue' - + # Test Enum instance immutability (once initialized) with self.assertRaises(AttributeError): self.color_inst.NEW_COLOR = 'yellow' @@ -63,7 +63,7 @@ def test_reverse_lookup_via_instance_call(self): member = self.color_inst('green') self.assertEqual(member.name, 'GREEN') - with self.assertRaises(ValueError): + with self.assertRaises(AttributeError): self.color_inst('yellow') def test_iteration(self): @@ -71,16 +71,17 @@ def test_iteration(self): members = list(self.status_inst) names = [m.name for m in members] values = [m.value for m in members] - + self.assertEqual(len(members), 3) self.assertIn('IDLE', names) self.assertIn(2, values) + # def test_functional_api(self): # """Test dynamic Enum creation using the Functional API.""" # # Dynamic creation via Enum(name, names_dict) # State = Enum(name='State', names={'ON': 1, 'OFF': 0}) -# +# # self.assertTrue(hasattr(State, 'ON')) # self.assertEqual(State.ON.value, 1) # self.assertEqual(State.OFF.name, 'OFF') @@ -92,18 +93,18 @@ def test_iteration(self): # print('c_repr', c_repr) # c_restored = eval(c_repr) # self.assertEqual(self.color_inst, c_restored) -# +# # # Test dynamically created instance # s_dynamic = Enum(name='State', names={'ON': 1, 'OFF': 0}) # s_repr = repr(s_dynamic) # s_restored = eval(s_repr) # self.assertEqual(s_dynamic, s_restored) - def test_len_and_list_members(self): - """Test utility functions like __len__ and list_members.""" + def test_len_and_list(self): + """Test utility functions like __len__ and list.""" self.assertEqual(len(self.color_inst), 3) - members_list = self.color_inst.list_members() - self.assertEqual(members_list, [('BLUE', 'blue'), ('GREEN', 'green'), ('RED', 'red')]) + members_list = self.color_inst.list() + self.assertEqual(members_list, [self.color_inst.BLUE, self.color_inst.GREEN, self.color_inst.RED]) def test_deletion_prevention(self): """Verify that members cannot be deleted.""" From c71549a1ecfcb7a7c428738613408334faf15d2c Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Wed, 22 Apr 2026 10:51:12 +0300 Subject: [PATCH 08/14] enum.py: Rename list_members to list. Signed-off-by: Ihor Nehrutsa --- python-stdlib/enum/enum.md | 2 +- python-stdlib/enum/enum.py | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/python-stdlib/enum/enum.md b/python-stdlib/enum/enum.md index e5a6b5285..fae553a93 100644 --- a/python-stdlib/enum/enum.md +++ b/python-stdlib/enum/enum.md @@ -101,7 +101,7 @@ The object representing a specific member of an Enum. ### `Enum` The base class for all enumerations. -* `list_members()`: Returns a list of `(name, value)` tuples for all defined members. +* `list()`: Returns a list of all defined members. * `is_value(value)`: Returns `True` if the provided raw value exists within the Enum. * `__len__`: Returns the total number of members. * `__iter__`: Allows looping through members (e.g., `[m.name for m in color_inst]`). diff --git a/python-stdlib/enum/enum.py b/python-stdlib/enum/enum.py index 6d501b28f..91436974c 100644 --- a/python-stdlib/enum/enum.py +++ b/python-stdlib/enum/enum.py @@ -67,9 +67,9 @@ def _lookup(cls, value): raise AttributeError(f"{value} is not in {cls.__name__}") - def list_members(self): - # Returns a list of tuples (name, value) for all members - return [(m.name, m.value) for m in self] + def list(self): + # Returns a list of all members + return [member for member in self] def _update(self, key, value): setattr(self.__class__, key, EnumValue(value, key)) @@ -77,7 +77,7 @@ def _update(self, key, value): 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', 'list_members') + ignored = ('is_value', 'list') for key in dir(self.__class__): # Skip internal names and methods if key.startswith('_') or key in ignored: @@ -93,7 +93,7 @@ def is_value(self, value): def __repr__(self): # Supports the condition: obj == eval(repr(obj)) - members = {m.name: m.value for m in self} + members = {member.name: member.value for member in self} if self.__class__.__name__ == 'Enum': return f"Enum(name='Enum', names={members})" # Return a string like: Name(names={'KEY1': VALUE1, 'KEY2': VALUE2, ..}) @@ -127,7 +127,7 @@ def __iter__(self): def __eq__(self, other): if not isinstance(other, Enum): return False - return self.list_members() == other.list_members() + return self.list() == other.list() if __name__ == '__main__': @@ -140,6 +140,7 @@ class Color(Enum): # Create instance c = Color() print(f"Enum c: {c}") + print("c.list():", c.list()) # Basic access print(f"RED: Name={c.RED.name}, Value={c.RED.value}, EnumValue={c.RED}, Call={c.RED()} ") From d88dc5c9cb6521e022bdb76bc5cce865590665e3 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Thu, 23 Apr 2026 13:15:19 +0300 Subject: [PATCH 09/14] enum.py: Make __iter__, _update, _scan_class_attrs, list a classmethods. igned-off-by: Ihor Nehrutsa --- python-stdlib/enum/enum.py | 91 ++++++++++++++++++++------------ python-stdlib/enum/test_enum.py | 3 ++ python-stdlib/enum/test_enum_.py | 2 +- 3 files changed, 61 insertions(+), 35 deletions(-) diff --git a/python-stdlib/enum/enum.py b/python-stdlib/enum/enum.py index 91436974c..366f7383b 100644 --- a/python-stdlib/enum/enum.py +++ b/python-stdlib/enum/enum.py @@ -1,5 +1,5 @@ # enum.py -# version="1.2.3" +# version="1.2.4" class EnumValue: @@ -11,9 +11,6 @@ def __init__(self, value, name): def __repr__(self): return f"{self.name}: {self.value}" -# def __str__(self): -# return str(self.value) - def __call__(self): return self.value @@ -22,9 +19,6 @@ def __eq__(self, other): return self.value == other.value return self.value == other -# def __int__(self): -# return self.value - def __setattr__(self, key, value): raise AttributeError("EnumValue is immutable") @@ -40,6 +34,9 @@ def __new__(cls, name=None, names=None): return super(Enum, cls).__new__(cls) def __init__(self, name=None, names=None): + if hasattr(self, '_initialized'): + return + # 1. Convert class-level attributes (constants) to EnumValue objects self._scan_class_attrs() @@ -64,29 +61,49 @@ def _lookup(cls, value): if not callable(attr) and attr == value: # Wrap static numbers found in class definition return EnumValue(attr, key) - raise AttributeError(f"{value} is not in {cls.__name__}") - def list(self): + @classmethod + def __iter__(cls): + if '_initialized' not in cls.__dict__: + cls._scan_class_attrs() + setattr(cls, '_initialized', True) + + for key in dir(cls): + if key.startswith('_'): + continue + attr = getattr(cls, key) + if isinstance(attr, EnumValue): + yield attr + + @classmethod + def list(cls): + if '_initialized' not in cls.__dict__: + cls._scan_class_attrs() + setattr(cls, '_initialized', True) + # Returns a list of all members - return [member for member in self] + return [getattr(cls, key) for key in dir(cls) + if isinstance(getattr(cls, key), EnumValue)] - def _update(self, key, value): - setattr(self.__class__, key, EnumValue(value, key)) + @classmethod + def _update(cls, key, value): + setattr(cls, key, EnumValue(value, key)) - def _scan_class_attrs(self): + @classmethod + def _scan_class_attrs(cls): # Converts static class attributes into EnumValue objects # List of methods and internal names that should not be converted ignored = ('is_value', 'list') - for key in dir(self.__class__): + for key in dir(cls): # Skip internal names and methods if key.startswith('_') or key in ignored: continue - value = getattr(self.__class__, key) + value = getattr(cls, key) # Convert only constants, not methods if not callable(value) and not isinstance(value, EnumValue): - self._update(key, value) + cls._update(key, value) def is_value(self, value): return any(member.value == value for member in self) @@ -100,8 +117,12 @@ def __repr__(self): return f"{self.__class__.__name__}(names={members})" def __call__(self, value): + if not hasattr(self, '_initialized'): + self._scan_class_attrs() + object.__setattr__(self, '_initialized', True) + for member in self: - if member.value == value: + if member.value == value or member.name == value: return member raise AttributeError(f"{value} is not in {self.__class__.__name__}") @@ -118,12 +139,6 @@ def __delattr__(self, 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 __eq__(self, other): if not isinstance(other, Enum): return False @@ -134,28 +149,36 @@ def __eq__(self, other): # --- Usage Example 1 --- # Standard Class Definition class Color(Enum): - RED = 'red' - GREEN = 'green' - + RED = 1 + GREEN = 2 + BLUE = 3 + + print("Color.list():", Color.list()) + print("Color().list():", Color().list()) + + # Iteration + print("Members list:", [member for member in Color()]) + print("Names list:", [member.name for member in Color()]) + print("Values list:", [member.value for member in Color()]) + # Create instance c = Color() print(f"Enum c: {c}") - print("c.list():", c.list()) # Basic access print(f"RED: Name={c.RED.name}, Value={c.RED.value}, EnumValue={c.RED}, Call={c.RED()} ") # Assertions assert c.RED.name == 'RED' - assert c.RED.value == 'red' - assert c.RED == 'red' - assert c.RED() == 'red' + assert c.RED.value == 1 + assert c.RED == 1 + assert c.RED() == 1 # Reverse Lookup via instance call - print(f"c('red') lookup object: {c('red')}, Name={c('red').name}, value={c('red').value}") # RED - assert c('red').name == 'RED' - assert c('red').value == 'red' - assert c('red') == 'red' + #print(f"c(1) lookup object: {c(1)}, Name={c(1).name}, value={c(1).value}") # RED + assert c(1).name == 'RED' + assert c(1).value == 1 + assert c(1) == 1 # Iteration print("Values list:", [member.value for member in c]) diff --git a/python-stdlib/enum/test_enum.py b/python-stdlib/enum/test_enum.py index 0007172d8..95e8396be 100644 --- a/python-stdlib/enum/test_enum.py +++ b/python-stdlib/enum/test_enum.py @@ -36,6 +36,9 @@ def test_call_reverse_lookup(self): result = self.color(1) self.assertEqual(result.name, 'RED') self.assertEqual(result.value, 1) + result = self.color('RED') + self.assertEqual(result.name, 'RED') + self.assertEqual(result.value, 1) # Перевірка виключення для неіснуючого значення with self.assertRaises(AttributeError): diff --git a/python-stdlib/enum/test_enum_.py b/python-stdlib/enum/test_enum_.py index 1ce8b21b9..eb259161f 100644 --- a/python-stdlib/enum/test_enum_.py +++ b/python-stdlib/enum/test_enum_.py @@ -104,7 +104,7 @@ def test_len_and_list(self): """Test utility functions like __len__ and list.""" self.assertEqual(len(self.color_inst), 3) members_list = self.color_inst.list() - self.assertEqual(members_list, [self.color_inst.BLUE, self.color_inst.GREEN, self.color_inst.RED]) + self.assertEqual(members_list, [self.color_inst.RED, self.color_inst.GREEN, self.color_inst.BLUE]) def test_deletion_prevention(self): """Verify that members cannot be deleted.""" From 3ba26928364bb80c6b4c209122ed6b25b5afc9d9 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Thu, 23 Apr 2026 15:39:38 +0300 Subject: [PATCH 10/14] enum.md: Compare with CPython. Signed-off-by: Ihor Nehrutsa --- python-stdlib/enum/enum.md | 63 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/python-stdlib/enum/enum.md b/python-stdlib/enum/enum.md index fae553a93..baf75cf22 100644 --- a/python-stdlib/enum/enum.md +++ b/python-stdlib/enum/enum.md @@ -114,3 +114,66 @@ The base class for all enumerations. * Raised when attempting to add new members to an initialized Enum. * Raised when a class-level lookup (`Status(999)`) fails. * Raised when an instance-level lookup (`s(999)`) fails. + +## Compare with CPython + +```python +# Run on MicroPython v1.28.0 on 2026-04-06; Generic ESP32 module with ESP32 +# Run on Python 3.12.10 +from enum import Enum + +# class syntax +class Color(Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + +# OR +# functional syntax +# Color = Enum('Color', {'RED': 1, 'GREEN': 2, 'BLUE': 3}) + +# List enum members +try: + print(list(Color)) +# [, , ] +except: + print(Color.list()) +# [RED: 1, GREEN: 2, BLUE: 3] + +# Accessing enum member by name +print(Color.GREEN, type(Color.GREEN)) +# Color.GREEN +# GREEN: 2 + +# Accessing enum member by name +try: + print(Color['GREEN']) +# Color.GREEN +except: + print(Color('GREEN')) +# GREEN: 2 + +# Accessing enum member by value +print(Color(2)) +# Color.GREEN + +# Accessing enum member name +print(Color.GREEN.name, type(Color.GREEN.name)) +# GREEN + +# Accessing enum member value +print(Color.GREEN.value, type(Color.GREEN.value)) +# 2 +``` + +### Output is: + +| MicroPython v1.28.0 | Python 3.12.10 | +| :--- | :--- | +| [RED: 1, GREEN: 2, BLUE: 3] | [, , ] | +| GREEN: 2 | Color.GREEN | +| GREEN: 2 | Color.GREEN | +| GREEN: 2 | Color.GREEN | +| GREEN | GREEN | +| 2 | 2 | + From 1ce2cc6a4ab550731df8661abcaa721e70b061bf Mon Sep 17 00:00:00 2001 From: Ihor Nehrutsa Date: Thu, 23 Apr 2026 20:10:53 +0300 Subject: [PATCH 11/14] enum.py: ruff formatter. --- python-stdlib/enum/enum.py | 51 +++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/python-stdlib/enum/enum.py b/python-stdlib/enum/enum.py index 366f7383b..fb1ab3827 100644 --- a/python-stdlib/enum/enum.py +++ b/python-stdlib/enum/enum.py @@ -5,8 +5,8 @@ class EnumValue: # An immutable object representing a specific enum member def __init__(self, value, name): - object.__setattr__(self, 'value', value) - object.__setattr__(self, 'name', name) + object.__setattr__(self, "value", value) + object.__setattr__(self, "name", name) def __repr__(self): return f"{self.name}: {self.value}" @@ -30,30 +30,30 @@ def __new__(cls, name=None, names=None): if cls is not Enum: return cls._lookup(name) - # Scenario 2: Functional API (e.g., Enum('Color', {'RED': 1})) + # Scenario 2: Functional API (e.g., Enum("Color", {"RED": 1})) return super(Enum, cls).__new__(cls) def __init__(self, name=None, names=None): - if hasattr(self, '_initialized'): + if hasattr(self, "_initialized"): return # 1. Convert class-level attributes (constants) to EnumValue objects self._scan_class_attrs() - # Support Functional API: Enum('Name', {'KEY': VALUE}) + # Support Functional API: Enum("Name", {"KEY": VALUE}) if name is not None and isinstance(names, dict): for key, value in names.items(): # Prevent addition if the key already exists if not hasattr(self, key): self._update(key, value) - object.__setattr__(self, '_initialized', True) + object.__setattr__(self, "_initialized", True) @classmethod def _lookup(cls, value): # Finds an EnumValue by its raw value for key in dir(cls): - if key.startswith('_'): + if key.startswith("_"): continue attr = getattr(cls, key) if isinstance(attr, EnumValue) and (attr.value == value or attr.name == value): @@ -65,12 +65,12 @@ def _lookup(cls, value): @classmethod def __iter__(cls): - if '_initialized' not in cls.__dict__: + if "_initialized" not in cls.__dict__: cls._scan_class_attrs() - setattr(cls, '_initialized', True) + setattr(cls, "_initialized", True) for key in dir(cls): - if key.startswith('_'): + if key.startswith("_"): continue attr = getattr(cls, key) if isinstance(attr, EnumValue): @@ -78,13 +78,12 @@ def __iter__(cls): @classmethod def list(cls): - if '_initialized' not in cls.__dict__: + if "_initialized" not in cls.__dict__: cls._scan_class_attrs() - setattr(cls, '_initialized', True) + setattr(cls, "_initialized", True) # Returns a list of all members - return [getattr(cls, key) for key in dir(cls) - if isinstance(getattr(cls, key), EnumValue)] + return [getattr(cls, key) for key in dir(cls) if isinstance(getattr(cls, key), EnumValue)] @classmethod def _update(cls, key, value): @@ -94,10 +93,10 @@ def _update(cls, key, value): def _scan_class_attrs(cls): # Converts static class attributes into EnumValue objects # List of methods and internal names that should not be converted - ignored = ('is_value', 'list') + ignored = ("is_value", "list") for key in dir(cls): # Skip internal names and methods - if key.startswith('_') or key in ignored: + if key.startswith("_") or key in ignored: continue value = getattr(cls, key) @@ -111,15 +110,15 @@ def is_value(self, value): def __repr__(self): # Supports the condition: obj == eval(repr(obj)) members = {member.name: member.value for member in self} - if self.__class__.__name__ == 'Enum': + if self.__class__.__name__ == "Enum": return f"Enum(name='Enum', names={members})" - # Return a string like: Name(names={'KEY1': VALUE1, 'KEY2': VALUE2, ..}) + # Return a string like: Name(names={"KEY1": VALUE1, "KEY2": VALUE2, ..}) return f"{self.__class__.__name__}(names={members})" def __call__(self, value): - if not hasattr(self, '_initialized'): + if not hasattr(self, "_initialized"): self._scan_class_attrs() - object.__setattr__(self, '_initialized', True) + object.__setattr__(self, "_initialized", True) for member in self: if member.value == value or member.name == value: @@ -127,7 +126,7 @@ def __call__(self, value): raise AttributeError(f"{value} is not in {self.__class__.__name__}") def __setattr__(self, key, value): - if hasattr(self, '_initialized'): + if hasattr(self, "_initialized"): raise AttributeError(f"Enum '{self.__class__.__name__}' is static") super().__setattr__(key, value) @@ -145,7 +144,7 @@ def __eq__(self, other): return self.list() == other.list() -if __name__ == '__main__': +if __name__ == "__main__": # --- Usage Example 1 --- # Standard Class Definition class Color(Enum): @@ -169,14 +168,14 @@ class Color(Enum): print(f"RED: Name={c.RED.name}, Value={c.RED.value}, EnumValue={c.RED}, Call={c.RED()} ") # Assertions - assert c.RED.name == 'RED' + assert c.RED.name == "RED" assert c.RED.value == 1 assert c.RED == 1 assert c.RED() == 1 # Reverse Lookup via instance call - #print(f"c(1) lookup object: {c(1)}, Name={c(1).name}, value={c(1).value}") # RED - assert c(1).name == 'RED' + print(f"c(1) lookup object: {c(1)}, Name={c(1).name}, value={c(1).value}") # RED + assert c(1).name == "RED" assert c(1).value == 1 assert c(1) == 1 @@ -242,6 +241,6 @@ class Status(Enum): state = eval("Enum(name='State', names={'ON':1, 'OFF':2})") print(f"Functional Enum instance (state): {state}") assert state.ON == 1 - assert state.ON.name == 'ON' + assert state.ON.name == "ON" print("\nAll tests passed successfully!") From a4e3daa81a5ef12a313f388ec461759aec1a98cf Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Fri, 24 Apr 2026 13:58:48 +0300 Subject: [PATCH 12/14] enum: Fix obj==eval(repr(obj)). Signed-off-by: Ihor Nehrutsa --- python-stdlib/enum/enum.py | 257 +++++++++++++++++-------------- python-stdlib/enum/test_enum.py | 95 +++++++++--- python-stdlib/enum/test_enum_.py | 115 -------------- 3 files changed, 216 insertions(+), 251 deletions(-) delete mode 100644 python-stdlib/enum/test_enum_.py diff --git a/python-stdlib/enum/enum.py b/python-stdlib/enum/enum.py index fb1ab3827..7c9759245 100644 --- a/python-stdlib/enum/enum.py +++ b/python-stdlib/enum/enum.py @@ -1,12 +1,12 @@ # enum.py -# version="1.2.4" +# version="1.2.5" class EnumValue: # An immutable object representing a specific enum member - def __init__(self, value, name): - object.__setattr__(self, "value", value) - object.__setattr__(self, "name", name) + def __init__(self, v, n): + object.__setattr__(self, "value", v) + object.__setattr__(self, "name", n) def __repr__(self): return f"{self.name}: {self.value}" @@ -14,134 +14,107 @@ def __repr__(self): def __call__(self): return self.value - def __eq__(self, other): - if isinstance(other, EnumValue): - return self.value == other.value - return self.value == other + def __eq__(self, o): + return self.value == (o.value if isinstance(o, EnumValue) else o) - def __setattr__(self, key, value): + def __setattr__(self, k, v): raise AttributeError("EnumValue is immutable") class Enum: def __new__(cls, name=None, names=None): - # Scenario 1: Reverse lookup by value (e.g., Status(1)) - if name is not None and names is None: - if cls is not Enum: - return cls._lookup(name) - - # Scenario 2: Functional API (e.g., Enum("Color", {"RED": 1})) - return super(Enum, cls).__new__(cls) + # If a name and names are provided, create a NEW subclass of Enum + if name and names: + # Support Functional API: Enum("Name", {"KEY": VALUE}) + # Dynamically create: class + new_cls = type(name, (cls, ), {}) + for k, v in names.items(): + new_cls._up(k, v) + new_cls._inited = True + return super().__new__(new_cls) + + # Reverse lookup by value or name (e.g., Color(1) or Color("RED")) + if name and not names and cls is not Enum: + return cls._lookup(name) + + return super().__new__(cls) def __init__(self, name=None, names=None): - if hasattr(self, "_initialized"): - return - - # 1. Convert class-level attributes (constants) to EnumValue objects - self._scan_class_attrs() - - # Support Functional API: Enum("Name", {"KEY": VALUE}) - if name is not None and isinstance(names, dict): - for key, value in names.items(): - # Prevent addition if the key already exists - if not hasattr(self, key): - self._update(key, value) - - object.__setattr__(self, "_initialized", True) + if "_inited" not in self.__class__.__dict__: + self._scan() @classmethod - def _lookup(cls, value): - # Finds an EnumValue by its raw value - for key in dir(cls): - if key.startswith("_"): - continue - attr = getattr(cls, key) - if isinstance(attr, EnumValue) and (attr.value == value or attr.name == value): - return attr - if not callable(attr) and attr == value: - # Wrap static numbers found in class definition - return EnumValue(attr, key) - raise AttributeError(f"{value} is not in {cls.__name__}") + def _lookup(cls, v): + if "_inited" not in cls.__dict__: + cls._scan() + + # Finds an EnumValue by its raw value or name + for k in dir(cls): + a = getattr(cls, k) + if isinstance(a, EnumValue) and (a.value == v or a.name == v): + return a + raise AttributeError(f"{v} is not in {cls.__name__}") @classmethod def __iter__(cls): - if "_initialized" not in cls.__dict__: - cls._scan_class_attrs() - setattr(cls, "_initialized", True) - - for key in dir(cls): - if key.startswith("_"): - continue - attr = getattr(cls, key) + if "_inited" not in cls.__dict__: + cls._scan() + + for k in dir(cls): + attr = getattr(cls, k) if isinstance(attr, EnumValue): yield attr @classmethod def list(cls): - if "_initialized" not in cls.__dict__: - cls._scan_class_attrs() - setattr(cls, "_initialized", True) + if "_inited" not in cls.__dict__: + cls._scan() # Returns a list of all members - return [getattr(cls, key) for key in dir(cls) if isinstance(getattr(cls, key), EnumValue)] + return [getattr(cls, k) for k in dir(cls) if isinstance(getattr(cls, k), EnumValue)] @classmethod - def _update(cls, key, value): - setattr(cls, key, EnumValue(value, key)) + def _up(cls, k, v): + setattr(cls, k, EnumValue(v, k)) @classmethod - def _scan_class_attrs(cls): - # Converts static class attributes into EnumValue objects - # List of methods and internal names that should not be converted - ignored = ("is_value", "list") - for key in dir(cls): - # Skip internal names and methods - if key.startswith("_") or key in ignored: - continue - - value = getattr(cls, key) - # Convert only constants, not methods - if not callable(value) and not isinstance(value, EnumValue): - cls._update(key, value) - - def is_value(self, value): - return any(member.value == value for member in self) + def _scan(cls): + # Convert class-level attributes (constants) to EnumValue objects + for k, v in list(cls.__dict__.items()): + if not k.startswith("_") and not callable(v) and not isinstance(v, EnumValue): + cls._up(k, v) + cls._inited = True + + def is_value(self, v): + return any(m.value == v for m in self) def __repr__(self): # Supports the condition: obj == eval(repr(obj)) - members = {member.name: member.value for member in self} - if self.__class__.__name__ == "Enum": - return f"Enum(name='Enum', names={members})" - # Return a string like: Name(names={"KEY1": VALUE1, "KEY2": VALUE2, ..}) - return f"{self.__class__.__name__}(names={members})" - - def __call__(self, value): - if not hasattr(self, "_initialized"): - self._scan_class_attrs() - object.__setattr__(self, "_initialized", True) - - for member in self: - if member.value == value or member.name == value: - return member - raise AttributeError(f"{value} is not in {self.__class__.__name__}") - - def __setattr__(self, key, value): - if hasattr(self, "_initialized"): - raise AttributeError(f"Enum '{self.__class__.__name__}' is static") - 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) + d = {m.name: m.value for m in self} + # Return a string like: Enum(name='Name', names={'KEY1': VALUE1, 'KEY2': VALUE2, ..}) + return f"Enum(name='{self.__class__.__name__}', names={d})" + + def __call__(self, v): + if "_inited" in self.__class__.__dict__: + self._scan() + + return self._lookup(v) + + def __setattr__(self, k, v): + if "_inited" in self.__class__.__dict__: + raise AttributeError(f"Enum '{self.__class__.__name__}' is immutable") + super().__setattr__(k, v) + + def __delattr__(self, k): + raise AttributeError("Enum is immutable") def __len__(self): return sum(1 for _ in self) - def __eq__(self, other): - if not isinstance(other, Enum): + def __eq__(self, o): + if not isinstance(o, Enum): return False - return self.list() == other.list() + return self.list() == o.list() if __name__ == "__main__": @@ -151,15 +124,14 @@ class Color(Enum): RED = 1 GREEN = 2 BLUE = 3 - + print("Color.list():", Color.list()) - print("Color().list():", Color().list()) - + # Iteration print("Members list:", [member for member in Color()]) print("Names list:", [member.name for member in Color()]) print("Values list:", [member.value for member in Color()]) - + # Create instance c = Color() print(f"Enum c: {c}") @@ -174,15 +146,11 @@ class Color(Enum): assert c.RED() == 1 # Reverse Lookup via instance call - print(f"c(1) lookup object: {c(1)}, Name={c(1).name}, value={c(1).value}") # RED + print(f"c(1) lookup object: {c(1)}, name={c(1).name}, value={c(1).value}") # RED assert c(1).name == "RED" assert c(1).value == 1 assert c(1) == 1 - # Iteration - print("Values list:", [member.value for member in c]) - print("Names list:", [member.name for member in c]) - try: c(999) except AttributeError as e: @@ -200,14 +168,15 @@ class Status(Enum): received_byte = 1 status = Status(received_byte) print(f"Lookup check: Received {received_byte} -> {status}") + assert status == received_byte assert status == Status.RUNNING assert status.name == "RUNNING" + assert status.value == received_byte # Test: Comparisons print(f"Comparison check: {status} == 1 is {status == 1}") assert status == 1 assert status != 0 - assert status == Status.RUNNING # Immutability Check try: @@ -228,19 +197,75 @@ class Status(Enum): print(f"\nAttributeError: Invalid lookup check: Caught expected error -> {e}\n") # --- Example 3: Functional API and serialization --- - print("\n--- Functional API and Eval Check ---") + print("--- Functional API and Eval Check ---") # Verify that eval(repr(obj)) restores the object - c2 = eval(repr(c)) - print(f"Original: {repr(c)}") - print(f"Restored: {repr(c2)}") - print(f"Objects are equal: {c == c2}") - assert c == c2 + c_repr = repr(c) + c_restored = eval(c_repr) + print(f"Original: {c_repr}") + print(f"Restored: {repr(c_restored)}") + print(f"Objects are equal: {c == c_restored}") + assert c == c_restored # Direct creation using the Enum base class state = eval("Enum(name='State', names={'ON':1, 'OFF':2})") print(f"Functional Enum instance (state): {state}") + print(type(state)) assert state.ON == 1 assert state.ON.name == "ON" + # --- 1. Unique Data Types & Class Methods --- + # Enums can hold more than just integers; here we use strings and add a method. + class HttpMethod(Enum): + GET = "GET" + POST = "POST" + DELETE = "DELETE" + + def is_safe(self): + # Demonstrates that custom logic can coexist with Enum members + return self.list()[0] == self.GET # Simplistic example check + + api_call = HttpMethod() + print(f"Member with string value: {api_call.GET}") + assert api_call.GET == "GET" + + # --- 2. Advanced Reverse Lookup Scenarios --- + # Demonstrates lookup by both name string and raw value string. + print(f"Lookup by value 'POST': {api_call('POST')}") + print(f"Lookup by name 'DELETE': {api_call('DELETE')}") + assert api_call("GET").name == "GET" + + # --- 3. Empty Enum Handling --- + # Verifies behavior when no members are defined. + class Empty(Enum): + pass + + empty_inst = Empty() + print(f"Empty Enum list: {empty_inst.list()}") + assert len(empty_inst) == 0 + + # --- 4. Deep Functional API & Serialization --- + # Testing complex name strings and verifying the 'eval' round-trip for functional enums. + complex_enum = Enum(name='Config', names={'MAX_RETRY': 5, 'TIMEOUT_SEC': 30}) + + # Verify serialization maintains the dynamic class name + repr_str = repr(complex_enum) + restored = eval(repr_str) + + print(f"Restored Functional Enum: {restored}") + assert restored.MAX_RETRY == 5 + assert type(restored).__name__ == 'Config' + + # --- 5. Immutability & Integrity Guard --- + # Ensuring the Enum structure cannot be tampered with after creation. + try: + api_call.NEW_METHOD = "PATCH" + except AttributeError as e: + print(f"Caught expected mutation error: {e}") + + try: + del api_call.GET + except AttributeError as e: + print(f"Caught expected deletion error: {e}") + print("\nAll tests passed successfully!") diff --git a/python-stdlib/enum/test_enum.py b/python-stdlib/enum/test_enum.py index 95e8396be..4c17cb347 100644 --- a/python-stdlib/enum/test_enum.py +++ b/python-stdlib/enum/test_enum.py @@ -1,5 +1,5 @@ # test_enum.py -# version="1.2.1" +# version="1.2.5" import unittest from enum import Enum, EnumValue @@ -7,77 +7,132 @@ class TestEnum(unittest.TestCase): def setUp(self): - # Створюємо базовий клас для тестів + # Standard Class Definitions for testing class Color(Enum): RED = 1 GREEN = 2 BLUE = 3 + class Status(Enum): + IDLE = 0 + RUNNING = 1 + ERROR = 2 + self.ColorClass = Color self.color = Color() + self.StatusClass = Status + self.status = Status() def test_class_attributes(self): - """Тест статичних атрибутів, визначених у класі""" + """Test basic access to Enum members, names, and values.""" self.assertEqual(self.color.RED.value, 1) self.assertEqual(self.color.RED.name, 'RED') self.assertIsInstance(self.color.RED, EnumValue) + self.assertEqual(self.status.IDLE.value, 0) def test_comparison(self): - """Тест порівняння EnumValue з числами та іншими об'єктами""" + """Test equality between EnumValues and raw values.""" self.assertTrue(self.color.RED == 1) self.assertFalse(self.color.RED == 2) self.assertEqual(self.color.RED, self.color.RED) - # Перевірка, що об'єкт не дорівнює значенню іншого типу + # Verify object does not equal value of different type self.assertFalse(self.color.RED == "1") def test_call_reverse_lookup(self): - """Тест зворотного пошуку Color(1) -> RED""" - # У вашій реалізації __call__ повертає об'єкт EnumValue + """Test reverse lookup via instance call Color(1) -> RED.""" result = self.color(1) self.assertEqual(result.name, 'RED') self.assertEqual(result.value, 1) + + # Test lookup by name string result = self.color('RED') self.assertEqual(result.name, 'RED') - self.assertEqual(result.value, 1) - # Перевірка виключення для неіснуючого значення + result = self.color(self.ColorClass.RED) + self.assertEqual(result, self.ColorClass.RED) + with self.assertRaises(AttributeError): self.color(999) + def test_constructor_reverse_lookup(self): + """Test reverse lookup via class constructor Status(1) -> RUNNING.""" + member = self.StatusClass(1) + self.assertEqual(member.name, "RUNNING") + self.assertEqual(member, self.StatusClass.RUNNING) + + with self.assertRaises(AttributeError): + self.StatusClass(999) + def test_is_value(self): - """Тест методу перевірки наявності значення is_value()""" + """Test utility method 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__)""" - # Створюємо список імен через ітерацію + """Test iteration over Enum instance.""" members = list(self.color) names = [m.name for m in members] - + self.assertEqual(len(members), 3) self.assertIn('RED', names) self.assertIn('GREEN', names) self.assertIn('BLUE', names) - self.assertEqual(len(members), 3) - def test_enum_value_immutability(self): - """Тест захисту від зміни значень EnumValue""" + def test_immutability(self): + """Verify EnumValue and Enum instance are immutable after init.""" + # EnumValue attribute protection with self.assertRaises(AttributeError): self.color.RED.value = 10 + # Enum instance attribute protection (static) with self.assertRaises(AttributeError): - self.color.RED.name = "NEW_NAME" + self.color.NEW_MEMBER = 4 - def test_len(self): - """Тест магічного методу __len__""" + def test_deletion_prevention(self): + """Verify that members cannot be deleted.""" + with self.assertRaises(AttributeError): + del self.color.RED + + def test_len_and_list(self): + """Test __len__ and list() utility methods.""" self.assertEqual(len(self.color), 3) + members_list = self.color.list() + self.assertEqual(members_list, [self.color.RED, self.color.GREEN, self.color.BLUE]) def test_call_method(self): - """Тест виклику об'єкта як функції c.RED() -> 1""" + """Test calling EnumValue as a function to get its value.""" self.assertEqual(self.color.RED(), 1) self.assertEqual(self.color.GREEN(), 2) + def test_functional_api(self): + """Test dynamic Enum creation using the Functional API.""" + # Logic restored from commented out sections + State = Enum(name='State', names={'ON': 1, 'OFF': 0}) + self.assertTrue(hasattr(State, 'ON')) + self.assertEqual(State.ON.value, 1) + self.assertEqual(State.OFF.name, 'OFF') + + def test_serialization_repr_eval(self): + """Verify eval(repr(obj)) restores the Enum correctly.""" + # Test standard instance + c_repr = repr(self.color) + c_restored = eval(c_repr) + # Check equality of members + self.assertEqual(self.color.list(), c_restored.list()) + self.assertEqual(type(self.color).__name__, type(c_restored).__name__) + + # Test functional instance + s_dynamic = Enum(name='StatusFunc', names={'START': 1, 'STOP': 0}) + s_repr = repr(s_dynamic) + s_restored = eval(s_repr) + self.assertEqual(s_dynamic.list(), s_restored.list()) + + # Test functional instance + s_dynamic = Enum(name='State', names={'ON': 1, 'OFF': 0}) + s_repr = repr(s_dynamic) + s_restored = eval(s_repr) + self.assertEqual(s_dynamic.list(), s_restored.list()) + if __name__ == '__main__': unittest.main() diff --git a/python-stdlib/enum/test_enum_.py b/python-stdlib/enum/test_enum_.py deleted file mode 100644 index eb259161f..000000000 --- a/python-stdlib/enum/test_enum_.py +++ /dev/null @@ -1,115 +0,0 @@ -# test_enum.py -# version="1.2.3" - -import unittest -from enum import Enum, EnumValue - - -class TestEnum(unittest.TestCase): - def setUp(self): - # Define standard Enum classes for testing - class Color(Enum): - RED = 'red' - GREEN = 'green' - BLUE = 'blue' - - class Status(Enum): - IDLE = 0 - RUNNING = 1 - ERROR = 2 - - self.Color = Color - self.Status = Status - self.color_inst = Color() - self.status_inst = Status() - - def test_member_access(self): - """Test basic access to Enum members, names, and values.""" - self.assertEqual(self.color_inst.RED.name, 'RED') - self.assertEqual(self.color_inst.RED.value, 'red') - self.assertEqual(self.status_inst.IDLE.value, 0) - - def test_member_equality(self): - """Test equality between EnumValues and raw values.""" - self.assertTrue(self.color_inst.RED == 'red') - self.assertTrue(self.status_inst.RUNNING == 1) - self.assertFalse(self.status_inst.ERROR == 0) - # Test equality between two EnumValue objects - self.assertEqual(self.color_inst.RED, self.color_inst.RED) - - def test_immutability(self): - """Verify that EnumValue attributes and Enum instances are static.""" - # Test EnumValue immutability - with self.assertRaises(AttributeError): - self.color_inst.RED.value = 'blue' - - # Test Enum instance immutability (once initialized) - with self.assertRaises(AttributeError): - self.color_inst.NEW_COLOR = 'yellow' - - def test_reverse_lookup_via_constructor(self): - """Test reverse lookup by passing a value to the class constructor.""" - # Using __new__ logic for reverse lookup - member = self.Status(1) - self.assertEqual(member.name, "RUNNING") - self.assertEqual(member, self.Status.RUNNING) - - with self.assertRaises(AttributeError): - self.Status(999) - - def test_reverse_lookup_via_instance_call(self): - """Test reverse lookup by calling the instance with a value.""" - # Using __call__ logic for reverse lookup - member = self.color_inst('green') - self.assertEqual(member.name, 'GREEN') - - with self.assertRaises(AttributeError): - self.color_inst('yellow') - - def test_iteration(self): - """Test that the Enum instance is iterable and returns all members.""" - members = list(self.status_inst) - names = [m.name for m in members] - values = [m.value for m in members] - - self.assertEqual(len(members), 3) - self.assertIn('IDLE', names) - self.assertIn(2, values) - - -# def test_functional_api(self): -# """Test dynamic Enum creation using the Functional API.""" -# # Dynamic creation via Enum(name, names_dict) -# State = Enum(name='State', names={'ON': 1, 'OFF': 0}) -# -# self.assertTrue(hasattr(State, 'ON')) -# self.assertEqual(State.ON.value, 1) -# self.assertEqual(State.OFF.name, 'OFF') - -# def test_serialization_repr_eval(self): -# """Verify that eval(repr(obj)) restores the Enum instance correctly.""" -# # Test standard class instance -# c_repr = repr(self.color_inst) -# print('c_repr', c_repr) -# c_restored = eval(c_repr) -# self.assertEqual(self.color_inst, c_restored) -# -# # Test dynamically created instance -# s_dynamic = Enum(name='State', names={'ON': 1, 'OFF': 0}) -# s_repr = repr(s_dynamic) -# s_restored = eval(s_repr) -# self.assertEqual(s_dynamic, s_restored) - - def test_len_and_list(self): - """Test utility functions like __len__ and list.""" - self.assertEqual(len(self.color_inst), 3) - members_list = self.color_inst.list() - self.assertEqual(members_list, [self.color_inst.RED, self.color_inst.GREEN, self.color_inst.BLUE]) - - def test_deletion_prevention(self): - """Verify that members cannot be deleted.""" - with self.assertRaises(AttributeError): - del self.color_inst.RED - -if __name__ == '__main__': - unittest.main() From c3c61fcd768adecd3521a53b6b3bf3a7f0299f96 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Mon, 27 Apr 2026 14:27:06 +0300 Subject: [PATCH 13/14] enum: Add arithmetic, bitwise and comparison. Signed-off-by: Ihor Nehrutsa --- python-stdlib/enum/enum.py | 111 +++++++++++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 5 deletions(-) diff --git a/python-stdlib/enum/enum.py b/python-stdlib/enum/enum.py index 7c9759245..6ddeb0811 100644 --- a/python-stdlib/enum/enum.py +++ b/python-stdlib/enum/enum.py @@ -1,5 +1,5 @@ # enum.py -# version="1.2.5" +# version="1.2.6" class EnumValue: @@ -14,12 +14,113 @@ def __repr__(self): def __call__(self): return self.value - def __eq__(self, o): - return self.value == (o.value if isinstance(o, EnumValue) else o) - def __setattr__(self, k, v): raise AttributeError("EnumValue is immutable") + # Helper function to extract the raw value + def _get_value(self, o): + return o.value if isinstance(o, EnumValue) else o + + # Arithmetic and Bitwise operations (Forward) + def __add__(self, o): + return self.value + self._get_value(o) + + def __sub__(self, o): + return self.value - self._get_value(o) + + def __mul__(self, o): + return self.value * self._get_value(o) + + def __truediv__(self, o): + return self.value / self._get_value(o) + + def __floordiv__(self, o): + return self.value // self._get_value(o) + + def __mod__(self, o): + return self.value % self._get_value(o) + + def __pow__(self, o): + return self.value ** self._get_value(o) + + def __and__(self, o): + return self.value & self._get_value(o) + + def __or__(self, o): + return self.value | self._get_value(o) + + def __xor__(self, o): + return self.value ^ self._get_value(o) + + def __lshift__(self, o): + return self.value << self._get_value(o) + + def __rshift__(self, o): + return self.value >> self._get_value(o) + + # Arithmetic and Bitwise operations (Reflected) + def __radd__(self, o): + return self._get_value(o) + self.value + + def __rsub__(self, o): + return self._get_value(o) - self.value + + def __rmul__(self, o): + return self._get_value(o) * self.value + + def __rtruediv__(self, o): + return self._get_value(o) / self.value + + def __rfloordiv__(self, o): + return self._get_value(o) // self.value + + def __rand__(self, o): + return self._get_value(o) & self.value + + def __ror__(self, o): + return self._get_value(o) | self.value + + def __rxor__(self, o): + return self._get_value(o) ^ self.value + + def __rlshift__(self, o): + return self._get_value(o) << self.value + + def __rrshift__(self, o): + return self._get_value(o) >> self.value + + # Unary operators + def __neg__(self): + return -self.value + + def __pos__(self): + return +self.value + + def __abs__(self): + return abs(self.value) + + def __invert__(self): + return ~self.value + + # Comparison + def __eq__(self, o): + return self.value == self._get_value(o) + + def __lt__(self, o): + return self.value < self._get_value(o) + + def __le__(self, o): + return self.value <= self._get_value(o) + + def __gt__(self, o): + return self.value > self._get_value(o) + + def __ge__(self, o): + return self.value >= self._get_value(o) + + def __ne__(self, o): + return self.value != self._get_value(o) + class Enum: def __new__(cls, name=None, names=None): @@ -27,7 +128,7 @@ def __new__(cls, name=None, names=None): if name and names: # Support Functional API: Enum("Name", {"KEY": VALUE}) # Dynamically create: class - new_cls = type(name, (cls, ), {}) + new_cls = type(name, (cls,), {}) for k, v in names.items(): new_cls._up(k, v) new_cls._inited = True From 826abb507d4e16c8b5eaeb6ddee2d594c055ee06 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Tue, 28 Apr 2026 17:31:01 +0300 Subject: [PATCH 14/14] enum: Version 1.3.0. Signed-off-by: Ihor Nehrutsa --- python-stdlib/enum/enum.md | 56 +++++--- python-stdlib/enum/enum.py | 223 +++++++++----------------------- python-stdlib/enum/manifest.py | 2 +- python-stdlib/enum/test_enum.py | 7 +- 4 files changed, 105 insertions(+), 183 deletions(-) diff --git a/python-stdlib/enum/enum.md b/python-stdlib/enum/enum.md index baf75cf22..6d1184bc4 100644 --- a/python-stdlib/enum/enum.md +++ b/python-stdlib/enum/enum.md @@ -30,10 +30,17 @@ class Color(Enum): # Initialize the enum to process attributes c = Color() -print(c.RED) # Output: RED: red -print(c.RED.name) # Output: RED -print(c.RED.value) # Output: red -print(c.RED()) # Output: red +print(c.RED) # Output: RED: red +print(c.RED.name) # Output: RED +print(c.RED.value) # Output: red +print(c.RED()) # Output: red +print(c.is_value("RED")) # Output: true +print(c.is_value(Color.RED)) # Output: true +print(c.is_value('red')) # Output: true +print(c.list()) # Output: [Color.RED: red, Color.GREEN: green] +print([m for m in c]) # Output: [Color.RED: red, Color.GREEN: green] +print([m.name for m in c]) # Output: ['RED', 'GREEN'] +print([m.value for m in c]) # Output: ['red', 'green'] ``` @@ -48,14 +55,16 @@ class Status(Enum): # Method A: Via Class (Simulates interpreting hardware/network bytes) # Uses __new__ logic to return the correct EnumValue current_status = Status(1) -print(current_status.name) # Output: RUNNING -print(current_status) # Output: RUNNING: 1 -print(current_status()) # Output: 1 +print(current_status.name) # Output: RUNNING +print(current_status.value) # Output: 1 +print(current_status) # Output: Status.RUNNING: 1 +print(current_status()) # Output: 1 # Method B: Via Instance Call s = Status() print(s(0).name) # Output: IDLE -print(s(0)) # Output: IDLE: 0 +print(s(0).value) # Output: 0 +print(s(0)) # Output: Status.IDLE: 0 print(s(0)()) # Output: 0 ``` @@ -67,8 +76,15 @@ If you need to create an Enum from external data (like a JSON config), use the f # Create a dynamic Enum instance State = Enum(name='State', names={'ON': 1, 'OFF': 2}) -print(State.ON) # Output: ON: 1 -assert State.ON == 1 # Comparison with raw value +print(State) # Output: Enum(name='State', names={'ON': 1, 'OFF': 2}) +print(State.ON) # Output: State.ON: 1 +print(State.ON.name) # Output: ON +print(State.ON.value) # Output: 1 +print(State.ON()) # Output: 1 +assert State.ON == 1 # Comparison +assert State.ON() == 1 # +assert State.ON.value == 1 # +assert State.ON.name == "ON" # ``` @@ -76,16 +92,22 @@ assert State.ON == 1 # Comparison with raw value The library ensures that the string representation can be used to perfectly reconstruct the object. ```python +from enum import Enum + +class Color(Enum): + RED = 'red' + GREEN = 'green' + BLUE = 3 + colors = Color() # Get serialized string serialized = repr(colors) # Reconstruct object restored_colors = eval(serialized) -print(f"Original: {colors}") # Output: Original: Color(names={'ON': 1, 'OFF': 2, 'GREEN': 'green', 'RED': 'red'}) -print(f"Restored: {restored_colors}") # Output: Restored: Color(names={'ON': 1, 'OFF': 2, 'GREEN': 'green', 'RED': 'red'}) +print(f"Original: {colors}") # Output: Original: Enum(name='Color', names={'BLUE': 3, 'RED': 'red', 'GREEN': 'green'}) +print(f"Restored: {restored_colors}") # Output: Restored: Enum(name='Color', names={'BLUE': 3, 'RED': 'red', 'GREEN': 'green'}) print(colors == restored_colors) # Output: True - ``` @@ -170,10 +192,10 @@ print(Color.GREEN.value, type(Color.GREEN.value)) | MicroPython v1.28.0 | Python 3.12.10 | | :--- | :--- | -| [RED: 1, GREEN: 2, BLUE: 3] | [, , ] | -| GREEN: 2 | Color.GREEN | -| GREEN: 2 | Color.GREEN | -| GREEN: 2 | Color.GREEN | +| [Color.RED: 1, Color.GREEN: 2, Color.BLUE: 3] | [, , ] | +| Color.GREEN: 2 | Color.GREEN | +| Color.GREEN: 2 | Color.GREEN | +| Color.GREEN: 2 | Color.GREEN | | GREEN | GREEN | | 2 | 2 | diff --git a/python-stdlib/enum/enum.py b/python-stdlib/enum/enum.py index 6ddeb0811..1112966a5 100644 --- a/python-stdlib/enum/enum.py +++ b/python-stdlib/enum/enum.py @@ -1,204 +1,83 @@ # enum.py -# version="1.2.6" +# version="1.3.0" -class EnumValue: - # An immutable object representing a specific enum member - def __init__(self, v, n): - object.__setattr__(self, "value", v) - object.__setattr__(self, "name", n) +def _make_enum(v, n, e): + T = type(v) - def __repr__(self): - return f"{self.name}: {self.value}" - - def __call__(self): - return self.value - - def __setattr__(self, k, v): + def _setattr(self, k, v): raise AttributeError("EnumValue is immutable") - # Helper function to extract the raw value - def _get_value(self, o): - return o.value if isinstance(o, EnumValue) else o - - # Arithmetic and Bitwise operations (Forward) - def __add__(self, o): - return self.value + self._get_value(o) - - def __sub__(self, o): - return self.value - self._get_value(o) - - def __mul__(self, o): - return self.value * self._get_value(o) - - def __truediv__(self, o): - return self.value / self._get_value(o) - - def __floordiv__(self, o): - return self.value // self._get_value(o) - - def __mod__(self, o): - return self.value % self._get_value(o) - - def __pow__(self, o): - return self.value ** self._get_value(o) - - def __and__(self, o): - return self.value & self._get_value(o) - - def __or__(self, o): - return self.value | self._get_value(o) - - def __xor__(self, o): - return self.value ^ self._get_value(o) - - def __lshift__(self, o): - return self.value << self._get_value(o) - - def __rshift__(self, o): - return self.value >> self._get_value(o) - - # Arithmetic and Bitwise operations (Reflected) - def __radd__(self, o): - return self._get_value(o) + self.value - - def __rsub__(self, o): - return self._get_value(o) - self.value - - def __rmul__(self, o): - return self._get_value(o) * self.value - - def __rtruediv__(self, o): - return self._get_value(o) / self.value - - def __rfloordiv__(self, o): - return self._get_value(o) // self.value - - def __rand__(self, o): - return self._get_value(o) & self.value - - def __ror__(self, o): - return self._get_value(o) | self.value - - def __rxor__(self, o): - return self._get_value(o) ^ self.value - - def __rlshift__(self, o): - return self._get_value(o) << self.value - - def __rrshift__(self, o): - return self._get_value(o) >> self.value - - # Unary operators - def __neg__(self): - return -self.value - - def __pos__(self): - return +self.value - - def __abs__(self): - return abs(self.value) - - def __invert__(self): - return ~self.value - - # Comparison - def __eq__(self, o): - return self.value == self._get_value(o) - - def __lt__(self, o): - return self.value < self._get_value(o) - - def __le__(self, o): - return self.value <= self._get_value(o) - - def __gt__(self, o): - return self.value > self._get_value(o) - - def __ge__(self, o): - return self.value >= self._get_value(o) - - def __ne__(self, o): - return self.value != self._get_value(o) + # Create class: type(name, bases, dict), which inherits a base type (int, str, etc.) + return type( + "EnumValue", + (T,), + { + "name": n, + "value": property(lambda s: v), + "__repr__": lambda s: f"{e}.{n}: {v}", + "__str__": lambda s: f"{e}.{n}: {v}", + "__call__": lambda s: v, + "__setattr__": _setattr, + }, + )(v) class Enum: def __new__(cls, name=None, names=None): # If a name and names are provided, create a NEW subclass of Enum if name and names: - # Support Functional API: Enum("Name", {"KEY": VALUE}) + # Support Functional API: Enum("Name", {"KEY1": VALUE1, "KEY2": VALUE2, ..}) # Dynamically create: class - new_cls = type(name, (cls,), {}) + new_cls = type(name, (cls,), {"_inited": True}) for k, v in names.items(): - new_cls._up(k, v) - new_cls._inited = True + setattr(new_cls, k, _make_enum(v, k, name)) return super().__new__(new_cls) # Reverse lookup by value or name (e.g., Color(1) or Color("RED")) - if name and not names and cls is not Enum: + if name and cls is not Enum: return cls._lookup(name) return super().__new__(cls) def __init__(self, name=None, names=None): if "_inited" not in self.__class__.__dict__: - self._scan() + self.list() @classmethod def _lookup(cls, v): - if "_inited" not in cls.__dict__: - cls._scan() - - # Finds an EnumValue by its raw value or name - for k in dir(cls): - a = getattr(cls, k) - if isinstance(a, EnumValue) and (a.value == v or a.name == v): - return a + for m in cls.list(): + if m.value == v or m.name == v: + return m raise AttributeError(f"{v} is not in {cls.__name__}") @classmethod def __iter__(cls): - if "_inited" not in cls.__dict__: - cls._scan() - - for k in dir(cls): - attr = getattr(cls, k) - if isinstance(attr, EnumValue): - yield attr + return iter(cls.list()) @classmethod def list(cls): if "_inited" not in cls.__dict__: - cls._scan() - - # Returns a list of all members - return [getattr(cls, k) for k in dir(cls) if isinstance(getattr(cls, k), EnumValue)] + # Copy dict.items() to avoid RuntimeError when changing the dictionary + for k, v in list(cls.__dict__.items()): + if not k.startswith("_") and not callable(v): + setattr(cls, k, _make_enum(v, k, cls.__name__)) + cls._inited = True + return [ + m for k in dir(cls) if not k.startswith("_") and hasattr(m := getattr(cls, k), "name") + ] @classmethod - def _up(cls, k, v): - setattr(cls, k, EnumValue(v, k)) - - @classmethod - def _scan(cls): - # Convert class-level attributes (constants) to EnumValue objects - for k, v in list(cls.__dict__.items()): - if not k.startswith("_") and not callable(v) and not isinstance(v, EnumValue): - cls._up(k, v) - cls._inited = True - - def is_value(self, v): - return any(m.value == v for m in self) + def is_value(cls, v): + return any(m.value == v or m.name == v for m in cls.list()) def __repr__(self): # Supports the condition: obj == eval(repr(obj)) - d = {m.name: m.value for m in self} + d = {m.name: m.value for m in self.__class__.list()} # Return a string like: Enum(name='Name', names={'KEY1': VALUE1, 'KEY2': VALUE2, ..}) return f"Enum(name='{self.__class__.__name__}', names={d})" def __call__(self, v): - if "_inited" in self.__class__.__dict__: - self._scan() - return self._lookup(v) def __setattr__(self, k, v): @@ -209,8 +88,9 @@ def __setattr__(self, k, v): def __delattr__(self, k): raise AttributeError("Enum is immutable") - def __len__(self): - return sum(1 for _ in self) + @classmethod + def __len__(cls): + return len(cls.list()) def __eq__(self, o): if not isinstance(o, Enum): @@ -226,19 +106,26 @@ class Color(Enum): GREEN = 2 BLUE = 3 + # Basic access + print(f"RED: repr={repr(Color.RED)}, type={type(Color.RED)}, {Color(1).name} ") + print(f"RED: name={Color.RED.name}, value={Color.RED.value}, str={str(Color.RED)}, call={Color.RED()} ") + assert Color(1).value == 1 + assert Color.BLUE.value >= Color.GREEN.value + print("Color.list():", Color.list()) # Iteration print("Members list:", [member for member in Color()]) print("Names list:", [member.name for member in Color()]) print("Values list:", [member.value for member in Color()]) + print() # Create instance c = Color() print(f"Enum c: {c}") # Basic access - print(f"RED: Name={c.RED.name}, Value={c.RED.value}, EnumValue={c.RED}, Call={c.RED()} ") + print(f"RED: name={c.RED.name}, value={c.RED.value}, str={str(c.RED)}, call={c.RED()} ") # Assertions assert c.RED.name == "RED" @@ -247,13 +134,15 @@ class Color(Enum): assert c.RED() == 1 # Reverse Lookup via instance call - print(f"c(1) lookup object: {c(1)}, name={c(1).name}, value={c(1).value}") # RED + o = c(1) + print(f"c(1) lookup object: {o}, name={o.name}, value={o.value}") assert c(1).name == "RED" assert c(1).value == 1 assert c(1) == 1 try: c(999) + 0 / 0 except AttributeError as e: print(f"\nAttributeError: {e}: {c}\n") @@ -269,6 +158,8 @@ class Status(Enum): received_byte = 1 status = Status(received_byte) print(f"Lookup check: Received {received_byte} -> {status}") + print(Status.__len__()) + print(len(Status())) assert status == received_byte assert status == Status.RUNNING assert status.name == "RUNNING" @@ -282,6 +173,7 @@ class Status(Enum): # Immutability Check try: Status.RUNNING.value = 999 + 0 / 0 except AttributeError as e: print(f"\nImmutability check: Passed (Cannot modify EnumValue): {e}\n") @@ -294,6 +186,7 @@ class Status(Enum): # Test: Error handling for invalid lookup try: Status(999) + 0 / 0 except AttributeError as e: print(f"\nAttributeError: Invalid lookup check: Caught expected error -> {e}\n") @@ -302,8 +195,8 @@ class Status(Enum): # Verify that eval(repr(obj)) restores the object c_repr = repr(c) - c_restored = eval(c_repr) print(f"Original: {c_repr}") + c_restored = eval(c_repr) print(f"Restored: {repr(c_restored)}") print(f"Objects are equal: {c == c_restored}") assert c == c_restored @@ -314,6 +207,8 @@ class Status(Enum): print(type(state)) assert state.ON == 1 assert state.ON.name == "ON" + assert state.ON > 0 + assert state.ON.value | state.OFF.value == 3 # --- 1. Unique Data Types & Class Methods --- # Enums can hold more than just integers; here we use strings and add a method. @@ -361,11 +256,13 @@ class Empty(Enum): # Ensuring the Enum structure cannot be tampered with after creation. try: api_call.NEW_METHOD = "PATCH" + 0 / 0 except AttributeError as e: print(f"Caught expected mutation error: {e}") try: del api_call.GET + 0 / 0 except AttributeError as e: print(f"Caught expected deletion error: {e}") diff --git a/python-stdlib/enum/manifest.py b/python-stdlib/enum/manifest.py index 3046f9b87..137ec22aa 100644 --- a/python-stdlib/enum/manifest.py +++ b/python-stdlib/enum/manifest.py @@ -1,3 +1,3 @@ -metadata(version="1.2.0") +metadata(version="1.3.0") module("enum.py") diff --git a/python-stdlib/enum/test_enum.py b/python-stdlib/enum/test_enum.py index 4c17cb347..caa8bee64 100644 --- a/python-stdlib/enum/test_enum.py +++ b/python-stdlib/enum/test_enum.py @@ -1,8 +1,8 @@ # test_enum.py -# version="1.2.5" +# version="1.3.0" import unittest -from enum import Enum, EnumValue +from enum import Enum class TestEnum(unittest.TestCase): @@ -27,6 +27,9 @@ def test_class_attributes(self): """Test basic access to Enum members, names, and values.""" self.assertEqual(self.color.RED.value, 1) self.assertEqual(self.color.RED.name, 'RED') + self.assertEqual(str(type(self.color.RED)), "") + self.assertEqual(type(self.color.RED).__name__, 'EnumValue') + EnumValue = type(self.color.RED) self.assertIsInstance(self.color.RED, EnumValue) self.assertEqual(self.status.IDLE.value, 0)