From c3afe66f83a4c4cfa01c98a208218c8010360211 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Thu, 2 Jul 2026 18:22:58 +0300 Subject: [PATCH] fix: RemoteOperations::rmdirs supports attempts and delays, too. os_ops::rmdirs has the following signature: def rmdirs( self, path: str, ignore_errors: bool = True, attempts: int = 3, delay: T_DELAY = 1, ) -> bool: --- src/local_ops.py | 18 ++++++++++----- src/os_ops.py | 16 ++++++++++++- src/remote_ops.py | 58 +++++++++++++++++++++++++++++++++++------------ 3 files changed, 70 insertions(+), 22 deletions(-) diff --git a/src/local_ops.py b/src/local_ops.py index caf7181..e1088fa 100644 --- a/src/local_ops.py +++ b/src/local_ops.py @@ -417,14 +417,20 @@ def makedir(self, path: str): return # [2025-02-03] Old name of parameter attempts is "retries". - def rmdirs(self, path, ignore_errors=True, attempts=3, delay=1): + def rmdirs( + self, + path: str, + ignore_errors: bool = True, + attempts: int = 3, + delay: OsOperations.T_DELAY = 1, + ) -> bool: """ Removes a directory and its contents, retrying on failure. - - :param path: Path to the directory. - :param ignore_errors: If True, ignore errors. - :param retries: Number of attempts to remove the directory. - :param delay: Delay between attempts in seconds. + Args: + - path (str): The path to the directory to be removed. + - ignore_errors (bool): If True, do not raise error if directory does not exist. + - attempts: Number of attempts to remove the directory. + - delay: Delay between attempts in seconds. """ assert type(path) is str assert type(ignore_errors) is bool diff --git a/src/os_ops.py b/src/os_ops.py index 18356fc..f25ec85 100644 --- a/src/os_ops.py +++ b/src/os_ops.py @@ -152,7 +152,21 @@ def makedir(self, path: str): assert type(path) is str raise NotImplementedError() - def rmdirs(self, path, ignore_errors=True): + T_DELAY = typing.Union[int, float] + + def rmdirs( + self, + path: str, + ignore_errors: bool = True, + attempts: int = 3, + delay: T_DELAY = 1, + ) -> bool: + assert type(path) is str + assert type(ignore_errors) is bool + assert type(attempts) is int + assert type(delay) is int or type(delay) is float + assert attempts > 0 + assert delay >= 0 raise NotImplementedError() def rmdir(self, path: str): diff --git a/src/remote_ops.py b/src/remote_ops.py index d37d48f..6aaaeef 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -10,6 +10,7 @@ import copy import re import signal as os_signal +import time from .exceptions import ExecUtilException from .exceptions import InvalidOperationException @@ -373,15 +374,27 @@ def makedir(self, path: str): cmd = ["mkdir", path] self.exec_command(cmd) - def rmdirs(self, path, ignore_errors=True): + def rmdirs( + self, + path: str, + ignore_errors: bool = True, + attempts: int = 3, + delay: OsOperations.T_DELAY = 1, + ) -> bool: """ - Remove a directory in the remote server. + Removes a directory and its contents, retrying on failure. Args: - path (str): The path to the directory to be removed. - ignore_errors (bool): If True, do not raise error if directory does not exist. + - attempts: Number of attempts to remove the directory. + - delay: Delay between attempts in seconds. """ assert type(path) is str assert type(ignore_errors) is bool + assert type(attempts) is int + assert type(delay) is int or type(delay) is float + assert attempts > 0 + assert delay >= 0 # ENOENT = 2 - No such file or directory # ENOTDIR = 20 - Not a directory @@ -397,21 +410,36 @@ def rmdirs(self, path, ignore_errors=True): cmd2 = ["sh", "-c", subprocess.list2cmdline(cmd1)] - try: - self.exec_command(cmd2, encoding=Helpers.GetDefaultEncoding()) - except ExecUtilException as e: - if e.exit_code == 2: # No such file or directory - return True + a = 0 + while True: + assert a < attempts + a += 1 + try: + self.exec_command( + cmd2, + encoding=Helpers.GetDefaultEncoding(), + ) + except ExecUtilException as e: + if e.exit_code == 2: # No such file or directory + return True - if not ignore_errors: - raise + if a < attempts: + errMsg = "Failed to remove directory {0} on attempt {1} ({2}): {3}".format( + path, a, type(e).__name__, e + ) + logging.warning(errMsg) + time.sleep(delay) + continue - errMsg = "Failed to remove directory {0} ({1}): {2}".format( - path, type(e).__name__, e - ) - logging.warning(errMsg) - return False - return True + assert a == attempts + + if not ignore_errors: + raise + + return False + + # OK! + return True def rmdir(self, path: str): assert type(path) is str