PythonIota 1.2.0__py3-none-any.whl → 1.3.0__py3-none-any.whl
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.
- pythoniota/__init__.py +3 -2
- pythoniota/enum.py +60 -7
- pythoniota/recipes.py +137 -0
- pythoniota/sequence.py +170 -19
- pythoniota-1.3.0.dist-info/METADATA +315 -0
- pythoniota-1.3.0.dist-info/RECORD +11 -0
- {pythoniota-1.2.0.dist-info → pythoniota-1.3.0.dist-info}/WHEEL +1 -1
- pythoniota-1.2.0.dist-info/METADATA +0 -16
- pythoniota-1.2.0.dist-info/RECORD +0 -11
- {pythoniota-1.2.0.dist-info → pythoniota-1.3.0.dist-info}/top_level.txt +0 -0
pythoniota/__init__.py
CHANGED
|
@@ -3,12 +3,12 @@ from pythoniota._bitflag import BitFlag
|
|
|
3
3
|
from pythoniota._safe_eval import safe_eval
|
|
4
4
|
from pythoniota.enum import (
|
|
5
5
|
IotaEnum, IotaBitFlags, IotaStringEnum,
|
|
6
|
-
FlagScope, iota_enum, _EnumMemberProxy,
|
|
6
|
+
FlagScope, iota_enum, unique, _EnumMemberProxy,
|
|
7
7
|
)
|
|
8
8
|
from pythoniota.sequence import IotaSequence as iota
|
|
9
9
|
from pythoniota import recipes
|
|
10
10
|
|
|
11
|
-
__version__ = "1.
|
|
11
|
+
__version__ = "1.3.0"
|
|
12
12
|
__all__ = [
|
|
13
13
|
"Iota",
|
|
14
14
|
"IotaEnum",
|
|
@@ -18,6 +18,7 @@ __all__ = [
|
|
|
18
18
|
"BitFlag",
|
|
19
19
|
"FlagScope",
|
|
20
20
|
"iota_enum",
|
|
21
|
+
"unique",
|
|
21
22
|
"safe_eval",
|
|
22
23
|
"recipes",
|
|
23
24
|
]
|
pythoniota/enum.py
CHANGED
|
@@ -113,20 +113,32 @@ class IotaNamespace(dict): # type: ignore[type-arg]
|
|
|
113
113
|
def __init__(self) -> None:
|
|
114
114
|
super().__init__()
|
|
115
115
|
self._counter = 0
|
|
116
|
+
self._step = 1
|
|
116
117
|
self._member_names: list[str] = []
|
|
117
118
|
|
|
119
|
+
def _skip(self, n: int = 1) -> None:
|
|
120
|
+
"""Advance the iota counter by `n` steps without defining a member (Go's blank `_`)."""
|
|
121
|
+
self._counter += n * self._step
|
|
122
|
+
return None
|
|
123
|
+
|
|
118
124
|
def __getitem__(self, key: str) -> Any:
|
|
119
125
|
if key == "iota":
|
|
120
126
|
val = _IotaCounter(self._counter)
|
|
121
|
-
self._counter +=
|
|
127
|
+
self._counter += self._step
|
|
122
128
|
return val
|
|
129
|
+
if key == "skip":
|
|
130
|
+
return self._skip
|
|
123
131
|
return super().__getitem__(key)
|
|
124
132
|
|
|
125
133
|
def __setitem__(self, key: str, value: Any) -> None:
|
|
134
|
+
if key == "_iota_start_":
|
|
135
|
+
self._counter = int(value)
|
|
136
|
+
elif key == "_iota_step_":
|
|
137
|
+
self._step = int(value)
|
|
126
138
|
super().__setitem__(key, value)
|
|
127
139
|
if (
|
|
128
140
|
not key.startswith("_")
|
|
129
|
-
and key
|
|
141
|
+
and key not in ("iota", "skip")
|
|
130
142
|
and isinstance(value, (int, float, str, _IotaCounter))
|
|
131
143
|
):
|
|
132
144
|
if key not in self._member_names:
|
|
@@ -182,7 +194,7 @@ class IotaEnumMeta(type):
|
|
|
182
194
|
members[mname] = flag
|
|
183
195
|
type.__setattr__(cls, mname, flag)
|
|
184
196
|
elif is_ordered:
|
|
185
|
-
for order, (mname, mval) in
|
|
197
|
+
for order, (mname, mval) in enumerate(members.items()):
|
|
186
198
|
proxy = _EnumMemberProxy(mname, mval, cls, order)
|
|
187
199
|
members[mname] = proxy
|
|
188
200
|
type.__setattr__(cls, mname, proxy)
|
|
@@ -388,6 +400,14 @@ class IotaEnum(metaclass=IotaEnumMeta):
|
|
|
388
400
|
return alias
|
|
389
401
|
return None
|
|
390
402
|
|
|
403
|
+
@classmethod
|
|
404
|
+
def has(cls, name: str) -> bool:
|
|
405
|
+
return name in cls._members_ # type: ignore[attr-defined]
|
|
406
|
+
|
|
407
|
+
@classmethod
|
|
408
|
+
def get(cls, name: str, default: Any = None) -> Any:
|
|
409
|
+
return cls._members_.get(name, default) # type: ignore[attr-defined]
|
|
410
|
+
|
|
391
411
|
@classmethod
|
|
392
412
|
def alias(cls, alias_name: str, target_name: str) -> None:
|
|
393
413
|
if target_name not in cls._members_: # type: ignore[attr-defined]
|
|
@@ -397,13 +417,21 @@ class IotaEnum(metaclass=IotaEnumMeta):
|
|
|
397
417
|
|
|
398
418
|
@classmethod
|
|
399
419
|
def to_dict(cls) -> dict[str, Any]:
|
|
400
|
-
|
|
420
|
+
d = {}
|
|
421
|
+
for k, v in cls._members_.items(): # type: ignore[attr-defined]
|
|
422
|
+
d[k] = v._value if isinstance(v, _EnumMemberProxy) else v
|
|
423
|
+
return d
|
|
401
424
|
|
|
402
425
|
@classmethod
|
|
403
426
|
def to_json(cls, **kwargs: Any) -> str:
|
|
404
427
|
d = {}
|
|
405
428
|
for k, v in cls._members_.items(): # type: ignore[attr-defined]
|
|
406
|
-
|
|
429
|
+
if isinstance(v, _EnumMemberProxy):
|
|
430
|
+
d[k] = v._value
|
|
431
|
+
elif isinstance(v, BitFlag):
|
|
432
|
+
d[k] = int(v)
|
|
433
|
+
else:
|
|
434
|
+
d[k] = v
|
|
407
435
|
return json.dumps(d, **kwargs)
|
|
408
436
|
|
|
409
437
|
@classmethod
|
|
@@ -443,6 +471,14 @@ class IotaBitFlags(metaclass=IotaEnumMeta):
|
|
|
443
471
|
return name
|
|
444
472
|
return None
|
|
445
473
|
|
|
474
|
+
@classmethod
|
|
475
|
+
def has(cls, name: str) -> bool:
|
|
476
|
+
return name in cls._members_ # type: ignore[attr-defined]
|
|
477
|
+
|
|
478
|
+
@classmethod
|
|
479
|
+
def get(cls, name: str, default: Any = None) -> Any:
|
|
480
|
+
return cls._members_.get(name, default) # type: ignore[attr-defined]
|
|
481
|
+
|
|
446
482
|
@classmethod
|
|
447
483
|
def to_dict(cls) -> dict[str, int]:
|
|
448
484
|
return {k: int(v) for k, v in cls._members_.items()} # type: ignore[attr-defined]
|
|
@@ -536,5 +572,22 @@ def iota_enum(cls: type | None = None, *, ordered: bool = False) -> Any:
|
|
|
536
572
|
return _wrap
|
|
537
573
|
|
|
538
574
|
|
|
539
|
-
|
|
540
|
-
|
|
575
|
+
def _member_value(val: Any) -> Any:
|
|
576
|
+
if isinstance(val, _EnumMemberProxy):
|
|
577
|
+
return val._value
|
|
578
|
+
if isinstance(val, BitFlag):
|
|
579
|
+
return int(val)
|
|
580
|
+
return val
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def unique(cls: type) -> type:
|
|
584
|
+
"""Class decorator that rejects an enum with duplicate member values."""
|
|
585
|
+
seen: dict[Any, str] = {}
|
|
586
|
+
for name, val in cls._members_.items(): # type: ignore[attr-defined]
|
|
587
|
+
v = _member_value(val)
|
|
588
|
+
if v in seen:
|
|
589
|
+
raise ValueError(
|
|
590
|
+
f"duplicate value {v!r} in {cls.__name__}: {seen[v]} and {name}"
|
|
591
|
+
)
|
|
592
|
+
seen[v] = name
|
|
593
|
+
return cls
|
pythoniota/recipes.py
CHANGED
|
@@ -234,3 +234,140 @@ def powers(base: int | float, n: int | None = None) -> IotaSequence:
|
|
|
234
234
|
seq._step = 1
|
|
235
235
|
seq._map = None
|
|
236
236
|
return seq
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def factorial(n: int | None = None) -> IotaSequence:
|
|
240
|
+
class FactorialSequence(IotaSequence):
|
|
241
|
+
def __iter__(self) -> Iterator[int]:
|
|
242
|
+
f = 1
|
|
243
|
+
i = 0
|
|
244
|
+
while self._stop is None or i < self._stop:
|
|
245
|
+
yield f
|
|
246
|
+
i += 1
|
|
247
|
+
f *= i
|
|
248
|
+
|
|
249
|
+
def __repr__(self) -> str:
|
|
250
|
+
return "factorial(infinite)" if self._stop is None else f"factorial({self._stop})"
|
|
251
|
+
|
|
252
|
+
seq = FactorialSequence.__new__(FactorialSequence)
|
|
253
|
+
seq._start = 0
|
|
254
|
+
seq._stop = n
|
|
255
|
+
seq._step = 1
|
|
256
|
+
seq._map = None
|
|
257
|
+
return seq
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def catalan(n: int | None = None) -> IotaSequence:
|
|
261
|
+
class CatalanSequence(IotaSequence):
|
|
262
|
+
def __iter__(self) -> Iterator[int]:
|
|
263
|
+
c = 1
|
|
264
|
+
k = 0
|
|
265
|
+
while self._stop is None or k < self._stop:
|
|
266
|
+
yield c
|
|
267
|
+
c = c * 2 * (2 * k + 1) // (k + 2)
|
|
268
|
+
k += 1
|
|
269
|
+
|
|
270
|
+
def __repr__(self) -> str:
|
|
271
|
+
return "catalan(infinite)" if self._stop is None else f"catalan({self._stop})"
|
|
272
|
+
|
|
273
|
+
seq = CatalanSequence.__new__(CatalanSequence)
|
|
274
|
+
seq._start = 0
|
|
275
|
+
seq._stop = n
|
|
276
|
+
seq._step = 1
|
|
277
|
+
seq._map = None
|
|
278
|
+
return seq
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def lucas(n: int | None = None) -> IotaSequence:
|
|
282
|
+
class LucasSequence(IotaSequence):
|
|
283
|
+
def __iter__(self) -> Iterator[int]:
|
|
284
|
+
a, b = 2, 1
|
|
285
|
+
if self._stop is None:
|
|
286
|
+
while True:
|
|
287
|
+
yield a
|
|
288
|
+
a, b = b, a + b
|
|
289
|
+
else:
|
|
290
|
+
count = 0
|
|
291
|
+
while count < self._stop:
|
|
292
|
+
yield a
|
|
293
|
+
a, b = b, a + b
|
|
294
|
+
count += 1
|
|
295
|
+
|
|
296
|
+
def __repr__(self) -> str:
|
|
297
|
+
return "lucas(infinite)" if self._stop is None else f"lucas({self._stop})"
|
|
298
|
+
|
|
299
|
+
seq = LucasSequence.__new__(LucasSequence)
|
|
300
|
+
seq._start = 0
|
|
301
|
+
seq._stop = n
|
|
302
|
+
seq._step = 1
|
|
303
|
+
seq._map = None
|
|
304
|
+
return seq
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def harmonic(n: int | None = None) -> IotaSequence:
|
|
308
|
+
"""Harmonic numbers H_k = sum(1/i for i in 1..k), yielded as floats starting at H_1."""
|
|
309
|
+
|
|
310
|
+
class HarmonicSequence(IotaSequence):
|
|
311
|
+
def __iter__(self) -> Iterator[float]:
|
|
312
|
+
total = 0.0
|
|
313
|
+
k = 1
|
|
314
|
+
while self._stop is None or k <= self._stop:
|
|
315
|
+
total += 1.0 / k
|
|
316
|
+
yield total
|
|
317
|
+
k += 1
|
|
318
|
+
|
|
319
|
+
def __repr__(self) -> str:
|
|
320
|
+
return "harmonic(infinite)" if self._stop is None else f"harmonic({self._stop})"
|
|
321
|
+
|
|
322
|
+
seq = HarmonicSequence.__new__(HarmonicSequence)
|
|
323
|
+
seq._start = 0
|
|
324
|
+
seq._stop = n
|
|
325
|
+
seq._step = 1
|
|
326
|
+
seq._map = None
|
|
327
|
+
return seq
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def pascal_row(row: int) -> IotaSequence:
|
|
331
|
+
"""The `row`-th row of Pascal's triangle: C(row, 0), C(row, 1), ..., C(row, row)."""
|
|
332
|
+
if row < 0:
|
|
333
|
+
raise ValueError("row must be non-negative")
|
|
334
|
+
return IotaSequence(row + 1, map=lambda k: math.comb(row, k))
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def primes_sieve(limit: int) -> IotaSequence:
|
|
338
|
+
"""All primes strictly less than `limit`, via the Sieve of Eratosthenes."""
|
|
339
|
+
|
|
340
|
+
class PrimesSieveSequence(IotaSequence):
|
|
341
|
+
def __init__(self, lim: int) -> None:
|
|
342
|
+
self._start = 0
|
|
343
|
+
self._stop = None
|
|
344
|
+
self._step = 1
|
|
345
|
+
self._map = None
|
|
346
|
+
self._limit = lim
|
|
347
|
+
|
|
348
|
+
@property
|
|
349
|
+
def infinite(self) -> bool:
|
|
350
|
+
return False
|
|
351
|
+
|
|
352
|
+
def __iter__(self) -> Iterator[int]:
|
|
353
|
+
lim = self._limit
|
|
354
|
+
if lim < 3:
|
|
355
|
+
return
|
|
356
|
+
sieve = bytearray([1]) * lim
|
|
357
|
+
sieve[0] = sieve[1] = 0
|
|
358
|
+
p = 2
|
|
359
|
+
while p * p < lim:
|
|
360
|
+
if sieve[p]:
|
|
361
|
+
sieve[p * p : lim : p] = bytearray(len(range(p * p, lim, p)))
|
|
362
|
+
p += 1
|
|
363
|
+
for i in range(2, lim):
|
|
364
|
+
if sieve[i]:
|
|
365
|
+
yield i
|
|
366
|
+
|
|
367
|
+
def __len__(self) -> int:
|
|
368
|
+
return sum(1 for _ in self)
|
|
369
|
+
|
|
370
|
+
def __repr__(self) -> str:
|
|
371
|
+
return f"primes_sieve({self._limit})"
|
|
372
|
+
|
|
373
|
+
return PrimesSieveSequence(limit)
|
pythoniota/sequence.py
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import itertools
|
|
4
|
-
import math
|
|
5
4
|
from collections import deque
|
|
6
5
|
from functools import reduce as _reduce
|
|
7
6
|
from typing import Any, Callable, Iterator, TypeVar, overload
|
|
8
7
|
|
|
9
8
|
T = TypeVar("T")
|
|
10
9
|
|
|
10
|
+
_MISSING: Any = object()
|
|
11
|
+
|
|
11
12
|
|
|
12
13
|
class IotaSequence:
|
|
13
14
|
|
|
@@ -66,14 +67,15 @@ class IotaSequence:
|
|
|
66
67
|
def __iter__(self) -> Iterator[Any]:
|
|
67
68
|
it = self._raw_iter()
|
|
68
69
|
if self._map is not None:
|
|
69
|
-
return
|
|
70
|
+
return map(self._map, it)
|
|
70
71
|
return it
|
|
71
72
|
|
|
72
73
|
def __len__(self) -> int:
|
|
73
74
|
if self.infinite:
|
|
74
75
|
raise TypeError("infinite sequence has no len()")
|
|
75
76
|
assert self._stop is not None
|
|
76
|
-
|
|
77
|
+
adjust = 1 if self._step > 0 else -1
|
|
78
|
+
n = (self._stop - self._start + self._step - adjust) // self._step
|
|
77
79
|
return max(0, n)
|
|
78
80
|
|
|
79
81
|
def __getitem__(self, index: int | slice) -> Any:
|
|
@@ -84,18 +86,42 @@ class IotaSequence:
|
|
|
84
86
|
if self.infinite:
|
|
85
87
|
raise IndexError("negative indexing not supported on infinite sequences")
|
|
86
88
|
index = len(self) + index
|
|
89
|
+
if type(self) is IotaSequence:
|
|
90
|
+
if index < 0 or (not self.infinite and index >= self.__len__()):
|
|
91
|
+
raise IndexError("iota index out of range")
|
|
92
|
+
raw = self._start + index * self._step
|
|
93
|
+
return self._map(raw) if self._map is not None else raw
|
|
87
94
|
return next(itertools.islice(iter(self), index, index + 1))
|
|
88
95
|
|
|
89
96
|
def __contains__(self, value: Any) -> bool:
|
|
90
97
|
if self.infinite:
|
|
91
98
|
raise TypeError("cannot check membership on infinite sequence")
|
|
99
|
+
if (
|
|
100
|
+
type(self) is IotaSequence
|
|
101
|
+
and self._map is None
|
|
102
|
+
and isinstance(value, int)
|
|
103
|
+
and not isinstance(value, bool)
|
|
104
|
+
):
|
|
105
|
+
assert self._stop is not None
|
|
106
|
+
if self._step > 0:
|
|
107
|
+
in_range = self._start <= value < self._stop
|
|
108
|
+
else:
|
|
109
|
+
in_range = self._stop < value <= self._start
|
|
110
|
+
return in_range and (value - self._start) % self._step == 0
|
|
92
111
|
return value in iter(self)
|
|
93
112
|
|
|
94
113
|
def __reversed__(self) -> Iterator[Any]:
|
|
95
114
|
if self.infinite:
|
|
96
115
|
raise TypeError("cannot reverse infinite sequence")
|
|
116
|
+
if type(self) is IotaSequence:
|
|
117
|
+
return self._reversed_arith()
|
|
97
118
|
return reversed(list(self))
|
|
98
119
|
|
|
120
|
+
def _reversed_arith(self) -> Iterator[Any]:
|
|
121
|
+
for i in range(self.__len__() - 1, -1, -1):
|
|
122
|
+
raw = self._start + i * self._step
|
|
123
|
+
yield self._map(raw) if self._map is not None else raw
|
|
124
|
+
|
|
99
125
|
def take(self, n: int) -> list[Any]:
|
|
100
126
|
return list(itertools.islice(iter(self), n))
|
|
101
127
|
|
|
@@ -113,16 +139,16 @@ class IotaSequence:
|
|
|
113
139
|
return seq
|
|
114
140
|
|
|
115
141
|
def filter(self, fn: Callable[[Any], bool]) -> Iterator[Any]:
|
|
116
|
-
return
|
|
142
|
+
return filter(fn, iter(self))
|
|
117
143
|
|
|
118
144
|
def accumulate(self, fn: Callable[[Any, Any], Any] | None = None) -> Iterator[Any]:
|
|
119
145
|
return itertools.accumulate(iter(self), fn)
|
|
120
146
|
|
|
121
147
|
def enumerate(self, start: int = 0) -> Iterator[tuple[int, Any]]:
|
|
122
|
-
return
|
|
148
|
+
return enumerate(iter(self), start)
|
|
123
149
|
|
|
124
150
|
def zip(self, *others: Any) -> Iterator[tuple[Any, ...]]:
|
|
125
|
-
return
|
|
151
|
+
return zip(iter(self), *others)
|
|
126
152
|
|
|
127
153
|
def chunk(self, n: int) -> Iterator[list[Any]]:
|
|
128
154
|
it = iter(self)
|
|
@@ -142,12 +168,145 @@ class IotaSequence:
|
|
|
142
168
|
win.append(item)
|
|
143
169
|
yield tuple(win)
|
|
144
170
|
|
|
145
|
-
def reduce(self, fn: Callable[[Any, Any], Any], initial: Any =
|
|
171
|
+
def reduce(self, fn: Callable[[Any, Any], Any], initial: Any = _MISSING) -> Any:
|
|
146
172
|
if self.infinite:
|
|
147
173
|
raise TypeError("cannot reduce infinite sequence")
|
|
148
|
-
if initial is
|
|
149
|
-
return _reduce(fn, iter(self)
|
|
150
|
-
return _reduce(fn, iter(self))
|
|
174
|
+
if initial is _MISSING:
|
|
175
|
+
return _reduce(fn, iter(self))
|
|
176
|
+
return _reduce(fn, iter(self), initial)
|
|
177
|
+
|
|
178
|
+
# --- lazy combinators (return iterators) ---
|
|
179
|
+
|
|
180
|
+
def takewhile(self, pred: Callable[[Any], bool]) -> Iterator[Any]:
|
|
181
|
+
return itertools.takewhile(pred, iter(self))
|
|
182
|
+
|
|
183
|
+
def dropwhile(self, pred: Callable[[Any], bool]) -> Iterator[Any]:
|
|
184
|
+
return itertools.dropwhile(pred, iter(self))
|
|
185
|
+
|
|
186
|
+
def pairwise(self) -> Iterator[tuple[Any, Any]]:
|
|
187
|
+
return itertools.pairwise(iter(self))
|
|
188
|
+
|
|
189
|
+
def flatten(self) -> Iterator[Any]:
|
|
190
|
+
for item in self:
|
|
191
|
+
if isinstance(item, (str, bytes)):
|
|
192
|
+
yield item
|
|
193
|
+
else:
|
|
194
|
+
try:
|
|
195
|
+
inner = iter(item)
|
|
196
|
+
except TypeError:
|
|
197
|
+
yield item
|
|
198
|
+
else:
|
|
199
|
+
yield from inner
|
|
200
|
+
|
|
201
|
+
def distinct(self, key: Callable[[Any], Any] | None = None) -> Iterator[Any]:
|
|
202
|
+
seen: set[Any] = set()
|
|
203
|
+
for item in self:
|
|
204
|
+
k = key(item) if key is not None else item
|
|
205
|
+
if k not in seen:
|
|
206
|
+
seen.add(k)
|
|
207
|
+
yield item
|
|
208
|
+
|
|
209
|
+
# --- terminal operations (consume the sequence) ---
|
|
210
|
+
|
|
211
|
+
def _reject_infinite(self, op: str) -> None:
|
|
212
|
+
if self.infinite:
|
|
213
|
+
raise TypeError(f"cannot {op} an infinite sequence")
|
|
214
|
+
|
|
215
|
+
def to_list(self) -> list[Any]:
|
|
216
|
+
self._reject_infinite("materialize")
|
|
217
|
+
return list(self)
|
|
218
|
+
|
|
219
|
+
def to_tuple(self) -> tuple[Any, ...]:
|
|
220
|
+
self._reject_infinite("materialize")
|
|
221
|
+
return tuple(self)
|
|
222
|
+
|
|
223
|
+
def to_set(self) -> set[Any]:
|
|
224
|
+
self._reject_infinite("materialize")
|
|
225
|
+
return set(self)
|
|
226
|
+
|
|
227
|
+
def to_dict(
|
|
228
|
+
self,
|
|
229
|
+
key: Callable[[Any], Any] | None = None,
|
|
230
|
+
value: Callable[[Any], Any] | None = None,
|
|
231
|
+
) -> dict[Any, Any]:
|
|
232
|
+
self._reject_infinite("materialize")
|
|
233
|
+
return {
|
|
234
|
+
(key(x) if key is not None else x): (value(x) if value is not None else x)
|
|
235
|
+
for x in self
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
def sum(self, start: Any = 0) -> Any:
|
|
239
|
+
self._reject_infinite("sum")
|
|
240
|
+
if type(self) is IotaSequence and self._map is None:
|
|
241
|
+
n = self.__len__()
|
|
242
|
+
if n == 0:
|
|
243
|
+
return start
|
|
244
|
+
last = self._start + (n - 1) * self._step
|
|
245
|
+
return start + n * (self._start + last) // 2
|
|
246
|
+
return sum(self, start)
|
|
247
|
+
|
|
248
|
+
def min(self, default: Any = _MISSING) -> Any:
|
|
249
|
+
self._reject_infinite("take min of")
|
|
250
|
+
if type(self) is IotaSequence and self._map is None:
|
|
251
|
+
n = self.__len__()
|
|
252
|
+
if n == 0:
|
|
253
|
+
if default is _MISSING:
|
|
254
|
+
raise ValueError("min() of an empty sequence")
|
|
255
|
+
return default
|
|
256
|
+
last = self._start + (n - 1) * self._step
|
|
257
|
+
return self._start if self._step > 0 else last
|
|
258
|
+
return min(self) if default is _MISSING else min(self, default=default)
|
|
259
|
+
|
|
260
|
+
def max(self, default: Any = _MISSING) -> Any:
|
|
261
|
+
self._reject_infinite("take max of")
|
|
262
|
+
if type(self) is IotaSequence and self._map is None:
|
|
263
|
+
n = self.__len__()
|
|
264
|
+
if n == 0:
|
|
265
|
+
if default is _MISSING:
|
|
266
|
+
raise ValueError("max() of an empty sequence")
|
|
267
|
+
return default
|
|
268
|
+
last = self._start + (n - 1) * self._step
|
|
269
|
+
return last if self._step > 0 else self._start
|
|
270
|
+
return max(self) if default is _MISSING else max(self, default=default)
|
|
271
|
+
|
|
272
|
+
def count(self) -> int:
|
|
273
|
+
self._reject_infinite("count")
|
|
274
|
+
if type(self) is IotaSequence:
|
|
275
|
+
return self.__len__()
|
|
276
|
+
return sum(1 for _ in self)
|
|
277
|
+
|
|
278
|
+
def first(self, default: Any = None) -> Any:
|
|
279
|
+
return next(iter(self), default)
|
|
280
|
+
|
|
281
|
+
def last(self, default: Any = None) -> Any:
|
|
282
|
+
self._reject_infinite("take last of")
|
|
283
|
+
if type(self) is IotaSequence:
|
|
284
|
+
n = self.__len__()
|
|
285
|
+
if n == 0:
|
|
286
|
+
return default
|
|
287
|
+
raw = self._start + (n - 1) * self._step
|
|
288
|
+
return self._map(raw) if self._map is not None else raw
|
|
289
|
+
result = default
|
|
290
|
+
for item in self:
|
|
291
|
+
result = item
|
|
292
|
+
return result
|
|
293
|
+
|
|
294
|
+
def nth(self, n: int, default: Any = None) -> Any:
|
|
295
|
+
if type(self) is IotaSequence and n >= 0:
|
|
296
|
+
if self.infinite or n < self.__len__():
|
|
297
|
+
raw = self._start + n * self._step
|
|
298
|
+
return self._map(raw) if self._map is not None else raw
|
|
299
|
+
return default
|
|
300
|
+
return next(itertools.islice(iter(self), n, n + 1), default)
|
|
301
|
+
|
|
302
|
+
def find(self, pred: Callable[[Any], bool], default: Any = None) -> Any:
|
|
303
|
+
return next((x for x in self if pred(x)), default)
|
|
304
|
+
|
|
305
|
+
def any(self, pred: Callable[[Any], bool] | None = None) -> bool:
|
|
306
|
+
return any(pred(x) if pred is not None else x for x in self)
|
|
307
|
+
|
|
308
|
+
def all(self, pred: Callable[[Any], bool] | None = None) -> bool:
|
|
309
|
+
return all(pred(x) if pred is not None else x for x in self)
|
|
151
310
|
|
|
152
311
|
def __add__(self, other: IotaSequence) -> _ConcatSequence:
|
|
153
312
|
return _ConcatSequence(self, other)
|
|
@@ -287,7 +446,7 @@ class _ZipMapSequence(IotaSequence):
|
|
|
287
446
|
return self._a.infinite and self._b.infinite
|
|
288
447
|
|
|
289
448
|
def __iter__(self) -> Iterator[tuple[Any, Any]]:
|
|
290
|
-
yield from
|
|
449
|
+
yield from zip(self._a, self._b)
|
|
291
450
|
|
|
292
451
|
def __len__(self) -> int:
|
|
293
452
|
if self.infinite:
|
|
@@ -300,11 +459,3 @@ class _ZipMapSequence(IotaSequence):
|
|
|
300
459
|
|
|
301
460
|
def __repr__(self) -> str:
|
|
302
461
|
return f"({self._a!r} @ {self._b!r})"
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
import builtins
|
|
306
|
-
|
|
307
|
-
builtins_map = builtins.map
|
|
308
|
-
builtins_filter = builtins.filter
|
|
309
|
-
builtins_enumerate = builtins.enumerate
|
|
310
|
-
builtins_zip = builtins.zip
|
|
@@ -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,11 @@
|
|
|
1
|
+
pythoniota/__init__.py,sha256=kSSASpZFdDUTmw7TfRNvppXWpp_nVTxd88Jn8SV1bIk,546
|
|
2
|
+
pythoniota/_bitflag.py,sha256=EOOwudYcOECBN665TSFMtWIT9l5bT-hrdN0Gr_x_RQk,3692
|
|
3
|
+
pythoniota/_compat.py,sha256=kscV09rl3-1ccU5hm_dGj7EocKSLj0qUmRVUNOTmBwU,2075
|
|
4
|
+
pythoniota/_safe_eval.py,sha256=FJgZxLp5eBDUg1MchVjkHSqnDOM9Jo6mmiupKZ2CvR4,2051
|
|
5
|
+
pythoniota/enum.py,sha256=PQNk3rbY93C7AwNoD5LmdDNlr3gvppFMt15QedsTBUo,19846
|
|
6
|
+
pythoniota/recipes.py,sha256=W005nhwH4rqLfSXIsuFjl6ZjZvzHFEC9nrJH2_VibIA,10835
|
|
7
|
+
pythoniota/sequence.py,sha256=XMtbBIP-elaXkBiYOpSt0qPIYQOz_lGo2lHDh5z3V-A,15344
|
|
8
|
+
pythoniota-1.3.0.dist-info/METADATA,sha256=22aDmgyCECjd4KSDllYc1HK1NVTRkd3up-zaPyKFdo0,8084
|
|
9
|
+
pythoniota-1.3.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
10
|
+
pythoniota-1.3.0.dist-info/top_level.txt,sha256=LoZ35nXIBPHY71-dvmTXyI_g9PBRt8hui5ubG4Huu8c,11
|
|
11
|
+
pythoniota-1.3.0.dist-info/RECORD,,
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: PythonIota
|
|
3
|
-
Version: 1.2.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
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
pythoniota/__init__.py,sha256=nQiMvhfaPQPcl6UyUmxv3aA39vqle0ARK4BwYMBhwlg,524
|
|
2
|
-
pythoniota/_bitflag.py,sha256=EOOwudYcOECBN665TSFMtWIT9l5bT-hrdN0Gr_x_RQk,3692
|
|
3
|
-
pythoniota/_compat.py,sha256=kscV09rl3-1ccU5hm_dGj7EocKSLj0qUmRVUNOTmBwU,2075
|
|
4
|
-
pythoniota/_safe_eval.py,sha256=FJgZxLp5eBDUg1MchVjkHSqnDOM9Jo6mmiupKZ2CvR4,2051
|
|
5
|
-
pythoniota/enum.py,sha256=Spwp_EosBA_ltMdHn3sRjJ-djF5_C8PJ-uKKbzTfd_4,18102
|
|
6
|
-
pythoniota/recipes.py,sha256=5L4iAiUL2tRi6YG7XUQYTl2cUFY20gMP72BoEbf-Leo,6847
|
|
7
|
-
pythoniota/sequence.py,sha256=Ov76UtMuhiVULdfYwb645eSeGuZZN-qEYhUGNFPiYe4,9559
|
|
8
|
-
pythoniota-1.2.0.dist-info/METADATA,sha256=O87uw7fQS2qThEhLYbcipk1VgGffNoNtPlasV_iLYIw,621
|
|
9
|
-
pythoniota-1.2.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
10
|
-
pythoniota-1.2.0.dist-info/top_level.txt,sha256=LoZ35nXIBPHY71-dvmTXyI_g9PBRt8hui5ubG4Huu8c,11
|
|
11
|
-
pythoniota-1.2.0.dist-info/RECORD,,
|
|
File without changes
|