|
4 | 4 | import stat
|
5 | 5 | import subprocess
|
6 | 6 | import tempfile
|
| 7 | +import time |
7 | 8 |
|
8 | 9 | import psutil
|
9 | 10 |
|
|
19 | 20 |
|
20 | 21 | CMD_TIMEOUT_SEC = 60
|
21 | 22 | error_markers = [b'error', b'Permission denied', b'fatal']
|
| 23 | +err_out_markers = [b'Failure'] |
22 | 24 |
|
23 | 25 |
|
24 |
| -def has_errors(output): |
| 26 | +def has_errors(output=None, error=None): |
25 | 27 | if output:
|
26 | 28 | if isinstance(output, str):
|
27 | 29 | output = output.encode(get_default_encoding())
|
28 |
| - return any(marker in output for marker in error_markers) |
| 30 | + return any(marker in output for marker in err_out_markers) |
| 31 | + if error: |
| 32 | + if isinstance(error, str): |
| 33 | + error = error.encode(get_default_encoding()) |
| 34 | + return any(marker in error for marker in error_markers) |
29 | 35 | return False
|
30 | 36 |
|
31 | 37 |
|
@@ -107,8 +113,8 @@ def exec_command(self, cmd, wait_exit=False, verbose=False, expect_error=False,
|
107 | 113 | process, output, error = self._run_command(cmd, shell, input, stdin, stdout, stderr, get_process, timeout, encoding)
|
108 | 114 | if get_process:
|
109 | 115 | return process
|
110 |
| - if process.returncode != 0 or (has_errors(error) and not expect_error): |
111 |
| - self._raise_exec_exception('Utility exited with non-zero code. Error `{}`', cmd, process.returncode, error) |
| 116 | + if (process.returncode != 0 or has_errors(output=output, error=error)) and not expect_error: |
| 117 | + self._raise_exec_exception('Utility exited with non-zero code. Error `{}`', cmd, process.returncode, error or output) |
112 | 118 |
|
113 | 119 | if verbose:
|
114 | 120 | return process.returncode, output, error
|
@@ -142,8 +148,27 @@ def makedirs(self, path, remove_existing=False):
|
142 | 148 | except FileExistsError:
|
143 | 149 | pass
|
144 | 150 |
|
145 |
| - def rmdirs(self, path, ignore_errors=True): |
146 |
| - return rmtree(path, ignore_errors=ignore_errors) |
| 151 | + def rmdirs(self, path, ignore_errors=True, retries=3, delay=1): |
| 152 | + """ |
| 153 | + Removes a directory and its contents, retrying on failure. |
| 154 | +
|
| 155 | + :param path: Path to the directory. |
| 156 | + :param ignore_errors: If True, ignore errors. |
| 157 | + :param retries: Number of attempts to remove the directory. |
| 158 | + :param delay: Delay between attempts in seconds. |
| 159 | + """ |
| 160 | + for attempt in range(retries): |
| 161 | + try: |
| 162 | + rmtree(path, ignore_errors=ignore_errors) |
| 163 | + if not os.path.exists(path): |
| 164 | + return True |
| 165 | + except FileNotFoundError: |
| 166 | + return True |
| 167 | + except Exception as e: |
| 168 | + print(f"Error: Failed to remove directory {path} on attempt {attempt + 1}: {e}") |
| 169 | + time.sleep(delay) |
| 170 | + print(f"Error: Failed to remove directory {path} after {retries} attempts.") |
| 171 | + return False |
147 | 172 |
|
148 | 173 | def listdir(self, path):
|
149 | 174 | return os.listdir(path)
|
|
0 commit comments