iker-python-common 1.0.8__tar.gz → 1.0.9__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 (64) hide show
  1. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/PKG-INFO +1 -1
  2. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/utils/dbutils.py +4 -4
  3. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/utils/dockerutils.py +7 -2
  4. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/utils/funcutils.py +2 -1
  5. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/utils/numutils.py +2 -1
  6. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/utils/s3utils.py +1 -2
  7. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/utils/sequtils.py +50 -24
  8. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/utils/span.py +2 -1
  9. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker_python_common.egg-info/PKG-INFO +1 -1
  10. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/common/utils/sequtils_test.py +144 -0
  11. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/.editorconfig +0 -0
  12. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/.github/workflows/pr.yml +0 -0
  13. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/.github/workflows/push.yml +0 -0
  14. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/.gitignore +0 -0
  15. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/MANIFEST.in +0 -0
  16. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/README.md +0 -0
  17. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/VERSION +0 -0
  18. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/pyproject.toml +0 -0
  19. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/resources/unittest/shutils/dir.baz/file.bar.baz +0 -0
  20. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/resources/unittest/shutils/dir.baz/file.foo.bar +0 -0
  21. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/resources/unittest/shutils/dir.baz/file.foo.baz +0 -0
  22. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/resources/unittest/shutils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz +0 -0
  23. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.bar.baz +0 -0
  24. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.foo.bar +0 -0
  25. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.foo.baz +0 -0
  26. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/resources/unittest/shutils/dir.foo/file.bar +0 -0
  27. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/resources/unittest/shutils/dir.foo/file.baz +0 -0
  28. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/resources/unittest/shutils/dir.foo/file.foo +0 -0
  29. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/setup.cfg +0 -0
  30. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/setup.py +0 -0
  31. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/__init__.py +0 -0
  32. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/utils/__init__.py +0 -0
  33. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/utils/argutils.py +0 -0
  34. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/utils/config.py +0 -0
  35. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/utils/dtutils.py +0 -0
  36. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/utils/logger.py +0 -0
  37. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/utils/randutils.py +0 -0
  38. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/utils/retry.py +0 -0
  39. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/utils/shutils.py +0 -0
  40. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/utils/strutils.py +0 -0
  41. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker/common/utils/testutils.py +0 -0
  42. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker_python_common.egg-info/SOURCES.txt +0 -0
  43. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker_python_common.egg-info/dependency_links.txt +0 -0
  44. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker_python_common.egg-info/not-zip-safe +0 -0
  45. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker_python_common.egg-info/requires.txt +0 -0
  46. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/src/iker_python_common.egg-info/top_level.txt +0 -0
  47. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/__init__.py +0 -0
  48. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/common/utils/argutils_test.py +0 -0
  49. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/common/utils/config_test.py +0 -0
  50. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/common/utils/dbutils_test.py +0 -0
  51. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/common/utils/dockerutils_test.py +0 -0
  52. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/common/utils/dtutils_test.py +0 -0
  53. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/common/utils/funcutils_test.py +0 -0
  54. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/common/utils/logger_test.py +0 -0
  55. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/common/utils/numutils_test.py +0 -0
  56. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/common/utils/randutils_test.py +0 -0
  57. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/common/utils/retry_test.py +0 -0
  58. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/common/utils/s3utils_test.py +0 -0
  59. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/common/utils/shutils_test.py +0 -0
  60. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/common/utils/span_test.py +0 -0
  61. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/common/utils/strutils_test.py +0 -0
  62. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/common/utils/testutils_test.py +0 -0
  63. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/docker_fixtures.py +0 -0
  64. {iker_python_common-1.0.8 → iker_python_common-1.0.9}/test/iker_tests/iker_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iker-python-common
3
- Version: 1.0.8
3
+ Version: 1.0.9
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.11
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -1,7 +1,7 @@
1
1
  import contextlib
2
2
  import dataclasses
3
3
  import urllib.parse
4
- from typing import Any, ContextManager, Type
4
+ from typing import Any
5
5
 
6
6
  import psycopg
7
7
  import pymysql
@@ -87,13 +87,13 @@ class ConnectionMaker(object):
87
87
  def make_connection(self):
88
88
  return self.engine.connect()
89
89
 
90
- def make_session(self, **kwargs) -> ContextManager[sqlalchemy.orm.Session]:
90
+ def make_session(self, **kwargs) -> contextlib.AbstractContextManager[sqlalchemy.orm.Session]:
91
91
  return contextlib.closing(sqlalchemy.orm.sessionmaker(self.engine, **{**self.session_opts, **kwargs})())
92
92
 
93
- def create_model(self, orm_base: Type[sqlalchemy.orm.DeclarativeBase]):
93
+ def create_model(self, orm_base: type[sqlalchemy.orm.DeclarativeBase]):
94
94
  orm_base.metadata.create_all(self.engine)
95
95
 
96
- def drop_model(self, orm_base: Type[sqlalchemy.orm.DeclarativeBase]):
96
+ def drop_model(self, orm_base: type[sqlalchemy.orm.DeclarativeBase]):
97
97
  orm_base.metadata.drop_all(self.engine)
98
98
 
99
99
  def execute(self, sql: str, **params):
@@ -1,7 +1,8 @@
1
1
  import contextlib
2
2
  import re
3
+ from collections.abc import Iterable
3
4
  from dataclasses import dataclass
4
- from typing import Any, ContextManager, Iterable
5
+ from typing import Any
5
6
 
6
7
  import docker
7
8
  import docker.errors
@@ -67,7 +68,11 @@ class ImageName(object):
67
68
  return None
68
69
 
69
70
 
70
- def docker_create_client(registry: str, username: str, password: str) -> ContextManager[docker.DockerClient]:
71
+ def docker_create_client(
72
+ registry: str,
73
+ username: str,
74
+ password: str,
75
+ ) -> contextlib.AbstractContextManager[docker.DockerClient]:
71
76
  try:
72
77
  client = docker.DockerClient()
73
78
  client.login(registry=registry, username=username, password=password, reauth=True)
@@ -1,5 +1,6 @@
1
1
  import functools
2
- from typing import Callable, TypeVar
2
+ from collections.abc import Callable
3
+ from typing import TypeVar
3
4
 
4
5
  __all__ = [
5
6
  "singleton",
@@ -1,9 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import math
4
+ from collections.abc import Callable, Sequence
4
5
  from decimal import Decimal
5
6
  from numbers import Real
6
- from typing import Any, Callable, Sequence, TypeVar
7
+ from typing import Any, TypeVar
7
8
 
8
9
  import numpy as np
9
10
 
@@ -5,7 +5,6 @@ import mimetypes
5
5
  import os
6
6
  import tempfile
7
7
  from dataclasses import dataclass
8
- from typing import ContextManager
9
8
 
10
9
  import boto3
11
10
  from botocore.client import BaseClient
@@ -39,7 +38,7 @@ def s3_make_client(
39
38
  secret_access_key: str = None,
40
39
  region_name: str = None,
41
40
  endpoint_url: str = None,
42
- ) -> ContextManager[BaseClient]:
41
+ ) -> contextlib.AbstractContextManager[BaseClient]:
43
42
  """
44
43
  Makes an AWS S3 client
45
44
 
@@ -2,7 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  import functools
4
4
  import itertools
5
- from typing import Callable, Generator, Generic, Iterable, Self, Sequence, TypeVar
5
+ from collections.abc import Callable, Generator, Iterable, Iterator, Sequence, Sized
6
+ from typing import Generic, Self, TypeVar
6
7
 
7
8
  __all__ = [
8
9
  "head",
@@ -26,6 +27,7 @@ T = TypeVar("T")
26
27
  K = TypeVar("K")
27
28
  U = TypeVar("U")
28
29
  V = TypeVar("V")
30
+ Tco = TypeVar("Tco", covariant=True)
29
31
 
30
32
 
31
33
  # See Haskell's list operations head, tail, init, and last
@@ -185,8 +187,8 @@ def merge_chunks(
185
187
  return merged_chunks
186
188
 
187
189
 
188
- class Seq(Generic[T]):
189
- def __init__(self, data: Iterable[T] | Self):
190
+ class Seq(Generic[Tco], Sequence[Tco], Sized):
191
+ def __init__(self, data: Iterable[Tco] | Self):
190
192
  if isinstance(data, Seq):
191
193
  self.data = data.data
192
194
  elif isinstance(data, Iterable):
@@ -195,12 +197,12 @@ class Seq(Generic[T]):
195
197
  raise ValueError("unsupported data type")
196
198
 
197
199
  @property
198
- def count(self) -> int:
200
+ def size(self) -> int:
199
201
  return len(self.data)
200
202
 
201
203
  @property
202
204
  def empty(self) -> bool:
203
- return self.count == 0
205
+ return self.size == 0
204
206
 
205
207
  def __add__(self, other: Self) -> Self:
206
208
  return self.concat(other)
@@ -212,6 +214,24 @@ class Seq(Generic[T]):
212
214
  return Seq([self.data[item]])
213
215
  raise ValueError("unsupported index type")
214
216
 
217
+ def __len__(self) -> int:
218
+ return len(self.data)
219
+
220
+ def __contains__(self, item) -> bool:
221
+ return item in self.data
222
+
223
+ def __iter__(self) -> Iterator[Tco]:
224
+ return iter(self.data)
225
+
226
+ def __reversed__(self) -> Iterator[Tco]:
227
+ return reversed(self.data)
228
+
229
+ def count(self, elem: Tco):
230
+ return self.count_if(lambda x: x == elem)
231
+
232
+ def count_if(self, func: Callable[[Tco], bool]):
233
+ return sum(1 for item in self.data if func(item))
234
+
215
235
  def concat(self, other: Self) -> Self:
216
236
  return Seq(self.data + other.data)
217
237
 
@@ -229,7 +249,7 @@ class Seq(Generic[T]):
229
249
  def distinct(self) -> Self:
230
250
  return Seq(list(set(self.data)))
231
251
 
232
- def scan_left(self, zero: U | None, func: Callable[[U, T], U]) -> "Seq[U]":
252
+ def scan_left(self, zero: U | None, func: Callable[[U, Tco], U]) -> "Seq[U]":
233
253
  def scan():
234
254
  accum = zero
235
255
  for elem in self.data:
@@ -238,7 +258,7 @@ class Seq(Generic[T]):
238
258
 
239
259
  return Seq(scan())
240
260
 
241
- def scan_right(self, zero: U | None, func: Callable[[U, T], U]) -> "Seq[U]":
261
+ def scan_right(self, zero: U | None, func: Callable[[U, Tco], U]) -> "Seq[U]":
242
262
  def scan():
243
263
  accum = zero
244
264
  for elem in reversed(self.data):
@@ -249,10 +269,10 @@ class Seq(Generic[T]):
249
269
 
250
270
  scan = scan_left
251
271
 
252
- def map(self, func: Callable[[T], U]) -> "Seq[U]":
272
+ def map(self, func: Callable[[Tco], U]) -> "Seq[U]":
253
273
  return self.scan(None, lambda x, y: func(y))
254
274
 
255
- def fold_left(self, zero: U | None, func: Callable[[U, T], U]) -> "Seq[U]":
275
+ def fold_left(self, zero: U | None, func: Callable[[U, Tco], U]) -> "Seq[U]":
256
276
  if self.empty:
257
277
  return Seq([]) if zero is None else Seq([zero])
258
278
  data = self.data if zero is None else [zero] + self.data
@@ -261,7 +281,7 @@ class Seq(Generic[T]):
261
281
  accum = func(accum, elem)
262
282
  return Seq([accum])
263
283
 
264
- def fold_right(self, zero: U | None, func: Callable[[U, T], U]) -> "Seq[U]":
284
+ def fold_right(self, zero: U | None, func: Callable[[U, Tco], U]) -> "Seq[U]":
265
285
  if self.empty:
266
286
  return Seq([]) if zero is None else Seq([zero])
267
287
  data = self.data if zero is None else self.data + [zero]
@@ -272,18 +292,18 @@ class Seq(Generic[T]):
272
292
 
273
293
  fold = fold_left
274
294
 
275
- def reduce(self, func: Callable[[T, T], T]) -> Self:
295
+ def reduce(self, func: Callable[[Tco, Tco], Tco]) -> Self:
276
296
  return self.fold(None, lambda x, y: func(x, y))
277
297
 
278
- def max(self, func: Callable[[T, T], bool] = None) -> Self:
298
+ def max(self, func: Callable[[Tco, Tco], bool] = None) -> Self:
279
299
  func = func or (lambda x, y: x > y)
280
300
  return self.reduce(lambda x, y: x if func(x, y) else y)
281
301
 
282
- def min(self, func: Callable[[T, T], bool] = None) -> Self:
302
+ def min(self, func: Callable[[Tco, Tco], bool] = None) -> Self:
283
303
  func = func or (lambda x, y: x < y)
284
304
  return self.reduce(lambda x, y: x if func(x, y) else y)
285
305
 
286
- def group(self, func: Callable[[T], K]) -> "Seq[tuple[K, list[T]]]":
306
+ def group(self, func: Callable[[Tco], K]) -> "Seq[tuple[K, list[Tco]]]":
287
307
  return Seq(grouped(self.data, key_func=func, keys_ordered=True))
288
308
 
289
309
  def keys(self):
@@ -295,10 +315,10 @@ class Seq(Generic[T]):
295
315
  def swap(self):
296
316
  return Seq((value, key) for key, value in self.data)
297
317
 
298
- def map_keys(self, func: Callable[[T], U]) -> Self:
318
+ def map_keys(self, func: Callable[[Tco], U]) -> Self:
299
319
  return Seq((func(key), value) for key, value in self.data)
300
320
 
301
- def map_values(self, func: Callable[[T], U]) -> Self:
321
+ def map_values(self, func: Callable[[Tco], U]) -> Self:
302
322
  return Seq((key, func(value)) for key, value in self.data)
303
323
 
304
324
  def flatten(self):
@@ -307,19 +327,19 @@ class Seq(Generic[T]):
307
327
  data.extend(d)
308
328
  return Seq(data)
309
329
 
310
- def flat_map(self, func: Callable[[T], U]) -> "Seq[U]":
330
+ def flat_map(self, func: Callable[[Tco], U]) -> "Seq[U]":
311
331
  return self.flatten().map(func)
312
332
 
313
- def group_map(self, group_func: Callable[[T], K], map_func: Callable[[T], U]) -> "Seq[tuple[K, list[U]]]":
333
+ def group_map(self, group_func: Callable[[Tco], K], map_func: Callable[[Tco], U]) -> "Seq[tuple[K, list[U]]]":
314
334
  return self.group(group_func).map_values(lambda x: list(map(map_func, x)))
315
335
 
316
- def filter(self, func: Callable[[T], bool]) -> Self:
336
+ def filter(self, func: Callable[[Tco], bool]) -> Self:
317
337
  return Seq(filter(func, self.data))
318
338
 
319
- def filter_not(self, func: Callable[[T], bool]) -> Self:
339
+ def filter_not(self, func: Callable[[Tco], bool]) -> Self:
320
340
  return self.filter(lambda x: not func(x))
321
341
 
322
- def sort(self, func: Callable[[T], K]) -> Self:
342
+ def sort(self, func: Callable[[Tco], K]) -> Self:
323
343
  return Seq(sorted(self.data, key=func))
324
344
 
325
345
  def head(self) -> Self:
@@ -334,15 +354,15 @@ class Seq(Generic[T]):
334
354
  def tail(self) -> Self:
335
355
  return Seq(tail(self.data))
336
356
 
337
- def foreach(self, func: Callable[[T], None]) -> Self:
357
+ def foreach(self, func: Callable[[Tco], None]) -> Self:
338
358
  for elem in self.data:
339
359
  func(elem)
340
360
  return self
341
361
 
342
- def exists(self, func: Callable[[T], bool]) -> "Seq[bool]":
362
+ def exists(self, func: Callable[[Tco], bool]) -> "Seq[bool]":
343
363
  return Seq([any(map(func, self.data))])
344
364
 
345
- def forall(self, func: Callable[[T], bool]) -> "Seq[bool]":
365
+ def forall(self, func: Callable[[Tco], bool]) -> "Seq[bool]":
346
366
  return Seq([all(map(func, self.data))])
347
367
 
348
368
  def union(self, other: Self) -> Self:
@@ -351,5 +371,11 @@ class Seq(Generic[T]):
351
371
  def intersect(self, other: Self) -> Self:
352
372
  return Seq(list(set(self.data).intersection(set(other.data))))
353
373
 
374
+ def zip(self, other: Seq[U]) -> Seq[tuple[Tco, U]]:
375
+ return Seq(zip(self.data, other.data))
376
+
377
+ def zip_fill(self, other: Seq[U], fill: Tco | U | None = None) -> Seq[tuple[Tco, U]]:
378
+ return Seq(itertools.zip_longest(self.data, other.data, fillvalue=fill))
379
+
354
380
 
355
381
  seq = Seq
@@ -2,7 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  import enum
4
4
  import itertools
5
- from typing import Sequence, TypeVar
5
+ from collections.abc import Sequence
6
+ from typing import TypeVar
6
7
 
7
8
  __all__ = [
8
9
  "SpanRelation",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iker-python-common
3
- Version: 1.0.8
3
+ Version: 1.0.9
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.11
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -805,6 +805,84 @@ class SeqUtilsTest(unittest.TestCase):
805
805
  actual = seq(data)[item]
806
806
  self.assertEqual(actual.data, expect)
807
807
 
808
+ @ddt.data(
809
+ ([], 0),
810
+ ([0], 1),
811
+ ([1, 2, 3, 4, 5], 5),
812
+ )
813
+ @ddt.unpack
814
+ def test_seq_builtin_len(self, data, expect):
815
+ actual = len(seq(data))
816
+ self.assertEqual(actual, expect)
817
+
818
+ @ddt.data(
819
+ ([], 0, False),
820
+ ([0], 0, True),
821
+ ([0], -1, False),
822
+ ([1, 2, 3, 4, 5], 0, False),
823
+ ([1, 2, 3, 4, 5], 1, True),
824
+ ([1, 2, 3, 4, 5], -1, False),
825
+ )
826
+ @ddt.unpack
827
+ def test_seq_builtin_contains(self, data, item, expect):
828
+ actual = item in seq(data)
829
+ self.assertEqual(actual, expect)
830
+
831
+ @ddt.data(
832
+ ([], []),
833
+ ([0], [0]),
834
+ ([1, 2, 3, 4, 5], [1, 2, 3, 4, 5]),
835
+ )
836
+ @ddt.unpack
837
+ def test_seq_builtin_iter(self, data, expect):
838
+ actual = list(x for x in seq(data))
839
+ self.assertEqual(actual, expect)
840
+
841
+ @ddt.data(
842
+ ([], []),
843
+ ([0], [0]),
844
+ ([1, 2, 3, 4, 5], [5, 4, 3, 2, 1]),
845
+ )
846
+ @ddt.unpack
847
+ def test_seq_builtin_reversed(self, data, expect):
848
+ actual = list(x for x in reversed(seq(data)))
849
+ self.assertEqual(actual, expect)
850
+
851
+ @ddt.data(
852
+ ([], 0, 0),
853
+ ([0], 0, 1),
854
+ ([0], -1, 0),
855
+ ([0, 0, 0], 0, 3),
856
+ ([0, 0, 0], -1, 0),
857
+ ([1, 2, 3, 4, 5], 0, 0),
858
+ ([1, 2, 3, 4, 5], 1, 1),
859
+ ([1, 2, 3, 4, 5], -1, 0),
860
+ )
861
+ @ddt.unpack
862
+ def test_seq_count(self, data, item, expect):
863
+ actual = seq(data).count(item)
864
+ self.assertEqual(actual, expect)
865
+
866
+ @ddt.data(
867
+ ([], lambda x: True, 0),
868
+ ([0], lambda x: True, 1),
869
+ ([0], lambda x: False, 0),
870
+ ([0], lambda x: x % 2 == 0, 1),
871
+ ([0], lambda x: x % 2 == 1, 0),
872
+ ([0, 0, 0], lambda x: True, 3),
873
+ ([0, 0, 0], lambda x: False, 0),
874
+ ([0, 0, 0], lambda x: x % 2 == 0, 3),
875
+ ([0, 0, 0], lambda x: x % 2 == 1, 0),
876
+ ([1, 2, 3, 4, 5], lambda x: x % 2 == 0, 2),
877
+ ([1, 2, 3, 4, 5], lambda x: x % 2 == 1, 3),
878
+ ([1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5], lambda x: x % 2 == 0, 6),
879
+ ([1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5], lambda x: x % 2 == 1, 9),
880
+ )
881
+ @ddt.unpack
882
+ def test_seq_count_if(self, data, func, expect):
883
+ actual = seq(data).count_if(func)
884
+ self.assertEqual(actual, expect)
885
+
808
886
  @ddt.data(
809
887
  ([], 0, lambda x, y: x, []),
810
888
  ([], None, lambda x, y: x, []),
@@ -955,3 +1033,69 @@ class SeqUtilsTest(unittest.TestCase):
955
1033
  def test_seq_flatten(self, data, expect):
956
1034
  actual = seq(data).flatten()
957
1035
  self.assertEqual(actual.data, expect)
1036
+
1037
+ @ddt.data(
1038
+ ([], [], []),
1039
+ ([0], [], []),
1040
+ ([], [0], []),
1041
+ ([0], [0], [(0, 0)]),
1042
+ ([0, 0], [0], [(0, 0)]),
1043
+ ([0], [0, 0], [(0, 0)]),
1044
+ ([1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]),
1045
+ ([1, 2, 3, 4, 5], [5, 4, 3, 2, 1], [(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)]),
1046
+ ([1, 2, 3, 4, 5], ["a", "b", "c", "d", "e"], [(1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e")]),
1047
+ (["a", "b", "c", "d", "e"], [1, 2, 3, 4, 5], [("a", 1), ("b", 2), ("c", 3), ("d", 4), ("e", 5)]),
1048
+ )
1049
+ @ddt.unpack
1050
+ def test_seq_zip(self, a, b, expect):
1051
+ actual = seq(a).zip(seq(b))
1052
+ self.assertEqual(actual.data, expect)
1053
+
1054
+ @ddt.data(
1055
+ ([], [], 0, []),
1056
+ ([0], [], None, [(0, None)]),
1057
+ ([0], [], 1, [(0, 1)]),
1058
+ ([], [0], None, [(None, 0)]),
1059
+ ([], [0], 1, [(1, 0)]),
1060
+ ([0], [0], 0, [(0, 0)]),
1061
+ ([0, 0], [0], None, [(0, 0), (0, None)]),
1062
+ ([0, 0], [0], 1, [(0, 0), (0, 1)]),
1063
+ ([0], [0, 0], None, [(0, 0), (None, 0)]),
1064
+ ([0], [0, 0], 1, [(0, 0), (1, 0)]),
1065
+ ([1, 2, 3, 4, 5], [1, 2, 3, 4, 5], None, [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]),
1066
+ ([1, 2, 3, 4, 5], [5, 4, 3, 2, 1], None, [(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)]),
1067
+ ([1, 2, 3, 4, 5, 6, 7], [1, 2, 3, 4, 5], 0, [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 0), (7, 0)]),
1068
+ ([1, 2, 3, 4, 5, 6, 7], [5, 4, 3, 2, 1], 0, [(1, 5), (2, 4), (3, 3), (4, 2), (5, 1), (6, 0), (7, 0)]),
1069
+ ([1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6, 7], 0, [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (0, 6), (0, 7)]),
1070
+ ([1, 2, 3, 4, 5], [7, 6, 5, 4, 3, 2, 1], 0, [(1, 7), (2, 6), (3, 5), (4, 4), (5, 3), (0, 2), (0, 1)]),
1071
+ ([1, 2, 3, 4, 5], ["a", "b", "c", "d", "e"], None, [(1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e")]),
1072
+ (["a", "b", "c", "d", "e"], [1, 2, 3, 4, 5], None, [("a", 1), ("b", 2), ("c", 3), ("d", 4), ("e", 5)]),
1073
+ (
1074
+ [1, 2, 3, 4, 5],
1075
+ ["a", "b", "c", "d", "e", "f", "g"],
1076
+ 0,
1077
+ [(1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e"), (0, "f"), (0, "g")],
1078
+ ),
1079
+ (
1080
+ [1, 2, 3, 4, 5],
1081
+ ["a", "b", "c", "d", "e", "f", "g"],
1082
+ "-",
1083
+ [(1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e"), ("-", "f"), ("-", "g")],
1084
+ ),
1085
+ (
1086
+ [1, 2, 3, 4, 5, 6, 7],
1087
+ ["a", "b", "c", "d", "e"],
1088
+ "-",
1089
+ [(1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e"), (6, "-"), (7, "-")],
1090
+ ),
1091
+ (
1092
+ [1, 2, 3, 4, 5, 6, 7],
1093
+ ["a", "b", "c", "d", "e"],
1094
+ 0,
1095
+ [(1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e"), (6, 0), (7, 0)],
1096
+ ),
1097
+ )
1098
+ @ddt.unpack
1099
+ def test_seq_zip_fill(self, a, b, fill, expect):
1100
+ actual = seq(a).zip_fill(seq(b), fill)
1101
+ self.assertEqual(actual.data, expect)