pyochain 0.5.1__py3-none-any.whl → 0.5.32__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.

Potentially problematic release.


This version of pyochain might be problematic. Click here for more details.

@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import itertools
4
4
  from collections.abc import Callable, Generator, Iterable, Iterator
5
5
  from functools import partial
6
- from typing import TYPE_CHECKING, Any, TypeGuard
6
+ from typing import TYPE_CHECKING, Any, TypeIs, overload
7
7
 
8
8
  import cytoolz as cz
9
9
  import more_itertools as mit
@@ -15,7 +15,11 @@ if TYPE_CHECKING:
15
15
 
16
16
 
17
17
  class BaseFilter[T](IterWrapper[T]):
18
- def filter(self, func: Callable[[T], bool]) -> Iter[T]:
18
+ @overload
19
+ def filter[U](self, func: Callable[[T], TypeIs[U]]) -> Iter[U]: ...
20
+ @overload
21
+ def filter(self, func: Callable[[T], bool]) -> Iter[T]: ...
22
+ def filter[U](self, func: Callable[[T], bool | TypeIs[U]]) -> Iter[T] | Iter[U]:
19
23
  """
20
24
  Return an iterator yielding those items of iterable for which function is true.
21
25
 
@@ -33,7 +37,7 @@ class BaseFilter[T](IterWrapper[T]):
33
37
  def _filter(data: Iterable[T]) -> Iterator[T]:
34
38
  return (x for x in data if func(x))
35
39
 
36
- return self.apply(_filter)
40
+ return self._lazy(_filter)
37
41
 
38
42
  def filter_isin(self, values: Iterable[T]) -> Iter[T]:
39
43
  """
@@ -54,7 +58,7 @@ class BaseFilter[T](IterWrapper[T]):
54
58
  value_set: set[T] = set(values)
55
59
  return (x for x in data if x in value_set)
56
60
 
57
- return self.apply(_filter_isin)
61
+ return self._lazy(_filter_isin)
58
62
 
59
63
  def filter_notin(self, values: Iterable[T]) -> Iter[T]:
60
64
  """
@@ -75,7 +79,7 @@ class BaseFilter[T](IterWrapper[T]):
75
79
  value_set: set[T] = set(values)
76
80
  return (x for x in data if x not in value_set)
77
81
 
78
- return self.apply(_filter_notin)
82
+ return self._lazy(_filter_notin)
79
83
 
80
84
  def filter_contain(
81
85
  self: IterWrapper[str], text: str, format: Callable[[str], str] | None = None
@@ -84,6 +88,7 @@ class BaseFilter[T](IterWrapper[T]):
84
88
  Return elements that contain the given text.
85
89
 
86
90
  Optionally, a format function can be provided to preprocess each element before checking for the substring.
91
+
87
92
  Args:
88
93
  text: Substring to check for.
89
94
  format: Optional function to preprocess each element before checking. Defaults to None.
@@ -107,7 +112,7 @@ class BaseFilter[T](IterWrapper[T]):
107
112
 
108
113
  return (x for x in data if _(x))
109
114
 
110
- return self.apply(_filter_contain)
115
+ return self._lazy(_filter_contain)
111
116
 
112
117
  def filter_attr[U](self, attr: str, dtype: type[U] = object) -> Iter[U]:
113
118
  """
@@ -130,12 +135,12 @@ class BaseFilter[T](IterWrapper[T]):
130
135
  """
131
136
 
132
137
  def check(data: Iterable[Any]) -> Generator[U, None, None]:
133
- def _(x: Any) -> TypeGuard[U]:
138
+ def _(x: Any) -> TypeIs[U]:
134
139
  return hasattr(x, attr)
135
140
 
136
141
  return (x for x in data if _(x))
137
142
 
138
- return self.apply(check)
143
+ return self._lazy(check)
139
144
 
140
145
  def filter_false(self, func: Callable[[T], bool]) -> Iter[T]:
141
146
  """
@@ -151,7 +156,7 @@ class BaseFilter[T](IterWrapper[T]):
151
156
 
152
157
  ```
153
158
  """
154
- return self.apply(partial(itertools.filterfalse, func))
159
+ return self._lazy(partial(itertools.filterfalse, func))
155
160
 
156
161
  def filter_except(
157
162
  self, func: Callable[[T], object], *exceptions: type[BaseException]
@@ -181,7 +186,7 @@ class BaseFilter[T](IterWrapper[T]):
181
186
  def _filter_except(data: Iterable[T]) -> Iterator[T]:
182
187
  return mit.filter_except(func, data, *exceptions)
183
188
 
184
- return self.apply(_filter_except)
189
+ return self._lazy(_filter_except)
185
190
 
186
191
  def take_while(self, predicate: Callable[[T], bool]) -> Iter[T]:
187
192
  """
@@ -197,7 +202,7 @@ class BaseFilter[T](IterWrapper[T]):
197
202
 
198
203
  ```
199
204
  """
200
- return self.apply(partial(itertools.takewhile, predicate))
205
+ return self._lazy(partial(itertools.takewhile, predicate))
201
206
 
202
207
  def skip_while(self, predicate: Callable[[T], bool]) -> Iter[T]:
203
208
  """
@@ -213,7 +218,7 @@ class BaseFilter[T](IterWrapper[T]):
213
218
 
214
219
  ```
215
220
  """
216
- return self.apply(partial(itertools.dropwhile, predicate))
221
+ return self._lazy(partial(itertools.dropwhile, predicate))
217
222
 
218
223
  def compress(self, *selectors: bool) -> Iter[T]:
219
224
  """
@@ -229,7 +234,7 @@ class BaseFilter[T](IterWrapper[T]):
229
234
 
230
235
  ```
231
236
  """
232
- return self.apply(itertools.compress, selectors)
237
+ return self._lazy(itertools.compress, selectors)
233
238
 
234
239
  def unique(self, key: Callable[[T], Any] | None = None) -> Iter[T]:
235
240
  """
@@ -253,7 +258,7 @@ class BaseFilter[T](IterWrapper[T]):
253
258
 
254
259
  ```
255
260
  """
256
- return self.apply(cz.itertoolz.unique, key=key)
261
+ return self._lazy(cz.itertoolz.unique, key=key)
257
262
 
258
263
  def take(self, n: int) -> Iter[T]:
259
264
  """
@@ -280,7 +285,7 @@ class BaseFilter[T](IterWrapper[T]):
280
285
  ```
281
286
  """
282
287
 
283
- return self.apply(partial(cz.itertoolz.take, n))
288
+ return self._lazy(partial(cz.itertoolz.take, n))
284
289
 
285
290
  def skip(self, n: int) -> Iter[T]:
286
291
  """
@@ -296,7 +301,7 @@ class BaseFilter[T](IterWrapper[T]):
296
301
 
297
302
  ```
298
303
  """
299
- return self.apply(partial(cz.itertoolz.drop, n))
304
+ return self._lazy(partial(cz.itertoolz.drop, n))
300
305
 
301
306
  def unique_justseen(self, key: Callable[[T], Any] | None = None) -> Iter[T]:
302
307
  """
@@ -314,7 +319,7 @@ class BaseFilter[T](IterWrapper[T]):
314
319
 
315
320
  ```
316
321
  """
317
- return self.apply(mit.unique_justseen, key=key)
322
+ return self._lazy(mit.unique_justseen, key=key)
318
323
 
319
324
  def unique_in_window(
320
325
  self, n: int, key: Callable[[T], Any] | None = None
@@ -323,6 +328,7 @@ class BaseFilter[T](IterWrapper[T]):
323
328
  Yield the items from iterable that haven't been seen recently.
324
329
 
325
330
  The items in iterable must be hashable.
331
+
326
332
  Args:
327
333
  n: Size of the lookback window.
328
334
  key: Function to transform items before comparison. Defaults to None.
@@ -342,7 +348,7 @@ class BaseFilter[T](IterWrapper[T]):
342
348
 
343
349
  ```
344
350
  """
345
- return self.apply(mit.unique_in_window, n, key=key)
351
+ return self._lazy(mit.unique_in_window, n, key=key)
346
352
 
347
353
  def extract(self, indices: Iterable[int]) -> Iter[T]:
348
354
  """
@@ -364,7 +370,7 @@ class BaseFilter[T](IterWrapper[T]):
364
370
 
365
371
  ```
366
372
  """
367
- return self.apply(mit.extract, indices)
373
+ return self._lazy(mit.extract, indices)
368
374
 
369
375
  def every(self, index: int) -> Iter[T]:
370
376
  """
@@ -380,28 +386,33 @@ class BaseFilter[T](IterWrapper[T]):
380
386
 
381
387
  ```
382
388
  """
383
- return self.apply(partial(cz.itertoolz.take_nth, index))
389
+ return self._lazy(partial(cz.itertoolz.take_nth, index))
384
390
 
385
- def slice(self, start: int | None = None, stop: int | None = None) -> Iter[T]:
391
+ def slice(
392
+ self, start: int | None = None, stop: int | None = None, step: int | None = None
393
+ ) -> Iter[T]:
386
394
  """
387
395
  Return a slice of the iterable.
388
396
 
389
397
  Args:
390
398
  start: Starting index of the slice. Defaults to None.
391
399
  stop: Ending index of the slice. Defaults to None.
400
+ step: Step size for the slice. Defaults to None.
392
401
  Example:
393
402
  ```python
394
403
  >>> import pyochain as pc
395
404
  >>> pc.Iter.from_([1, 2, 3, 4, 5]).slice(1, 4).into(list)
396
405
  [2, 3, 4]
406
+ >>> pc.Iter.from_([1, 2, 3, 4, 5]).slice(step=2).into(list)
407
+ [1, 3, 5]
397
408
 
398
409
  ```
399
410
  """
400
411
 
401
412
  def _slice(data: Iterable[T]) -> Iterator[T]:
402
- return itertools.islice(data, start, stop)
413
+ return itertools.islice(data, start, stop, step)
403
414
 
404
- return self.apply(_slice)
415
+ return self._lazy(_slice)
405
416
 
406
417
  def filter_subclass[U: type[Any], R](
407
418
  self: IterWrapper[U], parent: type[R], keep_parent: bool = True
@@ -441,14 +452,14 @@ class BaseFilter[T](IterWrapper[T]):
441
452
  else:
442
453
  return (x for x in data if issubclass(x, parent) and x is not parent)
443
454
 
444
- return self.apply(_filter_subclass)
455
+ return self._lazy(_filter_subclass)
445
456
 
446
- def filter_type[R](self, typ: type[R]) -> Iter[R]:
457
+ def filter_type[R](self, dtype: type[R]) -> Iter[R]:
447
458
  """
448
459
  Return elements that are instances of the given type.
449
460
 
450
461
  Args:
451
- typ: Type to check against.
462
+ dtype: Type to check against.
452
463
  Example:
453
464
  ```python
454
465
  >>> import pyochain as pc
@@ -459,29 +470,9 @@ class BaseFilter[T](IterWrapper[T]):
459
470
  """
460
471
 
461
472
  def _filter_type(data: Iterable[T]) -> Generator[R, None, None]:
462
- return (x for x in data if isinstance(x, typ))
463
-
464
- return self.apply(_filter_type)
465
-
466
- def filter_callable(self) -> Iter[Callable[..., Any]]:
467
- """
468
- Return only elements that are callable.
469
-
470
- Example:
471
- ```python
472
- >>> import pyochain as pc
473
- >>> pc.Iter.from_([len, 42, str, None, list]).filter_callable().into(list)
474
- [<built-in function len>, <class 'str'>, <class 'list'>]
475
-
476
- ```
477
- """
478
-
479
- def _filter_callable(
480
- data: Iterable[T],
481
- ) -> Generator[Callable[..., Any], None, None]:
482
- return (x for x in data if callable(x))
473
+ return (x for x in data if isinstance(x, dtype))
483
474
 
484
- return self.apply(_filter_callable)
475
+ return self._lazy(_filter_type)
485
476
 
486
477
  def filter_map[R](self, func: Callable[[T], R]) -> Iter[R]:
487
478
  """
@@ -500,4 +491,4 @@ class BaseFilter[T](IterWrapper[T]):
500
491
 
501
492
  ```
502
493
  """
503
- return self.apply(partial(mit.filter_map, func))
494
+ return self._lazy(partial(mit.filter_map, func))
pyochain/_iter/_joins.py CHANGED
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import itertools
4
4
  from collections.abc import Callable, Generator, Iterable, Iterator
5
- from typing import TYPE_CHECKING, Any, overload
5
+ from typing import TYPE_CHECKING, Any, TypeIs, overload
6
6
 
7
7
  import cytoolz as cz
8
8
  import more_itertools as mit
@@ -67,7 +67,7 @@ class BaseJoins[T](IterWrapper[T]):
67
67
 
68
68
  ```
69
69
  """
70
- return self.apply(zip, *others, strict=strict)
70
+ return self._lazy(zip, *others, strict=strict)
71
71
 
72
72
  def zip_offset[U](
73
73
  self,
@@ -113,7 +113,7 @@ class BaseJoins[T](IterWrapper[T]):
113
113
  fillvalue=fillvalue,
114
114
  )
115
115
 
116
- return self.apply(_zip_offset)
116
+ return self._lazy(_zip_offset)
117
117
 
118
118
  @overload
119
119
  def zip_broadcast[T1](
@@ -162,6 +162,7 @@ class BaseJoins[T](IterWrapper[T]):
162
162
  `str` and `bytes` are not treated as iterables.
163
163
 
164
164
  If the strict keyword argument is True, then UnequalIterablesError will be raised if any of the iterables have different lengths.
165
+
165
166
  Args:
166
167
  *others: Other iterables or scalars to zip with.
167
168
  strict: Whether to enforce equal lengths of iterables. Defaults to False.
@@ -182,15 +183,10 @@ class BaseJoins[T](IterWrapper[T]):
182
183
  ) -> Generator[tuple[Iterable[Any], ...] | tuple[object, ...], Any, None]:
183
184
  """from more_itertools.zip_broadcast"""
184
185
 
185
- def is_scalar(obj: Any) -> bool:
186
+ def is_scalar(obj: Any) -> TypeIs[object]:
186
187
  if isinstance(obj, (str, bytes)):
187
188
  return True
188
- try:
189
- iter(obj)
190
- except TypeError:
191
- return True
192
- else:
193
- return False
189
+ return cz.itertoolz.isiterable(obj) is False
194
190
 
195
191
  size = len(objects)
196
192
  if not size:
@@ -216,7 +212,7 @@ class BaseJoins[T](IterWrapper[T]):
216
212
  pass
217
213
  yield tuple(new_item)
218
214
 
219
- return self.apply(_zip_broadcast, *others)
215
+ return self._lazy(_zip_broadcast, *others)
220
216
 
221
217
  @overload
222
218
  def zip_equal(self) -> Iter[tuple[T]]: ...
@@ -265,7 +261,7 @@ class BaseJoins[T](IterWrapper[T]):
265
261
  def _zip_equal(data: Iterable[T]) -> Iterator[tuple[Any, ...]]:
266
262
  return mit.zip_equal(data, *others)
267
263
 
268
- return self.apply(_zip_equal)
264
+ return self._lazy(_zip_equal)
269
265
 
270
266
  def zip_longest[U](
271
267
  self, *others: Iterable[T], fill_value: U = None
@@ -284,7 +280,7 @@ class BaseJoins[T](IterWrapper[T]):
284
280
 
285
281
  ```
286
282
  """
287
- return self.apply(itertools.zip_longest, *others, fillvalue=fill_value)
283
+ return self._lazy(itertools.zip_longest, *others, fillvalue=fill_value)
288
284
 
289
285
  @overload
290
286
  def product(self) -> Iter[tuple[T]]: ...
@@ -328,7 +324,7 @@ class BaseJoins[T](IterWrapper[T]):
328
324
 
329
325
  ```
330
326
  """
331
- return self.apply(itertools.product, *others)
327
+ return self._lazy(itertools.product, *others)
332
328
 
333
329
  def diff_at(
334
330
  self,
@@ -341,6 +337,7 @@ class BaseJoins[T](IterWrapper[T]):
341
337
  Each output item is a tuple where the i-th element is from the i-th input iterable.
342
338
 
343
339
  If an input iterable is exhausted before others, then the corresponding output items will be filled with *default*.
340
+
344
341
  Args:
345
342
  *others: Other iterables to compare with.
346
343
  default: Value to use for missing elements. Defaults to None.
@@ -364,7 +361,7 @@ class BaseJoins[T](IterWrapper[T]):
364
361
 
365
362
  ```
366
363
  """
367
- return self.apply(cz.itertoolz.diff, *others, default=default, key=key)
364
+ return self._lazy(cz.itertoolz.diff, *others, default=default, key=key)
368
365
 
369
366
  def join[R, K](
370
367
  self,
@@ -404,4 +401,4 @@ class BaseJoins[T](IterWrapper[T]):
404
401
  right_default=right_default,
405
402
  )
406
403
 
407
- return self.apply(_join)
404
+ return self._lazy(_join)
pyochain/_iter/_lists.py CHANGED
@@ -29,7 +29,7 @@ class BaseList[T](IterWrapper[T]):
29
29
  def _implode(data: Iterable[T]) -> Generator[list[T], None, None]:
30
30
  return ([x] for x in data)
31
31
 
32
- return self.apply(_implode)
32
+ return self._lazy(_implode)
33
33
 
34
34
  def split_at(
35
35
  self,
@@ -73,7 +73,7 @@ class BaseList[T](IterWrapper[T]):
73
73
 
74
74
  ```
75
75
  """
76
- return self.apply(mit.split_at, pred, maxsplit, keep_separator)
76
+ return self._lazy(mit.split_at, pred, maxsplit, keep_separator)
77
77
 
78
78
  def split_after(
79
79
  self, predicate: Callable[[T], bool], max_split: int = -1
@@ -100,7 +100,7 @@ class BaseList[T](IterWrapper[T]):
100
100
 
101
101
  ```
102
102
  """
103
- return self.apply(mit.split_after, predicate, max_split)
103
+ return self._lazy(mit.split_after, predicate, max_split)
104
104
 
105
105
  def split_before(
106
106
  self, predicate: Callable[[T], bool], max_split: int = -1
@@ -133,7 +133,7 @@ class BaseList[T](IterWrapper[T]):
133
133
 
134
134
  ```
135
135
  """
136
- return self.apply(mit.split_before, predicate, max_split)
136
+ return self._lazy(mit.split_before, predicate, max_split)
137
137
 
138
138
  def split_into(self, sizes: Iterable[int | None]) -> Iter[list[T]]:
139
139
  """
@@ -179,7 +179,7 @@ class BaseList[T](IterWrapper[T]):
179
179
  - multiple columns represent elements of the same feature (e.g. a point represented by x,y,z)
180
180
  - the format is not the same for all columns.
181
181
  """
182
- return self.apply(mit.split_into, sizes)
182
+ return self._lazy(mit.split_into, sizes)
183
183
 
184
184
  def split_when(
185
185
  self, predicate: Callable[[T, T], bool], max_split: int = -1
@@ -209,7 +209,7 @@ class BaseList[T](IterWrapper[T]):
209
209
 
210
210
  ```
211
211
  """
212
- return self.apply(mit.split_when, predicate, max_split)
212
+ return self._lazy(mit.split_when, predicate, max_split)
213
213
 
214
214
  def chunks(self, n: int, strict: bool = False) -> Iter[list[T]]:
215
215
  """
@@ -225,6 +225,7 @@ class BaseList[T](IterWrapper[T]):
225
225
  - *strict* is `True`
226
226
 
227
227
  then `ValueError` will be raised before the last list is yielded.
228
+
228
229
  Args:
229
230
  n: Number of elements in each chunk.
230
231
  strict: Whether to raise an error if the last chunk is smaller than n. Defaults to False.
@@ -238,13 +239,14 @@ class BaseList[T](IterWrapper[T]):
238
239
 
239
240
  ```
240
241
  """
241
- return self.apply(mit.chunked, n, strict)
242
+ return self._lazy(mit.chunked, n, strict)
242
243
 
243
244
  def chunks_even(self, n: int) -> Iter[list[T]]:
244
245
  """
245
246
  Break iterable into lists of approximately length n.
246
247
 
247
248
  Items are distributed such the lengths of the lists differ by at most 1 item.
249
+
248
250
  Args:
249
251
  n: Approximate number of elements in each chunk.
250
252
  Example:
@@ -258,7 +260,7 @@ class BaseList[T](IterWrapper[T]):
258
260
 
259
261
  ```
260
262
  """
261
- return self.apply(mit.chunked_even, n)
263
+ return self._lazy(mit.chunked_even, n)
262
264
 
263
265
  def unique_to_each[U: Iterable[Any]](self: IterWrapper[U]) -> Iter[list[U]]:
264
266
  """
@@ -303,4 +305,4 @@ class BaseList[T](IterWrapper[T]):
303
305
  uniques: set[U] = {element for element in counts if counts[element] == 1}
304
306
  return ((list(filter(uniques.__contains__, it))) for it in pool)
305
307
 
306
- return self.apply(_unique_to_each)
308
+ return self._lazy(_unique_to_each)