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 a5fcdf22e..a109d1c45 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 \