iker-python-common 1.0.65__tar.gz → 1.0.67__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.65 → iker_python_common-1.0.67}/PKG-INFO +1 -1
  2. {iker_python_common-1.0.65/resources/unittest/csv → iker_python_common-1.0.67/resources/unittest/csvutils}/data.csv +1 -1
  3. {iker_python_common-1.0.65/resources/unittest/csv → iker_python_common-1.0.67/resources/unittest/csvutils}/data.tsv +1 -1
  4. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/config.py +2 -2
  5. iker_python_common-1.0.65/src/iker/common/utils/csv.py → iker_python_common-1.0.67/src/iker/common/utils/csvutils.py +41 -24
  6. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/retry.py +35 -48
  7. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/shutils.py +0 -3
  8. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/span.py +55 -16
  9. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker_python_common.egg-info/PKG-INFO +1 -1
  10. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker_python_common.egg-info/SOURCES.txt +7 -5
  11. iker_python_common-1.0.67/test/iker_tests/__init__.py +0 -0
  12. iker_python_common-1.0.67/test/iker_tests/common/__init__.py +0 -0
  13. iker_python_common-1.0.67/test/iker_tests/common/utils/__init__.py +0 -0
  14. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/config_test.py +1 -1
  15. iker_python_common-1.0.65/test/iker_tests/common/utils/csv_test.py → iker_python_common-1.0.67/test/iker_tests/common/utils/csvutils_test.py +29 -30
  16. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/retry_test.py +10 -12
  17. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/shutils_test.py +1 -1
  18. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/span_test.py +15 -3
  19. iker_python_common-1.0.65/test/iker_tests/__init__.py → iker_python_common-1.0.67/test/testenv.py +1 -1
  20. iker_python_common-1.0.65/test/iker_test.py +0 -13
  21. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/.editorconfig +0 -0
  22. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/.github/workflows/pr.yml +0 -0
  23. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/.github/workflows/push.yml +0 -0
  24. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/.gitignore +0 -0
  25. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/MANIFEST.in +0 -0
  26. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/README.md +0 -0
  27. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/VERSION +0 -0
  28. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/pyproject.toml +0 -0
  29. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/config/config.cfg +0 -0
  30. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.baz/file.bar.baz +0 -0
  31. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.baz/file.foo.bar +0 -0
  32. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.baz/file.foo.baz +0 -0
  33. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz +0 -0
  34. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.bar.baz +0 -0
  35. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.foo.bar +0 -0
  36. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.foo/dir.foo.bar/file.foo.baz +0 -0
  37. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.foo/file.bar +0 -0
  38. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.foo/file.baz +0 -0
  39. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.foo/file.foo +0 -0
  40. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/setup.cfg +0 -0
  41. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/setup.py +0 -0
  42. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/__init__.py +0 -0
  43. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/__init__.py +0 -0
  44. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/argutils.py +0 -0
  45. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/dbutils.py +0 -0
  46. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/dtutils.py +0 -0
  47. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/funcutils.py +0 -0
  48. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/iterutils.py +0 -0
  49. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/jsonutils.py +0 -0
  50. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/logger.py +0 -0
  51. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/numutils.py +0 -0
  52. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/randutils.py +0 -0
  53. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/strutils.py +0 -0
  54. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/testutils.py +0 -0
  55. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/typeutils.py +0 -0
  56. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker_python_common.egg-info/dependency_links.txt +0 -0
  57. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker_python_common.egg-info/not-zip-safe +0 -0
  58. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker_python_common.egg-info/requires.txt +0 -0
  59. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker_python_common.egg-info/top_level.txt +0 -0
  60. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/argutils_test.py +0 -0
  61. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/dbutils_test.py +0 -0
  62. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/dtutils_test.py +0 -0
  63. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/funcutils_test.py +0 -0
  64. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/iterutils_test.py +0 -0
  65. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/jsonutils_test.py +0 -0
  66. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/logger_test.py +0 -0
  67. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/numutils_test.py +0 -0
  68. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/randutils_test.py +0 -0
  69. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/strutils_test.py +0 -0
  70. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/testutils_test.py +0 -0
  71. {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/typeutils_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iker-python-common
3
- Version: 1.0.65
3
+ Version: 1.0.67
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.12
6
6
  Classifier: Programming Language :: Python :: 3.13
@@ -1,6 +1,6 @@
1
1
  dummy_str,dummy_bool,dummy_int,dummy_float,dummy_datetime,dummy_params
2
2
  foo,True,1,1.0,2020-01-01T00:00:00,
3
- bar,False,-1,-1.0,2020-01-01T00:00:00,key_1=value_1;key_2;!key_3
3
+ bar,False,-1,-1.0,2020-01-01T00:00:00,"key_1=value_1,key_2,!key_3"
4
4
  baz,\N,100,inf,2020-01-01T00:00:00,\N
5
5
  ,\N,-100,-inf,2020-01-01T00:00:00,
6
6
  \N,\N,0,nan,2020-01-01T00:00:00,\N
@@ -1,6 +1,6 @@
1
1
  dummy_str dummy_bool dummy_int dummy_float dummy_datetime dummy_params
2
2
  foo True 1 1.0 2020-01-01T00:00:00
3
- bar False -1 -1.0 2020-01-01T00:00:00 key_1:value_1#key_2#-key_3
3
+ bar False -1 -1.0 2020-01-01T00:00:00 key_1:value_1;key_2;-key_3
4
4
  baz <null> 100 inf 2020-01-01T00:00:00 <null>
5
5
  <null> -100 -inf 2020-01-01T00:00:00
6
6
  <null> <null> 0 nan 2020-01-01T00:00:00 <null>
@@ -31,7 +31,7 @@ class Config(object):
31
31
  self.config_parser.read(self.config_path, encoding="utf-8")
32
32
  return True
33
33
  except IOError as e:
34
- logger.exception("Failed to restore config from file <%s>", self.config_path)
34
+ logger.exception("Failed to restore config from file '%s'", self.config_path)
35
35
  return False
36
36
 
37
37
  def persist(self) -> bool:
@@ -42,7 +42,7 @@ class Config(object):
42
42
  self.config_parser.write(fh)
43
43
  return True
44
44
  except IOError as e:
45
- logger.exception("Failed to persist config to file <%s>", self.config_path)
45
+ logger.exception("Failed to persist config to file '%s'", self.config_path)
46
46
  return False
47
47
 
48
48
  def has_section(self, section: str) -> bool:
@@ -1,3 +1,4 @@
1
+ import csv
1
2
  import os
2
3
  from collections.abc import Callable, Generator, Iterable, Mapping, Sequence
3
4
  from typing import Any
@@ -41,19 +42,31 @@ class CSVView(object):
41
42
  files, with options for headers and dictionary or list output.
42
43
 
43
44
  :param schema: The sequence of ``CSVColumn`` objects defining the schema.
44
- :param row_delim: The delimiter for rows (default is ``'\n'``).
45
- :param col_delim: The delimiter for columns (default is ``','``).
45
+ :param column_delimiter: The delimiter used to separate columns in the CSV (default: ",").
46
+ :param line_terminator: The string used to terminate lines in the CSV (default: "\n").
47
+ :param quote_char: The character used to quote fields in the CSV (default: '"').
48
+ :param strict: Whether to raise an error on malformed CSV (default: ``True``).
46
49
  """
47
50
 
48
- def __init__(self, schema: Sequence[CSVColumn], *, row_delim: str = "\n", col_delim: str = ","):
51
+ def __init__(
52
+ self,
53
+ schema: Sequence[CSVColumn],
54
+ *,
55
+ column_delimiter: str = ",",
56
+ line_terminator: str = "\n",
57
+ quote_char: str = '"',
58
+ strict: bool = True,
59
+ ):
49
60
  self.schema = schema
50
- self.row_delim = row_delim
51
- self.col_delim = col_delim
61
+ self.column_delimiter = column_delimiter
62
+ self.line_terminator = line_terminator
63
+ self.quote_char = quote_char
64
+ self.strict = strict
52
65
 
53
66
  @overload
54
67
  def load_lines(
55
68
  self,
56
- lines: Iterable[str],
69
+ lines: Iterable[Sequence[str]],
57
70
  has_header: bool,
58
71
  ret_dict: False = False,
59
72
  ) -> Generator[list[Any], None, None]:
@@ -62,7 +75,7 @@ class CSVView(object):
62
75
  @overload
63
76
  def load_lines(
64
77
  self,
65
- lines: Iterable[str],
78
+ lines: Iterable[Sequence[str]],
66
79
  ret_dict: False = False,
67
80
  ) -> Generator[list[Any], None, None]:
68
81
  ...
@@ -70,7 +83,7 @@ class CSVView(object):
70
83
  @overload
71
84
  def load_lines(
72
85
  self,
73
- lines: Iterable[str],
86
+ lines: Iterable[Sequence[str]],
74
87
  has_header: bool,
75
88
  ret_dict: True = True,
76
89
  ) -> Generator[dict[str, Any], None, None]:
@@ -79,14 +92,14 @@ class CSVView(object):
79
92
  @overload
80
93
  def load_lines(
81
94
  self,
82
- lines: Iterable[str],
95
+ lines: Iterable[Sequence[str]],
83
96
  ret_dict: True = True,
84
97
  ) -> Generator[dict[str, Any], None, None]:
85
98
  ...
86
99
 
87
100
  def load_lines(
88
101
  self,
89
- lines: Iterable[str],
102
+ lines: Iterable[Sequence[str]],
90
103
  has_header: bool = True,
91
104
  ret_dict: bool = False,
92
105
  ) -> Generator[list[Any] | dict[str, Any], None, None]:
@@ -101,15 +114,13 @@ class CSVView(object):
101
114
  """
102
115
  rows_iter = iter(lines)
103
116
  if has_header:
104
- header_row = next(rows_iter)
105
- header_cols = header_row.split(self.col_delim)
117
+ header_cols = next(rows_iter)
106
118
  if len(self.schema) != len(header_cols):
107
119
  raise ValueError("size of the schema is not identical to size of the columns")
108
120
  for c, header_col in zip(self.schema, header_cols):
109
121
  if c.name != header_col:
110
122
  raise ValueError("name of the schema is not equal to the name of the columns")
111
- for row in rows_iter:
112
- cols = row.split(self.col_delim)
123
+ for cols in rows_iter:
113
124
  if len(self.schema) != len(cols):
114
125
  continue
115
126
  if ret_dict:
@@ -121,7 +132,7 @@ class CSVView(object):
121
132
  self,
122
133
  data: Iterable[Sequence[Any] | Mapping[str, Any]],
123
134
  has_header: bool = True,
124
- ) -> Generator[str, None, None]:
135
+ ) -> Generator[list[str], None, None]:
125
136
  """
126
137
  Dumps data to CSV lines according to the schema, optionally including a header row.
127
138
 
@@ -130,16 +141,14 @@ class CSVView(object):
130
141
  :return: A generator yielding CSV lines as strings.
131
142
  """
132
143
  if has_header:
133
- yield self.col_delim.join(c.name for c in self.schema)
144
+ yield list(c.name for c in self.schema)
134
145
  for cols in data:
135
146
  if isinstance(cols, Sequence):
136
147
  if len(self.schema) != len(cols):
137
148
  raise ValueError("size of the schema is not identical to size of the columns")
138
- yield self.col_delim.join(c.null_str if col is None else c.dumper(col)
139
- for c, col in zip(self.schema, cols))
149
+ yield list(c.null_str if col is None else c.dumper(col) for c, col in zip(self.schema, cols))
140
150
  if isinstance(cols, Mapping):
141
- yield self.col_delim.join(c.null_str if cols.get(c.name) is None else c.dumper(cols.get(c.name))
142
- for c in self.schema)
151
+ yield list(c.null_str if cols.get(c.name) is None else c.dumper(cols.get(c.name)) for c in self.schema)
143
152
 
144
153
  @overload
145
154
  def load_file(
@@ -196,8 +205,12 @@ class CSVView(object):
196
205
  :return: A generator yielding each row as a list or dictionary.
197
206
  """
198
207
  with open(file_path, mode="r", **kwargs) as fh:
199
- lines = fh.read().split(self.row_delim)
200
- yield from self.load_lines(lines, has_header, ret_dict)
208
+ reader = csv.reader(fh,
209
+ delimiter=self.column_delimiter,
210
+ lineterminator=self.line_terminator,
211
+ quotechar=self.quote_char,
212
+ strict=self.strict)
213
+ yield from self.load_lines(reader, has_header, ret_dict)
201
214
 
202
215
  def dump_file(
203
216
  self,
@@ -215,9 +228,13 @@ class CSVView(object):
215
228
  :param kwargs: Additional keyword arguments for file opening.
216
229
  """
217
230
  with open(file_path, mode="w", **kwargs) as fh:
231
+ writer = csv.writer(fh,
232
+ delimiter=self.column_delimiter,
233
+ lineterminator=self.line_terminator,
234
+ quotechar=self.quote_char,
235
+ strict=self.strict)
218
236
  for line in self.dump_lines(data, has_header):
219
- fh.write(line)
220
- fh.write(self.row_delim)
237
+ writer.writerow(line)
221
238
 
222
239
 
223
240
  column = CSVColumn
@@ -1,9 +1,11 @@
1
1
  import abc
2
- import random
3
2
  import time
3
+ from collections.abc import Callable
4
+ from typing import Any
4
5
 
5
6
  from iker.common.utils import logger
6
7
  from iker.common.utils.dtutils import dt_utc_now
8
+ from iker.common.utils.randutils import randomizer
7
9
 
8
10
  __all__ = [
9
11
  "Attempt",
@@ -68,12 +70,9 @@ class Retry(abc.ABC):
68
70
  class RetryWrapper(object):
69
71
  def __init__(
70
72
  self,
71
- cls,
73
+ target: Callable[..., Any] | Retry,
72
74
  wait: int = None,
73
- wait_exponent_init: int = None,
74
- wait_exponent_max: int = None,
75
- wait_random_min: int = None,
76
- wait_random_max: int = None,
75
+ wait_func: Callable[[int], int] = None,
77
76
  retrials: int = None,
78
77
  timeout: int = None,
79
78
  ):
@@ -81,21 +80,15 @@ class RetryWrapper(object):
81
80
  Retry executor that wraps a callable or ``Retry`` instance, providing flexible retry strategies including fixed,
82
81
  exponential, and random waits.
83
82
 
84
- :param cls: The target callable or ``Retry`` instance to execute.
83
+ :param target: The target callable or ``Retry`` instance to execute.
85
84
  :param wait: Fixed wait time (in seconds) between retrials.
86
- :param wait_exponent_init: Initial wait time for exponential backoff.
87
- :param wait_exponent_max: Maximum wait time for exponential backoff.
88
- :param wait_random_min: Minimum wait time for random backoff.
89
- :param wait_random_max: Maximum wait time for random backoff.
85
+ :param wait_func: Function to determine wait time based on attempt number.
90
86
  :param retrials: Maximum number of retrials (``None`` for unlimited).
91
87
  :param timeout: Maximum total time (in seconds) allowed for all attempts (``None`` for unlimited).
92
88
  """
93
- self.__wrapped = cls
89
+ self.target = target
94
90
  self.wait = wait
95
- self.wait_exponent_init = wait_exponent_init
96
- self.wait_exponent_max = wait_exponent_max
97
- self.wait_random_min = wait_random_min
98
- self.wait_random_max = wait_random_max
91
+ self.wait_func = wait_func
99
92
  self.retrials = retrials
100
93
  self.timeout = timeout
101
94
 
@@ -107,9 +100,9 @@ class RetryWrapper(object):
107
100
  :param kwargs: Keyword arguments for the operation.
108
101
  :return: The result of the operation.
109
102
  """
110
- return self.__run(*args, **kwargs)
103
+ return self.run(*args, **kwargs)
111
104
 
112
- def __next_wait(self, attempt_number: int):
105
+ def next_wait(self, attempt_number: int):
113
106
  """
114
107
  Determines the wait time before the next retry attempt based on the configured strategy.
115
108
 
@@ -118,16 +111,13 @@ class RetryWrapper(object):
118
111
  """
119
112
  if attempt_number <= 0:
120
113
  return None
121
- elif self.wait is not None:
114
+ if self.wait is not None:
122
115
  return self.wait
123
- elif self.wait_exponent_init is not None and self.wait_exponent_max is not None:
124
- return min(self.wait_exponent_init * (2 ** (attempt_number - 1)), self.wait_exponent_max)
125
- elif self.wait_random_min is not None and self.wait_random_max is not None:
126
- return random.randint(self.wait_random_min, self.wait_random_max)
127
- else:
128
- return 0
116
+ if self.wait_func is not None:
117
+ return self.wait_func(attempt_number)
118
+ return 0
129
119
 
130
- def __check_timeout(self, start_ts: float) -> tuple[bool, float]:
120
+ def check_timeout(self, start_ts: float) -> tuple[bool, float]:
131
121
  """
132
122
  Checks if the retry operation has exceeded the configured timeout.
133
123
 
@@ -139,7 +129,7 @@ class RetryWrapper(object):
139
129
  return True, current_ts
140
130
  return current_ts < start_ts + self.timeout, current_ts
141
131
 
142
- def __run(self, *args, **kwargs):
132
+ def run(self, *args, **kwargs):
143
133
  """
144
134
  Runs the retry loop, invoking the wrapped callable or ``Retry`` instance until success, retrials exhausted, or
145
135
  timeout reached.
@@ -156,31 +146,30 @@ class RetryWrapper(object):
156
146
  while self.retrials is None or attempt_number <= self.retrials:
157
147
  attempt_number += 1
158
148
 
159
- check_result, check_ts = self.__check_timeout(start_ts)
149
+ check_result, check_ts = self.check_timeout(start_ts)
160
150
  if not check_result:
161
151
  break
162
152
 
163
153
  attempt = Attempt(
164
154
  attempt_number,
165
- self.__next_wait(attempt_number - 1),
166
- self.__next_wait(attempt_number),
155
+ self.next_wait(attempt_number - 1),
156
+ self.next_wait(attempt_number),
167
157
  start_ts,
168
158
  check_ts,
169
159
  last_exception,
170
160
  )
171
161
  try:
172
- if isinstance(self.__wrapped, Retry):
173
- self.__wrapped.on_attempt(attempt)
174
- return self.__wrapped.execute(*args, **kwargs)
162
+ if isinstance(self.target, Retry):
163
+ self.target.on_attempt(attempt)
164
+ return self.target.execute(*args, **kwargs)
175
165
  else:
176
- return self.__wrapped(*args, **kwargs)
166
+ return self.target(*args, **kwargs)
177
167
  except Exception as e:
178
- logger.exception("Function target <%s> failed on attempt <%d>", self.__wrapped, attempt_number)
168
+ logger.exception("Function target '%s' failed on attempt '%d'", self.target, attempt_number)
179
169
  last_exception = e
180
- time.sleep(self.__next_wait(attempt_number))
170
+ time.sleep(self.next_wait(attempt_number))
181
171
 
182
- raise RuntimeError(
183
- "failed to execute function target <%s> after <%d> attempts" % (self.__wrapped, attempt_number))
172
+ raise RuntimeError(f"failed to execute function target '{self.target}' after '{attempt_number}' attempts")
184
173
 
185
174
 
186
175
  def retry(wait: int = None, retrials: int = None, timeout: int = None):
@@ -199,12 +188,12 @@ def retry(wait: int = None, retrials: int = None, timeout: int = None):
199
188
  return wrapper
200
189
 
201
190
 
202
- def retry_exponent(wait_exponent_init: int, wait_exponent_max: int, retrials: int = None, timeout: int = None):
191
+ def retry_exponent(wait_init: int, wait_max: int, retrials: int = None, timeout: int = None):
203
192
  """
204
193
  Decorator to apply exponential backoff retry logic to a function or callable.
205
194
 
206
- :param wait_exponent_init: Initial wait time for exponential backoff.
207
- :param wait_exponent_max: Maximum wait time for exponential backoff.
195
+ :param wait_init: Initial wait time for exponential backoff.
196
+ :param wait_max: Maximum wait time for exponential backoff.
208
197
  :param retrials: Maximum number of retrials (``None`` for unlimited).
209
198
  :param timeout: Maximum total time (in seconds) allowed for all attempts (``None`` for unlimited).
210
199
  :return: A decorated function with retry logic.
@@ -213,8 +202,7 @@ def retry_exponent(wait_exponent_init: int, wait_exponent_max: int, retrials: in
213
202
  def wrapper(target):
214
203
  return RetryWrapper(
215
204
  target,
216
- wait_exponent_init=wait_exponent_init,
217
- wait_exponent_max=wait_exponent_max,
205
+ wait_func=lambda x: min(wait_init * (2 ** (x - 1)), wait_max),
218
206
  retrials=retrials,
219
207
  timeout=timeout,
220
208
  )
@@ -222,12 +210,12 @@ def retry_exponent(wait_exponent_init: int, wait_exponent_max: int, retrials: in
222
210
  return wrapper
223
211
 
224
212
 
225
- def retry_random(wait_random_min: int, wait_random_max: int, retrials: int = None, timeout: int = None):
213
+ def retry_random(wait_min: int, wait_max: int, retrials: int = None, timeout: int = None):
226
214
  """
227
215
  Decorator to apply random wait retry logic to a function or callable.
228
216
 
229
- :param wait_random_min: Minimum wait time for random backoff.
230
- :param wait_random_max: Maximum wait time for random backoff.
217
+ :param wait_min: Minimum wait time for random backoff.
218
+ :param wait_max: Maximum wait time for random backoff.
231
219
  :param retrials: Maximum number of retrials (``None`` for unlimited).
232
220
  :param timeout: Maximum total time (in seconds) allowed for all attempts (``None`` for unlimited).
233
221
  :return: A decorated function with retry logic.
@@ -236,8 +224,7 @@ def retry_random(wait_random_min: int, wait_random_max: int, retrials: int = Non
236
224
  def wrapper(target):
237
225
  return RetryWrapper(
238
226
  target,
239
- wait_random_min=wait_random_min,
240
- wait_random_max=wait_random_max,
227
+ wait_func=lambda x: randomizer().next_int(wait_min, wait_max),
241
228
  retrials=retrials,
242
229
  timeout=timeout,
243
230
  )
@@ -3,7 +3,6 @@ import os
3
3
  import shutil
4
4
  from typing import Protocol
5
5
 
6
- from iker.common.utils import logger
7
6
  from iker.common.utils.iterutils import last, last_or_none, tail_iter
8
7
  from iker.common.utils.strutils import is_empty
9
8
 
@@ -212,7 +211,6 @@ def run(cmd: str) -> bool:
212
211
  :param cmd: Command to run.
213
212
  :return: ``True`` if the command has been successfully run, ``False`` otherwise.
214
213
  """
215
- logger.debug("Running command: %s", cmd)
216
214
  return os.system(cmd) == 0
217
215
 
218
216
 
@@ -224,7 +222,6 @@ def execute(cmd: str, strip: bool = True) -> str:
224
222
  :param strip: If ``True``, the contents will be stripped.
225
223
  :return: The content from standard output.
226
224
  """
227
- logger.debug("Executing command: %s", cmd)
228
225
  if strip:
229
226
  return os.popen(cmd).read().strip()
230
227
  return os.popen(cmd).read()
@@ -1,9 +1,13 @@
1
1
  import enum
2
2
  import itertools
3
- from collections.abc import Sequence
3
+ from collections.abc import Iterator, Sequence
4
+ from typing import Protocol, Self
4
5
 
5
6
  __all__ = [
6
7
  "SpanRelation",
8
+ "SpanEndpoint",
9
+ "Span",
10
+ "span",
7
11
  "span_relation",
8
12
  "spans_union",
9
13
  "spans_intersect",
@@ -40,7 +44,39 @@ class SpanRelation(enum.IntEnum):
40
44
  RightDetach = LeftRightOut | RightRightOut
41
45
 
42
46
 
43
- def span_relation(a: tuple[float, float], b: tuple[float, float]) -> int:
47
+ class SpanEndpoint(Protocol):
48
+ def __lt__(self, other: Self) -> bool: ...
49
+
50
+ def __sub__(self, other: Self) -> Self: ...
51
+
52
+
53
+ class Span[T: SpanEndpoint]:
54
+ def __init__(self, lo: T, hi: T):
55
+ self.lo = lo
56
+ self.hi = hi
57
+
58
+ def __eq__(self, other: object) -> bool:
59
+ if not isinstance(other, Span):
60
+ return NotImplemented
61
+ return self.lo == other.lo and self.hi == other.hi
62
+
63
+ def __lt__(self, other: Self) -> bool:
64
+ if not isinstance(other, Span):
65
+ return NotImplemented
66
+ if self.lo == other.lo:
67
+ return self.hi < other.hi
68
+ return self.lo < other.lo
69
+
70
+ def __iter__(self) -> Iterator[T]:
71
+ yield self.lo
72
+ yield self.hi
73
+
74
+ @property
75
+ def distance(self) -> T:
76
+ return self.hi - self.lo
77
+
78
+
79
+ def span_relation[T: SpanEndpoint](a: Span[T], b: Span[T]) -> int:
44
80
  (a0, a1), (b0, b1) = a, b
45
81
  rel = 0
46
82
  if a0 < b0:
@@ -66,7 +102,10 @@ def span_relation(a: tuple[float, float], b: tuple[float, float]) -> int:
66
102
  return rel
67
103
 
68
104
 
69
- def spans_union[T](a: Sequence[tuple[T, T]], *bs: Sequence[tuple[T, T]]) -> list[tuple[T, T]]:
105
+ span = Span
106
+
107
+
108
+ def spans_union[T: SpanEndpoint](a: Sequence[Span[T]], *bs: Sequence[Span[T]]) -> list[Span[T]]:
70
109
  """
71
110
  Computes the union of the given span lists. The spans in each of the lists must be sorted and must not
72
111
  mutually overlap.
@@ -76,11 +115,11 @@ def spans_union[T](a: Sequence[tuple[T, T]], *bs: Sequence[tuple[T, T]]) -> list
76
115
  :return: The union of the span lists, with spans sorted and merged where possible.
77
116
  """
78
117
 
79
- def union(xs: Sequence[tuple[T, T]], ys: Sequence[tuple[T, T]]) -> list[tuple[T, T]]:
118
+ def union(xs: Sequence[Span[T]], ys: Sequence[Span[T]]) -> list[Span[T]]:
80
119
  if not xs or not ys:
81
120
  return list(itertools.chain(xs, ys))
82
121
 
83
- result: list[tuple[T, T]] = []
122
+ result: list[Span[T]] = []
84
123
 
85
124
  i, j = 0, 0
86
125
  x0_lo, _ = xs[i]
@@ -106,12 +145,12 @@ def spans_union[T](a: Sequence[tuple[T, T]], *bs: Sequence[tuple[T, T]]) -> list
106
145
  curr_lo, curr_hi = curr
107
146
 
108
147
  if hi < curr_lo:
109
- result.append((lo, hi))
148
+ result.append(Span(lo, hi))
110
149
  lo, hi = curr
111
150
  else:
112
151
  hi = max(hi, curr_hi)
113
152
 
114
- result.append((lo, hi))
153
+ result.append(Span(lo, hi))
115
154
 
116
155
  return result
117
156
 
@@ -120,7 +159,7 @@ def spans_union[T](a: Sequence[tuple[T, T]], *bs: Sequence[tuple[T, T]]) -> list
120
159
  return a
121
160
 
122
161
 
123
- def spans_intersect[T](a: Sequence[tuple[T, T]], *bs: Sequence[tuple[T, T]]) -> list[tuple[T, T]]:
162
+ def spans_intersect[T: SpanEndpoint](a: Sequence[Span[T]], *bs: Sequence[Span[T]]) -> list[Span[T]]:
124
163
  """
125
164
  Computes the intersection of the given span lists. The spans in each of the lists must be sorted and must not
126
165
  mutually overlap.
@@ -130,11 +169,11 @@ def spans_intersect[T](a: Sequence[tuple[T, T]], *bs: Sequence[tuple[T, T]]) ->
130
169
  :return: The intersection of the span lists, with spans sorted.
131
170
  """
132
171
 
133
- def intersect(xs: Sequence[tuple[T, T]], ys: Sequence[tuple[T, T]]) -> list[tuple[T, T]]:
172
+ def intersect(xs: Sequence[Span[T]], ys: Sequence[Span[T]]) -> list[Span[T]]:
134
173
  if not xs or not ys:
135
174
  return []
136
175
 
137
- result: list[tuple[T, T]] = []
176
+ result: list[Span[T]] = []
138
177
 
139
178
  i, j = 0, 0
140
179
  while i < len(xs) and j < len(ys):
@@ -144,7 +183,7 @@ def spans_intersect[T](a: Sequence[tuple[T, T]], *bs: Sequence[tuple[T, T]]) ->
144
183
  hi = min(x_hi, y_hi)
145
184
 
146
185
  if not hi < lo:
147
- result.append((lo, hi))
186
+ result.append(Span(lo, hi))
148
187
 
149
188
  if x_hi < y_hi:
150
189
  i += 1
@@ -158,7 +197,7 @@ def spans_intersect[T](a: Sequence[tuple[T, T]], *bs: Sequence[tuple[T, T]]) ->
158
197
  return a
159
198
 
160
199
 
161
- def spans_subtract[T](a: Sequence[tuple[T, T]], *bs: Sequence[tuple[T, T]]) -> list[tuple[T, T]]:
200
+ def spans_subtract[T: SpanEndpoint](a: Sequence[Span[T]], *bs: Sequence[Span[T]]) -> list[Span[T]]:
162
201
  """
163
202
  Computes the subtraction on the first span list by the remaining span lists. The spans in each of the lists must be
164
203
  sorted and must not mutually overlap.
@@ -168,11 +207,11 @@ def spans_subtract[T](a: Sequence[tuple[T, T]], *bs: Sequence[tuple[T, T]]) -> l
168
207
  :return: The subtraction of the first span list by the remaining span lists, with spans sorted.
169
208
  """
170
209
 
171
- def subtract(xs: Sequence[tuple[T, T]], ys: Sequence[tuple[T, T]]) -> list[tuple[T, T]]:
210
+ def subtract(xs: Sequence[Span[T]], ys: Sequence[Span[T]]) -> list[Span[T]]:
172
211
  if not xs or not ys:
173
212
  return list(xs)
174
213
 
175
- result: list[tuple[T, T]] = []
214
+ result: list[Span[T]] = []
176
215
 
177
216
  i, j = 0, 0
178
217
  curr = xs[i]
@@ -184,9 +223,9 @@ def spans_subtract[T](a: Sequence[tuple[T, T]], *bs: Sequence[tuple[T, T]]) -> l
184
223
 
185
224
  if not lo > hi:
186
225
  if curr_lo < lo:
187
- result.append((curr_lo, lo))
226
+ result.append(Span(curr_lo, lo))
188
227
  if hi < curr_hi:
189
- curr = hi, curr_hi
228
+ curr = Span(hi, curr_hi)
190
229
  else:
191
230
  curr = None
192
231
  elif curr_hi < y_lo:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iker-python-common
3
- Version: 1.0.65
3
+ Version: 1.0.67
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.12
6
6
  Classifier: Programming Language :: Python :: 3.13
@@ -8,8 +8,8 @@ setup.py
8
8
  .github/workflows/pr.yml
9
9
  .github/workflows/push.yml
10
10
  resources/unittest/config/config.cfg
11
- resources/unittest/csv/data.csv
12
- resources/unittest/csv/data.tsv
11
+ resources/unittest/csvutils/data.csv
12
+ resources/unittest/csvutils/data.tsv
13
13
  resources/unittest/shutils/dir.baz/file.bar.baz
14
14
  resources/unittest/shutils/dir.baz/file.foo.bar
15
15
  resources/unittest/shutils/dir.baz/file.foo.baz
@@ -24,7 +24,7 @@ src/iker/common/__init__.py
24
24
  src/iker/common/utils/__init__.py
25
25
  src/iker/common/utils/argutils.py
26
26
  src/iker/common/utils/config.py
27
- src/iker/common/utils/csv.py
27
+ src/iker/common/utils/csvutils.py
28
28
  src/iker/common/utils/dbutils.py
29
29
  src/iker/common/utils/dtutils.py
30
30
  src/iker/common/utils/funcutils.py
@@ -45,11 +45,13 @@ src/iker_python_common.egg-info/dependency_links.txt
45
45
  src/iker_python_common.egg-info/not-zip-safe
46
46
  src/iker_python_common.egg-info/requires.txt
47
47
  src/iker_python_common.egg-info/top_level.txt
48
- test/iker_test.py
48
+ test/testenv.py
49
49
  test/iker_tests/__init__.py
50
+ test/iker_tests/common/__init__.py
51
+ test/iker_tests/common/utils/__init__.py
50
52
  test/iker_tests/common/utils/argutils_test.py
51
53
  test/iker_tests/common/utils/config_test.py
52
- test/iker_tests/common/utils/csv_test.py
54
+ test/iker_tests/common/utils/csvutils_test.py
53
55
  test/iker_tests/common/utils/dbutils_test.py
54
56
  test/iker_tests/common/utils/dtutils_test.py
55
57
  test/iker_tests/common/utils/funcutils_test.py
File without changes
@@ -4,7 +4,7 @@ import unittest
4
4
  import ddt
5
5
 
6
6
  from iker.common.utils.config import Config, ConfigVisitor
7
- from iker_tests import resources_directory
7
+ from testenv import resources_directory
8
8
 
9
9
 
10
10
  @ddt.ddt
@@ -1,4 +1,3 @@
1
- import datetime
2
1
  import itertools
3
2
  import math
4
3
  import os.path
@@ -6,12 +5,12 @@ import unittest
6
5
 
7
6
  import ddt
8
7
 
9
- from iker.common.utils import csv
10
- from iker.common.utils.dtutils import dt_format_iso, dt_parse_iso
8
+ from iker.common.utils import csvutils as csv
9
+ from iker.common.utils.dtutils import dt_format_iso, dt_from_ts, dt_parse_iso, dt_to_ts
11
10
  from iker.common.utils.jsonutils import json_compare
12
11
  from iker.common.utils.strutils import make_params_string, parse_params_string
13
12
  from iker.common.utils.strutils import parse_bool
14
- from iker_tests import resources_directory
13
+ from testenv import resources_directory
15
14
 
16
15
 
17
16
  @ddt.ddt
@@ -25,8 +24,8 @@ class CSVTest(unittest.TestCase):
25
24
  csv.column("dummy_int", loader=int, dumper=str, null_str=r"\N"),
26
25
  csv.column("dummy_float", loader=float, dumper=str, null_str=r"\N"),
27
26
  csv.column("dummy_datetime",
28
- loader=lambda x: dt_parse_iso(x).timestamp(),
29
- dumper=lambda x: dt_format_iso(datetime.datetime.fromtimestamp(x, tz=datetime.timezone.utc)),
27
+ loader=lambda x: dt_to_ts(dt_parse_iso(x)),
28
+ dumper=lambda x: dt_format_iso(dt_from_ts(x)),
30
29
  null_str=r"\N"),
31
30
  csv.column("dummy_params",
32
31
  loader=lambda x: parse_params_string(x, delim=";", kv_delim="=", neg_prefix="!"),
@@ -36,22 +35,22 @@ class CSVTest(unittest.TestCase):
36
35
  )
37
36
 
38
37
  lines = [
39
- r"dummy_str,dummy_bool,dummy_int,dummy_float,dummy_datetime,dummy_params",
40
- r"foo,True,1,1.0,2020-01-01T00:00:00,",
41
- r"bar,False,-1,-1.0,2020-01-01T00:00:00,key_1=value_1;key_2;!key_3",
42
- r"baz,\N,100,inf,2020-01-01T00:00:00,\N",
43
- r",\N,-100,-inf,2020-01-01T00:00:00,",
44
- r"\N,\N,0,nan,2020-01-01T00:00:00,\N",
45
- r"\N,\N,\N,\N,\N,\N",
38
+ ["dummy_str", "dummy_bool", "dummy_int", "dummy_float", "dummy_datetime", "dummy_params"],
39
+ ["foo", "True", "1", "1.0", "2020-01-01T00:00:00", ""],
40
+ ["bar", "False", "-1", "-1.0", "2020-01-01T00:00:00", "key_1=value_1;key_2;!key_3"],
41
+ ["baz", r"\N", "100", "inf", "2020-01-01T00:00:00", r"\N"],
42
+ ["", r"\N", "-100", "-inf", "2020-01-01T00:00:00", ""],
43
+ [r"\N", r"\N", "0", "nan", "2020-01-01T00:00:00", r"\N"],
44
+ [r"\N", r"\N", r"\N", r"\N", r"\N", r"\N"],
46
45
  ]
47
46
 
48
47
  lines_no_header = [
49
- r"foo,True,1,1.0,2020-01-01T00:00:00,",
50
- r"bar,False,-1,-1.0,2020-01-01T00:00:00,key_1=value_1;key_2;!key_3",
51
- r"baz,\N,100,inf,2020-01-01T00:00:00,\N",
52
- r",\N,-100,-inf,2020-01-01T00:00:00,",
53
- r"\N,\N,0,nan,2020-01-01T00:00:00,\N",
54
- r"\N,\N,\N,\N,\N,\N",
48
+ ["foo", "True", "1", "1.0", "2020-01-01T00:00:00", ""],
49
+ ["bar", "False", "-1", "-1.0", "2020-01-01T00:00:00", "key_1=value_1;key_2;!key_3"],
50
+ ["baz", r"\N", "100", "inf", "2020-01-01T00:00:00", r"\N"],
51
+ ["", r"\N", "-100", "-inf", "2020-01-01T00:00:00", ""],
52
+ [r"\N", r"\N", "0", "nan", "2020-01-01T00:00:00", r"\N"],
53
+ [r"\N", r"\N", r"\N", r"\N", r"\N", r"\N"],
55
54
  ]
56
55
 
57
56
  time = dt_parse_iso("2020-01-01T00:00:00").timestamp()
@@ -189,12 +188,12 @@ class CSVTest(unittest.TestCase):
189
188
  csv.column("dummy_int", loader=int, dumper=str, null_str=r"\N"),
190
189
  csv.column("dummy_float", loader=float, dumper=str, null_str=r"\N"),
191
190
  csv.column("dummy_datetime",
192
- loader=lambda x: dt_parse_iso(x).timestamp(),
193
- dumper=lambda x: dt_format_iso(datetime.datetime.fromtimestamp(x, tz=datetime.timezone.utc)),
191
+ loader=lambda x: dt_to_ts(dt_parse_iso(x)),
192
+ dumper=lambda x: dt_format_iso(dt_from_ts(x)),
194
193
  null_str=r"\N"),
195
194
  csv.column("dummy_params",
196
- loader=lambda x: parse_params_string(x, delim=";", kv_delim="=", neg_prefix="!"),
197
- dumper=lambda x: make_params_string(x, delim=";", kv_delim="=", neg_prefix="!"),
195
+ loader=lambda x: parse_params_string(x, delim=",", kv_delim="=", neg_prefix="!"),
196
+ dumper=lambda x: make_params_string(x, delim=",", kv_delim="=", neg_prefix="!"),
198
197
  null_str=r"\N"),
199
198
  ],
200
199
  )
@@ -206,15 +205,15 @@ class CSVTest(unittest.TestCase):
206
205
  csv.column("dummy_int", loader=int, dumper=str, null_str=r"<null>"),
207
206
  csv.column("dummy_float", loader=float, dumper=str, null_str=r"<null>"),
208
207
  csv.column("dummy_datetime",
209
- loader=lambda x: dt_parse_iso(x).timestamp(),
210
- dumper=lambda x: dt_format_iso(datetime.datetime.fromtimestamp(x, tz=datetime.timezone.utc)),
208
+ loader=lambda x: dt_to_ts(dt_parse_iso(x)),
209
+ dumper=lambda x: dt_format_iso(dt_from_ts(x)),
211
210
  null_str=r"<null>"),
212
211
  csv.column("dummy_params",
213
- loader=lambda x: parse_params_string(x, delim="#", kv_delim=":", neg_prefix="-"),
214
- dumper=lambda x: make_params_string(x, delim="#", kv_delim=":", neg_prefix="-"),
212
+ loader=lambda x: parse_params_string(x, delim=";", kv_delim=":", neg_prefix="-"),
213
+ dumper=lambda x: make_params_string(x, delim=";", kv_delim=":", neg_prefix="-"),
215
214
  null_str=r"<null>"),
216
215
  ],
217
- col_delim="\t",
216
+ column_delimiter="\t",
218
217
  )
219
218
 
220
219
  time = dt_parse_iso("2020-01-01T00:00:00").timestamp()
@@ -228,10 +227,10 @@ class CSVTest(unittest.TestCase):
228
227
  [None, None, None, None, None, None],
229
228
  ]
230
229
 
231
- for c, t, d in zip(view_csv.load_file(os.path.join(resources_directory, "unittest/csv/data.csv"),
230
+ for c, t, d in zip(view_csv.load_file(os.path.join(resources_directory, "unittest/csvutils/data.csv"),
232
231
  has_header=True,
233
232
  encoding="utf-8"),
234
- view_tsv.load_file(os.path.join(resources_directory, "unittest/csv/data.tsv"),
233
+ view_tsv.load_file(os.path.join(resources_directory, "unittest/csvutils/data.tsv"),
235
234
  has_header=True,
236
235
  encoding="utf-8"),
237
236
  data):
@@ -125,11 +125,10 @@ class RetryTest(unittest.TestCase):
125
125
 
126
126
  @ddt.idata(data_retry_exponent)
127
127
  @ddt.unpack
128
- def test_retry_exponent(self, content, wait_exponent_init, wait_exponent_max, retrials):
128
+ def test_retry_exponent(self, content, wait_init, wait_max, retrials):
129
129
  result = []
130
130
 
131
- @retry.retry_exponent(wait_exponent_init=wait_exponent_init, wait_exponent_max=wait_exponent_max,
132
- retrials=retrials)
131
+ @retry.retry_exponent(wait_init=wait_init, wait_max=wait_max, retrials=retrials)
133
132
  def callee(text):
134
133
  result.append(text)
135
134
  raise ValueError("dummy value error")
@@ -147,7 +146,7 @@ class RetryTest(unittest.TestCase):
147
146
 
148
147
  @ddt.idata(data_retry_exponent__on_retry_instance)
149
148
  @ddt.unpack
150
- def test_retry_exponent__on_retry_instance(self, content, wait_exponent_init, wait_exponent_max, retrials):
149
+ def test_retry_exponent__on_retry_instance(self, content, wait_init, wait_max, retrials):
151
150
  result = []
152
151
 
153
152
  class Callee(retry.Retry):
@@ -164,11 +163,10 @@ class RetryTest(unittest.TestCase):
164
163
  callee = Callee()
165
164
 
166
165
  with self.assertRaises(RuntimeError):
167
- retry.retry_exponent(wait_exponent_init, wait_exponent_max, retrials)(callee)(content)
166
+ retry.retry_exponent(wait_init, wait_max, retrials)(callee)(content)
168
167
 
169
168
  self.assertEqual([content for _ in range(retrials + 1)], result)
170
- self.assertTrue(all(
171
- a.next_wait == min(wait_exponent_init * (2 ** (a.number - 1)), wait_exponent_max) for a in callee.attempts))
169
+ self.assertTrue(all(a.next_wait == min(wait_init * (2 ** (a.number - 1)), wait_max) for a in callee.attempts))
172
170
 
173
171
  data_retry_random = [
174
172
  ("dummy-content", 1, 4, 3),
@@ -178,10 +176,10 @@ class RetryTest(unittest.TestCase):
178
176
 
179
177
  @ddt.idata(data_retry_random)
180
178
  @ddt.unpack
181
- def test_retry_random(self, content, wait_random_min, wait_random_max, retrials):
179
+ def test_retry_random(self, content, wait_min, wait_max, retrials):
182
180
  result = []
183
181
 
184
- @retry.retry_random(wait_random_min=wait_random_min, wait_random_max=wait_random_max, retrials=retrials)
182
+ @retry.retry_random(wait_min=wait_min, wait_max=wait_max, retrials=retrials)
185
183
  def callee(text):
186
184
  result.append(text)
187
185
  raise ValueError("dummy value error")
@@ -199,7 +197,7 @@ class RetryTest(unittest.TestCase):
199
197
 
200
198
  @ddt.idata(data_retry_random__on_retry_instance)
201
199
  @ddt.unpack
202
- def test_retry_random__on_retry_instance(self, content, wait_random_min, wait_random_max, retrials):
200
+ def test_retry_random__on_retry_instance(self, content, wait_min, wait_max, retrials):
203
201
  result = []
204
202
 
205
203
  class Callee(retry.Retry):
@@ -216,7 +214,7 @@ class RetryTest(unittest.TestCase):
216
214
  callee = Callee()
217
215
 
218
216
  with self.assertRaises(RuntimeError):
219
- retry.retry_random(wait_random_min, wait_random_max, retrials)(callee)(content)
217
+ retry.retry_random(wait_min, wait_max, retrials)(callee)(content)
220
218
 
221
219
  self.assertEqual([content for _ in range(retrials + 1)], result)
222
- self.assertTrue(all(wait_random_min <= a.next_wait <= wait_random_max for a in callee.attempts))
220
+ self.assertTrue(all(wait_min <= a.next_wait <= wait_max for a in callee.attempts))
@@ -10,7 +10,7 @@ from iker.common.utils.shutils import expanded_path, path_depth
10
10
  from iker.common.utils.shutils import extension, extensions, stem
11
11
  from iker.common.utils.shutils import glob_match
12
12
  from iker.common.utils.testutils import norm_path
13
- from iker_tests import resources_directory
13
+ from testenv import resources_directory
14
14
 
15
15
  work_dir = pathlib.Path.cwd
16
16
  home_dir = pathlib.Path.home
@@ -4,7 +4,7 @@ import unittest
4
4
  import ddt
5
5
 
6
6
  from iker.common.utils.iterutils import last
7
- from iker.common.utils.span import SpanRelation
7
+ from iker.common.utils.span import Span, SpanRelation
8
8
  from iker.common.utils.span import span_relation, spans_intersect, spans_subtract, spans_union
9
9
 
10
10
 
@@ -42,7 +42,7 @@ class SpanTest(unittest.TestCase):
42
42
  @ddt.idata(data_span_relation)
43
43
  @ddt.unpack
44
44
  def test_span_relation(self, b, a, expect):
45
- self.assertEqual(expect, span_relation(a, b))
45
+ self.assertEqual(expect, span_relation(Span(*a), Span(*b)))
46
46
 
47
47
  data_spans_union = [
48
48
  ([], [], []),
@@ -102,6 +102,10 @@ class SpanTest(unittest.TestCase):
102
102
  @ddt.idata(data_spans_union)
103
103
  @ddt.unpack
104
104
  def test_spans_union(self, xs, ys, expect):
105
+ xs = list(Span(*x) for x in xs)
106
+ ys = list(Span(*y) for y in ys)
107
+ expect = list(Span(*e) for e in expect)
108
+
105
109
  self.assertEqual(expect, spans_union(xs, ys))
106
110
  self.assertEqual(expect, spans_union(ys, xs))
107
111
  self.assertEqual(expect, spans_union(xs, *[[y] for y in ys]))
@@ -165,6 +169,10 @@ class SpanTest(unittest.TestCase):
165
169
  @ddt.idata(data_spans_intersect)
166
170
  @ddt.unpack
167
171
  def test_spans_intersect(self, xs, ys, expect):
172
+ xs = list(Span(*x) for x in xs)
173
+ ys = list(Span(*y) for y in ys)
174
+ expect = list(Span(*e) for e in expect)
175
+
168
176
  self.assertEqual(expect, spans_intersect(xs, ys))
169
177
  self.assertEqual(expect, spans_intersect(ys, xs))
170
178
  self.assertEqual(expect,
@@ -280,6 +288,10 @@ class SpanTest(unittest.TestCase):
280
288
  @ddt.idata(data_spans_subtract)
281
289
  @ddt.unpack
282
290
  def test_spans_subtract(self, xs, ys, expect):
291
+ xs = list(Span(*x) for x in xs)
292
+ ys = list(Span(*y) for y in ys)
293
+ expect = list(Span(*e) for e in expect)
294
+
283
295
  self.assertEqual(expect, spans_subtract(xs, ys))
284
296
  self.assertEqual(expect, spans_subtract(xs, *[[y] for y in ys]))
285
297
  self.assertEqual(expect, spans_subtract(xs, spans_intersect(xs, ys)))
@@ -292,7 +304,7 @@ class SpanTest(unittest.TestCase):
292
304
  vs = [float(random.randrange(lo, hi))]
293
305
  for _ in range(size * 2):
294
306
  vs.append(last(vs) + float(random.randrange(lo, hi)))
295
- return [(vs[2 * i], vs[2 * i + 1]) for i in range(size)]
307
+ return [Span(vs[2 * i], vs[2 * i + 1]) for i in range(size)]
296
308
 
297
309
  xs = make_spans(200, 2, 10)
298
310
  ys = make_spans(200, 2, 10)
@@ -8,7 +8,7 @@ __all__ = [
8
8
  "temporary_directory",
9
9
  ]
10
10
 
11
- module_directory: str = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
11
+ module_directory: str = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
12
12
  source_directory: str = os.path.abspath(os.path.join(module_directory, "src"))
13
13
  test_directory: str = os.path.abspath(os.path.join(module_directory, "test"))
14
14
  resources_directory: str = os.path.abspath(os.path.join(module_directory, "resources"))
@@ -1,13 +0,0 @@
1
- import unittest
2
-
3
- from iker_tests import *
4
-
5
-
6
- class Test(unittest.TestCase):
7
-
8
- def test(self):
9
- self.assertIsNotNone(module_directory)
10
- self.assertIsNotNone(source_directory)
11
- self.assertIsNotNone(test_directory)
12
- self.assertIsNotNone(resources_directory)
13
- self.assertIsNotNone(temporary_directory)