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.
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/PKG-INFO +1 -1
- {iker_python_common-1.0.65/resources/unittest/csv → iker_python_common-1.0.67/resources/unittest/csvutils}/data.csv +1 -1
- {iker_python_common-1.0.65/resources/unittest/csv → iker_python_common-1.0.67/resources/unittest/csvutils}/data.tsv +1 -1
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/config.py +2 -2
- 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
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/retry.py +35 -48
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/shutils.py +0 -3
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/span.py +55 -16
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker_python_common.egg-info/PKG-INFO +1 -1
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker_python_common.egg-info/SOURCES.txt +7 -5
- iker_python_common-1.0.67/test/iker_tests/__init__.py +0 -0
- iker_python_common-1.0.67/test/iker_tests/common/__init__.py +0 -0
- iker_python_common-1.0.67/test/iker_tests/common/utils/__init__.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/config_test.py +1 -1
- 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
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/retry_test.py +10 -12
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/shutils_test.py +1 -1
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/span_test.py +15 -3
- iker_python_common-1.0.65/test/iker_tests/__init__.py → iker_python_common-1.0.67/test/testenv.py +1 -1
- iker_python_common-1.0.65/test/iker_test.py +0 -13
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/.editorconfig +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/.github/workflows/pr.yml +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/.github/workflows/push.yml +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/.gitignore +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/MANIFEST.in +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/README.md +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/VERSION +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/pyproject.toml +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/config/config.cfg +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.baz/file.bar.baz +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.baz/file.foo.bar +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.baz/file.foo.baz +0 -0
- {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
- {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
- {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
- {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
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.foo/file.bar +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.foo/file.baz +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.foo/file.foo +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/setup.cfg +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/setup.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/__init__.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/__init__.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/argutils.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/dbutils.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/dtutils.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/funcutils.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/iterutils.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/jsonutils.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/logger.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/numutils.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/randutils.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/strutils.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/testutils.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker/common/utils/typeutils.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker_python_common.egg-info/dependency_links.txt +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker_python_common.egg-info/not-zip-safe +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker_python_common.egg-info/requires.txt +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker_python_common.egg-info/top_level.txt +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/argutils_test.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/dbutils_test.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/dtutils_test.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/funcutils_test.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/iterutils_test.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/jsonutils_test.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/logger_test.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/numutils_test.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/randutils_test.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/strutils_test.py +0 -0
- {iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/testutils_test.py +0 -0
- {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
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
45
|
-
:param
|
|
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__(
|
|
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.
|
|
51
|
-
self.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
200
|
-
|
|
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
|
-
|
|
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
|
-
|
|
73
|
+
target: Callable[..., Any] | Retry,
|
|
72
74
|
wait: int = None,
|
|
73
|
-
|
|
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
|
|
83
|
+
:param target: The target callable or ``Retry`` instance to execute.
|
|
85
84
|
:param wait: Fixed wait time (in seconds) between retrials.
|
|
86
|
-
:param
|
|
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.
|
|
89
|
+
self.target = target
|
|
94
90
|
self.wait = wait
|
|
95
|
-
self.
|
|
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.
|
|
103
|
+
return self.run(*args, **kwargs)
|
|
111
104
|
|
|
112
|
-
def
|
|
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
|
-
|
|
114
|
+
if self.wait is not None:
|
|
122
115
|
return self.wait
|
|
123
|
-
|
|
124
|
-
return
|
|
125
|
-
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
166
|
-
self.
|
|
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.
|
|
173
|
-
self.
|
|
174
|
-
return self.
|
|
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.
|
|
166
|
+
return self.target(*args, **kwargs)
|
|
177
167
|
except Exception as e:
|
|
178
|
-
logger.exception("Function target
|
|
168
|
+
logger.exception("Function target '%s' failed on attempt '%d'", self.target, attempt_number)
|
|
179
169
|
last_exception = e
|
|
180
|
-
time.sleep(self.
|
|
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(
|
|
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
|
|
207
|
-
:param
|
|
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
|
-
|
|
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(
|
|
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
|
|
230
|
-
:param
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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:
|
{iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker_python_common.egg-info/SOURCES.txt
RENAMED
|
@@ -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/
|
|
12
|
-
resources/unittest/
|
|
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/
|
|
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/
|
|
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/
|
|
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
|
|
File without changes
|
|
File without changes
|
|
@@ -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
|
|
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)
|
|
29
|
-
dumper=lambda x: dt_format_iso(
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
r"
|
|
43
|
-
r"
|
|
44
|
-
r"\N
|
|
45
|
-
r"\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
|
-
|
|
50
|
-
|
|
51
|
-
r"
|
|
52
|
-
r"
|
|
53
|
-
r"\N
|
|
54
|
-
r"\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)
|
|
193
|
-
dumper=lambda x: dt_format_iso(
|
|
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="
|
|
197
|
-
dumper=lambda x: make_params_string(x, delim="
|
|
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)
|
|
210
|
-
dumper=lambda x: dt_format_iso(
|
|
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="
|
|
214
|
-
dumper=lambda x: make_params_string(x, delim="
|
|
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
|
-
|
|
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/
|
|
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/
|
|
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):
|
{iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/retry_test.py
RENAMED
|
@@ -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,
|
|
128
|
+
def test_retry_exponent(self, content, wait_init, wait_max, retrials):
|
|
129
129
|
result = []
|
|
130
130
|
|
|
131
|
-
@retry.retry_exponent(
|
|
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,
|
|
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(
|
|
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,
|
|
179
|
+
def test_retry_random(self, content, wait_min, wait_max, retrials):
|
|
182
180
|
result = []
|
|
183
181
|
|
|
184
|
-
@retry.retry_random(
|
|
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,
|
|
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(
|
|
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(
|
|
220
|
+
self.assertTrue(all(wait_min <= a.next_wait <= wait_max for a in callee.attempts))
|
{iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/shutils_test.py
RENAMED
|
@@ -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
|
|
13
|
+
from testenv import resources_directory
|
|
14
14
|
|
|
15
15
|
work_dir = pathlib.Path.cwd
|
|
16
16
|
home_dir = pathlib.Path.home
|
{iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/span_test.py
RENAMED
|
@@ -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)
|
iker_python_common-1.0.65/test/iker_tests/__init__.py → iker_python_common-1.0.67/test/testenv.py
RENAMED
|
@@ -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(
|
|
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)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/config/config.cfg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.foo/file.bar
RENAMED
|
File without changes
|
{iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.foo/file.baz
RENAMED
|
File without changes
|
{iker_python_common-1.0.65 → iker_python_common-1.0.67}/resources/unittest/shutils/dir.foo/file.foo
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker_python_common.egg-info/not-zip-safe
RENAMED
|
File without changes
|
{iker_python_common-1.0.65 → iker_python_common-1.0.67}/src/iker_python_common.egg-info/requires.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/dbutils_test.py
RENAMED
|
File without changes
|
{iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/dtutils_test.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iker_python_common-1.0.65 → iker_python_common-1.0.67}/test/iker_tests/common/utils/logger_test.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|