iker-python-common 1.0.29__tar.gz → 1.0.31__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 (71) hide show
  1. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/.github/workflows/pr.yml +3 -0
  2. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/.github/workflows/push.yml +1 -0
  3. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/PKG-INFO +2 -2
  4. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/funcutils.py +5 -5
  5. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/sequtils.py +60 -57
  6. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker_python_common.egg-info/PKG-INFO +2 -2
  7. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/sequtils_test.py +121 -0
  8. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/.editorconfig +0 -0
  9. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/.gitignore +0 -0
  10. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/MANIFEST.in +0 -0
  11. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/README.md +0 -0
  12. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/VERSION +0 -0
  13. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/pyproject.toml +0 -0
  14. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/resources/unittest/config/config.cfg +0 -0
  15. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/resources/unittest/csv/data.csv +0 -0
  16. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/resources/unittest/csv/data.tsv +0 -0
  17. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/resources/unittest/shutils/dir.baz/file.bar.baz +0 -0
  18. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/resources/unittest/shutils/dir.baz/file.foo.bar +0 -0
  19. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/resources/unittest/shutils/dir.baz/file.foo.baz +0 -0
  20. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/resources/unittest/shutils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz +0 -0
  21. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.bar.baz +0 -0
  22. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.foo.bar +0 -0
  23. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.foo.baz +0 -0
  24. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/resources/unittest/shutils/dir.foo/file.bar +0 -0
  25. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/resources/unittest/shutils/dir.foo/file.baz +0 -0
  26. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/resources/unittest/shutils/dir.foo/file.foo +0 -0
  27. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/setup.cfg +0 -0
  28. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/setup.py +0 -0
  29. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/__init__.py +0 -0
  30. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/__init__.py +0 -0
  31. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/argutils.py +0 -0
  32. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/config.py +0 -0
  33. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/csv.py +0 -0
  34. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/dbutils.py +0 -0
  35. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/dockerutils.py +0 -0
  36. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/dtutils.py +0 -0
  37. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/jsonutils.py +0 -0
  38. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/logger.py +0 -0
  39. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/numutils.py +0 -0
  40. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/randutils.py +0 -0
  41. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/retry.py +0 -0
  42. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/s3utils.py +0 -0
  43. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/shutils.py +0 -0
  44. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/span.py +0 -0
  45. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/strutils.py +0 -0
  46. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker/common/utils/testutils.py +0 -0
  47. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker_python_common.egg-info/SOURCES.txt +0 -0
  48. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker_python_common.egg-info/dependency_links.txt +0 -0
  49. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker_python_common.egg-info/not-zip-safe +0 -0
  50. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker_python_common.egg-info/requires.txt +0 -0
  51. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/src/iker_python_common.egg-info/top_level.txt +0 -0
  52. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_test.py +0 -0
  53. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/__init__.py +0 -0
  54. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/argutils_test.py +0 -0
  55. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/config_test.py +0 -0
  56. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/csv_test.py +0 -0
  57. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/dbutils_test.py +0 -0
  58. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/dockerutils_test.py +0 -0
  59. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/dtutils_test.py +0 -0
  60. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/funcutils_test.py +0 -0
  61. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/jsonutils_test.py +0 -0
  62. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/logger_test.py +0 -0
  63. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/numutils_test.py +0 -0
  64. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/randutils_test.py +0 -0
  65. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/retry_test.py +0 -0
  66. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/s3utils_test.py +0 -0
  67. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/shutils_test.py +0 -0
  68. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/span_test.py +0 -0
  69. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/strutils_test.py +0 -0
  70. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/common/utils/testutils_test.py +0 -0
  71. {iker_python_common-1.0.29 → iker_python_common-1.0.31}/test/iker_tests/docker_fixtures.py +0 -0
@@ -21,6 +21,7 @@ jobs:
21
21
 
22
22
  - name: Build and Test
23
23
  run: |
24
+ sudo apt-get update
24
25
  sudo apt-get install libxml2-dev libxslt1-dev llvm-14-dev
25
26
  python -m pip install --upgrade pip
26
27
  python -m pip install .[test]
@@ -39,6 +40,7 @@ jobs:
39
40
 
40
41
  - name: Build and Test
41
42
  run: |
43
+ sudo apt-get update
42
44
  sudo apt-get install libxml2-dev libxslt1-dev llvm-14-dev
43
45
  python -m pip install --upgrade pip
44
46
  python -m pip install .[test]
@@ -57,6 +59,7 @@ jobs:
57
59
 
58
60
  - name: Build and Test
59
61
  run: |
62
+ sudo apt-get update
60
63
  sudo apt-get install libxml2-dev libxslt1-dev llvm-14-dev
61
64
  python -m pip install --upgrade pip
62
65
  python -m pip install .[test]
@@ -21,6 +21,7 @@ jobs:
21
21
 
22
22
  - name: Build and Upload
23
23
  run: |
24
+ sudo apt-get update
24
25
  sudo apt-get install libxml2-dev libxslt1-dev llvm-14-dev
25
26
  python -m pip install --upgrade pip build twine
26
27
  python -m pip install .[test]
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: iker-python-common
3
- Version: 1.0.29
3
+ Version: 1.0.31
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.11
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -9,10 +9,10 @@ __all__ = [
9
9
  "unique_returns",
10
10
  ]
11
11
 
12
- R = TypeVar("R")
12
+ RT = TypeVar("RT")
13
13
 
14
14
 
15
- def singleton(tar: Callable[..., R] = None):
15
+ def singleton(tar: Callable[..., RT] = None):
16
16
  def decorator(target):
17
17
  if not callable(target):
18
18
  raise TypeError("expected a callable")
@@ -30,7 +30,7 @@ def singleton(tar: Callable[..., R] = None):
30
30
  return decorator if tar is None else decorator(tar)
31
31
 
32
32
 
33
- def memorized(tar: Callable[..., R] = None, *, ordered: bool = False, typed: bool = False):
33
+ def memorized(tar: Callable[..., RT] = None, *, ordered: bool = False, typed: bool = False):
34
34
  def decorator(target):
35
35
  if not callable(target):
36
36
  raise TypeError("expected a callable")
@@ -64,7 +64,7 @@ def memorized(tar: Callable[..., R] = None, *, ordered: bool = False, typed: boo
64
64
  return decorator if tar is None else decorator(tar)
65
65
 
66
66
 
67
- def lazy(tar: Callable[..., R] = None):
67
+ def lazy(tar: Callable[..., RT] = None):
68
68
  def decorator(target):
69
69
  if not callable(target):
70
70
  raise TypeError("expected a callable")
@@ -78,7 +78,7 @@ def lazy(tar: Callable[..., R] = None):
78
78
  return decorator if tar is None else decorator(tar)
79
79
 
80
80
 
81
- def unique_returns(tar: Callable[..., R] = None, *, max_trials: int | None = None):
81
+ def unique_returns(tar: Callable[..., RT] = None, *, max_trials: int | None = None):
82
82
  def decorator(target):
83
83
  if not callable(target):
84
84
  raise TypeError("expected a callable")
@@ -1,7 +1,7 @@
1
1
  import functools
2
2
  import itertools
3
3
  from collections.abc import Callable, Generator, Iterable, Iterator, Sequence, Sized
4
- from typing import Generic, Self, TypeVar
4
+ from typing import Generic, TypeVar
5
5
 
6
6
  __all__ = [
7
7
  "head",
@@ -22,9 +22,9 @@ __all__ = [
22
22
  ]
23
23
 
24
24
  T = TypeVar("T")
25
- K = TypeVar("K")
26
- U = TypeVar("U")
27
- V = TypeVar("V")
25
+ KT = TypeVar("KT")
26
+ VT = TypeVar("VT")
27
+ TT = TypeVar("TT")
28
28
  Tco = TypeVar("Tco", covariant=True)
29
29
 
30
30
 
@@ -61,10 +61,10 @@ def init(ms: Sequence[T]) -> Sequence[T]:
61
61
 
62
62
  def grouped(
63
63
  ms: Sequence[T],
64
- key_func: Callable[[T], K],
64
+ key_func: Callable[[T], KT],
65
65
  keys_ordered: bool = False,
66
66
  values_only: bool = False,
67
- ) -> list[tuple[K, list[T]]] | list[list[T]]:
67
+ ) -> list[tuple[KT, list[T]]] | list[list[T]]:
68
68
  """
69
69
  Groups the given list of elements according to key generator function
70
70
 
@@ -76,7 +76,7 @@ def grouped(
76
76
  """
77
77
  if ms is None or len(ms) == 0:
78
78
  return []
79
- grouped_ms: dict[K, list[T]] = {}
79
+ grouped_ms: dict[KT, list[T]] = {}
80
80
  for m in ms:
81
81
  k = key_func(m)
82
82
  grouped_ms.setdefault(k, []).append(m)
@@ -148,7 +148,7 @@ def chunk_between(ms: Sequence[T], chunk_func: Callable[[T, T], bool], exclusive
148
148
  return chunk(ms, lambda x, y: chunk_func(last(x), y), exclusive_end)
149
149
 
150
150
 
151
- def chunk_with_key(ms: Sequence[T], key_func: Callable[[T], K], exclusive_end: bool = False) -> list[list[T]]:
151
+ def chunk_with_key(ms: Sequence[T], key_func: Callable[[T], KT], exclusive_end: bool = False) -> list[list[T]]:
152
152
  return chunk_between(ms, lambda x, y: key_func(x) != key_func(y), exclusive_end)
153
153
 
154
154
 
@@ -185,8 +185,11 @@ def merge_chunks(
185
185
  return merged_chunks
186
186
 
187
187
 
188
+ SeqT = TypeVar("SeqT", bound="Seq")
189
+
190
+
188
191
  class Seq(Generic[Tco], Sequence[Tco], Sized):
189
- def __init__(self, data: Iterable[Tco] | Self):
192
+ def __init__(self, data: Iterable[Tco] | SeqT):
190
193
  if isinstance(data, Seq):
191
194
  self.data = data.data
192
195
  elif isinstance(data, Iterable):
@@ -202,14 +205,14 @@ class Seq(Generic[Tco], Sequence[Tco], Sized):
202
205
  def empty(self) -> bool:
203
206
  return self.size == 0
204
207
 
205
- def __add__(self, other: Self) -> Self:
208
+ def __add__(self: SeqT, other: SeqT) -> SeqT:
206
209
  return self.concat(other)
207
210
 
208
- def __getitem__(self, item):
211
+ def __getitem__(self: SeqT, item) -> SeqT:
209
212
  if isinstance(item, slice):
210
- return Seq(self.data[item])
213
+ return type(self)(self.data[item])
211
214
  elif isinstance(item, int):
212
- return Seq([self.data[item]])
215
+ return type(self)([self.data[item]])
213
216
  raise ValueError("unsupported index type")
214
217
 
215
218
  def __len__(self) -> int:
@@ -230,28 +233,28 @@ class Seq(Generic[Tco], Sequence[Tco], Sized):
230
233
  def count_if(self, func: Callable[[Tco], bool]):
231
234
  return sum(1 for item in self.data if func(item))
232
235
 
233
- def concat(self, other: Self) -> Self:
234
- return Seq(self.data + other.data)
236
+ def concat(self: SeqT, other: SeqT) -> SeqT:
237
+ return type(self)(self.data + other.data)
235
238
 
236
- def take_left(self, n: int) -> Self:
239
+ def take_left(self: SeqT, n: int) -> SeqT:
237
240
  if n <= 0:
238
- return Seq([])
241
+ return type(self)([])
239
242
  return self[:n]
240
243
 
241
- def take_right(self, n: int) -> Self:
244
+ def take_right(self: SeqT, n: int) -> SeqT:
242
245
  if n <= 0:
243
- return Seq([])
246
+ return type(self)([])
244
247
  return self[-n:]
245
248
 
246
249
  take = take_left
247
250
 
248
- def reverse(self) -> Self:
249
- return Seq(reversed(self.data))
251
+ def reverse(self: SeqT) -> SeqT:
252
+ return type(self)(reversed(self.data))
250
253
 
251
- def distinct(self) -> Self:
252
- return Seq(list(set(self.data)))
254
+ def distinct(self: SeqT) -> SeqT:
255
+ return type(self)(sorted(set(self.data)))
253
256
 
254
- def scan_left(self, zero: U | None, func: Callable[[U, Tco], U]) -> "Seq[U]":
257
+ def scan_left(self, zero: TT | None, func: Callable[[TT, Tco], TT]) -> "Seq[TT]":
255
258
  def scan():
256
259
  accum = zero
257
260
  for elem in self.data:
@@ -260,7 +263,7 @@ class Seq(Generic[Tco], Sequence[Tco], Sized):
260
263
 
261
264
  return Seq(scan())
262
265
 
263
- def scan_right(self, zero: U | None, func: Callable[[U, Tco], U]) -> "Seq[U]":
266
+ def scan_right(self, zero: TT | None, func: Callable[[TT, Tco], TT]) -> "Seq[TT]":
264
267
  def scan():
265
268
  accum = zero
266
269
  for elem in reversed(self.data):
@@ -271,10 +274,10 @@ class Seq(Generic[Tco], Sequence[Tco], Sized):
271
274
 
272
275
  scan = scan_left
273
276
 
274
- def map(self, func: Callable[[Tco], U]) -> "Seq[U]":
277
+ def map(self, func: Callable[[Tco], TT]) -> "Seq[TT]":
275
278
  return self.scan(None, lambda x, y: func(y))
276
279
 
277
- def fold_left(self, zero: U | None, func: Callable[[U, Tco], U]) -> "Seq[U]":
280
+ def fold_left(self, zero: TT | None, func: Callable[[TT, Tco], TT]) -> "Seq[TT]":
278
281
  if self.empty:
279
282
  return Seq([]) if zero is None else Seq([zero])
280
283
  data = self.data if zero is None else [zero] + self.data
@@ -283,7 +286,7 @@ class Seq(Generic[Tco], Sequence[Tco], Sized):
283
286
  accum = func(accum, elem)
284
287
  return Seq([accum])
285
288
 
286
- def fold_right(self, zero: U | None, func: Callable[[U, Tco], U]) -> "Seq[U]":
289
+ def fold_right(self, zero: TT | None, func: Callable[[TT, Tco], TT]) -> "Seq[TT]":
287
290
  if self.empty:
288
291
  return Seq([]) if zero is None else Seq([zero])
289
292
  data = self.data if zero is None else self.data + [zero]
@@ -294,18 +297,18 @@ class Seq(Generic[Tco], Sequence[Tco], Sized):
294
297
 
295
298
  fold = fold_left
296
299
 
297
- def reduce(self, func: Callable[[Tco, Tco], Tco]) -> Self:
298
- return self.fold(None, lambda x, y: func(x, y))
300
+ def reduce(self: SeqT, func: Callable[[Tco, Tco], Tco]) -> SeqT:
301
+ return type(self)(self.fold(None, lambda x, y: func(x, y)))
299
302
 
300
- def max(self, func: Callable[[Tco, Tco], bool] = None) -> Self:
303
+ def max(self: SeqT, func: Callable[[Tco, Tco], bool] = None) -> SeqT:
301
304
  func = func or (lambda x, y: x > y)
302
305
  return self.reduce(lambda x, y: x if func(x, y) else y)
303
306
 
304
- def min(self, func: Callable[[Tco, Tco], bool] = None) -> Self:
307
+ def min(self: SeqT, func: Callable[[Tco, Tco], bool] = None) -> SeqT:
305
308
  func = func or (lambda x, y: x < y)
306
309
  return self.reduce(lambda x, y: x if func(x, y) else y)
307
310
 
308
- def group(self, func: Callable[[Tco], K]) -> "Seq[tuple[K, list[Tco]]]":
311
+ def group(self, func: Callable[[Tco], KT]) -> "Seq[tuple[KT, list[Tco]]]":
309
312
  return Seq(grouped(self.data, key_func=func, keys_ordered=True))
310
313
 
311
314
  def keys(self):
@@ -317,13 +320,13 @@ class Seq(Generic[Tco], Sequence[Tco], Sized):
317
320
  def swap(self):
318
321
  return Seq((value, key) for key, value in self.data)
319
322
 
320
- def map_keys(self, func: Callable[[Tco], U]) -> Self:
323
+ def map_keys(self, func: Callable[[Tco], TT]):
321
324
  return Seq((func(key), value) for key, value in self.data)
322
325
 
323
- def map_values(self, func: Callable[[Tco], U]) -> Self:
326
+ def map_values(self, func: Callable[[Tco], TT]):
324
327
  return Seq((key, func(value)) for key, value in self.data)
325
328
 
326
- def flat_map(self, func: Callable[[Tco], Iterable[U]]) -> "Seq[U]":
329
+ def flat_map(self, func: Callable[[Tco], Iterable[TT]]) -> "Seq[TT]":
327
330
  data = []
328
331
  for d in self.data:
329
332
  data.extend(func(d))
@@ -332,31 +335,31 @@ class Seq(Generic[Tco], Sequence[Tco], Sized):
332
335
  def flatten(self):
333
336
  return self.flat_map(lambda x: list(x))
334
337
 
335
- def group_map(self, group_func: Callable[[Tco], K], map_func: Callable[[Tco], U]) -> "Seq[tuple[K, list[U]]]":
338
+ def group_map(self, group_func: Callable[[Tco], KT], map_func: Callable[[Tco], TT]) -> "Seq[tuple[KT, list[TT]]]":
336
339
  return self.group(group_func).map_values(lambda x: list(map(map_func, x)))
337
340
 
338
- def filter(self, func: Callable[[Tco], bool]) -> Self:
339
- return Seq(filter(func, self.data))
341
+ def filter(self: SeqT, func: Callable[[Tco], bool]) -> SeqT:
342
+ return type(self)(filter(func, self.data))
340
343
 
341
- def filter_not(self, func: Callable[[Tco], bool]) -> Self:
344
+ def filter_not(self: SeqT, func: Callable[[Tco], bool]) -> SeqT:
342
345
  return self.filter(lambda x: not func(x))
343
346
 
344
- def sort(self, func: Callable[[Tco], K]) -> Self:
345
- return Seq(sorted(self.data, key=func))
347
+ def sort(self: SeqT, func: Callable[[Tco], KT]) -> SeqT:
348
+ return type(self)(sorted(self.data, key=func))
346
349
 
347
- def head(self) -> Self:
348
- return Seq([head(self.data)])
350
+ def head(self: SeqT) -> SeqT:
351
+ return type(self)([head(self.data)])
349
352
 
350
- def last(self) -> Self:
351
- return Seq([last(self.data)])
353
+ def last(self: SeqT) -> SeqT:
354
+ return type(self)([last(self.data)])
352
355
 
353
- def init(self) -> Self:
354
- return Seq(init(self.data))
356
+ def init(self: SeqT) -> SeqT:
357
+ return type(self)(init(self.data))
355
358
 
356
- def tail(self) -> Self:
357
- return Seq(tail(self.data))
359
+ def tail(self: SeqT) -> SeqT:
360
+ return type(self)(tail(self.data))
358
361
 
359
- def foreach(self, func: Callable[[Tco], None]) -> Self:
362
+ def foreach(self: SeqT, func: Callable[[Tco], None]) -> SeqT:
360
363
  for elem in self.data:
361
364
  func(elem)
362
365
  return self
@@ -367,16 +370,16 @@ class Seq(Generic[Tco], Sequence[Tco], Sized):
367
370
  def forall(self, func: Callable[[Tco], bool]) -> "Seq[bool]":
368
371
  return Seq([all(map(func, self.data))])
369
372
 
370
- def union(self, other: Self) -> Self:
371
- return Seq(list(set(self.data).union(set(other.data))))
373
+ def union(self: SeqT, other: SeqT) -> SeqT:
374
+ return type(self)(sorted(set(self.data).union(set(other.data))))
372
375
 
373
- def intersect(self, other: Self) -> Self:
374
- return Seq(list(set(self.data).intersection(set(other.data))))
376
+ def intersect(self: SeqT, other: SeqT) -> SeqT:
377
+ return type(self)(sorted(set(self.data).intersection(set(other.data))))
375
378
 
376
- def zip(self, other: "Seq[U]") -> "Seq[tuple[Tco, U]]":
379
+ def zip(self, other: "Seq[TT]") -> "Seq[tuple[Tco, TT]]":
377
380
  return Seq(zip(self.data, other.data))
378
381
 
379
- def zip_fill(self, other: "Seq[U]", fill: Tco | U | None = None) -> "Seq[tuple[Tco, U]]":
382
+ def zip_fill(self, other: "Seq[TT]", fill: Tco | TT | None = None) -> "Seq[tuple[Tco, TT]]":
380
383
  return Seq(itertools.zip_longest(self.data, other.data, fillvalue=fill))
381
384
 
382
385
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: iker-python-common
3
- Version: 1.0.29
3
+ Version: 1.0.31
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,11 @@
1
1
  import unittest
2
+ from collections.abc import Iterable
3
+ from dataclasses import dataclass
4
+ from typing import Self
2
5
 
3
6
  import ddt
4
7
 
8
+ from iker.common.utils.sequtils import Seq
5
9
  from iker.common.utils.sequtils import batch_yield
6
10
  from iker.common.utils.sequtils import chunk, chunk_between, chunk_with_key, merge_chunks
7
11
  from iker.common.utils.sequtils import deduped, grouped
@@ -790,6 +794,26 @@ class SeqUtilsTest(unittest.TestCase):
790
794
  self.assertEqual(expect, merge_chunks(data, merge_func=merge_func, drop_exclusive_end=True))
791
795
 
792
796
 
797
+ @dataclass(frozen=True, eq=True, order=True)
798
+ class Naming(object):
799
+ serial: int
800
+ name: str
801
+ gender: str
802
+
803
+
804
+ class NamingSeq(Seq[Naming]):
805
+
806
+ def __init__(self, data: Iterable[Naming]):
807
+ super(NamingSeq, self).__init__(data)
808
+ self.serials = list(map(lambda x: x.serial, self.data))
809
+
810
+ def identity(self) -> Self:
811
+ return self
812
+
813
+ def axis(self) -> list[int]:
814
+ return self.serials
815
+
816
+
793
817
  @ddt.ddt
794
818
  class SeqTest(unittest.TestCase):
795
819
 
@@ -1315,3 +1339,100 @@ class SeqTest(unittest.TestCase):
1315
1339
  def test_zip_fill(self, a, b, fill, expect):
1316
1340
  actual = seq(a).zip_fill(seq(b), fill)
1317
1341
  self.assertEqual(actual.data, expect)
1342
+
1343
+ def test_naming_seq(self):
1344
+ names = [
1345
+ ("Andrew", "M"),
1346
+ ("Alice", "W"),
1347
+ ("Benjamin", "M"),
1348
+ ("Bella", "W"),
1349
+ ("Charles", "M"),
1350
+ ("Charlotte", "W"),
1351
+ ("Daniel", "M"),
1352
+ ("Diana", "W"),
1353
+ ("Ethan", "M"),
1354
+ ("Emily", "W"),
1355
+ ("Felix", "M"),
1356
+ ("Fiona", "W"),
1357
+ ("George", "M"),
1358
+ ("Grace", "W"),
1359
+ ("Henry", "M"),
1360
+ ("Hannah", "W"),
1361
+ ("Isaac", "M"),
1362
+ ("Isabella", "W"),
1363
+ ("Jacob", "M"),
1364
+ ("Julia", "W"),
1365
+ ("Kevin", "M"),
1366
+ ("Katherine", "W"),
1367
+ ("Liam", "M"),
1368
+ ("Lily", "W"),
1369
+ ("Michael", "M"),
1370
+ ("Madison", "W"),
1371
+ ("Nathan", "M"),
1372
+ ("Natalie", "W"),
1373
+ ("Oliver", "M"),
1374
+ ("Olivia", "W"),
1375
+ ("Patrick", "M"),
1376
+ ("Penelope", "W"),
1377
+ ("Quentin", "M"),
1378
+ ("Quinn", "W"),
1379
+ ("Ryan", "M"),
1380
+ ("Rebecca", "W"),
1381
+ ("Samuel", "M"),
1382
+ ("Sophia", "W"),
1383
+ ("Thomas", "M"),
1384
+ ("Tiffany", "W"),
1385
+ ("Ulysses", "M"),
1386
+ ("Ursula", "W"),
1387
+ ("Victor", "M"),
1388
+ ("Victoria", "W"),
1389
+ ("William", "M"),
1390
+ ("Wendy", "W"),
1391
+ ("Xavier", "M"),
1392
+ ("Xenia", "W"),
1393
+ ("Yosef", "M"),
1394
+ ("Yvonne", "W"),
1395
+ ("Zachary", "M"),
1396
+ ("Zoe", "W"),
1397
+ ]
1398
+ naming_seq = NamingSeq(Naming(serial, name, gender) for serial, (name, gender) in enumerate(names))
1399
+
1400
+ self.assertEqual(naming_seq, naming_seq)
1401
+ self.assertEqual(naming_seq.concat(naming_seq).axis(), list(range(0, 52)) + list(range(0, 52)))
1402
+ self.assertEqual(naming_seq.take_left(26).axis(), list(range(0, 26)))
1403
+ self.assertEqual(naming_seq.take_right(26).axis(), list(range(26, 52)))
1404
+ self.assertEqual(naming_seq.reverse().axis(), list(reversed(range(0, 52))))
1405
+ self.assertEqual(naming_seq.distinct().axis(), list(range(0, 52)))
1406
+ self.assertEqual(naming_seq.reduce(lambda x, y: x).axis(), [0])
1407
+ self.assertEqual(naming_seq.reduce(lambda x, y: y).axis(), [51])
1408
+ self.assertEqual(naming_seq.min(lambda x, y: x.serial < y.serial).axis(), [0])
1409
+ self.assertEqual(naming_seq.max(lambda x, y: x.serial > y.serial).axis(), [51])
1410
+ self.assertEqual(naming_seq.filter(lambda x: x.gender == "M").axis(), list(range(0, 52, 2)))
1411
+ self.assertEqual(naming_seq.filter(lambda x: x.gender == "W").axis(), list(range(1, 52, 2)))
1412
+ self.assertEqual(naming_seq.filter_not(lambda x: x.gender == "M").axis(),
1413
+ naming_seq.filter(lambda x: x.gender == "W").axis())
1414
+ self.assertEqual(naming_seq.filter_not(lambda x: x.gender == "W").axis(),
1415
+ naming_seq.filter(lambda x: x.gender == "M").axis())
1416
+ self.assertEqual(naming_seq.sort(lambda x: -x.serial).axis(), naming_seq.reverse().axis())
1417
+ self.assertEqual(naming_seq.head().axis(), [0])
1418
+ self.assertEqual(naming_seq.last().axis(), [51])
1419
+ self.assertEqual(naming_seq.init().axis(), list(range(0, 51)))
1420
+ self.assertEqual(naming_seq.tail().axis(), list(range(1, 52)))
1421
+ self.assertEqual(naming_seq.foreach(lambda x: x).data, naming_seq.data)
1422
+ self.assertEqual(naming_seq.union(naming_seq).data, naming_seq.data)
1423
+ self.assertEqual(naming_seq.intersect(naming_seq).data, naming_seq.data)
1424
+
1425
+ self.assertEqual(naming_seq.scan_left(0, lambda x, y: x + y.serial).data,
1426
+ seq(range(0, 52)).scan_left(0, lambda x, y: x + y).data)
1427
+ self.assertEqual(naming_seq.scan_right(0, lambda x, y: x + y.serial).data,
1428
+ seq(range(0, 52)).scan_right(0, lambda x, y: x + y).data)
1429
+ self.assertEqual(naming_seq.map(lambda x: x.serial).data, list(range(0, 52)))
1430
+
1431
+ self.assertEqual(naming_seq.fold_left(0, lambda x, y: x + y.serial).data,
1432
+ seq(range(0, 52)).fold_left(0, lambda x, y: x + y).data)
1433
+ self.assertEqual(naming_seq.fold_right(0, lambda x, y: x + y.serial).data,
1434
+ seq(range(0, 52)).fold_right(0, lambda x, y: x + y).data)
1435
+
1436
+ self.assertEqual(naming_seq.group(lambda x: x.serial % 2).size, 2)
1437
+ self.assertEqual(naming_seq.group(lambda x: x.gender).size, 2)
1438
+ self.assertEqual(naming_seq.group(lambda x: x.name[0]).size, 26)