dycw-utilities 0.174.2__tar.gz → 0.174.4__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.
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/PKG-INFO +1 -1
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/pyproject.toml +2 -2
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/__init__.py +1 -1
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/docker.py +29 -4
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/subprocess.py +80 -82
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/types.py +5 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/README.md +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/aeventkit.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/asyncio.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/atools.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/cachetools.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/click.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/fastapi.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/functions.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/git.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/grp.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/gzip.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/http.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/hypothesis.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/importlib.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/inflect.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/jinja2.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/json.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/libcst.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/lightweight_charts.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/math.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/orjson.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/os.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/parse.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/polars_ols.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/postgres.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/pottery.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/psutil.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/pwd.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/pydantic_settings.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/pydantic_settings_sops.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/pytest_plugins/__init__.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/pytest_plugins/pytest_randomly.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/pytest_plugins/pytest_regressions.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/random.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/re.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/redis.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/sentinel.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/shutil.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/slack_sdk.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/sqlalchemy.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/statsmodels.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/string.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/testbook.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/text.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/traceback.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/tzdata.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/version.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/whenever.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/zipfile.py +0 -0
- {dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/zoneinfo.py +0 -0
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
name = "dycw-utilities"
|
|
102
102
|
readme = "README.md"
|
|
103
103
|
requires-python = ">= 3.12"
|
|
104
|
-
version = "0.174.
|
|
104
|
+
version = "0.174.4"
|
|
105
105
|
|
|
106
106
|
[project.entry-points.pytest11]
|
|
107
107
|
pytest-randomly = "utilities.pytest_plugins.pytest_randomly"
|
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
# bump-my-version
|
|
136
136
|
[tool.bumpversion]
|
|
137
137
|
allow_dirty = true
|
|
138
|
-
current_version = "0.174.
|
|
138
|
+
current_version = "0.174.4"
|
|
139
139
|
|
|
140
140
|
[[tool.bumpversion.files]]
|
|
141
141
|
filename = "src/utilities/__init__.py"
|
|
@@ -5,6 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
from typing import TYPE_CHECKING, Literal, overload
|
|
6
6
|
|
|
7
7
|
from utilities.errors import ImpossibleCaseError
|
|
8
|
+
from utilities.logging import to_logger
|
|
8
9
|
from utilities.subprocess import (
|
|
9
10
|
MKTEMP_DIR_CMD,
|
|
10
11
|
maybe_sudo_cmd,
|
|
@@ -17,7 +18,7 @@ from utilities.subprocess import (
|
|
|
17
18
|
if TYPE_CHECKING:
|
|
18
19
|
from collections.abc import Iterator
|
|
19
20
|
|
|
20
|
-
from utilities.types import LoggerLike, PathLike, StrStrMapping
|
|
21
|
+
from utilities.types import LoggerLike, PathLike, Retry, StrStrMapping
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
@overload
|
|
@@ -106,6 +107,7 @@ def docker_exec(
|
|
|
106
107
|
return_: Literal[True],
|
|
107
108
|
return_stdout: bool = False,
|
|
108
109
|
return_stderr: bool = False,
|
|
110
|
+
retry: Retry | None = None,
|
|
109
111
|
logger: LoggerLike | None = None,
|
|
110
112
|
**env_kwargs: str,
|
|
111
113
|
) -> str: ...
|
|
@@ -125,6 +127,7 @@ def docker_exec(
|
|
|
125
127
|
return_: bool = False,
|
|
126
128
|
return_stdout: Literal[True],
|
|
127
129
|
return_stderr: bool = False,
|
|
130
|
+
retry: Retry | None = None,
|
|
128
131
|
logger: LoggerLike | None = None,
|
|
129
132
|
**env_kwargs: str,
|
|
130
133
|
) -> str: ...
|
|
@@ -144,6 +147,7 @@ def docker_exec(
|
|
|
144
147
|
return_: bool = False,
|
|
145
148
|
return_stdout: bool = False,
|
|
146
149
|
return_stderr: Literal[True],
|
|
150
|
+
retry: Retry | None = None,
|
|
147
151
|
logger: LoggerLike | None = None,
|
|
148
152
|
**env_kwargs: str,
|
|
149
153
|
) -> str: ...
|
|
@@ -163,6 +167,7 @@ def docker_exec(
|
|
|
163
167
|
return_: Literal[False] = False,
|
|
164
168
|
return_stdout: Literal[False] = False,
|
|
165
169
|
return_stderr: Literal[False] = False,
|
|
170
|
+
retry: Retry | None = None,
|
|
166
171
|
logger: LoggerLike | None = None,
|
|
167
172
|
**env_kwargs: str,
|
|
168
173
|
) -> None: ...
|
|
@@ -182,6 +187,7 @@ def docker_exec(
|
|
|
182
187
|
return_: bool = False,
|
|
183
188
|
return_stdout: bool = False,
|
|
184
189
|
return_stderr: bool = False,
|
|
190
|
+
retry: Retry | None = None,
|
|
185
191
|
logger: LoggerLike | None = None,
|
|
186
192
|
**env_kwargs: str,
|
|
187
193
|
) -> str | None: ...
|
|
@@ -200,6 +206,7 @@ def docker_exec(
|
|
|
200
206
|
return_: bool = False,
|
|
201
207
|
return_stdout: bool = False,
|
|
202
208
|
return_stderr: bool = False,
|
|
209
|
+
retry: Retry | None = None,
|
|
203
210
|
logger: LoggerLike | None = None,
|
|
204
211
|
**env_kwargs: str,
|
|
205
212
|
) -> str | None:
|
|
@@ -222,6 +229,7 @@ def docker_exec(
|
|
|
222
229
|
return_=return_,
|
|
223
230
|
return_stdout=return_stdout,
|
|
224
231
|
return_stderr=return_stderr,
|
|
232
|
+
retry=retry,
|
|
225
233
|
logger=logger,
|
|
226
234
|
)
|
|
227
235
|
|
|
@@ -253,15 +261,32 @@ def docker_exec_cmd(
|
|
|
253
261
|
|
|
254
262
|
@contextmanager
|
|
255
263
|
def yield_docker_temp_dir(
|
|
256
|
-
container: str,
|
|
264
|
+
container: str,
|
|
265
|
+
/,
|
|
266
|
+
*,
|
|
267
|
+
user: str | None = None,
|
|
268
|
+
retry: Retry | None = None,
|
|
269
|
+
logger: LoggerLike | None = None,
|
|
270
|
+
keep: bool = False,
|
|
257
271
|
) -> Iterator[Path]:
|
|
258
272
|
path = Path( # skipif-ci
|
|
259
|
-
docker_exec(
|
|
273
|
+
docker_exec(
|
|
274
|
+
container,
|
|
275
|
+
*MKTEMP_DIR_CMD,
|
|
276
|
+
user=user,
|
|
277
|
+
return_=True,
|
|
278
|
+
retry=retry,
|
|
279
|
+
logger=logger,
|
|
280
|
+
)
|
|
260
281
|
)
|
|
261
282
|
try: # skipif-ci
|
|
262
283
|
yield path
|
|
263
284
|
finally: # skipif-ci
|
|
264
|
-
|
|
285
|
+
if keep:
|
|
286
|
+
if logger is not None:
|
|
287
|
+
to_logger(logger).info("Keeping temporary directory '%s'...", path)
|
|
288
|
+
else:
|
|
289
|
+
docker_exec(container, *rm_cmd(path), user=user, retry=retry, logger=logger)
|
|
265
290
|
|
|
266
291
|
|
|
267
292
|
__all__ = ["docker_cp_cmd", "docker_exec", "docker_exec_cmd", "yield_docker_temp_dir"]
|
|
@@ -13,13 +13,12 @@ from typing import IO, TYPE_CHECKING, Literal, assert_never, overload
|
|
|
13
13
|
from utilities.errors import ImpossibleCaseError
|
|
14
14
|
from utilities.logging import to_logger
|
|
15
15
|
from utilities.text import strip_and_dedent
|
|
16
|
+
from utilities.whenever import to_seconds
|
|
16
17
|
|
|
17
18
|
if TYPE_CHECKING:
|
|
18
19
|
from collections.abc import Iterator
|
|
19
20
|
|
|
20
|
-
from
|
|
21
|
-
|
|
22
|
-
from utilities.types import LoggerLike, PathLike, StrMapping, StrStrMapping
|
|
21
|
+
from utilities.types import LoggerLike, PathLike, Retry, StrMapping, StrStrMapping
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
_HOST_KEY_ALGORITHMS = ["ssh-ed25519"]
|
|
@@ -82,6 +81,7 @@ def run(
|
|
|
82
81
|
return_: Literal[True],
|
|
83
82
|
return_stdout: bool = False,
|
|
84
83
|
return_stderr: bool = False,
|
|
84
|
+
retry: Retry | None = None,
|
|
85
85
|
logger: LoggerLike | None = None,
|
|
86
86
|
) -> str: ...
|
|
87
87
|
@overload
|
|
@@ -101,6 +101,7 @@ def run(
|
|
|
101
101
|
return_: bool = False,
|
|
102
102
|
return_stdout: Literal[True],
|
|
103
103
|
return_stderr: bool = False,
|
|
104
|
+
retry: Retry | None = None,
|
|
104
105
|
logger: LoggerLike | None = None,
|
|
105
106
|
) -> str: ...
|
|
106
107
|
@overload
|
|
@@ -120,6 +121,7 @@ def run(
|
|
|
120
121
|
return_: bool = False,
|
|
121
122
|
return_stdout: bool = False,
|
|
122
123
|
return_stderr: Literal[True],
|
|
124
|
+
retry: Retry | None = None,
|
|
123
125
|
logger: LoggerLike | None = None,
|
|
124
126
|
) -> str: ...
|
|
125
127
|
@overload
|
|
@@ -139,6 +141,7 @@ def run(
|
|
|
139
141
|
return_: Literal[False] = False,
|
|
140
142
|
return_stdout: Literal[False] = False,
|
|
141
143
|
return_stderr: Literal[False] = False,
|
|
144
|
+
retry: Retry | None = None,
|
|
142
145
|
logger: LoggerLike | None = None,
|
|
143
146
|
) -> None: ...
|
|
144
147
|
@overload
|
|
@@ -158,6 +161,7 @@ def run(
|
|
|
158
161
|
return_: bool = False,
|
|
159
162
|
return_stdout: bool = False,
|
|
160
163
|
return_stderr: bool = False,
|
|
164
|
+
retry: Retry | None = None,
|
|
161
165
|
logger: LoggerLike | None = None,
|
|
162
166
|
) -> str | None: ...
|
|
163
167
|
def run(
|
|
@@ -176,10 +180,11 @@ def run(
|
|
|
176
180
|
return_: bool = False,
|
|
177
181
|
return_stdout: bool = False,
|
|
178
182
|
return_stderr: bool = False,
|
|
183
|
+
retry: Retry | None = None,
|
|
179
184
|
logger: LoggerLike | None = None,
|
|
180
185
|
) -> str | None:
|
|
181
186
|
args: list[str] = []
|
|
182
|
-
if user is not None:
|
|
187
|
+
if user is not None: # pragma: no cover
|
|
183
188
|
args.extend(["su", "-", str(user)])
|
|
184
189
|
args.extend([cmd, *cmds_or_args])
|
|
185
190
|
buffer = StringIO()
|
|
@@ -211,8 +216,8 @@ def run(
|
|
|
211
216
|
if proc.stderr is None: # pragma: no cover
|
|
212
217
|
raise ImpossibleCaseError(case=[f"{proc.stderr=}"])
|
|
213
218
|
with (
|
|
214
|
-
_yield_write(proc.stdout,
|
|
215
|
-
_yield_write(proc.stderr,
|
|
219
|
+
_yield_write(proc.stdout, *stdout_outputs),
|
|
220
|
+
_yield_write(proc.stderr, *stderr_outputs),
|
|
216
221
|
):
|
|
217
222
|
if input is not None:
|
|
218
223
|
_ = proc.stdin.write(input)
|
|
@@ -232,6 +237,10 @@ def run(
|
|
|
232
237
|
case 0, False, False:
|
|
233
238
|
return None
|
|
234
239
|
case _, _, _:
|
|
240
|
+
if retry is None:
|
|
241
|
+
attempts = delta = None
|
|
242
|
+
else:
|
|
243
|
+
attempts, delta = retry
|
|
235
244
|
_ = stdout.seek(0)
|
|
236
245
|
stdout_text = stdout.read()
|
|
237
246
|
_ = stderr.seek(0)
|
|
@@ -246,24 +255,52 @@ def run(
|
|
|
246
255
|
- shell = {shell}
|
|
247
256
|
- cwd = {cwd}
|
|
248
257
|
- env = {env}
|
|
249
|
-
- input = {input}
|
|
250
258
|
|
|
259
|
+
-- stdin ----------------------------------------------------------------------
|
|
260
|
+
{"" if input is None else input}-------------------------------------------------------------------------------
|
|
251
261
|
-- stdout ---------------------------------------------------------------------
|
|
252
262
|
{stdout_text}-------------------------------------------------------------------------------
|
|
253
263
|
-- stderr ---------------------------------------------------------------------
|
|
254
264
|
{stderr_text}-------------------------------------------------------------------------------
|
|
255
265
|
""")
|
|
266
|
+
if (attempts is not None) and (attempts >= 1):
|
|
267
|
+
if delta is None:
|
|
268
|
+
msg = f"{msg}\n\nRetrying {attempts} more time(s)..."
|
|
269
|
+
else:
|
|
270
|
+
msg = f"{msg}\n\nRetrying {attempts} more time(s) after {delta}..."
|
|
256
271
|
to_logger(logger).error(msg)
|
|
257
|
-
|
|
272
|
+
error = CalledProcessError(
|
|
258
273
|
return_code, args, output=stdout_text, stderr=stderr_text
|
|
259
274
|
)
|
|
275
|
+
if (attempts is None) or (attempts <= 0):
|
|
276
|
+
raise error
|
|
277
|
+
if delta is not None:
|
|
278
|
+
sleep(to_seconds(delta))
|
|
279
|
+
return run(
|
|
280
|
+
cmd,
|
|
281
|
+
*cmds_or_args,
|
|
282
|
+
user=user,
|
|
283
|
+
executable=executable,
|
|
284
|
+
shell=shell,
|
|
285
|
+
cwd=cwd,
|
|
286
|
+
env=env,
|
|
287
|
+
input=input,
|
|
288
|
+
print=print,
|
|
289
|
+
print_stdout=print_stdout,
|
|
290
|
+
print_stderr=print_stderr,
|
|
291
|
+
return_=return_,
|
|
292
|
+
return_stdout=return_stdout,
|
|
293
|
+
return_stderr=return_stderr,
|
|
294
|
+
retry=(attempts - 1, delta),
|
|
295
|
+
logger=logger,
|
|
296
|
+
)
|
|
260
297
|
case never:
|
|
261
298
|
assert_never(never)
|
|
262
299
|
|
|
263
300
|
|
|
264
301
|
@contextmanager
|
|
265
|
-
def _yield_write(input_: IO[str],
|
|
266
|
-
thread = Thread(target=_run_target, args=(input_,
|
|
302
|
+
def _yield_write(input_: IO[str], /, *outputs: IO[str]) -> Iterator[None]:
|
|
303
|
+
thread = Thread(target=_run_target, args=(input_, *outputs), daemon=True)
|
|
267
304
|
thread.start()
|
|
268
305
|
try:
|
|
269
306
|
yield
|
|
@@ -271,14 +308,10 @@ def _yield_write(input_: IO[str], desc: str, /, *outputs: IO[str]) -> Iterator[N
|
|
|
271
308
|
thread.join()
|
|
272
309
|
|
|
273
310
|
|
|
274
|
-
def _run_target(input_: IO[str],
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
_write_to_streams(text, *outputs)
|
|
279
|
-
except ValueError:
|
|
280
|
-
_ = sys.stderr.write(f"Failed to write to {desc!r}...")
|
|
281
|
-
raise
|
|
311
|
+
def _run_target(input_: IO[str], /, *outputs: IO[str]) -> None:
|
|
312
|
+
with input_:
|
|
313
|
+
for text in iter(input_.readline, ""):
|
|
314
|
+
_write_to_streams(text, *outputs)
|
|
282
315
|
|
|
283
316
|
|
|
284
317
|
def _write_to_streams(text: str, /, *outputs: IO[str]) -> None:
|
|
@@ -302,8 +335,8 @@ def ssh(
|
|
|
302
335
|
return_: Literal[True],
|
|
303
336
|
return_stdout: bool = False,
|
|
304
337
|
return_stderr: bool = False,
|
|
338
|
+
retry: Retry | None = None,
|
|
305
339
|
logger: LoggerLike | None = None,
|
|
306
|
-
retry: tuple[int, TimeDelta] | None = None,
|
|
307
340
|
) -> str: ...
|
|
308
341
|
@overload
|
|
309
342
|
def ssh(
|
|
@@ -321,8 +354,8 @@ def ssh(
|
|
|
321
354
|
return_: bool = False,
|
|
322
355
|
return_stdout: Literal[True],
|
|
323
356
|
return_stderr: bool = False,
|
|
357
|
+
retry: Retry | None = None,
|
|
324
358
|
logger: LoggerLike | None = None,
|
|
325
|
-
retry: tuple[int, TimeDelta] | None = None,
|
|
326
359
|
) -> str: ...
|
|
327
360
|
@overload
|
|
328
361
|
def ssh(
|
|
@@ -340,8 +373,8 @@ def ssh(
|
|
|
340
373
|
return_: bool = False,
|
|
341
374
|
return_stdout: bool = False,
|
|
342
375
|
return_stderr: Literal[True],
|
|
376
|
+
retry: Retry | None = None,
|
|
343
377
|
logger: LoggerLike | None = None,
|
|
344
|
-
retry: tuple[int, TimeDelta] | None = None,
|
|
345
378
|
) -> str: ...
|
|
346
379
|
@overload
|
|
347
380
|
def ssh(
|
|
@@ -359,8 +392,8 @@ def ssh(
|
|
|
359
392
|
return_: Literal[False] = False,
|
|
360
393
|
return_stdout: Literal[False] = False,
|
|
361
394
|
return_stderr: Literal[False] = False,
|
|
395
|
+
retry: Retry | None = None,
|
|
362
396
|
logger: LoggerLike | None = None,
|
|
363
|
-
retry: tuple[int, TimeDelta] | None = None,
|
|
364
397
|
) -> None: ...
|
|
365
398
|
@overload
|
|
366
399
|
def ssh(
|
|
@@ -378,8 +411,8 @@ def ssh(
|
|
|
378
411
|
return_: bool = False,
|
|
379
412
|
return_stdout: bool = False,
|
|
380
413
|
return_stderr: bool = False,
|
|
414
|
+
retry: Retry | None = None,
|
|
381
415
|
logger: LoggerLike | None = None,
|
|
382
|
-
retry: tuple[int, TimeDelta] | None = None,
|
|
383
416
|
) -> str | None: ...
|
|
384
417
|
def ssh(
|
|
385
418
|
user: str,
|
|
@@ -396,8 +429,8 @@ def ssh(
|
|
|
396
429
|
return_: bool = False,
|
|
397
430
|
return_stdout: bool = False,
|
|
398
431
|
return_stderr: bool = False,
|
|
432
|
+
retry: Retry | None = None,
|
|
399
433
|
logger: LoggerLike | None = None,
|
|
400
|
-
retry: tuple[int, TimeDelta] | None = None,
|
|
401
434
|
) -> str | None:
|
|
402
435
|
cmd_and_args = ssh_cmd( # skipif-ci
|
|
403
436
|
user,
|
|
@@ -407,61 +440,18 @@ def ssh(
|
|
|
407
440
|
host_key_algorithms=host_key_algorithms,
|
|
408
441
|
strict_host_key_checking=strict_host_key_checking,
|
|
409
442
|
)
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
except CalledProcessError as error: # skipif-ci
|
|
423
|
-
if retry is None:
|
|
424
|
-
raise
|
|
425
|
-
attempts, delta = retry
|
|
426
|
-
if attempts <= 0:
|
|
427
|
-
raise
|
|
428
|
-
if logger is not None:
|
|
429
|
-
msg = strip_and_dedent(f"""
|
|
430
|
-
'ssh' failed with:
|
|
431
|
-
- user = {user}
|
|
432
|
-
- hostname = {hostname}
|
|
433
|
-
- cmd_and_cmds_or_args = {cmd_and_cmds_or_args}
|
|
434
|
-
- batch_mode = {batch_mode}
|
|
435
|
-
- host_key_algorithms = {host_key_algorithms}
|
|
436
|
-
- strict_host_key_checking = {strict_host_key_checking}
|
|
437
|
-
- input = {input}
|
|
438
|
-
|
|
439
|
-
-- stdout ---------------------------------------------------------------------
|
|
440
|
-
{error.stdout}-------------------------------------------------------------------------------
|
|
441
|
-
-- stderr ---------------------------------------------------------------------
|
|
442
|
-
{error.stderr}-------------------------------------------------------------------------------
|
|
443
|
-
|
|
444
|
-
Retrying {attempts} more time(s) after {delta}...
|
|
445
|
-
""")
|
|
446
|
-
to_logger(logger).error(msg)
|
|
447
|
-
sleep(delta.in_seconds())
|
|
448
|
-
return ssh(
|
|
449
|
-
user,
|
|
450
|
-
hostname,
|
|
451
|
-
*cmd_and_cmds_or_args,
|
|
452
|
-
batch_mode=batch_mode,
|
|
453
|
-
host_key_algorithms=host_key_algorithms,
|
|
454
|
-
strict_host_key_checking=strict_host_key_checking,
|
|
455
|
-
input=input,
|
|
456
|
-
print=print,
|
|
457
|
-
print_stdout=print_stdout,
|
|
458
|
-
print_stderr=print_stderr,
|
|
459
|
-
return_=return_,
|
|
460
|
-
return_stdout=return_stdout,
|
|
461
|
-
return_stderr=return_stderr,
|
|
462
|
-
logger=logger,
|
|
463
|
-
retry=(attempts - 1, delta),
|
|
464
|
-
)
|
|
443
|
+
return run( # skipif-ci
|
|
444
|
+
*cmd_and_args,
|
|
445
|
+
input=input,
|
|
446
|
+
print=print,
|
|
447
|
+
print_stdout=print_stdout,
|
|
448
|
+
print_stderr=print_stderr,
|
|
449
|
+
return_=return_,
|
|
450
|
+
return_stdout=return_stdout,
|
|
451
|
+
return_stderr=return_stderr,
|
|
452
|
+
retry=retry,
|
|
453
|
+
logger=logger,
|
|
454
|
+
)
|
|
465
455
|
|
|
466
456
|
|
|
467
457
|
def ssh_cmd(
|
|
@@ -492,9 +482,17 @@ def touch_cmd(path: PathLike, /) -> list[str]:
|
|
|
492
482
|
|
|
493
483
|
@contextmanager
|
|
494
484
|
def yield_ssh_temp_dir(
|
|
495
|
-
user: str,
|
|
485
|
+
user: str,
|
|
486
|
+
hostname: str,
|
|
487
|
+
/,
|
|
488
|
+
*,
|
|
489
|
+
retry: Retry | None = None,
|
|
490
|
+
logger: LoggerLike | None = None,
|
|
491
|
+
keep: bool = False,
|
|
496
492
|
) -> Iterator[Path]:
|
|
497
|
-
path = Path(
|
|
493
|
+
path = Path(
|
|
494
|
+
ssh(user, hostname, *MKTEMP_DIR_CMD, return_=True, retry=retry, logger=logger)
|
|
495
|
+
)
|
|
498
496
|
try:
|
|
499
497
|
yield path
|
|
500
498
|
finally:
|
|
@@ -502,7 +500,7 @@ def yield_ssh_temp_dir(
|
|
|
502
500
|
if logger is not None:
|
|
503
501
|
to_logger(logger).info("Keeping temporary directory '%s'...", path)
|
|
504
502
|
else:
|
|
505
|
-
ssh(user, hostname, *rm_cmd(path))
|
|
503
|
+
ssh(user, hostname, *rm_cmd(path), retry=retry, logger=logger)
|
|
506
504
|
|
|
507
505
|
|
|
508
506
|
__all__ = [
|
|
@@ -231,6 +231,10 @@ type Seed = int | float | str | bytes | bytearray | Random
|
|
|
231
231
|
type PatternLike = MaybeStr[Pattern[str]]
|
|
232
232
|
|
|
233
233
|
|
|
234
|
+
# retry
|
|
235
|
+
type Retry = tuple[int, Delta | None]
|
|
236
|
+
|
|
237
|
+
|
|
234
238
|
# text
|
|
235
239
|
type MaybeCallableStr = MaybeCallable[str]
|
|
236
240
|
|
|
@@ -332,6 +336,7 @@ __all__ = [
|
|
|
332
336
|
"PathLike",
|
|
333
337
|
"PatternLike",
|
|
334
338
|
"PlainDateTimeLike",
|
|
339
|
+
"Retry",
|
|
335
340
|
"Seed",
|
|
336
341
|
"SequenceStr",
|
|
337
342
|
"SerializeObjectExtra",
|
|
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
|
|
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
|
|
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
|
|
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
|
{dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/pytest_plugins/pytest_randomly.py
RENAMED
|
File without changes
|
{dycw_utilities-0.174.2 → dycw_utilities-0.174.4}/src/utilities/pytest_plugins/pytest_regressions.py
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
|
|
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
|