PythonIota 0.0.2__tar.gz → 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. pythoniota-1.1.0/PKG-INFO +16 -0
  2. pythoniota-1.1.0/pyproject.toml +28 -0
  3. pythoniota-1.1.0/src/PythonIota.egg-info/PKG-INFO +16 -0
  4. pythoniota-1.1.0/src/PythonIota.egg-info/SOURCES.txt +22 -0
  5. pythoniota-1.1.0/src/PythonIota.egg-info/top_level.txt +1 -0
  6. pythoniota-1.1.0/src/pythoniota/__init__.py +18 -0
  7. pythoniota-1.1.0/src/pythoniota/_bitflag.py +105 -0
  8. pythoniota-1.1.0/src/pythoniota/_compat.py +64 -0
  9. pythoniota-1.1.0/src/pythoniota/_safe_eval.py +67 -0
  10. pythoniota-1.1.0/src/pythoniota/enum.py +321 -0
  11. pythoniota-1.1.0/src/pythoniota/recipes.py +236 -0
  12. pythoniota-1.1.0/src/pythoniota/sequence.py +172 -0
  13. pythoniota-1.1.0/tests/test_bitflags.py +132 -0
  14. pythoniota-1.1.0/tests/test_compat.py +88 -0
  15. pythoniota-1.1.0/tests/test_enum.py +178 -0
  16. pythoniota-1.1.0/tests/test_enum_enhanced.py +89 -0
  17. pythoniota-1.1.0/tests/test_integration.py +66 -0
  18. pythoniota-1.1.0/tests/test_recipes.py +130 -0
  19. pythoniota-1.1.0/tests/test_safe_eval.py +118 -0
  20. pythoniota-1.1.0/tests/test_sequence.py +153 -0
  21. pythoniota-1.1.0/tests/test_serialization.py +86 -0
  22. pythoniota-1.1.0/tests/test_string_enum.py +114 -0
  23. pythoniota-0.0.2/LICENSE.txt +0 -19
  24. pythoniota-0.0.2/PKG-INFO +0 -31
  25. pythoniota-0.0.2/PythonIota/__init__.py +0 -71
  26. pythoniota-0.0.2/PythonIota.egg-info/PKG-INFO +0 -31
  27. pythoniota-0.0.2/PythonIota.egg-info/SOURCES.txt +0 -8
  28. pythoniota-0.0.2/PythonIota.egg-info/top_level.txt +0 -1
  29. pythoniota-0.0.2/README.md +0 -19
  30. pythoniota-0.0.2/setup.py +0 -20
  31. {pythoniota-0.0.2 → pythoniota-1.1.0}/setup.cfg +0 -0
  32. {pythoniota-0.0.2 → pythoniota-1.1.0/src}/PythonIota.egg-info/dependency_links.txt +0 -0
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.4
2
+ Name: PythonIota
3
+ Version: 1.1.0
4
+ Summary: Go-style iota enumerations and flexible sequence generators for Python
5
+ Author: Equinox
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Typing :: Typed
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "PythonIota"
7
+ version = "1.1.0"
8
+ description = "Go-style iota enumerations and flexible sequence generators for Python"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ authors = [{name = "Equinox"}]
12
+ requires-python = ">=3.10"
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.10",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Operating System :: OS Independent",
21
+ "Typing :: Typed",
22
+ ]
23
+
24
+ [tool.setuptools.packages.find]
25
+ where = ["src"]
26
+
27
+ [tool.pytest.ini_options]
28
+ testpaths = ["tests"]
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.4
2
+ Name: PythonIota
3
+ Version: 1.1.0
4
+ Summary: Go-style iota enumerations and flexible sequence generators for Python
5
+ Author: Equinox
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Typing :: Typed
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
@@ -0,0 +1,22 @@
1
+ pyproject.toml
2
+ src/PythonIota.egg-info/PKG-INFO
3
+ src/PythonIota.egg-info/SOURCES.txt
4
+ src/PythonIota.egg-info/dependency_links.txt
5
+ src/PythonIota.egg-info/top_level.txt
6
+ src/pythoniota/__init__.py
7
+ src/pythoniota/_bitflag.py
8
+ src/pythoniota/_compat.py
9
+ src/pythoniota/_safe_eval.py
10
+ src/pythoniota/enum.py
11
+ src/pythoniota/recipes.py
12
+ src/pythoniota/sequence.py
13
+ tests/test_bitflags.py
14
+ tests/test_compat.py
15
+ tests/test_enum.py
16
+ tests/test_enum_enhanced.py
17
+ tests/test_integration.py
18
+ tests/test_recipes.py
19
+ tests/test_safe_eval.py
20
+ tests/test_sequence.py
21
+ tests/test_serialization.py
22
+ tests/test_string_enum.py
@@ -0,0 +1 @@
1
+ pythoniota
@@ -0,0 +1,18 @@
1
+ from pythoniota._compat import Iota
2
+ from pythoniota._bitflag import BitFlag
3
+ from pythoniota._safe_eval import safe_eval
4
+ from pythoniota.enum import IotaEnum, IotaBitFlags, IotaStringEnum
5
+ from pythoniota.sequence import IotaSequence as iota
6
+ from pythoniota import recipes
7
+
8
+ __version__ = "1.1.0"
9
+ __all__ = [
10
+ "Iota",
11
+ "IotaEnum",
12
+ "IotaBitFlags",
13
+ "IotaStringEnum",
14
+ "iota",
15
+ "BitFlag",
16
+ "safe_eval",
17
+ "recipes",
18
+ ]
@@ -0,0 +1,105 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Iterator
4
+
5
+
6
+ class BitFlag:
7
+ __slots__ = ("_value", "_name")
8
+
9
+ def __init__(self, value: int, name: str = "") -> None:
10
+ object.__setattr__(self, "_value", value)
11
+ object.__setattr__(self, "_name", name)
12
+
13
+ @property
14
+ def value(self) -> int:
15
+ return self._value
16
+
17
+ @property
18
+ def name(self) -> str:
19
+ return self._name
20
+
21
+ def __int__(self) -> int:
22
+ return self._value
23
+
24
+ def __index__(self) -> int:
25
+ return self._value
26
+
27
+ def __bool__(self) -> bool:
28
+ return self._value != 0
29
+
30
+ def __hash__(self) -> int:
31
+ return hash(self._value)
32
+
33
+ def __eq__(self, other: object) -> bool:
34
+ if isinstance(other, BitFlag):
35
+ return self._value == other._value
36
+ if isinstance(other, int):
37
+ return self._value == other
38
+ return NotImplemented
39
+
40
+ def __or__(self, other: BitFlag | int) -> BitFlag:
41
+ other_val = other._value if isinstance(other, BitFlag) else int(other)
42
+ name = f"{self._name}|{other._name if isinstance(other, BitFlag) else other_val}"
43
+ return BitFlag(self._value | other_val, name)
44
+
45
+ def __ror__(self, other: int) -> BitFlag:
46
+ return BitFlag(other | self._value, f"{other}|{self._name}")
47
+
48
+ def __and__(self, other: BitFlag | int) -> BitFlag:
49
+ other_val = other._value if isinstance(other, BitFlag) else int(other)
50
+ return BitFlag(self._value & other_val)
51
+
52
+ def __rand__(self, other: int) -> BitFlag:
53
+ return BitFlag(other & self._value)
54
+
55
+ def __xor__(self, other: BitFlag | int) -> BitFlag:
56
+ other_val = other._value if isinstance(other, BitFlag) else int(other)
57
+ return BitFlag(self._value ^ other_val)
58
+
59
+ def __rxor__(self, other: int) -> BitFlag:
60
+ return BitFlag(other ^ self._value)
61
+
62
+ def __invert__(self) -> BitFlag:
63
+ return BitFlag(~self._value, f"~{self._name}")
64
+
65
+ def __contains__(self, item: BitFlag | int) -> bool:
66
+ item_val = item._value if isinstance(item, BitFlag) else int(item)
67
+ return (self._value & item_val) == item_val and item_val != 0
68
+
69
+ def has(self, flag: BitFlag | int) -> bool:
70
+ return flag in self
71
+
72
+ def has_any(self, *flags: BitFlag | int) -> bool:
73
+ for f in flags:
74
+ fv = f._value if isinstance(f, BitFlag) else int(f)
75
+ if self._value & fv:
76
+ return True
77
+ return False
78
+
79
+ def has_all(self, *flags: BitFlag | int) -> bool:
80
+ for f in flags:
81
+ fv = f._value if isinstance(f, BitFlag) else int(f)
82
+ if (self._value & fv) != fv:
83
+ return False
84
+ return True
85
+
86
+ def decompose(self) -> list[BitFlag]:
87
+ result = []
88
+ v = self._value
89
+ bit = 1
90
+ while bit <= v:
91
+ if v & bit:
92
+ result.append(BitFlag(bit))
93
+ bit <<= 1
94
+ return result
95
+
96
+ def __repr__(self) -> str:
97
+ if self._name:
98
+ return f"BitFlag({self._name}={self._value})"
99
+ return f"BitFlag({self._value})"
100
+
101
+ def __str__(self) -> str:
102
+ return self._name or str(self._value)
103
+
104
+ def __iter__(self) -> Iterator[BitFlag]:
105
+ return iter(self.decompose())
@@ -0,0 +1,64 @@
1
+ from __future__ import annotations
2
+
3
+ import warnings
4
+ from typing import Any
5
+
6
+ from pythoniota._safe_eval import safe_eval
7
+
8
+
9
+ class Iota:
10
+ """Legacy Iota class for backward compatibility. Prefer IotaEnum or iota()."""
11
+
12
+ def __init__(self, initial: int = 0) -> None:
13
+ warnings.warn(
14
+ "Iota is deprecated. Use IotaEnum for enumerations or iota() for sequences.",
15
+ DeprecationWarning,
16
+ stacklevel=2,
17
+ )
18
+ self.current: int = initial
19
+ self.values: dict[str, Any] = {}
20
+
21
+ def __call__(self, **kwargs: str) -> None:
22
+ keys = list(kwargs.keys())
23
+ for i, (key, value) in enumerate(kwargs.items()):
24
+ if value == "iota":
25
+ self.values[key] = 0
26
+ self.current = 1
27
+ elif value == "":
28
+ self.values[key] = self.current
29
+ self.current += 1
30
+ elif value == "_":
31
+ self.current += 1
32
+ continue
33
+ elif value.isdigit():
34
+ self.values[key] = int(value)
35
+ self.current = self.values[key] + 1
36
+ else:
37
+ variables = {"iota": self.current}
38
+ variables.update({k: v for k, v in self.values.items() if isinstance(v, (int, float))})
39
+ result = safe_eval(value, variables)
40
+ self.values[key] = result
41
+ self.current = int(result) + 1
42
+
43
+ if key != "_":
44
+ setattr(self, key, self.values[key])
45
+
46
+ def parse_input(self, input_str: str) -> None:
47
+ lines = input_str.strip().split("\n")
48
+ args: dict[str, str] = {}
49
+ for line in lines:
50
+ if "=" in line:
51
+ key, value = line.split("=", 1)
52
+ key = key.strip()
53
+ value = value.strip()
54
+ else:
55
+ key = line.strip()
56
+ value = ""
57
+ args[key] = value
58
+ self(**args)
59
+
60
+ # Backward-compatible alias
61
+ __parseInput__ = parse_input
62
+
63
+ def get_values(self) -> dict[str, Any]:
64
+ return self.values
@@ -0,0 +1,67 @@
1
+ from __future__ import annotations
2
+
3
+ import ast
4
+ import operator
5
+ from typing import Any
6
+
7
+ _BIN_OPS: dict[type, Any] = {
8
+ ast.Add: operator.add,
9
+ ast.Sub: operator.sub,
10
+ ast.Mult: operator.mul,
11
+ ast.Div: operator.truediv,
12
+ ast.FloorDiv: operator.floordiv,
13
+ ast.Mod: operator.mod,
14
+ ast.Pow: operator.pow,
15
+ ast.LShift: operator.lshift,
16
+ ast.RShift: operator.rshift,
17
+ ast.BitOr: operator.or_,
18
+ ast.BitAnd: operator.and_,
19
+ ast.BitXor: operator.xor,
20
+ }
21
+
22
+ _UNARY_OPS: dict[type, Any] = {
23
+ ast.USub: operator.neg,
24
+ ast.UAdd: operator.pos,
25
+ ast.Invert: operator.invert,
26
+ }
27
+
28
+
29
+ def _eval_node(node: ast.expr, variables: dict[str, int | float]) -> int | float:
30
+ if isinstance(node, ast.Constant):
31
+ if not isinstance(node.value, (int, float)):
32
+ raise ValueError(f"Unsupported constant type: {type(node.value).__name__}")
33
+ return node.value
34
+
35
+ if isinstance(node, ast.Name):
36
+ if node.id not in variables:
37
+ raise ValueError(f"Undefined variable: {node.id}")
38
+ return variables[node.id]
39
+
40
+ if isinstance(node, ast.BinOp):
41
+ op_fn = _BIN_OPS.get(type(node.op))
42
+ if op_fn is None:
43
+ raise ValueError(f"Unsupported operator: {type(node.op).__name__}")
44
+ return op_fn(_eval_node(node.left, variables), _eval_node(node.right, variables))
45
+
46
+ if isinstance(node, ast.UnaryOp):
47
+ op_fn = _UNARY_OPS.get(type(node.op))
48
+ if op_fn is None:
49
+ raise ValueError(f"Unsupported unary operator: {type(node.op).__name__}")
50
+ return op_fn(_eval_node(node.operand, variables))
51
+
52
+ raise ValueError(f"Unsupported node: {type(node).__name__}")
53
+
54
+
55
+ def safe_eval(expr: str, variables: dict[str, int | float] | None = None) -> int | float:
56
+ if variables is None:
57
+ variables = {}
58
+ tree = ast.parse(expr.strip(), mode="eval")
59
+ return _eval_node(tree.body, variables)
60
+
61
+
62
+ def is_safe_expression(expr: str) -> bool:
63
+ try:
64
+ safe_eval(expr)
65
+ return True
66
+ except (ValueError, SyntaxError):
67
+ return False
@@ -0,0 +1,321 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from collections import OrderedDict
5
+ from typing import Any, Iterator
6
+
7
+ from pythoniota._bitflag import BitFlag
8
+
9
+
10
+ class _IotaCounter:
11
+ """Sentinel returned by IotaNamespace when 'iota' is read. Holds the current counter value."""
12
+
13
+ __slots__ = ("value",)
14
+
15
+ def __init__(self, value: int) -> None:
16
+ self.value = value
17
+
18
+ def __int__(self) -> int:
19
+ return self.value
20
+
21
+ def __index__(self) -> int:
22
+ return self.value
23
+
24
+ def __lshift__(self, other: Any) -> int:
25
+ return self.value << int(other)
26
+
27
+ def __rlshift__(self, other: Any) -> int:
28
+ return int(other) << self.value
29
+
30
+ def __rshift__(self, other: Any) -> int:
31
+ return self.value >> int(other)
32
+
33
+ def __rrshift__(self, other: Any) -> int:
34
+ return int(other) >> self.value
35
+
36
+ def __add__(self, other: Any) -> int:
37
+ return self.value + int(other)
38
+
39
+ def __radd__(self, other: Any) -> int:
40
+ return int(other) + self.value
41
+
42
+ def __sub__(self, other: Any) -> int:
43
+ return self.value - int(other)
44
+
45
+ def __rsub__(self, other: Any) -> int:
46
+ return int(other) - self.value
47
+
48
+ def __mul__(self, other: Any) -> int:
49
+ return self.value * int(other)
50
+
51
+ def __rmul__(self, other: Any) -> int:
52
+ return int(other) * self.value
53
+
54
+ def __floordiv__(self, other: Any) -> int:
55
+ return self.value // int(other)
56
+
57
+ def __rfloordiv__(self, other: Any) -> int:
58
+ return int(other) // self.value
59
+
60
+ def __mod__(self, other: Any) -> int:
61
+ return self.value % int(other)
62
+
63
+ def __rmod__(self, other: Any) -> int:
64
+ return int(other) % self.value
65
+
66
+ def __pow__(self, other: Any) -> int:
67
+ return self.value ** int(other)
68
+
69
+ def __rpow__(self, other: Any) -> int:
70
+ return int(other) ** self.value
71
+
72
+ def __or__(self, other: Any) -> int:
73
+ return self.value | int(other)
74
+
75
+ def __ror__(self, other: Any) -> int:
76
+ return int(other) | self.value
77
+
78
+ def __and__(self, other: Any) -> int:
79
+ return self.value & int(other)
80
+
81
+ def __rand__(self, other: Any) -> int:
82
+ return int(other) & self.value
83
+
84
+ def __xor__(self, other: Any) -> int:
85
+ return self.value ^ int(other)
86
+
87
+ def __rxor__(self, other: Any) -> int:
88
+ return int(other) ^ self.value
89
+
90
+ def __neg__(self) -> int:
91
+ return -self.value
92
+
93
+ def __pos__(self) -> int:
94
+ return +self.value
95
+
96
+ def __invert__(self) -> int:
97
+ return ~self.value
98
+
99
+ def __eq__(self, other: Any) -> bool:
100
+ return self.value == int(other) if isinstance(other, (int, _IotaCounter)) else NotImplemented
101
+
102
+ def __hash__(self) -> int:
103
+ return hash(self.value)
104
+
105
+ def __repr__(self) -> str:
106
+ return f"iota({self.value})"
107
+
108
+
109
+ class IotaNamespace(dict): # type: ignore[type-arg]
110
+
111
+ def __init__(self) -> None:
112
+ super().__init__()
113
+ self._counter = 0
114
+ self._member_names: list[str] = []
115
+
116
+ def __getitem__(self, key: str) -> Any:
117
+ if key == "iota":
118
+ val = _IotaCounter(self._counter)
119
+ self._counter += 1
120
+ return val
121
+ return super().__getitem__(key)
122
+
123
+ def __setitem__(self, key: str, value: Any) -> None:
124
+ super().__setitem__(key, value)
125
+ if (
126
+ not key.startswith("_")
127
+ and key != "iota"
128
+ and isinstance(value, (int, float, str, _IotaCounter))
129
+ ):
130
+ if key not in self._member_names:
131
+ self._member_names.append(key)
132
+
133
+
134
+ class IotaEnumMeta(type):
135
+
136
+ @classmethod
137
+ def __prepare__(mcs, name: str, bases: tuple[type, ...], **kwargs: Any) -> IotaNamespace: # type: ignore[override]
138
+ return IotaNamespace()
139
+
140
+ def __new__(
141
+ mcs,
142
+ name: str,
143
+ bases: tuple[type, ...],
144
+ namespace: IotaNamespace, # type: ignore[override]
145
+ **kwargs: Any,
146
+ ) -> IotaEnumMeta:
147
+ member_names = namespace._member_names if isinstance(namespace, IotaNamespace) else []
148
+ members: OrderedDict[str, Any] = OrderedDict()
149
+ for mname in member_names:
150
+ val = namespace[mname] if mname in namespace else 0
151
+ members[mname] = int(val) if isinstance(val, _IotaCounter) else val
152
+
153
+ cls = super().__new__(mcs, name, bases, dict(namespace))
154
+ cls._members_ = members # type: ignore[attr-defined]
155
+ cls._aliases_: dict[str, str] = {} # type: ignore[attr-defined]
156
+
157
+ is_bitflag = any(
158
+ hasattr(b, "_is_bitflag_base") for b in bases
159
+ )
160
+ is_string_enum = any(
161
+ hasattr(b, "_is_string_enum_base") for b in bases
162
+ )
163
+
164
+ if is_string_enum:
165
+ fmt = namespace.get("_format_", None)
166
+ for mname, mval in list(members.items()):
167
+ if isinstance(mval, _IotaCounter) or isinstance(mval, int):
168
+ if fmt:
169
+ members[mname] = fmt.format(name=mname, index=mval if isinstance(mval, int) else int(mval))
170
+ else:
171
+ members[mname] = mname
172
+ type.__setattr__(cls, mname, members[mname])
173
+ elif is_bitflag:
174
+ for mname, mval in members.items():
175
+ flag = BitFlag(int(mval), mname)
176
+ members[mname] = flag
177
+ type.__setattr__(cls, mname, flag)
178
+ else:
179
+ for mname, mval in members.items():
180
+ type.__setattr__(cls, mname, mval)
181
+
182
+ doc_lines = [f"{name} enum members:"]
183
+ for mname, mval in members.items():
184
+ doc_lines.append(f" {mname} = {mval}")
185
+ cls.__doc__ = "\n".join(doc_lines)
186
+
187
+ return cls
188
+
189
+ def __iter__(cls) -> Iterator[tuple[str, int | float]]:
190
+ return iter(cls._members_.items()) # type: ignore[attr-defined]
191
+
192
+ def __contains__(cls, item: Any) -> bool:
193
+ if isinstance(item, str):
194
+ return item in cls._members_ # type: ignore[attr-defined]
195
+ return item in cls._members_.values() # type: ignore[attr-defined]
196
+
197
+ def __len__(cls) -> int:
198
+ return len(cls._members_) # type: ignore[attr-defined]
199
+
200
+ def __getitem__(cls, key: str) -> int | float:
201
+ return cls._members_[key] # type: ignore[attr-defined]
202
+
203
+ def __setattr__(cls, name: str, value: Any) -> None:
204
+ if hasattr(cls, "_members_") and name in cls._members_: # type: ignore[attr-defined]
205
+ raise AttributeError(f"Cannot modify enum member '{name}'")
206
+ super().__setattr__(name, value)
207
+
208
+ def __delattr__(cls, name: str) -> None:
209
+ if hasattr(cls, "_members_") and name in cls._members_: # type: ignore[attr-defined]
210
+ raise AttributeError(f"Cannot delete enum member '{name}'")
211
+ super().__delattr__(name)
212
+
213
+ def __repr__(cls) -> str:
214
+ items = ", ".join(f"{k}={v}" for k, v in cls._members_.items()) # type: ignore[attr-defined]
215
+ return f"<{cls.__name__}: {items}>"
216
+
217
+
218
+ class IotaEnum(metaclass=IotaEnumMeta):
219
+
220
+ @classmethod
221
+ def names(cls) -> list[str]:
222
+ return list(cls._members_.keys()) # type: ignore[attr-defined]
223
+
224
+ @classmethod
225
+ def values(cls) -> list[Any]:
226
+ return list(cls._members_.values()) # type: ignore[attr-defined]
227
+
228
+ @classmethod
229
+ def items(cls) -> list[tuple[str, Any]]:
230
+ return list(cls._members_.items()) # type: ignore[attr-defined]
231
+
232
+ @classmethod
233
+ def from_value(cls, value: Any) -> str | None:
234
+ for name, val in cls._members_.items(): # type: ignore[attr-defined]
235
+ if val == value:
236
+ return name
237
+ for alias, target in cls._aliases_.items(): # type: ignore[attr-defined]
238
+ if cls._members_[target] == value: # type: ignore[attr-defined]
239
+ return alias
240
+ return None
241
+
242
+ @classmethod
243
+ def alias(cls, alias_name: str, target_name: str) -> None:
244
+ if target_name not in cls._members_: # type: ignore[attr-defined]
245
+ raise KeyError(f"'{target_name}' is not a member of {cls.__name__}")
246
+ cls._aliases_[alias_name] = target_name # type: ignore[attr-defined]
247
+ type.__setattr__(cls, alias_name, cls._members_[target_name]) # type: ignore[attr-defined]
248
+
249
+ @classmethod
250
+ def to_dict(cls) -> dict[str, Any]:
251
+ return dict(cls._members_) # type: ignore[attr-defined]
252
+
253
+ @classmethod
254
+ def to_json(cls, **kwargs: Any) -> str:
255
+ d = {}
256
+ for k, v in cls._members_.items(): # type: ignore[attr-defined]
257
+ d[k] = int(v) if isinstance(v, (BitFlag, float)) and not isinstance(v, str) else v
258
+ return json.dumps(d, **kwargs)
259
+
260
+ @classmethod
261
+ def from_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
262
+ result = {}
263
+ for name, value in data.items():
264
+ if name in cls._members_: # type: ignore[attr-defined]
265
+ result[name] = cls._members_[name] # type: ignore[attr-defined]
266
+ else:
267
+ raise KeyError(f"'{name}' is not a member of {cls.__name__}")
268
+ return result
269
+
270
+ @classmethod
271
+ def from_json(cls, s: str) -> dict[str, Any]:
272
+ return cls.from_dict(json.loads(s))
273
+
274
+
275
+ class IotaBitFlags(metaclass=IotaEnumMeta):
276
+ _is_bitflag_base = True
277
+
278
+ @classmethod
279
+ def names(cls) -> list[str]:
280
+ return list(cls._members_.keys()) # type: ignore[attr-defined]
281
+
282
+ @classmethod
283
+ def values(cls) -> list[BitFlag]:
284
+ return list(cls._members_.values()) # type: ignore[attr-defined]
285
+
286
+ @classmethod
287
+ def items(cls) -> list[tuple[str, BitFlag]]:
288
+ return list(cls._members_.items()) # type: ignore[attr-defined]
289
+
290
+ @classmethod
291
+ def from_value(cls, value: int) -> str | None:
292
+ for name, flag in cls._members_.items(): # type: ignore[attr-defined]
293
+ if int(flag) == value:
294
+ return name
295
+ return None
296
+
297
+ @classmethod
298
+ def to_dict(cls) -> dict[str, int]:
299
+ return {k: int(v) for k, v in cls._members_.items()} # type: ignore[attr-defined]
300
+
301
+ @classmethod
302
+ def to_json(cls, **kwargs: Any) -> str:
303
+ return json.dumps(cls.to_dict(), **kwargs)
304
+
305
+ @classmethod
306
+ def from_dict(cls, data: dict[str, Any]) -> dict[str, BitFlag]:
307
+ result = {}
308
+ for name in data:
309
+ if name in cls._members_: # type: ignore[attr-defined]
310
+ result[name] = cls._members_[name] # type: ignore[attr-defined]
311
+ else:
312
+ raise KeyError(f"'{name}' is not a member of {cls.__name__}")
313
+ return result
314
+
315
+ @classmethod
316
+ def from_json(cls, s: str) -> dict[str, BitFlag]:
317
+ return cls.from_dict(json.loads(s))
318
+
319
+
320
+ class IotaStringEnum(IotaEnum):
321
+ _is_string_enum_base = True