PythonIota 1.2.0__tar.gz → 1.7.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 (43) hide show
  1. pythoniota-1.7.0/PKG-INFO +368 -0
  2. pythoniota-1.7.0/README.md +351 -0
  3. {pythoniota-1.2.0 → pythoniota-1.7.0}/pyproject.toml +2 -2
  4. pythoniota-1.7.0/src/PythonIota.egg-info/PKG-INFO +368 -0
  5. {pythoniota-1.2.0 → pythoniota-1.7.0}/src/PythonIota.egg-info/SOURCES.txt +16 -0
  6. {pythoniota-1.2.0 → pythoniota-1.7.0}/src/pythoniota/__init__.py +7 -2
  7. pythoniota-1.7.0/src/pythoniota/cli/__init__.py +27 -0
  8. pythoniota-1.7.0/src/pythoniota/cli/_argument.py +70 -0
  9. pythoniota-1.7.0/src/pythoniota/cli/_errors.py +9 -0
  10. pythoniota-1.7.0/src/pythoniota/cli/_formatter.py +93 -0
  11. pythoniota-1.7.0/src/pythoniota/cli/_namespace.py +17 -0
  12. pythoniota-1.7.0/src/pythoniota/cli/_parser.py +321 -0
  13. {pythoniota-1.2.0 → pythoniota-1.7.0}/src/pythoniota/enum.py +60 -7
  14. {pythoniota-1.2.0 → pythoniota-1.7.0}/src/pythoniota/recipes.py +137 -0
  15. pythoniota-1.7.0/src/pythoniota/regex/__init__.py +66 -0
  16. pythoniota-1.7.0/src/pythoniota/regex/_compiler.py +177 -0
  17. pythoniota-1.7.0/src/pythoniota/regex/_parser.py +463 -0
  18. pythoniota-1.7.0/src/pythoniota/regex/_pattern.py +210 -0
  19. pythoniota-1.7.0/src/pythoniota/regex/_vm.py +123 -0
  20. {pythoniota-1.2.0 → pythoniota-1.7.0}/src/pythoniota/sequence.py +170 -19
  21. pythoniota-1.7.0/tests/test_cli.py +244 -0
  22. {pythoniota-1.2.0 → pythoniota-1.7.0}/tests/test_integration.py +1 -1
  23. pythoniota-1.7.0/tests/test_new_features.py +243 -0
  24. pythoniota-1.7.0/tests/test_regex.py +145 -0
  25. pythoniota-1.7.0/tests/test_regex_extended.py +200 -0
  26. pythoniota-1.2.0/PKG-INFO +0 -16
  27. pythoniota-1.2.0/src/PythonIota.egg-info/PKG-INFO +0 -16
  28. {pythoniota-1.2.0 → pythoniota-1.7.0}/setup.cfg +0 -0
  29. {pythoniota-1.2.0 → pythoniota-1.7.0}/src/PythonIota.egg-info/dependency_links.txt +0 -0
  30. {pythoniota-1.2.0 → pythoniota-1.7.0}/src/PythonIota.egg-info/top_level.txt +0 -0
  31. {pythoniota-1.2.0 → pythoniota-1.7.0}/src/pythoniota/_bitflag.py +0 -0
  32. {pythoniota-1.2.0 → pythoniota-1.7.0}/src/pythoniota/_compat.py +0 -0
  33. {pythoniota-1.2.0 → pythoniota-1.7.0}/src/pythoniota/_safe_eval.py +0 -0
  34. {pythoniota-1.2.0 → pythoniota-1.7.0}/tests/test_advanced.py +0 -0
  35. {pythoniota-1.2.0 → pythoniota-1.7.0}/tests/test_bitflags.py +0 -0
  36. {pythoniota-1.2.0 → pythoniota-1.7.0}/tests/test_compat.py +0 -0
  37. {pythoniota-1.2.0 → pythoniota-1.7.0}/tests/test_enum.py +0 -0
  38. {pythoniota-1.2.0 → pythoniota-1.7.0}/tests/test_enum_enhanced.py +0 -0
  39. {pythoniota-1.2.0 → pythoniota-1.7.0}/tests/test_recipes.py +0 -0
  40. {pythoniota-1.2.0 → pythoniota-1.7.0}/tests/test_safe_eval.py +0 -0
  41. {pythoniota-1.2.0 → pythoniota-1.7.0}/tests/test_sequence.py +0 -0
  42. {pythoniota-1.2.0 → pythoniota-1.7.0}/tests/test_serialization.py +0 -0
  43. {pythoniota-1.2.0 → pythoniota-1.7.0}/tests/test_string_enum.py +0 -0
@@ -0,0 +1,368 @@
1
+ Metadata-Version: 2.4
2
+ Name: PythonIota
3
+ Version: 1.7.0
4
+ Summary: A zero-dependency Python toolkit: Go-style iota enums, sequence generators, a from-scratch regex engine, and an argparse-style CLI parser
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
17
+
18
+ # PythonIota
19
+
20
+ A **zero-dependency Python toolkit** of from-scratch building blocks — Go-style
21
+ `iota` enumerations, lazy sequence generators, and a linear-time regex engine.
22
+
23
+ ```bash
24
+ pip install PythonIota
25
+ ```
26
+
27
+ Requires Python 3.10+.
28
+
29
+ **What's inside**
30
+
31
+ - **Enums** — Go-style `iota`, bit flags, string enums (`pythoniota.IotaEnum`, ...)
32
+ - **Sequences** — lazy, chainable generators (`pythoniota.iota`)
33
+ - **Regex** — a from-scratch NFA (Pike VM) engine, no catastrophic backtracking (`pythoniota.regex`)
34
+
35
+ ---
36
+
37
+ ## Enums
38
+
39
+ ### Go-style `iota`
40
+
41
+ `iota` starts at `0` and auto-increments each time it is read. Literal assignments do **not** consume it.
42
+
43
+ ```python
44
+ from pythoniota import IotaEnum
45
+
46
+ class Color(IotaEnum):
47
+ Red = iota # 0
48
+ Green = iota # 1
49
+ Blue = iota # 2
50
+
51
+ Color.Green # 1
52
+ Color.names() # ['Red', 'Green', 'Blue']
53
+ Color.values() # [0, 1, 2]
54
+ list(Color) # [('Red', 0), ('Green', 1), ('Blue', 2)]
55
+ 0 in Color # True (by value)
56
+ 'Red' in Color # True (by name)
57
+ Color.from_value(2) # 'Blue'
58
+ ```
59
+
60
+ `iota` works inside expressions — the value is the current counter:
61
+
62
+ ```python
63
+ class Perm(IotaEnum):
64
+ Read = 1 << iota # 1
65
+ Write = 1 << iota # 2
66
+ Exec = 1 << iota # 4
67
+ All = Read | Write | Exec # 7
68
+
69
+ class Size(IotaEnum):
70
+ KB = 1 << (iota + 10) # 1024
71
+ MB = 1 << (iota + 10) # 2048
72
+ GB = 1 << (iota + 10) # 4096
73
+ ```
74
+
75
+ ### Skipping values & custom start/step
76
+
77
+ ```python
78
+ class Errno(IotaEnum):
79
+ EPERM = iota # 0
80
+ skip(2) # skip 1, 2
81
+ EBADF = iota # 3
82
+
83
+ class Port(IotaEnum):
84
+ _iota_start_ = 8000
85
+ _iota_step_ = 10
86
+ HTTP = iota # 8000
87
+ HTTPS = iota # 8010
88
+ ```
89
+
90
+ `_ = iota` also skips a single value (Go's blank identifier).
91
+
92
+ ### Immutability, aliases, serialization
93
+
94
+ ```python
95
+ Color.Red = 99 # AttributeError: cannot modify enum member
96
+ Color.alias('R', 'Red') # Color.R == 0
97
+ Color.to_dict() # {'Red': 0, 'Green': 1, 'Blue': 2}
98
+ Color.to_json() # '{"Red": 0, "Green": 1, "Blue": 2}'
99
+ Color.from_json(s) # -> dict of members
100
+ Color.has('Red') # True
101
+ Color.get('Nope', -1) # -1
102
+ ```
103
+
104
+ ### `@unique` — reject duplicate values
105
+
106
+ ```python
107
+ from pythoniota import unique
108
+
109
+ @unique
110
+ class Status(IotaEnum):
111
+ Active = iota
112
+ Inactive = iota
113
+ # raises ValueError if two members share a value
114
+ ```
115
+
116
+ ### Ordered enums
117
+
118
+ Add `_ordered_ = True` to make members comparable by declaration order:
119
+
120
+ ```python
121
+ class Priority(IotaEnum):
122
+ _ordered_ = True
123
+ Low = iota
124
+ Medium = iota
125
+ High = iota
126
+
127
+ Priority.Low < Priority.High # True
128
+ ```
129
+
130
+ ### Bit flags
131
+
132
+ ```python
133
+ from pythoniota import IotaBitFlags, FlagScope
134
+
135
+ class Access(IotaBitFlags):
136
+ Read = 1 << iota # 1
137
+ Write = 1 << iota # 2
138
+ Exec = 1 << iota # 4
139
+
140
+ flags = Access.Read | Access.Write
141
+ flags.has(Access.Read) # True
142
+ flags.has_all(Access.Read, Access.Exec) # False
143
+ flags.has_any(Access.Read, Access.Exec) # True
144
+ list(flags.decompose()) # [BitFlag(1), BitFlag(2)]
145
+
146
+ with FlagScope() as scope:
147
+ scope.grant(Access.Read, Access.Write)
148
+ scope.revoke(Access.Write)
149
+ scope.has(Access.Read) # True
150
+ ```
151
+
152
+ ### String enums
153
+
154
+ Members resolve to their own name, or a custom format:
155
+
156
+ ```python
157
+ from pythoniota import IotaStringEnum
158
+
159
+ class Color(IotaStringEnum):
160
+ Red = iota # 'Red'
161
+ Blue = iota # 'Blue'
162
+
163
+ class Code(IotaStringEnum):
164
+ _format_ = "{name}_{index:03d}"
165
+ OK = iota # 'OK_000'
166
+ Error = iota # 'Error_001'
167
+ ```
168
+
169
+ ### `@iota_enum` decorator
170
+
171
+ Turn a plain class into an enum:
172
+
173
+ ```python
174
+ from pythoniota import iota_enum
175
+
176
+ @iota_enum
177
+ class Color:
178
+ Red = 0
179
+ Green = 1
180
+ Blue = 2
181
+
182
+ @iota_enum(ordered=True)
183
+ class Priority:
184
+ Low = 0
185
+ High = 1
186
+ ```
187
+
188
+ ### `match` / `case`
189
+
190
+ Enum members match by value:
191
+
192
+ ```python
193
+ match color:
194
+ case Color.Red: ...
195
+ case Color.Blue: ...
196
+ case _: ...
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Sequences
202
+
203
+ `iota(...)` is a lazy, composable sequence. Construction mirrors `range`, plus an optional `map`:
204
+
205
+ ```python
206
+ from pythoniota import iota
207
+
208
+ iota() # 0, 1, 2, ... (infinite)
209
+ iota(5) # 0, 1, 2, 3, 4
210
+ iota(2, 10, 2) # 2, 4, 6, 8
211
+ iota(5, map=lambda i: i*i) # 0, 1, 4, 9, 16
212
+
213
+ s = iota(10)
214
+ len(s) # 10 (O(1))
215
+ s[7] # 7 (O(1) arithmetic indexing)
216
+ s[2:5] # [2, 3, 4]
217
+ list(reversed(s)) # 9, 8, ... 0 (lazy)
218
+ 5 in s # True (O(1))
219
+ s.take(3) # [0, 1, 2]
220
+ ```
221
+
222
+ ### Operators
223
+
224
+ ```python
225
+ iota(3) + iota(3, 6) # concat: 0,1,2,3,4,5
226
+ iota(3) * 2 # repeat: 0,1,2,0,1,2
227
+ iota(3) | iota(3, 6) # interleave: 0,3,1,4,2,5
228
+ iota(3) @ iota(3, 6) # zip: (0,3),(1,4),(2,5)
229
+ ```
230
+
231
+ ### Lazy combinators
232
+
233
+ ```python
234
+ iota(10).filter(lambda x: x % 2 == 0) # 0,2,4,6,8
235
+ iota().map(lambda x: x*x) # 0,1,4,9,...
236
+ iota().takewhile(lambda x: x < 5) # 0,1,2,3,4
237
+ iota(10).dropwhile(lambda x: x < 7) # 7,8,9
238
+ iota(4).pairwise() # (0,1),(1,2),(2,3)
239
+ iota(5).window(3) # (0,1,2),(1,2,3),(2,3,4)
240
+ iota(7).chunk(3) # [0,1,2],[3,4,5],[6]
241
+ iota(6).map(lambda x: x % 3).distinct() # 0,1,2
242
+ seq.flatten() # flatten one level
243
+ iota().enumerate() .accumulate() .zip(other)
244
+ ```
245
+
246
+ ### Terminal operations
247
+
248
+ Reductions on plain arithmetic sequences are O(1); infinite sequences raise where they would not terminate.
249
+
250
+ ```python
251
+ iota(1, 101).sum() # 5050 (closed-form, O(1))
252
+ iota(3, 10).min() # 3
253
+ iota(3, 10).max() # 9
254
+ iota(0, 10, 2).count() # 5
255
+ iota(5).last() # 4
256
+ iota(10).nth(3) # 3
257
+ iota().first() # 0
258
+ iota().find(lambda x: x > 100) # 101
259
+ iota(1, 5).all(lambda x: x > 0) # True
260
+ iota(5).any(lambda x: x == 3) # True
261
+ iota(3).reduce(lambda a, b: a + b) # 3
262
+
263
+ iota(3).to_list() # [0, 1, 2]
264
+ iota(3).to_tuple() # (0, 1, 2)
265
+ iota(3).to_set() # {0, 1, 2}
266
+ iota(3).to_dict(value=lambda x: x*x) # {0: 0, 1: 1, 2: 4}
267
+ ```
268
+
269
+ ### Async iteration
270
+
271
+ ```python
272
+ async for x in iota(5):
273
+ ...
274
+ ```
275
+
276
+ ---
277
+
278
+ ## Recipes
279
+
280
+ ```python
281
+ from pythoniota.recipes import (
282
+ fibonacci, lucas, factorial, catalan, harmonic,
283
+ triangle, powers, geometric, primes, primes_sieve,
284
+ pascal_row, collatz, repeat, cycle,
285
+ )
286
+
287
+ fibonacci(10).to_list() # 0,1,1,2,3,5,8,13,21,34
288
+ lucas(7).to_list() # 2,1,3,4,7,11,18
289
+ factorial(6).to_list() # 1,1,2,6,24,120
290
+ catalan(6).to_list() # 1,1,2,5,14,42
291
+ harmonic(4).to_list() # 1.0, 1.5, 1.833..., 2.083...
292
+ triangle(5).to_list() # 0,1,3,6,10
293
+ powers(2, 8).to_list() # 1,2,4,...,128
294
+ geometric(3, 2, 4).to_list() # 3,6,12,24
295
+ primes(5).to_list() # 2,3,5,7,11 (first n primes)
296
+ primes_sieve(20).to_list()# 2,3,5,7,11,13,17,19 (< limit, Eratosthenes)
297
+ pascal_row(4).to_list() # 1,4,6,4,1
298
+ collatz(6).to_list() # 6,3,10,5,16,8,4,2,1
299
+ repeat('x', 3).to_list() # ['x','x','x']
300
+ cycle([1,2], 5).to_list() # 1,2,1,2,1
301
+ ```
302
+
303
+ Recipes without a count argument produce infinite sequences: `fibonacci().take(20)`, `primes().takewhile(lambda p: p < 100)`.
304
+
305
+ ---
306
+
307
+ ## Safe expression evaluation
308
+
309
+ `safe_eval` evaluates arithmetic expressions over a whitelisted AST (no `eval`, no names/calls beyond provided variables):
310
+
311
+ ```python
312
+ from pythoniota import safe_eval
313
+
314
+ safe_eval("2 ** 10") # 1024
315
+ safe_eval("a * b + 1", {"a": 3, "b": 4}) # 13
316
+ ```
317
+
318
+ ---
319
+
320
+ ## Regex
321
+
322
+ A from-scratch regular-expression engine. Unlike Python's backtracking `re`, it
323
+ compiles the pattern to a tiny bytecode and runs it with a **Pike VM** (a
324
+ thread-list NFA simulation), so matching is linear in `len(text) × len(program)`
325
+ — pathological patterns like `(a+)+$` finish instantly instead of hanging.
326
+
327
+ ```python
328
+ from pythoniota import regex
329
+
330
+ m = regex.match(r"(\d{4})-(\d{2})-(\d{2})", "2026-08-02")
331
+ m.groups() # ('2026', '08', '02')
332
+
333
+ regex.findall(r"\w+", "hello world") # ['hello', 'world']
334
+ regex.sub(r"\s+", "_", "a b c") # 'a_b_c'
335
+ regex.split(r"[,;]\s*", "a, b; c,d") # ['a', 'b', 'c', 'd']
336
+
337
+ named = regex.search(r"(?P<user>\w+)@(?P<host>\w+)", "user@host")
338
+ named.groupdict() # {'user': 'user', 'host': 'host'}
339
+ ```
340
+
341
+ Supported: literals, `.`, `* + ?` and their lazy forms `*? +? ??`, alternation
342
+ `|`, groups `(...)` / non-capturing `(?:...)` / named `(?P<name>...)`, lookahead
343
+ `(?=...)` / `(?!...)`, character classes `[...]`, counted repeats `{m,n}`,
344
+ shorthands `\d \w \s` (and `\D \W \S`), word boundaries `\b \B`, and anchors
345
+ `^ $ \A \Z`. Flags: `IGNORECASE`, `MULTILINE`, `DOTALL`, `VERBOSE` (also inline
346
+ `(?imsx)`). API mirrors `re`: `compile`, `match`, `fullmatch`, `search`,
347
+ `findall`, `finditer`, `sub`, `split`, `escape`, with `Match.group` / `groups` /
348
+ `span` / `groupdict`.
349
+
350
+ ```python
351
+ regex.findall(r"[a-z]+", "AbCdEf", regex.IGNORECASE) # ['AbCdEf']
352
+ regex.findall(r"\d+(?=px)", "10px 20em 30px") # ['10', '30'] (lookahead)
353
+ ```
354
+
355
+ Backreferences (`\1`) are intentionally *not* supported: they can't be matched
356
+ by a finite automaton in linear time — that's the price of the no-backtracking
357
+ guarantee (the same trade-off Go's `regexp` and RE2 make).
358
+
359
+ ```python
360
+ # no ReDoS: this returns in microseconds, where re takes ~13s at length 28
361
+ regex.search(r"(a+)+$", "a" * 40 + "!") # None, instantly
362
+ ```
363
+
364
+ ---
365
+
366
+ ## License
367
+
368
+ MIT
@@ -0,0 +1,351 @@
1
+ # PythonIota
2
+
3
+ A **zero-dependency Python toolkit** of from-scratch building blocks — Go-style
4
+ `iota` enumerations, lazy sequence generators, and a linear-time regex engine.
5
+
6
+ ```bash
7
+ pip install PythonIota
8
+ ```
9
+
10
+ Requires Python 3.10+.
11
+
12
+ **What's inside**
13
+
14
+ - **Enums** — Go-style `iota`, bit flags, string enums (`pythoniota.IotaEnum`, ...)
15
+ - **Sequences** — lazy, chainable generators (`pythoniota.iota`)
16
+ - **Regex** — a from-scratch NFA (Pike VM) engine, no catastrophic backtracking (`pythoniota.regex`)
17
+
18
+ ---
19
+
20
+ ## Enums
21
+
22
+ ### Go-style `iota`
23
+
24
+ `iota` starts at `0` and auto-increments each time it is read. Literal assignments do **not** consume it.
25
+
26
+ ```python
27
+ from pythoniota import IotaEnum
28
+
29
+ class Color(IotaEnum):
30
+ Red = iota # 0
31
+ Green = iota # 1
32
+ Blue = iota # 2
33
+
34
+ Color.Green # 1
35
+ Color.names() # ['Red', 'Green', 'Blue']
36
+ Color.values() # [0, 1, 2]
37
+ list(Color) # [('Red', 0), ('Green', 1), ('Blue', 2)]
38
+ 0 in Color # True (by value)
39
+ 'Red' in Color # True (by name)
40
+ Color.from_value(2) # 'Blue'
41
+ ```
42
+
43
+ `iota` works inside expressions — the value is the current counter:
44
+
45
+ ```python
46
+ class Perm(IotaEnum):
47
+ Read = 1 << iota # 1
48
+ Write = 1 << iota # 2
49
+ Exec = 1 << iota # 4
50
+ All = Read | Write | Exec # 7
51
+
52
+ class Size(IotaEnum):
53
+ KB = 1 << (iota + 10) # 1024
54
+ MB = 1 << (iota + 10) # 2048
55
+ GB = 1 << (iota + 10) # 4096
56
+ ```
57
+
58
+ ### Skipping values & custom start/step
59
+
60
+ ```python
61
+ class Errno(IotaEnum):
62
+ EPERM = iota # 0
63
+ skip(2) # skip 1, 2
64
+ EBADF = iota # 3
65
+
66
+ class Port(IotaEnum):
67
+ _iota_start_ = 8000
68
+ _iota_step_ = 10
69
+ HTTP = iota # 8000
70
+ HTTPS = iota # 8010
71
+ ```
72
+
73
+ `_ = iota` also skips a single value (Go's blank identifier).
74
+
75
+ ### Immutability, aliases, serialization
76
+
77
+ ```python
78
+ Color.Red = 99 # AttributeError: cannot modify enum member
79
+ Color.alias('R', 'Red') # Color.R == 0
80
+ Color.to_dict() # {'Red': 0, 'Green': 1, 'Blue': 2}
81
+ Color.to_json() # '{"Red": 0, "Green": 1, "Blue": 2}'
82
+ Color.from_json(s) # -> dict of members
83
+ Color.has('Red') # True
84
+ Color.get('Nope', -1) # -1
85
+ ```
86
+
87
+ ### `@unique` — reject duplicate values
88
+
89
+ ```python
90
+ from pythoniota import unique
91
+
92
+ @unique
93
+ class Status(IotaEnum):
94
+ Active = iota
95
+ Inactive = iota
96
+ # raises ValueError if two members share a value
97
+ ```
98
+
99
+ ### Ordered enums
100
+
101
+ Add `_ordered_ = True` to make members comparable by declaration order:
102
+
103
+ ```python
104
+ class Priority(IotaEnum):
105
+ _ordered_ = True
106
+ Low = iota
107
+ Medium = iota
108
+ High = iota
109
+
110
+ Priority.Low < Priority.High # True
111
+ ```
112
+
113
+ ### Bit flags
114
+
115
+ ```python
116
+ from pythoniota import IotaBitFlags, FlagScope
117
+
118
+ class Access(IotaBitFlags):
119
+ Read = 1 << iota # 1
120
+ Write = 1 << iota # 2
121
+ Exec = 1 << iota # 4
122
+
123
+ flags = Access.Read | Access.Write
124
+ flags.has(Access.Read) # True
125
+ flags.has_all(Access.Read, Access.Exec) # False
126
+ flags.has_any(Access.Read, Access.Exec) # True
127
+ list(flags.decompose()) # [BitFlag(1), BitFlag(2)]
128
+
129
+ with FlagScope() as scope:
130
+ scope.grant(Access.Read, Access.Write)
131
+ scope.revoke(Access.Write)
132
+ scope.has(Access.Read) # True
133
+ ```
134
+
135
+ ### String enums
136
+
137
+ Members resolve to their own name, or a custom format:
138
+
139
+ ```python
140
+ from pythoniota import IotaStringEnum
141
+
142
+ class Color(IotaStringEnum):
143
+ Red = iota # 'Red'
144
+ Blue = iota # 'Blue'
145
+
146
+ class Code(IotaStringEnum):
147
+ _format_ = "{name}_{index:03d}"
148
+ OK = iota # 'OK_000'
149
+ Error = iota # 'Error_001'
150
+ ```
151
+
152
+ ### `@iota_enum` decorator
153
+
154
+ Turn a plain class into an enum:
155
+
156
+ ```python
157
+ from pythoniota import iota_enum
158
+
159
+ @iota_enum
160
+ class Color:
161
+ Red = 0
162
+ Green = 1
163
+ Blue = 2
164
+
165
+ @iota_enum(ordered=True)
166
+ class Priority:
167
+ Low = 0
168
+ High = 1
169
+ ```
170
+
171
+ ### `match` / `case`
172
+
173
+ Enum members match by value:
174
+
175
+ ```python
176
+ match color:
177
+ case Color.Red: ...
178
+ case Color.Blue: ...
179
+ case _: ...
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Sequences
185
+
186
+ `iota(...)` is a lazy, composable sequence. Construction mirrors `range`, plus an optional `map`:
187
+
188
+ ```python
189
+ from pythoniota import iota
190
+
191
+ iota() # 0, 1, 2, ... (infinite)
192
+ iota(5) # 0, 1, 2, 3, 4
193
+ iota(2, 10, 2) # 2, 4, 6, 8
194
+ iota(5, map=lambda i: i*i) # 0, 1, 4, 9, 16
195
+
196
+ s = iota(10)
197
+ len(s) # 10 (O(1))
198
+ s[7] # 7 (O(1) arithmetic indexing)
199
+ s[2:5] # [2, 3, 4]
200
+ list(reversed(s)) # 9, 8, ... 0 (lazy)
201
+ 5 in s # True (O(1))
202
+ s.take(3) # [0, 1, 2]
203
+ ```
204
+
205
+ ### Operators
206
+
207
+ ```python
208
+ iota(3) + iota(3, 6) # concat: 0,1,2,3,4,5
209
+ iota(3) * 2 # repeat: 0,1,2,0,1,2
210
+ iota(3) | iota(3, 6) # interleave: 0,3,1,4,2,5
211
+ iota(3) @ iota(3, 6) # zip: (0,3),(1,4),(2,5)
212
+ ```
213
+
214
+ ### Lazy combinators
215
+
216
+ ```python
217
+ iota(10).filter(lambda x: x % 2 == 0) # 0,2,4,6,8
218
+ iota().map(lambda x: x*x) # 0,1,4,9,...
219
+ iota().takewhile(lambda x: x < 5) # 0,1,2,3,4
220
+ iota(10).dropwhile(lambda x: x < 7) # 7,8,9
221
+ iota(4).pairwise() # (0,1),(1,2),(2,3)
222
+ iota(5).window(3) # (0,1,2),(1,2,3),(2,3,4)
223
+ iota(7).chunk(3) # [0,1,2],[3,4,5],[6]
224
+ iota(6).map(lambda x: x % 3).distinct() # 0,1,2
225
+ seq.flatten() # flatten one level
226
+ iota().enumerate() .accumulate() .zip(other)
227
+ ```
228
+
229
+ ### Terminal operations
230
+
231
+ Reductions on plain arithmetic sequences are O(1); infinite sequences raise where they would not terminate.
232
+
233
+ ```python
234
+ iota(1, 101).sum() # 5050 (closed-form, O(1))
235
+ iota(3, 10).min() # 3
236
+ iota(3, 10).max() # 9
237
+ iota(0, 10, 2).count() # 5
238
+ iota(5).last() # 4
239
+ iota(10).nth(3) # 3
240
+ iota().first() # 0
241
+ iota().find(lambda x: x > 100) # 101
242
+ iota(1, 5).all(lambda x: x > 0) # True
243
+ iota(5).any(lambda x: x == 3) # True
244
+ iota(3).reduce(lambda a, b: a + b) # 3
245
+
246
+ iota(3).to_list() # [0, 1, 2]
247
+ iota(3).to_tuple() # (0, 1, 2)
248
+ iota(3).to_set() # {0, 1, 2}
249
+ iota(3).to_dict(value=lambda x: x*x) # {0: 0, 1: 1, 2: 4}
250
+ ```
251
+
252
+ ### Async iteration
253
+
254
+ ```python
255
+ async for x in iota(5):
256
+ ...
257
+ ```
258
+
259
+ ---
260
+
261
+ ## Recipes
262
+
263
+ ```python
264
+ from pythoniota.recipes import (
265
+ fibonacci, lucas, factorial, catalan, harmonic,
266
+ triangle, powers, geometric, primes, primes_sieve,
267
+ pascal_row, collatz, repeat, cycle,
268
+ )
269
+
270
+ fibonacci(10).to_list() # 0,1,1,2,3,5,8,13,21,34
271
+ lucas(7).to_list() # 2,1,3,4,7,11,18
272
+ factorial(6).to_list() # 1,1,2,6,24,120
273
+ catalan(6).to_list() # 1,1,2,5,14,42
274
+ harmonic(4).to_list() # 1.0, 1.5, 1.833..., 2.083...
275
+ triangle(5).to_list() # 0,1,3,6,10
276
+ powers(2, 8).to_list() # 1,2,4,...,128
277
+ geometric(3, 2, 4).to_list() # 3,6,12,24
278
+ primes(5).to_list() # 2,3,5,7,11 (first n primes)
279
+ primes_sieve(20).to_list()# 2,3,5,7,11,13,17,19 (< limit, Eratosthenes)
280
+ pascal_row(4).to_list() # 1,4,6,4,1
281
+ collatz(6).to_list() # 6,3,10,5,16,8,4,2,1
282
+ repeat('x', 3).to_list() # ['x','x','x']
283
+ cycle([1,2], 5).to_list() # 1,2,1,2,1
284
+ ```
285
+
286
+ Recipes without a count argument produce infinite sequences: `fibonacci().take(20)`, `primes().takewhile(lambda p: p < 100)`.
287
+
288
+ ---
289
+
290
+ ## Safe expression evaluation
291
+
292
+ `safe_eval` evaluates arithmetic expressions over a whitelisted AST (no `eval`, no names/calls beyond provided variables):
293
+
294
+ ```python
295
+ from pythoniota import safe_eval
296
+
297
+ safe_eval("2 ** 10") # 1024
298
+ safe_eval("a * b + 1", {"a": 3, "b": 4}) # 13
299
+ ```
300
+
301
+ ---
302
+
303
+ ## Regex
304
+
305
+ A from-scratch regular-expression engine. Unlike Python's backtracking `re`, it
306
+ compiles the pattern to a tiny bytecode and runs it with a **Pike VM** (a
307
+ thread-list NFA simulation), so matching is linear in `len(text) × len(program)`
308
+ — pathological patterns like `(a+)+$` finish instantly instead of hanging.
309
+
310
+ ```python
311
+ from pythoniota import regex
312
+
313
+ m = regex.match(r"(\d{4})-(\d{2})-(\d{2})", "2026-08-02")
314
+ m.groups() # ('2026', '08', '02')
315
+
316
+ regex.findall(r"\w+", "hello world") # ['hello', 'world']
317
+ regex.sub(r"\s+", "_", "a b c") # 'a_b_c'
318
+ regex.split(r"[,;]\s*", "a, b; c,d") # ['a', 'b', 'c', 'd']
319
+
320
+ named = regex.search(r"(?P<user>\w+)@(?P<host>\w+)", "user@host")
321
+ named.groupdict() # {'user': 'user', 'host': 'host'}
322
+ ```
323
+
324
+ Supported: literals, `.`, `* + ?` and their lazy forms `*? +? ??`, alternation
325
+ `|`, groups `(...)` / non-capturing `(?:...)` / named `(?P<name>...)`, lookahead
326
+ `(?=...)` / `(?!...)`, character classes `[...]`, counted repeats `{m,n}`,
327
+ shorthands `\d \w \s` (and `\D \W \S`), word boundaries `\b \B`, and anchors
328
+ `^ $ \A \Z`. Flags: `IGNORECASE`, `MULTILINE`, `DOTALL`, `VERBOSE` (also inline
329
+ `(?imsx)`). API mirrors `re`: `compile`, `match`, `fullmatch`, `search`,
330
+ `findall`, `finditer`, `sub`, `split`, `escape`, with `Match.group` / `groups` /
331
+ `span` / `groupdict`.
332
+
333
+ ```python
334
+ regex.findall(r"[a-z]+", "AbCdEf", regex.IGNORECASE) # ['AbCdEf']
335
+ regex.findall(r"\d+(?=px)", "10px 20em 30px") # ['10', '30'] (lookahead)
336
+ ```
337
+
338
+ Backreferences (`\1`) are intentionally *not* supported: they can't be matched
339
+ by a finite automaton in linear time — that's the price of the no-backtracking
340
+ guarantee (the same trade-off Go's `regexp` and RE2 make).
341
+
342
+ ```python
343
+ # no ReDoS: this returns in microseconds, where re takes ~13s at length 28
344
+ regex.search(r"(a+)+$", "a" * 40 + "!") # None, instantly
345
+ ```
346
+
347
+ ---
348
+
349
+ ## License
350
+
351
+ MIT