iker-python-common 1.0.58__tar.gz → 1.0.60__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 (83) hide show
  1. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/PKG-INFO +1 -3
  2. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/pyproject.toml +0 -4
  3. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/funcutils.py +69 -1
  4. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/jsonutils.py +97 -40
  5. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/shutils.py +9 -5
  6. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker_python_common.egg-info/PKG-INFO +1 -3
  7. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker_python_common.egg-info/SOURCES.txt +0 -12
  8. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker_python_common.egg-info/requires.txt +0 -2
  9. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/funcutils_test.py +40 -0
  10. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/jsonutils_test.py +284 -1
  11. iker_python_common-1.0.58/resources/unittest/shutils/dir.baz/file.bar.baz +0 -0
  12. iker_python_common-1.0.58/resources/unittest/shutils/dir.baz/file.foo.bar +0 -0
  13. iker_python_common-1.0.58/resources/unittest/shutils/dir.baz/file.foo.baz +0 -0
  14. iker_python_common-1.0.58/resources/unittest/shutils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz +0 -0
  15. iker_python_common-1.0.58/resources/unittest/shutils/dir.foo/dir.foo.bar/file.bar.baz +0 -0
  16. iker_python_common-1.0.58/resources/unittest/shutils/dir.foo/dir.foo.bar/file.foo.bar +0 -0
  17. iker_python_common-1.0.58/resources/unittest/shutils/dir.foo/dir.foo.bar/file.foo.baz +0 -0
  18. iker_python_common-1.0.58/resources/unittest/shutils/dir.foo/file.bar +0 -0
  19. iker_python_common-1.0.58/resources/unittest/shutils/dir.foo/file.baz +0 -0
  20. iker_python_common-1.0.58/resources/unittest/shutils/dir.foo/file.foo +0 -0
  21. iker_python_common-1.0.58/src/iker/common/utils/s3utils.py +0 -270
  22. iker_python_common-1.0.58/test/iker_tests/common/utils/s3utils_test.py +0 -422
  23. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/.editorconfig +0 -0
  24. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/.github/workflows/pr.yml +0 -0
  25. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/.github/workflows/push.yml +0 -0
  26. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/.gitignore +0 -0
  27. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/MANIFEST.in +0 -0
  28. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/README.md +0 -0
  29. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/VERSION +0 -0
  30. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/resources/unittest/config/config.cfg +0 -0
  31. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/resources/unittest/csv/data.csv +0 -0
  32. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/resources/unittest/csv/data.tsv +0 -0
  33. {iker_python_common-1.0.58/resources/unittest/s3utils → iker_python_common-1.0.60/resources/unittest/shutils}/dir.baz/file.bar.baz +0 -0
  34. {iker_python_common-1.0.58/resources/unittest/s3utils → iker_python_common-1.0.60/resources/unittest/shutils}/dir.baz/file.foo.bar +0 -0
  35. {iker_python_common-1.0.58/resources/unittest/s3utils → iker_python_common-1.0.60/resources/unittest/shutils}/dir.baz/file.foo.baz +0 -0
  36. {iker_python_common-1.0.58/resources/unittest/s3utils → iker_python_common-1.0.60/resources/unittest/shutils}/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz +0 -0
  37. {iker_python_common-1.0.58/resources/unittest/s3utils → iker_python_common-1.0.60/resources/unittest/shutils}/dir.foo/dir.foo.bar/file.bar.baz +0 -0
  38. {iker_python_common-1.0.58/resources/unittest/s3utils → iker_python_common-1.0.60/resources/unittest/shutils}/dir.foo/dir.foo.bar/file.foo.bar +0 -0
  39. {iker_python_common-1.0.58/resources/unittest/s3utils → iker_python_common-1.0.60/resources/unittest/shutils}/dir.foo/dir.foo.bar/file.foo.baz +0 -0
  40. {iker_python_common-1.0.58/resources/unittest/s3utils → iker_python_common-1.0.60/resources/unittest/shutils}/dir.foo/file.bar +0 -0
  41. {iker_python_common-1.0.58/resources/unittest/s3utils → iker_python_common-1.0.60/resources/unittest/shutils}/dir.foo/file.baz +0 -0
  42. {iker_python_common-1.0.58/resources/unittest/s3utils → iker_python_common-1.0.60/resources/unittest/shutils}/dir.foo/file.foo +0 -0
  43. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/setup.cfg +0 -0
  44. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/setup.py +0 -0
  45. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/__init__.py +0 -0
  46. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/__init__.py +0 -0
  47. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/argutils.py +0 -0
  48. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/config.py +0 -0
  49. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/csv.py +0 -0
  50. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/dbutils.py +0 -0
  51. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/dockerutils.py +0 -0
  52. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/dtutils.py +0 -0
  53. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/logger.py +0 -0
  54. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/numutils.py +0 -0
  55. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/randutils.py +0 -0
  56. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/retry.py +0 -0
  57. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/sequtils.py +0 -0
  58. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/span.py +0 -0
  59. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/strutils.py +0 -0
  60. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/testutils.py +0 -0
  61. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker/common/utils/typeutils.py +0 -0
  62. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker_python_common.egg-info/dependency_links.txt +0 -0
  63. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker_python_common.egg-info/not-zip-safe +0 -0
  64. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/src/iker_python_common.egg-info/top_level.txt +0 -0
  65. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_test.py +0 -0
  66. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/__init__.py +0 -0
  67. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/argutils_test.py +0 -0
  68. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/config_test.py +0 -0
  69. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/csv_test.py +0 -0
  70. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/dbutils_test.py +0 -0
  71. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/dockerutils_test.py +0 -0
  72. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/dtutils_test.py +0 -0
  73. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/logger_test.py +0 -0
  74. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/numutils_test.py +0 -0
  75. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/randutils_test.py +0 -0
  76. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/retry_test.py +0 -0
  77. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/sequtils_test.py +0 -0
  78. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/shutils_test.py +0 -0
  79. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/span_test.py +0 -0
  80. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/strutils_test.py +0 -0
  81. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/testutils_test.py +0 -0
  82. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/common/utils/typeutils_test.py +0 -0
  83. {iker_python_common-1.0.58 → iker_python_common-1.0.60}/test/iker_tests/docker_fixtures.py +0 -0
@@ -1,12 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iker-python-common
3
- Version: 1.0.58
3
+ Version: 1.0.60
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.12
6
6
  Classifier: Programming Language :: Python :: 3.13
7
7
  Classifier: Programming Language :: Python :: 3.14
8
8
  Requires-Python: <3.15,>=3.12
9
- Requires-Dist: boto3>=1.35
10
9
  Requires-Dist: docker>=7.1
11
10
  Requires-Dist: numpy>=2.3
12
11
  Requires-Dist: psycopg>=3.2
@@ -16,7 +15,6 @@ Provides-Extra: all
16
15
  Requires-Dist: iker-python-common; extra == "all"
17
16
  Provides-Extra: test
18
17
  Requires-Dist: ddt>=1.7; extra == "test"
19
- Requires-Dist: moto[all,ec2,s3]>=5.0; extra == "test"
20
18
  Requires-Dist: pytest-cov>=5.0; extra == "test"
21
19
  Requires-Dist: pytest-mysql>=3.0; extra == "test"
22
20
  Requires-Dist: pytest-order>=1.3; extra == "test"
@@ -8,7 +8,6 @@ build-backend = "setuptools.build_meta"
8
8
 
9
9
  [dependency-groups]
10
10
  dev = [
11
- "boto3>=1.35",
12
11
  "docker>=7.1",
13
12
  "numpy>=2.3",
14
13
  "psycopg>=3.2",
@@ -17,7 +16,6 @@ dev = [
17
16
  ]
18
17
  test = [
19
18
  "ddt>=1.7",
20
- "moto[ec2,s3,all]>=5.0",
21
19
  "pytest-cov>=5.0",
22
20
  "pytest-mysql>=3.0",
23
21
  "pytest-order>=1.3",
@@ -36,7 +34,6 @@ classifiers = [
36
34
  "Programming Language :: Python :: 3.14",
37
35
  ]
38
36
  dependencies = [
39
- "boto3>=1.35",
40
37
  "docker>=7.1",
41
38
  "numpy>=2.3",
42
39
  "psycopg>=3.2",
@@ -50,7 +47,6 @@ all = [
50
47
  ]
51
48
  test = [
52
49
  "ddt>=1.7",
53
- "moto[ec2,s3,all]>=5.0",
54
50
  "pytest-cov>=5.0",
55
51
  "pytest-mysql>=3.0",
56
52
  "pytest-order>=1.3",
@@ -1,8 +1,12 @@
1
1
  import functools
2
2
  from collections.abc import Callable
3
- from typing import Protocol
3
+ from typing import Any, Protocol
4
4
 
5
5
  __all__ = [
6
+ "const",
7
+ "first",
8
+ "second",
9
+ "packed",
6
10
  "identity",
7
11
  "composable",
8
12
  "singleton",
@@ -12,6 +16,70 @@ __all__ = [
12
16
  ]
13
17
 
14
18
 
19
+ def const[T](value: T) -> Callable[..., T]:
20
+ """
21
+ Returns a function that always returns the specified ``value``, regardless of the input arguments.
22
+
23
+ :param value: The constant value to return.
24
+ :return: A function that takes any arguments and returns ``value``.
25
+ """
26
+
27
+ def getter(*args: Any, **kwargs: Any) -> T:
28
+ return value
29
+
30
+ return getter
31
+
32
+
33
+ def first[K]() -> Callable[[tuple[K, Any]], K]:
34
+ """
35
+ Returns a function that extracts the first element (key) from a 2-tuple.
36
+
37
+ :return: A function that takes a 2-tuple and returns its first element.
38
+ """
39
+
40
+ def getter(item: tuple[K, Any]) -> K:
41
+ if not isinstance(item, tuple) or len(item) != 2:
42
+ raise ValueError("item must be a 2-tuple")
43
+ return item[0]
44
+
45
+ return getter
46
+
47
+
48
+ def second[V]() -> Callable[[tuple[Any, V]], V]:
49
+ """
50
+ Returns a function that extracts the second element (value) from a 2-tuple.
51
+
52
+ :return: A function that takes a 2-tuple and returns its second element.
53
+ """
54
+
55
+ def getter(item: tuple[Any, V]) -> V:
56
+ if not isinstance(item, tuple) or len(item) != 2:
57
+ raise ValueError("item must be a 2-tuple")
58
+ return item[1]
59
+
60
+ return getter
61
+
62
+
63
+ def packed[R](func: Callable[..., R]) -> Callable[[tuple[Any, ...]], R]:
64
+ """
65
+ Wraps a function to accept its arguments as a single tuple, unpacking them when called. This is useful for
66
+ scenarios where arguments are naturally grouped in tuples, such as when working with data structures like maps or
67
+ lists of tuples, or when interfacing with APIs that provide arguments in tuple form.
68
+
69
+ >>> data = [(1, 2), (3, 4), (5, 6)]
70
+ >>> sums = map(packed(lambda x, y: x + y), data)
71
+
72
+ :param func: The function to wrap.
73
+ :return: A function that takes a tuple of arguments and calls the original function with them unpacked.
74
+ """
75
+
76
+ @functools.wraps(func)
77
+ def wrapper(args: tuple[Any, ...]) -> R:
78
+ return func(*args)
79
+
80
+ return wrapper
81
+
82
+
15
83
  def identity[T](instance: T) -> T:
16
84
  """
17
85
  Returns the input ``instance`` unchanged. This is a utility function often used as a default or placeholder.
@@ -1,5 +1,5 @@
1
1
  import math
2
- from collections.abc import Callable, Mapping, MutableMapping, MutableSequence, Sequence, Set
2
+ from collections.abc import Callable, Generator, Mapping, MutableMapping, MutableSequence, Sequence, Set
3
3
  from typing import Any, SupportsFloat, SupportsInt
4
4
 
5
5
  from iker.common.utils.numutils import is_normal_real
@@ -24,6 +24,8 @@ __all__ = [
24
24
  "json_traverse",
25
25
  "json_reformat",
26
26
  "json_sanitize",
27
+ "json_difference",
28
+ "json_equals",
27
29
  "json_compare",
28
30
  ]
29
31
 
@@ -347,79 +349,134 @@ def json_sanitize(obj: Any, *, str_inf_nan: bool = True, str_unregistered: bool
347
349
  unregistered_formatter=unregistered_formatter)
348
350
 
349
351
 
350
- def json_compare(
352
+ def json_difference(
351
353
  a: JsonTypeCompatible,
352
354
  b: JsonTypeCompatible,
355
+ node_path: NodePath | None = None,
353
356
  *,
354
357
  int_strict: bool = False,
355
358
  float_tol: float = 1e-5,
356
359
  list_order: bool = True,
357
360
  dict_extra: bool = False,
358
- ) -> bool:
361
+ ) -> Generator[tuple[NodePath, str], None, None]:
359
362
  """
360
- Compares two JSON-like structures for equality, with options for integer strictness, float tolerance, list order,
361
- and dictionary key matching.
363
+ Compares two JSON-like structures and yields differences found, with options for integer strictness, float
364
+ tolerance, list order, and dictionary key matching.
362
365
 
363
366
  :param a: The first JSON-compatible object to compare.
364
367
  :param b: The second JSON-compatible object to compare.
368
+ :param node_path: The current node path during recursion (used internally).
365
369
  :param int_strict: Whether to require strict integer type matching.
366
370
  :param float_tol: The tolerance for comparing float values.
367
371
  :param list_order: Whether to require list order to match.
368
372
  :param dict_extra: Whether to allow extra keys in dictionaries.
369
- :return: ``True`` if the structures are considered equal, ``False`` otherwise.
373
+ :return: Tuples of node paths and difference descriptions.
370
374
  """
371
375
  if a is None or b is None:
372
- return a is None and b is None
376
+ if not (a is None and b is None):
377
+ yield node_path, "one value is None while the other is not"
378
+ return
373
379
 
374
380
  if isinstance(a, (str, bool)):
375
381
  if type(a) != type(b):
376
- return False
377
- return a == b
382
+ yield node_path, f"type mismatch: '{type(a)}' vs '{type(b)}'"
383
+ elif a != b:
384
+ yield node_path, f"value mismatch: '{a}' vs '{b}'"
385
+ return
378
386
 
379
387
  if isinstance(a, (SupportsFloat, SupportsInt)) and isinstance(b, (SupportsFloat, SupportsInt)):
380
388
  isint_a = isinstance(a, int) or not isinstance(a, SupportsFloat)
381
389
  isint_b = isinstance(b, int) or not isinstance(b, SupportsFloat)
382
390
  if isint_a and isint_b:
383
- return int(a) == int(b)
391
+ if int(a) != int(b):
392
+ yield node_path, f"integer value mismatch: '{int(a)}' vs '{int(b)}'"
393
+ return
384
394
  if int_strict and (isint_a or isint_b):
385
- return False
395
+ yield node_path, "integer type mismatch under strict mode"
396
+ return
386
397
  va = int(a) if isint_a else float(a)
387
398
  vb = int(b) if isint_b else float(b)
388
- if math.isnan(va) and math.isnan(vb):
389
- return True
399
+ if math.isnan(va) or math.isnan(vb):
400
+ if not (math.isnan(va) and math.isnan(vb)):
401
+ yield node_path, "NaN mismatch"
402
+ return
390
403
  if math.isinf(va) and math.isinf(vb):
391
- return va == vb
392
- return abs(va - vb) <= float_tol
404
+ if va != vb:
405
+ yield node_path, "infinity sign mismatch"
406
+ return
407
+ if abs(va - vb) > float_tol:
408
+ yield node_path, f"float value mismatch: '{va}' vs '{vb}' with tolerance '{float_tol}'"
409
+ return
393
410
 
394
411
  if isinstance(a, Mapping) and isinstance(b, Mapping):
395
412
  if not dict_extra and set(a.keys()) != set(b.keys()):
396
- return False
397
- return all(json_compare(a[k],
398
- b[k],
399
- int_strict=int_strict,
400
- float_tol=float_tol,
401
- list_order=list_order,
402
- dict_extra=dict_extra)
403
- for k in set(a.keys()) & set(b.keys()))
413
+ yield node_path, f"dictionary key mismatch: '{set(a.keys())}' vs '{set(b.keys())}'"
414
+ return
415
+ for k in set(a.keys()) & set(b.keys()):
416
+ yield from json_difference(a[k],
417
+ b[k],
418
+ (node_path or []) + [k],
419
+ int_strict=int_strict,
420
+ float_tol=float_tol,
421
+ list_order=list_order,
422
+ dict_extra=dict_extra)
423
+ return
404
424
 
405
425
  if isinstance(a, Sequence) and isinstance(b, Sequence):
406
426
  if len(a) != len(b):
407
- return False
427
+ yield node_path, f"list length mismatch: '{len(a)}' vs '{len(b)}'"
428
+ return
408
429
  if list_order:
409
- return all(json_compare(va,
410
- vb,
411
- int_strict=int_strict,
412
- float_tol=float_tol,
413
- list_order=list_order,
414
- dict_extra=dict_extra)
415
- for va, vb in zip(a, b))
430
+ for i, (va, vb) in enumerate(zip(a, b)):
431
+ yield from json_difference(va,
432
+ vb,
433
+ (node_path or []) + [i],
434
+ int_strict=int_strict,
435
+ float_tol=float_tol,
436
+ list_order=list_order,
437
+ dict_extra=dict_extra)
416
438
  else:
417
- return all(json_compare(va,
418
- vb,
419
- int_strict=int_strict,
420
- float_tol=float_tol,
421
- list_order=list_order,
422
- dict_extra=dict_extra)
423
- for va, vb in zip(sorted(a), sorted(b)))
424
-
425
- raise ValueError(f"incompatible type '{type(a)}' and '{type(b)}'")
439
+ for i, (va, vb) in enumerate(zip(sorted(a), sorted(b))):
440
+ yield from json_difference(va,
441
+ vb,
442
+ (node_path or []) + [i],
443
+ int_strict=int_strict,
444
+ float_tol=float_tol,
445
+ list_order=list_order,
446
+ dict_extra=dict_extra)
447
+ return
448
+
449
+ yield node_path, f"type mismatch: '{type(a)}' vs '{type(b)}'"
450
+
451
+
452
+ def json_equals(
453
+ a: JsonTypeCompatible,
454
+ b: JsonTypeCompatible,
455
+ *,
456
+ int_strict: bool = False,
457
+ float_tol: float = 1e-5,
458
+ list_order: bool = True,
459
+ dict_extra: bool = False,
460
+ ) -> bool:
461
+ """
462
+ Compares two JSON-like structures for equality based on specified criteria.
463
+
464
+ :param a: The first JSON-compatible object to compare.
465
+ :param b: The second JSON-compatible object to compare.
466
+ :param int_strict: Whether to require strict integer type matching.
467
+ :param float_tol: The tolerance for comparing float values.
468
+ :param list_order: Whether to require list order to match.
469
+ :param dict_extra: Whether to allow extra keys in dictionaries.
470
+ :return: ``True`` if the structures are considered equal, ``False`` otherwise.
471
+ """
472
+ return next(json_difference(a,
473
+ b,
474
+ node_path=[],
475
+ int_strict=int_strict,
476
+ float_tol=float_tol,
477
+ list_order=list_order,
478
+ dict_extra=dict_extra),
479
+ None) is None
480
+
481
+
482
+ json_compare = json_equals
@@ -95,7 +95,11 @@ def path_depth(root: str, child: str) -> int:
95
95
  return child_expanded[len(root_expanded):].count(os.sep)
96
96
 
97
97
 
98
- def glob_match(names: list[str], include_patterns: list[str] = None, exclude_patterns: list[str] = None) -> list[str]:
98
+ def glob_match(
99
+ names: list[str],
100
+ include_patterns: list[str] | None = None,
101
+ exclude_patterns: list[str] | None = None,
102
+ ) -> list[str]:
99
103
  """
100
104
  Applies the given inclusive and exclusive glob patterns to the given ``names`` and returns the filtered result.
101
105
 
@@ -121,8 +125,8 @@ class CopyFuncProtocol(Protocol):
121
125
  def listfile(
122
126
  path: str,
123
127
  *,
124
- include_patterns: list[str] = None,
125
- exclude_patterns: list[str] = None,
128
+ include_patterns: list[str] | None = None,
129
+ exclude_patterns: list[str] | None = None,
126
130
  depth: int = 0,
127
131
  ) -> list[str]:
128
132
  """
@@ -153,8 +157,8 @@ def copy(
153
157
  src: str,
154
158
  dst: str,
155
159
  *,
156
- include_patterns: list[str] = None,
157
- exclude_patterns: list[str] = None,
160
+ include_patterns: list[str] | None = None,
161
+ exclude_patterns: list[str] | None = None,
158
162
  depth: int = 0,
159
163
  follow_symlinks: bool = False,
160
164
  ignore_dangling_symlinks: bool = False,
@@ -1,12 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iker-python-common
3
- Version: 1.0.58
3
+ Version: 1.0.60
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.12
6
6
  Classifier: Programming Language :: Python :: 3.13
7
7
  Classifier: Programming Language :: Python :: 3.14
8
8
  Requires-Python: <3.15,>=3.12
9
- Requires-Dist: boto3>=1.35
10
9
  Requires-Dist: docker>=7.1
11
10
  Requires-Dist: numpy>=2.3
12
11
  Requires-Dist: psycopg>=3.2
@@ -16,7 +15,6 @@ Provides-Extra: all
16
15
  Requires-Dist: iker-python-common; extra == "all"
17
16
  Provides-Extra: test
18
17
  Requires-Dist: ddt>=1.7; extra == "test"
19
- Requires-Dist: moto[all,ec2,s3]>=5.0; extra == "test"
20
18
  Requires-Dist: pytest-cov>=5.0; extra == "test"
21
19
  Requires-Dist: pytest-mysql>=3.0; extra == "test"
22
20
  Requires-Dist: pytest-order>=1.3; extra == "test"
@@ -10,16 +10,6 @@ setup.py
10
10
  resources/unittest/config/config.cfg
11
11
  resources/unittest/csv/data.csv
12
12
  resources/unittest/csv/data.tsv
13
- resources/unittest/s3utils/dir.baz/file.bar.baz
14
- resources/unittest/s3utils/dir.baz/file.foo.bar
15
- resources/unittest/s3utils/dir.baz/file.foo.baz
16
- resources/unittest/s3utils/dir.foo/file.bar
17
- resources/unittest/s3utils/dir.foo/file.baz
18
- resources/unittest/s3utils/dir.foo/file.foo
19
- resources/unittest/s3utils/dir.foo/dir.foo.bar/file.bar.baz
20
- resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.bar
21
- resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.baz
22
- resources/unittest/s3utils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz
23
13
  resources/unittest/shutils/dir.baz/file.bar.baz
24
14
  resources/unittest/shutils/dir.baz/file.foo.bar
25
15
  resources/unittest/shutils/dir.baz/file.foo.baz
@@ -44,7 +34,6 @@ src/iker/common/utils/logger.py
44
34
  src/iker/common/utils/numutils.py
45
35
  src/iker/common/utils/randutils.py
46
36
  src/iker/common/utils/retry.py
47
- src/iker/common/utils/s3utils.py
48
37
  src/iker/common/utils/sequtils.py
49
38
  src/iker/common/utils/shutils.py
50
39
  src/iker/common/utils/span.py
@@ -72,7 +61,6 @@ test/iker_tests/common/utils/logger_test.py
72
61
  test/iker_tests/common/utils/numutils_test.py
73
62
  test/iker_tests/common/utils/randutils_test.py
74
63
  test/iker_tests/common/utils/retry_test.py
75
- test/iker_tests/common/utils/s3utils_test.py
76
64
  test/iker_tests/common/utils/sequtils_test.py
77
65
  test/iker_tests/common/utils/shutils_test.py
78
66
  test/iker_tests/common/utils/span_test.py
@@ -1,4 +1,3 @@
1
- boto3>=1.35
2
1
  docker>=7.1
3
2
  numpy>=2.3
4
3
  psycopg>=3.2
@@ -10,7 +9,6 @@ iker-python-common
10
9
 
11
10
  [test]
12
11
  ddt>=1.7
13
- moto[all,ec2,s3]>=5.0
14
12
  pytest-cov>=5.0
15
13
  pytest-mysql>=3.0
16
14
  pytest-order>=1.3
@@ -4,6 +4,7 @@ import unittest
4
4
  import ddt
5
5
 
6
6
  from iker.common.utils.funcutils import composable, identity
7
+ from iker.common.utils.funcutils import const, first, packed, second
7
8
  from iker.common.utils.funcutils import lazy, memorized, singleton, unique_returns
8
9
  from iker.common.utils.randutils import randomizer
9
10
 
@@ -19,6 +20,45 @@ class Counter(object):
19
20
  @ddt.ddt
20
21
  class FuncUtilsTest(unittest.TestCase):
21
22
 
23
+ def test_const(self):
24
+ self.assertEqual(5, const(5)())
25
+ self.assertEqual(5, const(5)(1, 2, 3))
26
+ self.assertEqual(5, const(5)(a=1, b=2))
27
+
28
+ self.assertEqual("hello", const("hello")())
29
+ self.assertEqual("hello", const("hello")(1, 2, 3))
30
+ self.assertEqual("hello", const("hello")(a=1, b=2))
31
+
32
+ def test_first(self):
33
+ self.assertEqual(1, first()((1, "a")))
34
+ self.assertEqual("key", first()(("key", 12)))
35
+ self.assertEqual((1, 2), first()(((1, 2), "value")))
36
+
37
+ with self.assertRaises(ValueError):
38
+ first()((1, 2, 3))
39
+ with self.assertRaises(ValueError):
40
+ first()(123)
41
+
42
+ def test_second(self):
43
+ self.assertEqual("a", second()((1, "a")))
44
+ self.assertEqual(12, second()(("key", 12)))
45
+ self.assertEqual("value", second()(((1, 2), "value")))
46
+
47
+ with self.assertRaises(ValueError):
48
+ second()((1, 2, 3))
49
+ with self.assertRaises(ValueError):
50
+ second()(123)
51
+
52
+ def test_packed(self):
53
+ self.assertEqual(3, packed(lambda x, y: x + y)((1, 2)))
54
+ self.assertEqual(12, packed(lambda x, y, z: x + y + z)((3, 4, 5)))
55
+ self.assertEqual("aab", packed(lambda a, b, x, y: a * x + b * y)(("a", "b", 2, 1)))
56
+
57
+ with self.assertRaises(TypeError):
58
+ packed(lambda x, y, z: x ** y ** z)((2, 3, 4, 5))
59
+ with self.assertRaises(TypeError):
60
+ packed(lambda x, y, z: x ** y ** z)((2, 3))
61
+
22
62
  def test_identity(self):
23
63
  self.assertEqual(1, identity(1))
24
64
  self.assertEqual("test", identity("test"))