From ca814f20889f0d1aed1eda064d3c45915960bfbd Mon Sep 17 00:00:00 2001 From: Ihor Nehrutsa Date: Wed, 5 Mar 2025 12:24:05 +0200 Subject: [PATCH 1/2] python-stdlib\enum\enum.py: Add Enum class. Signed-off-by: Ihor Nehrutsa --- python-stdlib/enum/README.md | 201 ++++++++++++++++++++++++ python-stdlib/enum/enum.py | 269 ++++++++++++++++++++++++++++++++ python-stdlib/enum/manifest.py | 3 + python-stdlib/enum/test_enum.py | 141 +++++++++++++++++ tools/ci.sh | 1 + 5 files changed, 615 insertions(+) create mode 100644 python-stdlib/enum/README.md 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/README.md b/python-stdlib/enum/README.md new file mode 100644 index 000000000..6d1184bc4 --- /dev/null +++ b/python-stdlib/enum/README.md @@ -0,0 +1,201 @@ +# Enum Library + +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. + +--- + +## Core Features +* **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. 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 Color(Enum): + RED = 'red' + GREEN = 'green' + +# 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.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'] +``` + + +### 2. Reverse Lookup +The library provides two ways to find a member based on its raw value. + +```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.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).value) # Output: 0 +print(s(0)) # Output: Status.IDLE: 0 +print(s(0)()) # Output: 0 +``` + + +### 3. Functional API (Dynamic Creation) +If you need to create an Enum from external data (like a JSON config), use the functional constructor. + +```python +# Create a dynamic Enum instance +State = Enum(name='State', names={'ON': 1, 'OFF': 2}) + +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" # +``` + + +### 4. Serialization (Repr / Eval) +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: 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 +``` + + +--- + +## API Reference + +### `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()`: 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]`). + +--- + +## 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. + * 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 | +| :--- | :--- | +| [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 new file mode 100644 index 000000000..28472b4e7 --- /dev/null +++ b/python-stdlib/enum/enum.py @@ -0,0 +1,269 @@ +# enum.py +# version="1.3.0" + + +def _make_enum(v, n, e): + T = type(v) + + def _setattr(self, k, v): + raise AttributeError(f"{self.__class__.__name__} is immutable") + + # 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", {"KEY1": VALUE1, "KEY2": VALUE2, ..}) + # Dynamically create: class + new_cls = type(name, (cls,), {"_i": True}) # _inited + for k, v in names.items(): + 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 cls is not Enum: + return cls._lookup(name) + + return super().__new__(cls) + + def __init__(self, name=None, names=None): + if "_i" not in self.__class__.__dict__: + self.list() + + @classmethod + def _lookup(cls, v): + 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): + return iter(cls.list()) + + @classmethod + def list(cls): + if "_i" not in cls.__dict__: + # 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._i = True + return [ + m for k in dir(cls) if not k.startswith("_") and hasattr(m := getattr(cls, k), "name") + ] + + @classmethod + 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.__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): + return self._lookup(v) + + def __setattr__(self, k, v): + if "_i" in self.__class__.__dict__: + raise AttributeError(f"{self.__class__.__name__} is immutable") + super().__setattr__(k, v) + + def __delattr__(self, k): + raise AttributeError(f"{self.__class__.__name__} is immutable") + + @classmethod + def __len__(cls): + return len(cls.list()) + + def __eq__(self, o): + if not isinstance(o, Enum): + return False + return self.list() == o.list() + + +if __name__ == "__main__": + # --- Usage Example 1 --- + # Standard Class Definition + class Color(Enum): + RED = 1 + 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}, str={str(c.RED)}, call={c.RED()} ") + + # Assertions + assert c.RED.name == "RED" + assert c.RED.value == 1 + assert c.RED == 1 + assert c.RED() == 1 + + # Reverse Lookup via instance call + 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") + + # --- Usage Example 2 --- + # 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}") + print(Status.__len__()) + print(len(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 + + # Immutability Check + try: + Status.RUNNING.value = 999 + 0 / 0 + except AttributeError as e: + print(f"\nImmutability check: Passed (Cannot modify EnumValue): {e}\n") + + # Test: Iteration + print("Iteration check: ", end="") + for m in Status(): + print(f"{m.name}, ", end="") + print("-> Passed") + + # 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") + + # --- Example 3: Functional API and serialization --- + print("--- Functional API and Eval Check ---") + + # Verify that eval(repr(obj)) restores the object + c_repr = repr(c) + 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 + + # 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" + 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. + 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" + 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}") + + print("\nAll tests passed successfully!") diff --git a/python-stdlib/enum/manifest.py b/python-stdlib/enum/manifest.py new file mode 100644 index 000000000..137ec22aa --- /dev/null +++ b/python-stdlib/enum/manifest.py @@ -0,0 +1,3 @@ +metadata(version="1.3.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..caa8bee64 --- /dev/null +++ b/python-stdlib/enum/test_enum.py @@ -0,0 +1,141 @@ +# test_enum.py +# version="1.3.0" + +import unittest +from enum import Enum + + +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.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) + + def test_comparison(self): + """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): + """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') + + 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): + """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): + """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) + + 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.NEW_MEMBER = 4 + + 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): + """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/tools/ci.sh b/tools/ci.sh index 7ee7eb4d1..999482c8e 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -65,6 +65,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 f261dd2f24512f7b9a223cb47aa0ce1b01e57aaa Mon Sep 17 00:00:00 2001 From: Ihor Nehrutsa Date: Mon, 5 Oct 2026 21:00:17 +0300 Subject: [PATCH 2/2] python-stdlib\enum: Bump to version="1.4.0". Signed-off-by: Ihor Nehrutsa --- python-stdlib/enum/README.md | 222 ++++-------------- python-stdlib/enum/enum.py | 375 ++++++++++++------------------- python-stdlib/enum/enum_check.py | 175 +++++++++++++++ python-stdlib/enum/enum_usage.py | 155 +++++++++++++ python-stdlib/enum/manifest.py | 2 +- python-stdlib/enum/test_enum.py | 65 +++--- 6 files changed, 559 insertions(+), 435 deletions(-) create mode 100644 python-stdlib/enum/enum_check.py create mode 100644 python-stdlib/enum/enum_usage.py diff --git a/python-stdlib/enum/README.md b/python-stdlib/enum/README.md index 6d1184bc4..a83ef786f 100644 --- a/python-stdlib/enum/README.md +++ b/python-stdlib/enum/README.md @@ -1,201 +1,75 @@ -# Enum Library +# Lightweight Python Enum (No Metaclasses) -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. +A lightweight Python implementation of enumerations (`Enum`, `IntEnum`, `StrEnum`) created without using metaclasses. Supports class syntax, functional API, strict value type checking, and immutability. ---- +## ?? Features -## Core Features -* **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. +* **No Metaclasses**: Dynamic type construction using standard `type()`. +* **Types**: Base `Enum`, as well as strictly typed `IntEnum` and `StrEnum`. +* **Dual Syntax**: Declaration via standard `class` statements or functional API calls. +* **Immutability**: Attribute mutation guard preventing changes after member creation. +* **Serialization**: `.dump()` method for easy string representation and reconstruction via `eval()`. ---- +## ?? Usage -## Usage Examples - -### 1. Standard Class Definition -Define your enumeration by inheriting from the `Enum` class. Class-level constants are automatically converted into `EnumValue` objects upon initialization. +### 1. Class Syntax Declaration ```python -from enum import Enum +from enum import Enum, IntEnum, StrEnum class Color(Enum): - RED = 'red' - GREEN = 'green' - -# 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.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'] -``` - - -### 2. Reverse Lookup -The library provides two ways to find a member based on its raw value. - -```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.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).value) # Output: 0 -print(s(0)) # Output: Status.IDLE: 0 -print(s(0)()) # Output: 0 -``` - + RED = 1 + GREEN = 2 + BLUE = 3 -### 3. Functional API (Dynamic Creation) -If you need to create an Enum from external data (like a JSON config), use the functional constructor. +class Status(StrEnum): + PENDING = "pending" + DONE = "done" -```python -# Create a dynamic Enum instance -State = Enum(name='State', names={'ON': 1, 'OFF': 2}) - -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" # +# Accessing members +print(Color.RED) # +print(Color.RED.name) # 'RED' +print(Color.RED.value) # 1 +print(Status.PENDING) # 'pending' (for StrEnum, string output equals value) ``` - -### 4. Serialization (Repr / Eval) -The library ensures that the string representation can be used to perfectly reconstruct the object. +### 2. Functional API ```python -from enum import Enum - -class Color(Enum): - RED = 'red' - GREEN = 'green' - BLUE = 3 +# Enum with sequential integer values +Priority = Enum("Priority", "LOW MEDIUM HIGH", start=1) +print(Priority.HIGH.value) # 3 -colors = Color() -# Get serialized string -serialized = repr(colors) -# Reconstruct object -restored_colors = eval(serialized) - -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 +# Enum from a dictionary mapping +HTTPStatus = IntEnum("HTTPStatus", {"OK": 200, "NOT_FOUND": 404}) +print(HTTPStatus.NOT_FOUND.value) # 404 ``` - ---- - -## API Reference - -### `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()`: 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]`). - ---- - -## 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. - * Raised when an instance-level lookup (`s(999)`) fails. - -## Compare with CPython +### 3. Lookup, Iteration, and Type Enforcement ```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 +# Lookup by value or name +item = Color(1) # Color.RED +item = Color("RED") # Color.RED -# class syntax -class Color(Enum): - RED = 1 - GREEN = 2 - BLUE = 3 +# Iteration over members +for member in Color: + print(member.name, member.value) -# 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 +# Strict type checks in IntEnum / StrEnum 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 + class BadEnum(IntEnum): + INVALID = "not an int" # Raises TypeError +except TypeError as e: + print(e) ``` -### Output is: +### 4. Serialization & Evaluation -| MicroPython v1.28.0 | Python 3.12.10 | -| :--- | :--- | -| [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 | +```python +dump_str = Color.dump() +print(dump_str) # "Enum('Color', {'RED': 1, 'GREEN': 2, 'BLUE': 3})" +# Reconstructing the Enum class object +RestoredColor = eval(dump_str) +``` \ No newline at end of file diff --git a/python-stdlib/enum/enum.py b/python-stdlib/enum/enum.py index 28472b4e7..83778b7bb 100644 --- a/python-stdlib/enum/enum.py +++ b/python-stdlib/enum/enum.py @@ -1,269 +1,186 @@ # enum.py -# version="1.3.0" - - -def _make_enum(v, n, e): - T = type(v) - - def _setattr(self, k, v): - raise AttributeError(f"{self.__class__.__name__} is immutable") +# Enum implementation without metaclasses +# version="1.4.0" + +# ============================================================================== +# Variable & Abbreviation Definitions: +# ============================================================================== +# Functions & Helper Methods: +# _c(e, n, v, v_t) -> create_enum_item: Helper function to construct a typed enum instance. +# _l(v) -> lookup: Classmethod to find an enum member by name or value. +# _i() -> items: Classmethod to retrieve/initialize tuple of all enum instances. +# _s(s, k, v) -> __setattr__: Attribute mutation guard raising AttributeError. +# +# Helper Arguments & Local Variables: +# e - enum_name / class_name: String representing the Enum class name (e.g., "Color"). +# n - name: String representing the enum member key (e.g., "RED"). +# v - value: Actual value assigned to the enum member (e.g., 1, "pending"). +# v_t - value_type: Target constraint type (`int`, `str`, or `None` for generic Enum). +# l - names_list: List of parsed string keys from comma/space-separated inputs. +# d - dict / mapping: Temporary dictionary for parsed enum members or dump serialization. +# i, __i - items: List or tuple containing initialized enum member instances. +# c - class / new_class: Dynamically created Enum class object via `type()`. +# s, k, o - self, key/attribute_name, other_object (standard short parameters). +# ============================================================================== + + +def _c(e, n, v, v_t=None): + # Create enum item + def _s(s, k, v): + raise AttributeError("cannot set attribute") + + # Type checks for IntEnum and StrEnum + if v_t is int and not isinstance(v, int): + raise TypeError(f"IntEnum member {n!r} value must be int, got {type(v).__name__}") + elif v_t is str and not isinstance(v, str): + raise TypeError(f"StrEnum member {n!r} value must be str, got {type(v).__name__}") # Create class: type(name, bases, dict), which inherits a base type (int, str, etc.) return type( - "EnumValue", - (T,), + f"{e}.{n}", + (type(v),), { "name": n, - "value": property(lambda s: v), - "__repr__": lambda s: f"{e}.{n}: {v}", - "__str__": lambda s: f"{e}.{n}: {v}", + "value": v, + "__str__": lambda s: str(v) if v_t in (int, str) else f"{e}.{n}", + "__repr__": lambda s: f"<{e}.{n}: {v!r}>", "__call__": lambda s: v, - "__setattr__": _setattr, + "__setattr__": _s, }, )(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", {"KEY1": VALUE1, "KEY2": VALUE2, ..}) - # Dynamically create: class - new_cls = type(name, (cls,), {"_i": True}) # _inited - for k, v in names.items(): - 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 cls is not Enum: - return cls._lookup(name) + _v_t = None # value type + + def __new__(cls, value=None, names=None, *, start=1): + # Functional API: dynamic creation of a new Enum class + if value is not None and names is not None: + # Parse 'names' parameter into a key-value dictionary: + # strings(comma/space-separated), lists, tuples, dictionaries + if isinstance(names, str): + l = names.replace(",", " ").split() # names_list + d = ( + {k: k for k in l} + if cls is StrEnum + else {k: v for v, k in enumerate(l, start=start)} + ) + elif isinstance(names, (list, tuple)): + if names and isinstance(names[0], (list, tuple)): + d = dict(names) + else: + d = ( + {k: k for k in names} + if issubclass(cls, StrEnum) + else {k: v for v, k in enumerate(names, start=start)} + ) + elif isinstance(names, dict): + d = names + else: + d = {k: v for v, k in enumerate(names, start=start)} + + # Construct and return a new class type + c = type(str(value), (cls,), {}) + i = [] + for k, v in d.items(): + e = _c(value, k, v, v_t=cls._v_t) + setattr(c, k, e) + i.append(e) + if i: + c.__i = tuple(i) + return c + + # Lookup existing member by value or name: e.g., Color(1) or Color("RED") + if value is not None and cls not in (Enum, IntEnum, StrEnum): + return cls._l(value) return super().__new__(cls) - def __init__(self, name=None, names=None): - if "_i" not in self.__class__.__dict__: - self.list() + def __init__(self, *args, **kwargs): + # Trigger lazy items initialization for class syntax declarations + if "__i" not in self.__class__.__dict__: + self._i() @classmethod - def _lookup(cls, v): - for m in cls.list(): - if m.value == v or m.name == v: - return m - raise AttributeError(f"{v} is not in {cls.__name__}") + def _l(cls, v): + # Lookup by value or name + for i in cls._i(): + if i.value == v or i.name == v: + return i + raise ValueError(f"{v!r} is not a valid {cls.__name__}") @classmethod def __iter__(cls): - return iter(cls.list()) + return iter(cls._i()) @classmethod - def list(cls): - if "_i" not in cls.__dict__: - # Copy dict.items() to avoid RuntimeError when changing the dictionary + def _i(cls): + # Get enum items + if "__i" not in cls.__dict__: + # Convert raw class attributes into EnumValue instances + i = [] # items 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._i = True - return [ - m for k in dir(cls) if not k.startswith("_") and hasattr(m := getattr(cls, k), "name") - ] - - @classmethod - def is_value(cls, v): - return any(m.value == v or m.name == v for m in cls.list()) + e = _c(cls.__name__, k, v, v_t=cls._v_t) + setattr(cls, k, e) + i.append(e) + cls.__i = tuple(i) + return cls.__i + + def __getitem__(self, k): + # Instance-level container lookup: Color()["RED"] + for i in self._i(): + if i.name == k: + return i + raise KeyError(k) def __repr__(self): - # Supports the condition: obj == eval(repr(obj)) - 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})" + if hasattr(self, "value") and self.value is not None: + return f"<{type(self).__name__}.{self.name}: {self.value!r}>" + return f"" + + def __str__(self): + if hasattr(self, "value") and self.value is not None: + return f"{type(self).__name__}.{self.name}" + return type(self).__name__ + + @classmethod + def dump(cls): + # Serialize enum members to string representation for eval compatibility + # obj == eval(dump(obj)) + if cls._v_t is None: + e = "Enum" + else: + e = cls._v_t.__name__[0].upper() + cls._v_t.__name__[1:] + "Enum" + d = {i.name: i.value for i in cls._i()} + return f"{e}('{cls.__name__}', {d})" def __call__(self, v): - return self._lookup(v) + return self._l(v) - def __setattr__(self, k, v): - if "_i" in self.__class__.__dict__: - raise AttributeError(f"{self.__class__.__name__} is immutable") - super().__setattr__(k, v) + @classmethod + def __setattr__(cls, k, v): + raise AttributeError("cannot set attribute") - def __delattr__(self, k): - raise AttributeError(f"{self.__class__.__name__} is immutable") + @classmethod + def __delattr__(cls, k): + raise AttributeError("cannot delete attribute") @classmethod def __len__(cls): - return len(cls.list()) + return len(cls._i()) def __eq__(self, o): - if not isinstance(o, Enum): - return False - return self.list() == o.list() - - -if __name__ == "__main__": - # --- Usage Example 1 --- - # Standard Class Definition - class Color(Enum): - RED = 1 - 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}, str={str(c.RED)}, call={c.RED()} ") - - # Assertions - assert c.RED.name == "RED" - assert c.RED.value == 1 - assert c.RED == 1 - assert c.RED() == 1 + return isinstance(o, Enum) and self._i() == o._i() - # Reverse Lookup via instance call - 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") - - # --- Usage Example 2 --- - # 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}") - print(Status.__len__()) - print(len(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 - - # Immutability Check - try: - Status.RUNNING.value = 999 - 0 / 0 - except AttributeError as e: - print(f"\nImmutability check: Passed (Cannot modify EnumValue): {e}\n") - - # Test: Iteration - print("Iteration check: ", end="") - for m in Status(): - print(f"{m.name}, ", end="") - print("-> Passed") - - # 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") - - # --- Example 3: Functional API and serialization --- - print("--- Functional API and Eval Check ---") - - # Verify that eval(repr(obj)) restores the object - c_repr = repr(c) - 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 - - # 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" - 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. - 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) + @classmethod + def items(cls): + return cls._i() - 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" - 0 / 0 - except AttributeError as e: - print(f"Caught expected mutation error: {e}") +class IntEnum(Enum): + _v_t = int - try: - del api_call.GET - 0 / 0 - except AttributeError as e: - print(f"Caught expected deletion error: {e}") - print("\nAll tests passed successfully!") +class StrEnum(Enum): + _v_t = str diff --git a/python-stdlib/enum/enum_check.py b/python-stdlib/enum/enum_check.py new file mode 100644 index 000000000..5600eafad --- /dev/null +++ b/python-stdlib/enum/enum_check.py @@ -0,0 +1,175 @@ +# enum_check.py +# version="1.4.0" + +from enum import Enum, IntEnum, StrEnum + +# --- Usage Example 1: Standard Class Definition & Access --- +class Color(Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + +print("Color.dump():", Color.dump()) + +# Basic class-level property and method calls +print(f"RED: repr={repr(Color.RED)}, type={type(Color.RED)}, name={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.items():", Color.items()) + +# Access members via instance indexing and call syntax +assert Color()["RED"] == Color.RED +assert Color("RED") == Color.RED + +# Iteration over Enum members via class instance call +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() + +# Instance-based interaction and comparison checks +c = Color() +print(f"Enum c instance: {c}") + +assert c.RED.name == "RED" +assert c.RED.value == 1 +assert c.RED == 1 +assert c.RED() == 1 + +# Reverse Lookup via instance call +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 + +# Verify ValueError is raised for invalid key/value lookup +try: + Color(999) + assert False, "Should have raised ValueError" +except ValueError as e: + print(f"ValueError (Caught expected error): {e}\n") + + +# --- Usage Example 2: Enum Operations & Comparisons --- +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}") +print(f"Enum length via class: {len(status)}") + +assert status == received_byte +assert status == Status.RUNNING +assert status.name == "RUNNING" +assert status.value == received_byte + +# Test equality and arithmetic operations +print(f"Comparison check: {status} == 1 is {status == 1}") +assert status == 1 +assert status != 0 +assert status + 10 == 11 # Enum arithmetic check + +# Verify immutability of Enum member attributes +try: + Status.RUNNING.value = 999 + assert False, "Should have raised AttributeError" +except AttributeError as e: + print(f"Immutability check: Passed (Cannot modify EnumValue): {e}\n") + +# Iteration over Enum members +print("Iteration check: ", end="") +for m in Status(): + print(f"{m.name}, ", end="") +print("-> Passed") + +try: + Status(999) + assert False, "Should have raised ValueError" +except ValueError as e: + print(f"ValueError (Invalid lookup check): Caught expected error -> {e}\n") + + +# --- Example 3: Functional API, Serialization and Eval --- +print("--- Functional API and Eval Check ---") + +c_dump = Color.dump() +print(f"Original Dump: {c_dump}") +c2 = eval(c_dump) +print(f"Restored Class Dump: {c2.dump()}") + +# Verify structural equality between restored and original class items +assert Color.items() == c2.items() +print("Objects are equal: True") + +# Dynamic class creation via functional API with eval() +state = eval("Enum('State', {'ON':1, 'OFF':2})") +print(f"Functional Enum instance (state): {state}") +print(f"Type: {type(state)}") +assert state.ON == 1 +assert state.ON.name == "ON" +assert state.ON > 0 +assert state.ON.value | state.OFF.value == 3 + + +# --- Example 4: Enum with String Values --- +# Standard Enum holding string values +class HttpMethod(Enum): + GET = "GET" + POST = "POST" + DELETE = "DELETE" + +api_call = HttpMethod() +print(f"Member with string value: {api_call.GET}") +assert api_call.GET == "GET" + +# Lookup by raw value string and member name string via instance invocation +print(f"Lookup by value 'POST': {api_call('POST')}") +print(f"Lookup by name 'DELETE': {api_call('DELETE')}") +assert api_call("GET").name == "GET" + + +# --- Example 5: Empty Enum Handling --- +# Verifies container behavior when no members are defined +class Empty(Enum): + pass + +empty = Empty() +print(f"Empty Enum items: {empty.items()}") +assert len(empty) == 0 + + +# --- Example 6: Deep Functional API & Serialization --- +# Functional creation with explicit class name parameter and dictionary mapping +complex_enum = Enum('Config', {'MAX_RETRY': 5, 'TIMEOUT_SEC': 30}) + +# Verify dump output and reconstruction via eval() +dump_str = complex_enum.dump() +restored = eval(dump_str) + +print(f"Restored Functional Enum: {restored.dump()}") +assert restored.MAX_RETRY == 5 +assert restored.__name__ == 'Config' + + +# --- Example 7: Immutability & Integrity Guard --- +# Ensuring that members cannot be added or deleted dynamically post-instantiation +try: + api_call.NEW_METHOD = "PATCH" + assert False, "Should have raised AttributeError" +except AttributeError as e: + print(f"Caught expected mutation error: {e}") + +try: + del api_call.GET + assert False, "Should have raised AttributeError" +except AttributeError as e: + print(f"Caught expected deletion error: {e}") + +print("\nAll tests passed successfully!") diff --git a/python-stdlib/enum/enum_usage.py b/python-stdlib/enum/enum_usage.py new file mode 100644 index 000000000..36bc7f963 --- /dev/null +++ b/python-stdlib/enum/enum_usage.py @@ -0,0 +1,155 @@ +# enum_usage.py +# version="1.4.0" + +from enum import Enum, IntEnum, StrEnum + +# ============================================================================== +# Usage Examples +# ============================================================================== + +# ============================================================================== +# Class Definition Syntax +# ============================================================================== + +class Color(Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + + +color = Color() # Trigger initialization + +# 1. Canonical output (str and repr) +print(Color.RED) # Color.RED +print(repr(Color.RED)) # + +# 2. Attribute access (name and value) +print(Color.RED.name) # RED +print(Color.RED.value) # 1 + +# 3. Equality and identity checks +print(Color.RED == Color.RED) # True +print(Color.RED is Color.RED) # True +print(Color.RED == 1) # True (like as CPython IntEnum). CPython standard Enum returns False here + +# 4. Lookup by value or name (via call) +print(Color(2)) # Color.GREEN +print(Color("RED")) # Color.RED + +# 5. Container access (via instance) +print(Color()["RED"]) # Color.RED (via instance) +# print(Color["RED"]) # not work + +# 6. Length and iteration (order may vary) +print(len(Color())) # 3 +print([x for x in Color()]) # [, , ] + +# 7. Serialization and list items (order may vary) +print(Color.items()) # (, , ) +print(Color.dump()) # Enum('Color', {'RED': 1, 'GREEN': 2, 'BLUE': 3}) + +# 8. Immutability check +try: + Color.RED.value = 100 + 0 / 0 +except AttributeError as e: + print("Immutability test passed:", e) +print(Color.RED) +assert(Color.RED == 1) + +try: + color.RED = 100 + 0 / 0 +except AttributeError as e: + print("Immutability test passed:", e) +print(color.RED) +assert(color.RED == 1) + +try: + del color.RED + 0 / 0 +except AttributeError as e: + print("Immutability test passed:", e) +print(color.RED) +assert(color.RED == 1) + +# # test NOT passed +# try: +# del Color.RED +# 0 / 0 +# except AttributeError as e: +# print("Immutability test passed:", e) +# print(Color.RED) + +# 9. Functional API: Comma / space separated string with custom start +State = Enum("State", "OFF, ON", start=10) +print(State.OFF) # State.OFF +print(State.ON) # State.ON +print(State.ON.value) # 11 +print(repr(State.ON)) # + +# 10. Functional API: Dictionary +Load = Enum("Load", {"LOW": "low", "HIGH": "high"}) +print(Load.HIGH) # Load.HIGH +print(Load.HIGH.value) # high +print(Load.LOW == "low") # True (like as CPython StrEnum). CPython standard Enum returns False here + +# 11. Functional API: Tuple of pairs +Do = Enum("Do", (("START", 200), ("STOP", 300))) +print(Do.START) # Do.START +print(Do.STOP) # Do.STOP +print(Do.STOP.value) # 300 + + +# ============================================================================== +# IntEnum Example +# ============================================================================== +class Priority(IntEnum): + LOW = 1 + MEDIUM = 2 + HIGH = 3 + +print("\n--- IntEnum ---") +print(Priority.LOW) # 1 +print(repr(Priority.LOW)) # + +# Arithmetic and numeric comparison (inherits int) +print(Priority.LOW == 1) # True +print(Priority.HIGH > 2) # True +print(Priority.LOW + 10) # 11 + +# Preserving int type inheritance +print(isinstance(Priority.LOW, int)) # True +print(Priority.dump()) # IntEnum('Priority', {'LOW': 1, 'MEDIUM': 2, 'HIGH': 3}) + + +# ============================================================================== +# StrEnum Example +# ============================================================================== +class Status(StrEnum): + PENDING = "pending" + RUNNING = "running" + DONE = "done" + +print("\n--- StrEnum ---") +print(Status.PENDING) # pending +print(repr(Status.PENDING)) # + +# String operations (inherits str) +print(Status.PENDING == "pending") # True +print(Status.RUNNING.upper()) # RUNNING + +# Preserving str type inheritance +print(isinstance(Status.PENDING, str)) # True +print(Status.dump()) # StrEnum('Status', {'PENDING': 'pending', 'RUNNING': 'running', 'DONE': 'done'}) + + +# ============================================================================== +# StrEnum Functional API +# ============================================================================== +HttpCode = StrEnum("HttpCode", "OK NOT_FOUND INTERNAL_ERROR") + +print("\n--- StrEnum Functional API ---") +print(HttpCode.OK.value) # OK +print(HttpCode.NOT_FOUND == "NOT_FOUND") # True +print(HttpCode.dump()) # StrEnum('HttpCode', {'OK': 'OK', 'NOT_FOUND': 'NOT_FOUND', 'INTERNAL_ERROR': 'INTERNAL_ERROR'}) diff --git a/python-stdlib/enum/manifest.py b/python-stdlib/enum/manifest.py index 137ec22aa..76d44eb3c 100644 --- a/python-stdlib/enum/manifest.py +++ b/python-stdlib/enum/manifest.py @@ -1,3 +1,3 @@ -metadata(version="1.3.0") +metadata(version="1.4.0") module("enum.py") diff --git a/python-stdlib/enum/test_enum.py b/python-stdlib/enum/test_enum.py index caa8bee64..bb899d252 100644 --- a/python-stdlib/enum/test_enum.py +++ b/python-stdlib/enum/test_enum.py @@ -1,5 +1,5 @@ # test_enum.py -# version="1.3.0" +# version="1.4.0" import unittest from enum import Enum @@ -27,14 +27,23 @@ 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(str(type(self.color.RED)), "") + self.assertEqual(type(self.color.RED).__name__, 'Color.RED') + + enum_value_cls = type(self.color.RED) + self.assertIsInstance(self.color.RED, enum_value_cls) self.assertEqual(self.status.IDLE.value, 0) + def test_getitem_access(self): + """Test item access via square brackets.""" + self.assertEqual(self.ColorClass()["RED"], self.color.RED) + self.assertEqual(self.ColorClass()["BLUE"], self.color.BLUE) + + with self.assertRaises(KeyError): + _ = self.ColorClass()["YELLOW"] + def test_comparison(self): - """Test equality between EnumValues and raw values.""" + """Test equality between Enum and raw values.""" self.assertTrue(self.color.RED == 1) self.assertFalse(self.color.RED == 2) self.assertEqual(self.color.RED, self.color.RED) @@ -54,7 +63,7 @@ def test_call_reverse_lookup(self): result = self.color(self.ColorClass.RED) self.assertEqual(result, self.ColorClass.RED) - with self.assertRaises(AttributeError): + with self.assertRaises(ValueError): self.color(999) def test_constructor_reverse_lookup(self): @@ -63,15 +72,9 @@ def test_constructor_reverse_lookup(self): self.assertEqual(member.name, "RUNNING") self.assertEqual(member, self.StatusClass.RUNNING) - with self.assertRaises(AttributeError): + with self.assertRaises(ValueError): self.StatusClass(999) - def test_is_value(self): - """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): """Test iteration over Enum instance.""" members = list(self.color) @@ -96,11 +99,11 @@ def test_deletion_prevention(self): with self.assertRaises(AttributeError): del self.color.RED - def test_len_and_list(self): - """Test __len__ and list() utility methods.""" + def test_len_and_items(self): + """Test __len__ and items() 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]) + members = self.color.items() + self.assertEqual(members, (self.color.RED, self.color.BLUE, self.color.GREEN)) def test_call_method(self): """Test calling EnumValue as a function to get its value.""" @@ -110,7 +113,7 @@ def test_call_method(self): 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}) + State = Enum(value='State', names={'ON': 1, 'OFF': 0}) self.assertTrue(hasattr(State, 'ON')) self.assertEqual(State.ON.value, 1) self.assertEqual(State.OFF.name, 'OFF') @@ -118,23 +121,23 @@ def test_functional_api(self): 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) + c_dump = self.color.dump() + c_restored = eval(c_dump) # Check equality of members - self.assertEqual(self.color.list(), c_restored.list()) - self.assertEqual(type(self.color).__name__, type(c_restored).__name__) + self.assertEqual(self.color.items(), c_restored.items()) + self.assertEqual(type(self.color).__name__, 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()) + s_dynamic = Enum(value='StatusFunc', names={'START': 1, 'STOP': 0}) + s_dump = s_dynamic.dump() + s_restored = eval(s_dump) + self.assertEqual(s_dynamic.items(), s_restored.items()) # 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()) + s_dynamic = Enum(value='State', names={'ON': 1, 'OFF': 0}) + s_dump = s_dynamic.dump() + s_restored = eval(s_dump) + self.assertEqual(s_dynamic.items(), s_restored.items()) if __name__ == '__main__':