dycw-utilities 0.174.3__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.
Files changed (102) hide show
  1. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/PKG-INFO +1 -1
  2. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/pyproject.toml +2 -2
  3. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/__init__.py +1 -1
  4. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/docker.py +29 -4
  5. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/subprocess.py +24 -18
  6. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/types.py +5 -0
  7. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/README.md +0 -0
  8. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/aeventkit.py +0 -0
  9. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/altair.py +0 -0
  10. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/asyncio.py +0 -0
  11. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/atomicwrites.py +0 -0
  12. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/atools.py +0 -0
  13. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/cachetools.py +0 -0
  14. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/click.py +0 -0
  15. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/concurrent.py +0 -0
  16. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/contextlib.py +0 -0
  17. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/contextvars.py +0 -0
  18. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/cryptography.py +0 -0
  19. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/cvxpy.py +0 -0
  20. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/dataclasses.py +0 -0
  21. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/enum.py +0 -0
  22. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/errors.py +0 -0
  23. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/fastapi.py +0 -0
  24. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/fpdf2.py +0 -0
  25. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/functions.py +0 -0
  26. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/functools.py +0 -0
  27. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/getpass.py +0 -0
  28. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/git.py +0 -0
  29. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/grp.py +0 -0
  30. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/gzip.py +0 -0
  31. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/hashlib.py +0 -0
  32. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/http.py +0 -0
  33. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/hypothesis.py +0 -0
  34. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/importlib.py +0 -0
  35. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/inflect.py +0 -0
  36. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/ipython.py +0 -0
  37. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/iterables.py +0 -0
  38. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/jinja2.py +0 -0
  39. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/json.py +0 -0
  40. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/jupyter.py +0 -0
  41. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/libcst.py +0 -0
  42. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/lightweight_charts.py +0 -0
  43. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/logging.py +0 -0
  44. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/math.py +0 -0
  45. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/memory_profiler.py +0 -0
  46. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/modules.py +0 -0
  47. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/more_itertools.py +0 -0
  48. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/numpy.py +0 -0
  49. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/operator.py +0 -0
  50. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/optuna.py +0 -0
  51. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/orjson.py +0 -0
  52. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/os.py +0 -0
  53. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/parse.py +0 -0
  54. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/pathlib.py +0 -0
  55. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/pickle.py +0 -0
  56. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/platform.py +0 -0
  57. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/polars.py +0 -0
  58. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/polars_ols.py +0 -0
  59. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/postgres.py +0 -0
  60. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/pottery.py +0 -0
  61. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/pqdm.py +0 -0
  62. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/psutil.py +0 -0
  63. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/pwd.py +0 -0
  64. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/py.typed +0 -0
  65. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/pydantic.py +0 -0
  66. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/pydantic_settings.py +0 -0
  67. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/pydantic_settings_sops.py +0 -0
  68. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/pyinstrument.py +0 -0
  69. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/pytest.py +0 -0
  70. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/pytest_plugins/__init__.py +0 -0
  71. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/pytest_plugins/pytest_randomly.py +0 -0
  72. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/pytest_plugins/pytest_regressions.py +0 -0
  73. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/pytest_regressions.py +0 -0
  74. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/random.py +0 -0
  75. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/re.py +0 -0
  76. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/redis.py +0 -0
  77. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/reprlib.py +0 -0
  78. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/scipy.py +0 -0
  79. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/sentinel.py +0 -0
  80. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/shelve.py +0 -0
  81. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/shutil.py +0 -0
  82. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/slack_sdk.py +0 -0
  83. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/socket.py +0 -0
  84. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/sqlalchemy.py +0 -0
  85. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/sqlalchemy_polars.py +0 -0
  86. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/statsmodels.py +0 -0
  87. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/string.py +0 -0
  88. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/tempfile.py +0 -0
  89. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/testbook.py +0 -0
  90. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/text.py +0 -0
  91. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/threading.py +0 -0
  92. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/timer.py +0 -0
  93. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/traceback.py +0 -0
  94. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/typing.py +0 -0
  95. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/tzdata.py +0 -0
  96. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/tzlocal.py +0 -0
  97. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/uuid.py +0 -0
  98. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/version.py +0 -0
  99. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/warnings.py +0 -0
  100. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/whenever.py +0 -0
  101. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/zipfile.py +0 -0
  102. {dycw_utilities-0.174.3 → dycw_utilities-0.174.4}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dycw-utilities
3
- Version: 0.174.3
3
+ Version: 0.174.4
4
4
  Author: Derek Wan
5
5
  Author-email: Derek Wan <d.wan@icloud.com>
6
6
  Requires-Dist: atomicwrites>=1.4.1,<1.5
@@ -101,7 +101,7 @@
101
101
  name = "dycw-utilities"
102
102
  readme = "README.md"
103
103
  requires-python = ">= 3.12"
104
- version = "0.174.3"
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.3"
138
+ current_version = "0.174.4"
139
139
 
140
140
  [[tool.bumpversion.files]]
141
141
  filename = "src/utilities/__init__.py"
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.174.3"
3
+ __version__ = "0.174.4"
@@ -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, /, *, user: str | None = None, logger: LoggerLike | None = None
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(container, *MKTEMP_DIR_CMD, user=user, return_=True, logger=logger)
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
- docker_exec(container, *rm_cmd(path), user=user, logger=logger)
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,16 +13,14 @@ 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.types import Delta
17
16
  from utilities.whenever import to_seconds
18
17
 
19
18
  if TYPE_CHECKING:
20
19
  from collections.abc import Iterator
21
20
 
22
- from utilities.types import LoggerLike, PathLike, StrMapping, StrStrMapping
21
+ from utilities.types import LoggerLike, PathLike, Retry, StrMapping, StrStrMapping
23
22
 
24
23
 
25
- type _Retry = tuple[int, Delta | None]
26
24
  _HOST_KEY_ALGORITHMS = ["ssh-ed25519"]
27
25
  BASH_LC = ["bash", "-lc"]
28
26
  BASH_LS = ["bash", "-ls"]
@@ -83,7 +81,7 @@ def run(
83
81
  return_: Literal[True],
84
82
  return_stdout: bool = False,
85
83
  return_stderr: bool = False,
86
- retry: _Retry | None = None,
84
+ retry: Retry | None = None,
87
85
  logger: LoggerLike | None = None,
88
86
  ) -> str: ...
89
87
  @overload
@@ -103,7 +101,7 @@ def run(
103
101
  return_: bool = False,
104
102
  return_stdout: Literal[True],
105
103
  return_stderr: bool = False,
106
- retry: _Retry | None = None,
104
+ retry: Retry | None = None,
107
105
  logger: LoggerLike | None = None,
108
106
  ) -> str: ...
109
107
  @overload
@@ -123,7 +121,7 @@ def run(
123
121
  return_: bool = False,
124
122
  return_stdout: bool = False,
125
123
  return_stderr: Literal[True],
126
- retry: _Retry | None = None,
124
+ retry: Retry | None = None,
127
125
  logger: LoggerLike | None = None,
128
126
  ) -> str: ...
129
127
  @overload
@@ -143,7 +141,7 @@ def run(
143
141
  return_: Literal[False] = False,
144
142
  return_stdout: Literal[False] = False,
145
143
  return_stderr: Literal[False] = False,
146
- retry: _Retry | None = None,
144
+ retry: Retry | None = None,
147
145
  logger: LoggerLike | None = None,
148
146
  ) -> None: ...
149
147
  @overload
@@ -163,7 +161,7 @@ def run(
163
161
  return_: bool = False,
164
162
  return_stdout: bool = False,
165
163
  return_stderr: bool = False,
166
- retry: _Retry | None = None,
164
+ retry: Retry | None = None,
167
165
  logger: LoggerLike | None = None,
168
166
  ) -> str | None: ...
169
167
  def run(
@@ -182,7 +180,7 @@ def run(
182
180
  return_: bool = False,
183
181
  return_stdout: bool = False,
184
182
  return_stderr: bool = False,
185
- retry: _Retry | None = None,
183
+ retry: Retry | None = None,
186
184
  logger: LoggerLike | None = None,
187
185
  ) -> str | None:
188
186
  args: list[str] = []
@@ -337,7 +335,7 @@ def ssh(
337
335
  return_: Literal[True],
338
336
  return_stdout: bool = False,
339
337
  return_stderr: bool = False,
340
- retry: _Retry | None = None,
338
+ retry: Retry | None = None,
341
339
  logger: LoggerLike | None = None,
342
340
  ) -> str: ...
343
341
  @overload
@@ -356,7 +354,7 @@ def ssh(
356
354
  return_: bool = False,
357
355
  return_stdout: Literal[True],
358
356
  return_stderr: bool = False,
359
- retry: _Retry | None = None,
357
+ retry: Retry | None = None,
360
358
  logger: LoggerLike | None = None,
361
359
  ) -> str: ...
362
360
  @overload
@@ -375,7 +373,7 @@ def ssh(
375
373
  return_: bool = False,
376
374
  return_stdout: bool = False,
377
375
  return_stderr: Literal[True],
378
- retry: _Retry | None = None,
376
+ retry: Retry | None = None,
379
377
  logger: LoggerLike | None = None,
380
378
  ) -> str: ...
381
379
  @overload
@@ -394,7 +392,7 @@ def ssh(
394
392
  return_: Literal[False] = False,
395
393
  return_stdout: Literal[False] = False,
396
394
  return_stderr: Literal[False] = False,
397
- retry: _Retry | None = None,
395
+ retry: Retry | None = None,
398
396
  logger: LoggerLike | None = None,
399
397
  ) -> None: ...
400
398
  @overload
@@ -413,7 +411,7 @@ def ssh(
413
411
  return_: bool = False,
414
412
  return_stdout: bool = False,
415
413
  return_stderr: bool = False,
416
- retry: _Retry | None = None,
414
+ retry: Retry | None = None,
417
415
  logger: LoggerLike | None = None,
418
416
  ) -> str | None: ...
419
417
  def ssh(
@@ -431,7 +429,7 @@ def ssh(
431
429
  return_: bool = False,
432
430
  return_stdout: bool = False,
433
431
  return_stderr: bool = False,
434
- retry: _Retry | None = None,
432
+ retry: Retry | None = None,
435
433
  logger: LoggerLike | None = None,
436
434
  ) -> str | None:
437
435
  cmd_and_args = ssh_cmd( # skipif-ci
@@ -484,9 +482,17 @@ def touch_cmd(path: PathLike, /) -> list[str]:
484
482
 
485
483
  @contextmanager
486
484
  def yield_ssh_temp_dir(
487
- user: str, hostname: str, /, *, keep: bool = False, logger: LoggerLike | None = None
485
+ user: str,
486
+ hostname: str,
487
+ /,
488
+ *,
489
+ retry: Retry | None = None,
490
+ logger: LoggerLike | None = None,
491
+ keep: bool = False,
488
492
  ) -> Iterator[Path]:
489
- path = Path(ssh(user, hostname, *MKTEMP_DIR_CMD, return_=True))
493
+ path = Path(
494
+ ssh(user, hostname, *MKTEMP_DIR_CMD, return_=True, retry=retry, logger=logger)
495
+ )
490
496
  try:
491
497
  yield path
492
498
  finally:
@@ -494,7 +500,7 @@ def yield_ssh_temp_dir(
494
500
  if logger is not None:
495
501
  to_logger(logger).info("Keeping temporary directory '%s'...", path)
496
502
  else:
497
- ssh(user, hostname, *rm_cmd(path))
503
+ ssh(user, hostname, *rm_cmd(path), retry=retry, logger=logger)
498
504
 
499
505
 
500
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",