PythonIota 1.1.0__tar.gz → 1.3.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 (30) hide show
  1. pythoniota-1.3.0/PKG-INFO +315 -0
  2. pythoniota-1.3.0/README.md +298 -0
  3. {pythoniota-1.1.0 → pythoniota-1.3.0}/pyproject.toml +1 -1
  4. pythoniota-1.3.0/src/PythonIota.egg-info/PKG-INFO +315 -0
  5. {pythoniota-1.1.0 → pythoniota-1.3.0}/src/PythonIota.egg-info/SOURCES.txt +3 -0
  6. {pythoniota-1.1.0 → pythoniota-1.3.0}/src/pythoniota/__init__.py +8 -2
  7. {pythoniota-1.1.0 → pythoniota-1.3.0}/src/pythoniota/_bitflag.py +14 -0
  8. {pythoniota-1.1.0 → pythoniota-1.3.0}/src/pythoniota/enum.py +277 -5
  9. {pythoniota-1.1.0 → pythoniota-1.3.0}/src/pythoniota/recipes.py +137 -0
  10. pythoniota-1.3.0/src/pythoniota/sequence.py +461 -0
  11. pythoniota-1.3.0/tests/test_advanced.py +377 -0
  12. {pythoniota-1.1.0 → pythoniota-1.3.0}/tests/test_integration.py +1 -1
  13. pythoniota-1.3.0/tests/test_new_features.py +243 -0
  14. pythoniota-1.1.0/PKG-INFO +0 -16
  15. pythoniota-1.1.0/src/PythonIota.egg-info/PKG-INFO +0 -16
  16. pythoniota-1.1.0/src/pythoniota/sequence.py +0 -172
  17. {pythoniota-1.1.0 → pythoniota-1.3.0}/setup.cfg +0 -0
  18. {pythoniota-1.1.0 → pythoniota-1.3.0}/src/PythonIota.egg-info/dependency_links.txt +0 -0
  19. {pythoniota-1.1.0 → pythoniota-1.3.0}/src/PythonIota.egg-info/top_level.txt +0 -0
  20. {pythoniota-1.1.0 → pythoniota-1.3.0}/src/pythoniota/_compat.py +0 -0
  21. {pythoniota-1.1.0 → pythoniota-1.3.0}/src/pythoniota/_safe_eval.py +0 -0
  22. {pythoniota-1.1.0 → pythoniota-1.3.0}/tests/test_bitflags.py +0 -0
  23. {pythoniota-1.1.0 → pythoniota-1.3.0}/tests/test_compat.py +0 -0
  24. {pythoniota-1.1.0 → pythoniota-1.3.0}/tests/test_enum.py +0 -0
  25. {pythoniota-1.1.0 → pythoniota-1.3.0}/tests/test_enum_enhanced.py +0 -0
  26. {pythoniota-1.1.0 → pythoniota-1.3.0}/tests/test_recipes.py +0 -0
  27. {pythoniota-1.1.0 → pythoniota-1.3.0}/tests/test_safe_eval.py +0 -0
  28. {pythoniota-1.1.0 → pythoniota-1.3.0}/tests/test_sequence.py +0 -0
  29. {pythoniota-1.1.0 → pythoniota-1.3.0}/tests/test_serialization.py +0 -0
  30. {pythoniota-1.1.0 → pythoniota-1.3.0}/tests/test_string_enum.py +0 -0
@@ -0,0 +1,315 @@
1
+ Metadata-Version: 2.4
2
+ Name: PythonIota
3
+ Version: 1.3.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
17
+
18
+ # PythonIota
19
+
20
+ Go-style `iota` enumerations and flexible, lazy sequence generators for Python — zero dependencies, fully typed.
21
+
22
+ ```bash
23
+ pip install PythonIota
24
+ ```
25
+
26
+ Requires Python 3.10+.
27
+
28
+ ---
29
+
30
+ ## Enums
31
+
32
+ ### Go-style `iota`
33
+
34
+ `iota` starts at `0` and auto-increments each time it is read. Literal assignments do **not** consume it.
35
+
36
+ ```python
37
+ from pythoniota import IotaEnum
38
+
39
+ class Color(IotaEnum):
40
+ Red = iota # 0
41
+ Green = iota # 1
42
+ Blue = iota # 2
43
+
44
+ Color.Green # 1
45
+ Color.names() # ['Red', 'Green', 'Blue']
46
+ Color.values() # [0, 1, 2]
47
+ list(Color) # [('Red', 0), ('Green', 1), ('Blue', 2)]
48
+ 0 in Color # True (by value)
49
+ 'Red' in Color # True (by name)
50
+ Color.from_value(2) # 'Blue'
51
+ ```
52
+
53
+ `iota` works inside expressions — the value is the current counter:
54
+
55
+ ```python
56
+ class Perm(IotaEnum):
57
+ Read = 1 << iota # 1
58
+ Write = 1 << iota # 2
59
+ Exec = 1 << iota # 4
60
+ All = Read | Write | Exec # 7
61
+
62
+ class Size(IotaEnum):
63
+ KB = 1 << (iota + 10) # 1024
64
+ MB = 1 << (iota + 10) # 2048
65
+ GB = 1 << (iota + 10) # 4096
66
+ ```
67
+
68
+ ### Skipping values & custom start/step
69
+
70
+ ```python
71
+ class Errno(IotaEnum):
72
+ EPERM = iota # 0
73
+ skip(2) # skip 1, 2
74
+ EBADF = iota # 3
75
+
76
+ class Port(IotaEnum):
77
+ _iota_start_ = 8000
78
+ _iota_step_ = 10
79
+ HTTP = iota # 8000
80
+ HTTPS = iota # 8010
81
+ ```
82
+
83
+ `_ = iota` also skips a single value (Go's blank identifier).
84
+
85
+ ### Immutability, aliases, serialization
86
+
87
+ ```python
88
+ Color.Red = 99 # AttributeError: cannot modify enum member
89
+ Color.alias('R', 'Red') # Color.R == 0
90
+ Color.to_dict() # {'Red': 0, 'Green': 1, 'Blue': 2}
91
+ Color.to_json() # '{"Red": 0, "Green": 1, "Blue": 2}'
92
+ Color.from_json(s) # -> dict of members
93
+ Color.has('Red') # True
94
+ Color.get('Nope', -1) # -1
95
+ ```
96
+
97
+ ### `@unique` — reject duplicate values
98
+
99
+ ```python
100
+ from pythoniota import unique
101
+
102
+ @unique
103
+ class Status(IotaEnum):
104
+ Active = iota
105
+ Inactive = iota
106
+ # raises ValueError if two members share a value
107
+ ```
108
+
109
+ ### Ordered enums
110
+
111
+ Add `_ordered_ = True` to make members comparable by declaration order:
112
+
113
+ ```python
114
+ class Priority(IotaEnum):
115
+ _ordered_ = True
116
+ Low = iota
117
+ Medium = iota
118
+ High = iota
119
+
120
+ Priority.Low < Priority.High # True
121
+ ```
122
+
123
+ ### Bit flags
124
+
125
+ ```python
126
+ from pythoniota import IotaBitFlags, FlagScope
127
+
128
+ class Access(IotaBitFlags):
129
+ Read = 1 << iota # 1
130
+ Write = 1 << iota # 2
131
+ Exec = 1 << iota # 4
132
+
133
+ flags = Access.Read | Access.Write
134
+ flags.has(Access.Read) # True
135
+ flags.has_all(Access.Read, Access.Exec) # False
136
+ flags.has_any(Access.Read, Access.Exec) # True
137
+ list(flags.decompose()) # [BitFlag(1), BitFlag(2)]
138
+
139
+ with FlagScope() as scope:
140
+ scope.grant(Access.Read, Access.Write)
141
+ scope.revoke(Access.Write)
142
+ scope.has(Access.Read) # True
143
+ ```
144
+
145
+ ### String enums
146
+
147
+ Members resolve to their own name, or a custom format:
148
+
149
+ ```python
150
+ from pythoniota import IotaStringEnum
151
+
152
+ class Color(IotaStringEnum):
153
+ Red = iota # 'Red'
154
+ Blue = iota # 'Blue'
155
+
156
+ class Code(IotaStringEnum):
157
+ _format_ = "{name}_{index:03d}"
158
+ OK = iota # 'OK_000'
159
+ Error = iota # 'Error_001'
160
+ ```
161
+
162
+ ### `@iota_enum` decorator
163
+
164
+ Turn a plain class into an enum:
165
+
166
+ ```python
167
+ from pythoniota import iota_enum
168
+
169
+ @iota_enum
170
+ class Color:
171
+ Red = 0
172
+ Green = 1
173
+ Blue = 2
174
+
175
+ @iota_enum(ordered=True)
176
+ class Priority:
177
+ Low = 0
178
+ High = 1
179
+ ```
180
+
181
+ ### `match` / `case`
182
+
183
+ Enum members match by value:
184
+
185
+ ```python
186
+ match color:
187
+ case Color.Red: ...
188
+ case Color.Blue: ...
189
+ case _: ...
190
+ ```
191
+
192
+ ---
193
+
194
+ ## Sequences
195
+
196
+ `iota(...)` is a lazy, composable sequence. Construction mirrors `range`, plus an optional `map`:
197
+
198
+ ```python
199
+ from pythoniota import iota
200
+
201
+ iota() # 0, 1, 2, ... (infinite)
202
+ iota(5) # 0, 1, 2, 3, 4
203
+ iota(2, 10, 2) # 2, 4, 6, 8
204
+ iota(5, map=lambda i: i*i) # 0, 1, 4, 9, 16
205
+
206
+ s = iota(10)
207
+ len(s) # 10 (O(1))
208
+ s[7] # 7 (O(1) arithmetic indexing)
209
+ s[2:5] # [2, 3, 4]
210
+ list(reversed(s)) # 9, 8, ... 0 (lazy)
211
+ 5 in s # True (O(1))
212
+ s.take(3) # [0, 1, 2]
213
+ ```
214
+
215
+ ### Operators
216
+
217
+ ```python
218
+ iota(3) + iota(3, 6) # concat: 0,1,2,3,4,5
219
+ iota(3) * 2 # repeat: 0,1,2,0,1,2
220
+ iota(3) | iota(3, 6) # interleave: 0,3,1,4,2,5
221
+ iota(3) @ iota(3, 6) # zip: (0,3),(1,4),(2,5)
222
+ ```
223
+
224
+ ### Lazy combinators
225
+
226
+ ```python
227
+ iota(10).filter(lambda x: x % 2 == 0) # 0,2,4,6,8
228
+ iota().map(lambda x: x*x) # 0,1,4,9,...
229
+ iota().takewhile(lambda x: x < 5) # 0,1,2,3,4
230
+ iota(10).dropwhile(lambda x: x < 7) # 7,8,9
231
+ iota(4).pairwise() # (0,1),(1,2),(2,3)
232
+ iota(5).window(3) # (0,1,2),(1,2,3),(2,3,4)
233
+ iota(7).chunk(3) # [0,1,2],[3,4,5],[6]
234
+ iota(6).map(lambda x: x % 3).distinct() # 0,1,2
235
+ seq.flatten() # flatten one level
236
+ iota().enumerate() .accumulate() .zip(other)
237
+ ```
238
+
239
+ ### Terminal operations
240
+
241
+ Reductions on plain arithmetic sequences are O(1); infinite sequences raise where they would not terminate.
242
+
243
+ ```python
244
+ iota(1, 101).sum() # 5050 (closed-form, O(1))
245
+ iota(3, 10).min() # 3
246
+ iota(3, 10).max() # 9
247
+ iota(0, 10, 2).count() # 5
248
+ iota(5).last() # 4
249
+ iota(10).nth(3) # 3
250
+ iota().first() # 0
251
+ iota().find(lambda x: x > 100) # 101
252
+ iota(1, 5).all(lambda x: x > 0) # True
253
+ iota(5).any(lambda x: x == 3) # True
254
+ iota(3).reduce(lambda a, b: a + b) # 3
255
+
256
+ iota(3).to_list() # [0, 1, 2]
257
+ iota(3).to_tuple() # (0, 1, 2)
258
+ iota(3).to_set() # {0, 1, 2}
259
+ iota(3).to_dict(value=lambda x: x*x) # {0: 0, 1: 1, 2: 4}
260
+ ```
261
+
262
+ ### Async iteration
263
+
264
+ ```python
265
+ async for x in iota(5):
266
+ ...
267
+ ```
268
+
269
+ ---
270
+
271
+ ## Recipes
272
+
273
+ ```python
274
+ from pythoniota.recipes import (
275
+ fibonacci, lucas, factorial, catalan, harmonic,
276
+ triangle, powers, geometric, primes, primes_sieve,
277
+ pascal_row, collatz, repeat, cycle,
278
+ )
279
+
280
+ fibonacci(10).to_list() # 0,1,1,2,3,5,8,13,21,34
281
+ lucas(7).to_list() # 2,1,3,4,7,11,18
282
+ factorial(6).to_list() # 1,1,2,6,24,120
283
+ catalan(6).to_list() # 1,1,2,5,14,42
284
+ harmonic(4).to_list() # 1.0, 1.5, 1.833..., 2.083...
285
+ triangle(5).to_list() # 0,1,3,6,10
286
+ powers(2, 8).to_list() # 1,2,4,...,128
287
+ geometric(3, 2, 4).to_list() # 3,6,12,24
288
+ primes(5).to_list() # 2,3,5,7,11 (first n primes)
289
+ primes_sieve(20).to_list()# 2,3,5,7,11,13,17,19 (< limit, Eratosthenes)
290
+ pascal_row(4).to_list() # 1,4,6,4,1
291
+ collatz(6).to_list() # 6,3,10,5,16,8,4,2,1
292
+ repeat('x', 3).to_list() # ['x','x','x']
293
+ cycle([1,2], 5).to_list() # 1,2,1,2,1
294
+ ```
295
+
296
+ Recipes without a count argument produce infinite sequences: `fibonacci().take(20)`, `primes().takewhile(lambda p: p < 100)`.
297
+
298
+ ---
299
+
300
+ ## Safe expression evaluation
301
+
302
+ `safe_eval` evaluates arithmetic expressions over a whitelisted AST (no `eval`, no names/calls beyond provided variables):
303
+
304
+ ```python
305
+ from pythoniota import safe_eval
306
+
307
+ safe_eval("2 ** 10") # 1024
308
+ safe_eval("a * b + 1", {"a": 3, "b": 4}) # 13
309
+ ```
310
+
311
+ ---
312
+
313
+ ## License
314
+
315
+ MIT
@@ -0,0 +1,298 @@
1
+ # PythonIota
2
+
3
+ Go-style `iota` enumerations and flexible, lazy sequence generators for Python — zero dependencies, fully typed.
4
+
5
+ ```bash
6
+ pip install PythonIota
7
+ ```
8
+
9
+ Requires Python 3.10+.
10
+
11
+ ---
12
+
13
+ ## Enums
14
+
15
+ ### Go-style `iota`
16
+
17
+ `iota` starts at `0` and auto-increments each time it is read. Literal assignments do **not** consume it.
18
+
19
+ ```python
20
+ from pythoniota import IotaEnum
21
+
22
+ class Color(IotaEnum):
23
+ Red = iota # 0
24
+ Green = iota # 1
25
+ Blue = iota # 2
26
+
27
+ Color.Green # 1
28
+ Color.names() # ['Red', 'Green', 'Blue']
29
+ Color.values() # [0, 1, 2]
30
+ list(Color) # [('Red', 0), ('Green', 1), ('Blue', 2)]
31
+ 0 in Color # True (by value)
32
+ 'Red' in Color # True (by name)
33
+ Color.from_value(2) # 'Blue'
34
+ ```
35
+
36
+ `iota` works inside expressions — the value is the current counter:
37
+
38
+ ```python
39
+ class Perm(IotaEnum):
40
+ Read = 1 << iota # 1
41
+ Write = 1 << iota # 2
42
+ Exec = 1 << iota # 4
43
+ All = Read | Write | Exec # 7
44
+
45
+ class Size(IotaEnum):
46
+ KB = 1 << (iota + 10) # 1024
47
+ MB = 1 << (iota + 10) # 2048
48
+ GB = 1 << (iota + 10) # 4096
49
+ ```
50
+
51
+ ### Skipping values & custom start/step
52
+
53
+ ```python
54
+ class Errno(IotaEnum):
55
+ EPERM = iota # 0
56
+ skip(2) # skip 1, 2
57
+ EBADF = iota # 3
58
+
59
+ class Port(IotaEnum):
60
+ _iota_start_ = 8000
61
+ _iota_step_ = 10
62
+ HTTP = iota # 8000
63
+ HTTPS = iota # 8010
64
+ ```
65
+
66
+ `_ = iota` also skips a single value (Go's blank identifier).
67
+
68
+ ### Immutability, aliases, serialization
69
+
70
+ ```python
71
+ Color.Red = 99 # AttributeError: cannot modify enum member
72
+ Color.alias('R', 'Red') # Color.R == 0
73
+ Color.to_dict() # {'Red': 0, 'Green': 1, 'Blue': 2}
74
+ Color.to_json() # '{"Red": 0, "Green": 1, "Blue": 2}'
75
+ Color.from_json(s) # -> dict of members
76
+ Color.has('Red') # True
77
+ Color.get('Nope', -1) # -1
78
+ ```
79
+
80
+ ### `@unique` — reject duplicate values
81
+
82
+ ```python
83
+ from pythoniota import unique
84
+
85
+ @unique
86
+ class Status(IotaEnum):
87
+ Active = iota
88
+ Inactive = iota
89
+ # raises ValueError if two members share a value
90
+ ```
91
+
92
+ ### Ordered enums
93
+
94
+ Add `_ordered_ = True` to make members comparable by declaration order:
95
+
96
+ ```python
97
+ class Priority(IotaEnum):
98
+ _ordered_ = True
99
+ Low = iota
100
+ Medium = iota
101
+ High = iota
102
+
103
+ Priority.Low < Priority.High # True
104
+ ```
105
+
106
+ ### Bit flags
107
+
108
+ ```python
109
+ from pythoniota import IotaBitFlags, FlagScope
110
+
111
+ class Access(IotaBitFlags):
112
+ Read = 1 << iota # 1
113
+ Write = 1 << iota # 2
114
+ Exec = 1 << iota # 4
115
+
116
+ flags = Access.Read | Access.Write
117
+ flags.has(Access.Read) # True
118
+ flags.has_all(Access.Read, Access.Exec) # False
119
+ flags.has_any(Access.Read, Access.Exec) # True
120
+ list(flags.decompose()) # [BitFlag(1), BitFlag(2)]
121
+
122
+ with FlagScope() as scope:
123
+ scope.grant(Access.Read, Access.Write)
124
+ scope.revoke(Access.Write)
125
+ scope.has(Access.Read) # True
126
+ ```
127
+
128
+ ### String enums
129
+
130
+ Members resolve to their own name, or a custom format:
131
+
132
+ ```python
133
+ from pythoniota import IotaStringEnum
134
+
135
+ class Color(IotaStringEnum):
136
+ Red = iota # 'Red'
137
+ Blue = iota # 'Blue'
138
+
139
+ class Code(IotaStringEnum):
140
+ _format_ = "{name}_{index:03d}"
141
+ OK = iota # 'OK_000'
142
+ Error = iota # 'Error_001'
143
+ ```
144
+
145
+ ### `@iota_enum` decorator
146
+
147
+ Turn a plain class into an enum:
148
+
149
+ ```python
150
+ from pythoniota import iota_enum
151
+
152
+ @iota_enum
153
+ class Color:
154
+ Red = 0
155
+ Green = 1
156
+ Blue = 2
157
+
158
+ @iota_enum(ordered=True)
159
+ class Priority:
160
+ Low = 0
161
+ High = 1
162
+ ```
163
+
164
+ ### `match` / `case`
165
+
166
+ Enum members match by value:
167
+
168
+ ```python
169
+ match color:
170
+ case Color.Red: ...
171
+ case Color.Blue: ...
172
+ case _: ...
173
+ ```
174
+
175
+ ---
176
+
177
+ ## Sequences
178
+
179
+ `iota(...)` is a lazy, composable sequence. Construction mirrors `range`, plus an optional `map`:
180
+
181
+ ```python
182
+ from pythoniota import iota
183
+
184
+ iota() # 0, 1, 2, ... (infinite)
185
+ iota(5) # 0, 1, 2, 3, 4
186
+ iota(2, 10, 2) # 2, 4, 6, 8
187
+ iota(5, map=lambda i: i*i) # 0, 1, 4, 9, 16
188
+
189
+ s = iota(10)
190
+ len(s) # 10 (O(1))
191
+ s[7] # 7 (O(1) arithmetic indexing)
192
+ s[2:5] # [2, 3, 4]
193
+ list(reversed(s)) # 9, 8, ... 0 (lazy)
194
+ 5 in s # True (O(1))
195
+ s.take(3) # [0, 1, 2]
196
+ ```
197
+
198
+ ### Operators
199
+
200
+ ```python
201
+ iota(3) + iota(3, 6) # concat: 0,1,2,3,4,5
202
+ iota(3) * 2 # repeat: 0,1,2,0,1,2
203
+ iota(3) | iota(3, 6) # interleave: 0,3,1,4,2,5
204
+ iota(3) @ iota(3, 6) # zip: (0,3),(1,4),(2,5)
205
+ ```
206
+
207
+ ### Lazy combinators
208
+
209
+ ```python
210
+ iota(10).filter(lambda x: x % 2 == 0) # 0,2,4,6,8
211
+ iota().map(lambda x: x*x) # 0,1,4,9,...
212
+ iota().takewhile(lambda x: x < 5) # 0,1,2,3,4
213
+ iota(10).dropwhile(lambda x: x < 7) # 7,8,9
214
+ iota(4).pairwise() # (0,1),(1,2),(2,3)
215
+ iota(5).window(3) # (0,1,2),(1,2,3),(2,3,4)
216
+ iota(7).chunk(3) # [0,1,2],[3,4,5],[6]
217
+ iota(6).map(lambda x: x % 3).distinct() # 0,1,2
218
+ seq.flatten() # flatten one level
219
+ iota().enumerate() .accumulate() .zip(other)
220
+ ```
221
+
222
+ ### Terminal operations
223
+
224
+ Reductions on plain arithmetic sequences are O(1); infinite sequences raise where they would not terminate.
225
+
226
+ ```python
227
+ iota(1, 101).sum() # 5050 (closed-form, O(1))
228
+ iota(3, 10).min() # 3
229
+ iota(3, 10).max() # 9
230
+ iota(0, 10, 2).count() # 5
231
+ iota(5).last() # 4
232
+ iota(10).nth(3) # 3
233
+ iota().first() # 0
234
+ iota().find(lambda x: x > 100) # 101
235
+ iota(1, 5).all(lambda x: x > 0) # True
236
+ iota(5).any(lambda x: x == 3) # True
237
+ iota(3).reduce(lambda a, b: a + b) # 3
238
+
239
+ iota(3).to_list() # [0, 1, 2]
240
+ iota(3).to_tuple() # (0, 1, 2)
241
+ iota(3).to_set() # {0, 1, 2}
242
+ iota(3).to_dict(value=lambda x: x*x) # {0: 0, 1: 1, 2: 4}
243
+ ```
244
+
245
+ ### Async iteration
246
+
247
+ ```python
248
+ async for x in iota(5):
249
+ ...
250
+ ```
251
+
252
+ ---
253
+
254
+ ## Recipes
255
+
256
+ ```python
257
+ from pythoniota.recipes import (
258
+ fibonacci, lucas, factorial, catalan, harmonic,
259
+ triangle, powers, geometric, primes, primes_sieve,
260
+ pascal_row, collatz, repeat, cycle,
261
+ )
262
+
263
+ fibonacci(10).to_list() # 0,1,1,2,3,5,8,13,21,34
264
+ lucas(7).to_list() # 2,1,3,4,7,11,18
265
+ factorial(6).to_list() # 1,1,2,6,24,120
266
+ catalan(6).to_list() # 1,1,2,5,14,42
267
+ harmonic(4).to_list() # 1.0, 1.5, 1.833..., 2.083...
268
+ triangle(5).to_list() # 0,1,3,6,10
269
+ powers(2, 8).to_list() # 1,2,4,...,128
270
+ geometric(3, 2, 4).to_list() # 3,6,12,24
271
+ primes(5).to_list() # 2,3,5,7,11 (first n primes)
272
+ primes_sieve(20).to_list()# 2,3,5,7,11,13,17,19 (< limit, Eratosthenes)
273
+ pascal_row(4).to_list() # 1,4,6,4,1
274
+ collatz(6).to_list() # 6,3,10,5,16,8,4,2,1
275
+ repeat('x', 3).to_list() # ['x','x','x']
276
+ cycle([1,2], 5).to_list() # 1,2,1,2,1
277
+ ```
278
+
279
+ Recipes without a count argument produce infinite sequences: `fibonacci().take(20)`, `primes().takewhile(lambda p: p < 100)`.
280
+
281
+ ---
282
+
283
+ ## Safe expression evaluation
284
+
285
+ `safe_eval` evaluates arithmetic expressions over a whitelisted AST (no `eval`, no names/calls beyond provided variables):
286
+
287
+ ```python
288
+ from pythoniota import safe_eval
289
+
290
+ safe_eval("2 ** 10") # 1024
291
+ safe_eval("a * b + 1", {"a": 3, "b": 4}) # 13
292
+ ```
293
+
294
+ ---
295
+
296
+ ## License
297
+
298
+ MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "PythonIota"
7
- version = "1.1.0"
7
+ version = "1.3.0"
8
8
  description = "Go-style iota enumerations and flexible sequence generators for Python"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}