From 4194672b5552e47567967f0f5a4a326bc757d640 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:13:21 +0300 Subject: [PATCH 01/29] remote-tt: _transport_popen and _transport_run are added --- src/remote_ops.py | 160 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/src/remote_ops.py b/src/remote_ops.py index 7fc9591..f4ab075 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1801,6 +1801,166 @@ def create_file(self, filename: str) -> None: self.exec_command(cmd, encoding=get_default_encoding()) return + def _transport_popen( + self, + cmd: T_OS_CMD, + text: typing.Optional[bool] = None, + encoding: typing.Optional[str] = None, + shell: bool = False, + stdin: typing.Optional[T_OS_IO_ID] = subprocess.PIPE, + stdout: typing.Optional[T_OS_IO_ID] = subprocess.PIPE, + stderr: typing.Optional[T_OS_IO_ID] = subprocess.PIPE, + exec_env: typing.Optional[OsOperations.T_EXEC_ENV] = None, + cwd: typing.Optional[str] = None + ) -> subprocess.Popen: + assert type(cmd) in [str, list] + assert text is None or type(text) is bool + assert encoding is None or type(encoding) is str + assert type(shell) is bool + assert stdin is None or type(stdin) is int or isinstance(stdin, io.IOBase) + assert stdout is None or type(stdout) is int or isinstance(stdout, io.IOBase) + assert stderr is None or type(stderr) is int or isinstance(stderr, io.IOBase) + assert exec_env is None or type(exec_env) is dict + assert cwd is None or type(cwd) is str + + cmds = [] + + if cwd is not None: + cmds.append(__class__._build_cmdline(["cd", cwd])) + + assert self._remote_env_guard is not None + assert type(self._remote_env) is dict + + exec_env2: typing.Optional[__class__.T_ENVS] = None + with self._remote_env_guard: + if len(self._remote_env) > 0: + exec_env2 = self._remote_env.copy() + + if exec_env2 is None: + exec_env2 = exec_env + elif exec_env is not None: + exec_env2.update(exec_env) + + # Construct the final command, recording the PID and replacing the process via exec + cmd2 = __class__._ensure_cmdline(cmd) + + target_cmdline = __class__._build_cmdline(cmd2, exec_env2) + + cmds.append(target_cmdline) + + cmdline = " && ".join(cmds) + + assert type(self._ssh_cmd) is list + assert len(self._ssh_cmd) > 0 + ssh_cmd = self._ssh_cmd + [cmdline] + + if encoding is not None and text is None: + text = True + + result = subprocess.Popen( + ssh_cmd, + stdin=stdin, + stdout=stdout, + stderr=stderr, + text=text, + encoding=encoding, + shell=False, + ) + + assert type(result) is subprocess.Popen + return result + + class tagTransportRunResult: + T_IO_RESULT = typing.Union[str, bytes] + + returncode: int + stdout: T_IO_RESULT + stderr: T_IO_RESULT + + def __init__( + self, + returncode: int, + stdout: T_IO_RESULT, + stderr: T_IO_RESULT, + ): + assert type(returncode) is int + assert type(stdout) in [str, bytes] + assert type(stderr) in [str, bytes] + self.returncode = returncode + self.stdout = stdout + self.stderr = stderr + return + + def _transport_run( + self, + cmd: T_OS_CMD, + text: typing.Optional[bool] = None, + encoding: typing.Optional[str] = None, + shell: bool = False, + input: typing.Optional[T_OS_RUN_INPUT] = None, + stdin: typing.Optional[T_OS_IO_ID] = subprocess.PIPE, + stdout: typing.Optional[T_OS_IO_ID] = subprocess.PIPE, + stderr: typing.Optional[T_OS_IO_ID] = subprocess.PIPE, + exec_env: typing.Optional[OsOperations.T_EXEC_ENV] = None, + cwd: typing.Optional[str] = None, + check: bool = True, + ) -> tagTransportRunResult: + assert type(cmd) in [str, list] + assert text is None or type(text) is bool + assert encoding is None or type(encoding) is str + assert type(shell) is bool + assert input is None or type(input) in [str, bytes] + assert stdin is None or type(stdin) is int or isinstance(stdin, io.IOBase) + assert stdout is None or type(stdout) is int or isinstance(stdout, io.IOBase) + assert stderr is None or type(stderr) is int or isinstance(stderr, io.IOBase) + assert exec_env is None or type(exec_env) is dict + assert cwd is None or type(cwd) is str + + input = Helpers.prepare_process_input( + input, + encoding, + ) + + p = self._transport_popen( + cmd, + text=text, + encoding=encoding, + shell=shell, + stdin=stdin, + stdout=stdout, + stderr=stderr, + exec_env=exec_env, + cwd=cwd, + ) + assert type(p) is subprocess.Popen + + with p: + communicate_r = p.communicate(input=input) + assert type(communicate_r) is tuple + assert len(communicate_r) == 2 + + returncode = p.returncode + assert type(returncode) is int + + result = __class__.tagTransportRunResult( + returncode, + communicate_r[0], + communicate_r[1], + ) + + if returncode == 0: + pass + elif check: + RaiseError.UtilityExitedWithNonZeroCode( + cmd, + result.returncode, + msg_arg=result.stderr, + error=result.stderr, + out=result.stdout, + ) + + return result + @staticmethod def _build_cmdline( cmd, From 421416bdd0a20de05206ef5ced1793f8f97f79f5 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:27:22 +0300 Subject: [PATCH 02/29] remote-tt: PsUtilProcessProxy uses self.ssh._transport_run --- src/remote_ops.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index f4ab075..1237d04 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -44,13 +44,13 @@ def kill(self): assert isinstance(self.ssh, RemoteOperations) assert type(self.pid) is int command = ["kill", str(self.pid)] - self.ssh.exec_command(command, encoding=get_default_encoding()) + self.ssh._transport_run(command, encoding=get_default_encoding()) def cmdline(self): assert isinstance(self.ssh, RemoteOperations) assert type(self.pid) is int command = ["ps", "-p", str(self.pid), "-o", "cmd", "--no-headers"] - output = self.ssh.exec_command(command, encoding=get_default_encoding()) + output = self.ssh._transport_run(command, encoding=get_default_encoding()).stdout assert type(output) is str cmdline = output.strip() # TODO: This code work wrong if command line contains quoted values. Yes? From b2d537d538c6bca463aeecb65b8e381aeee9ce6e Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:29:07 +0300 Subject: [PATCH 03/29] remote-tt: RemoteOperations::cwd is updated --- src/remote_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index 1237d04..ce2437f 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -898,7 +898,7 @@ def environ(self, var_name: str) -> typing.Optional[str]: def cwd(self) -> str: cmd = 'pwd' - stdout = self.exec_command(cmd, encoding=get_default_encoding()) + stdout = self._transport_run(cmd, encoding=get_default_encoding()).stdout assert type(stdout) is str return stdout.rstrip() From 55c9b83d0c944ab9055eb36f01402cf106bd11a3 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:33:29 +0300 Subject: [PATCH 04/29] remote-tt: RemoteOperations::makedir is updated --- src/remote_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index ce2437f..78e78d6 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1044,7 +1044,7 @@ def makedirs( def makedir(self, path: str) -> None: assert type(path) is str cmd = "mkdir " + __class__._quote_path(path) - self.exec_command(cmd, encoding=get_default_encoding()) + self._transport_run(cmd, encoding=get_default_encoding()) return def rmdirs( From d71b9521e025ca425335f195422ed4bf8aff1c26 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:34:55 +0300 Subject: [PATCH 05/29] remote-tt: RemoteOperations::makedirs is updated --- src/remote_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index 78e78d6..c764303 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1035,7 +1035,7 @@ def makedirs( cmd = " ".join(cmd_p) - self.exec_command( + self._transport_run( cmd, encoding=get_default_encoding(), ) From de8aa38cc26615361ab630cf79fa7e6b21de9b8c Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:36:48 +0300 Subject: [PATCH 06/29] remote-tt: RemoteOperations::rmdirs is updated --- src/remote_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index c764303..3c34624 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1092,7 +1092,7 @@ def rmdirs( assert a < attempts a += 1 try: - self.exec_command( + self._transport_run( cmd2, encoding=Helpers.get_default_encoding(), ) From 63b27a187b11dd779cbaeaac59459b353293eb98 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:40:11 +0300 Subject: [PATCH 07/29] remote-tt: RemoteOperations::environ is updated --- src/remote_ops.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index 3c34624..60a8dc4 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -864,36 +864,32 @@ def environ(self, var_name: str) -> typing.Optional[str]: cmd = ["printenv", var_name] - exec_r = self.exec_command( + exec_r = self._transport_run( cmd, encoding=get_default_encoding(), - verbose=True, - ignore_errors=True, + check=False, ) - assert type(exec_r) is tuple - assert len(exec_r) == 3 + assert type(exec_r) is __class__.tagTransportRunResult - exit_code, stdout, stderr = exec_r + assert type(exec_r.returncode) is int + assert type(exec_r.stdout) is str + assert type(exec_r.stderr) is str - assert type(exit_code) is int - assert type(stdout) is str - assert type(stderr) is str - - if exit_code == 0: - return __class__._strip_last_eol(stdout) + if exec_r.returncode == 0: + return __class__._strip_last_eol(exec_r.stdout) - if exit_code == 1: + if exec_r.returncode == 1: return None error = "Failed to read environment variable {!r} value.".format(var_name) RaiseError.UtilityExitedWithNonZeroCode( cmd=cmd, - exit_code=exit_code, + exit_code=exec_r.returncode, msg_arg=error, - error=stderr, - out=stdout, + error=exec_r.stderr, + out=exec_r.stdout, ) def cwd(self) -> str: From 92adbb38a2e18008059e0a23f90d3f2ce78de9a4 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:41:28 +0300 Subject: [PATCH 08/29] remote-tt: RemoteOperations::is_executable is updated --- src/remote_ops.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index 60a8dc4..3a7c42b 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -921,39 +921,35 @@ def is_executable(self, file: str) -> bool: command = "test -x " + __class__._quote_path(file) - exec_r = self.exec_command( + exec_r = self._transport_run( cmd=command, encoding=get_default_encoding(), - ignore_errors=True, - verbose=True, + check=False, ) - assert type(exec_r) is tuple - assert len(exec_r) == 3 - - exit_status, output, error = exec_r + assert type(exec_r) is __class__.tagTransportRunResult - assert type(exit_status) is int - assert type(output) is str - assert type(error) is str + assert type(exec_r.returncode) is int + assert type(exec_r.stdout) is str + assert type(exec_r.stderr) is str - if exit_status == 0: + if exec_r.returncode == 0: return True - if exit_status == 1: + if exec_r.returncode == 1: return False errMsg = "Test operation returns an unknown result code: {0}. File name is [{1}].".format( - exit_status, + exec_r.returncode, file, ) RaiseError.CommandExecutionError( cmd=command, - exit_code=exit_status, + exit_code=exec_r.returncode, message=errMsg, - error=error, - out=output + error=exec_r.stderr, + out=exec_r.stdout, ) def set_env( From 9b2d01d59a74df707d7d7e71eead38d93886fae8 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:43:22 +0300 Subject: [PATCH 09/29] remote-tt: RemoteOperations::rmdir is updated --- src/remote_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index 3a7c42b..aa1517f 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1113,7 +1113,7 @@ def rmdirs( def rmdir(self, path: str) -> None: assert type(path) is str cmd = "rmdir " + __class__._quote_path(path) - self.exec_command(cmd, encoding=get_default_encoding()) + self._transport_run(cmd, encoding=get_default_encoding()) return def listdir(self, path: str) -> typing.List[str]: From 0d35f2bb95f68c4ea78942b18813cbbcc81e0c0a Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:45:10 +0300 Subject: [PATCH 10/29] remote-tt: RemoteOperations::listdir is updated --- src/remote_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index aa1517f..a98edbe 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1124,7 +1124,7 @@ def listdir(self, path: str) -> typing.List[str]: """ assert type(path) is str command = "ls " + __class__._quote_path(path) - output = self.exec_command(cmd=command, encoding=get_default_encoding()) + output = self._transport_run(cmd=command, encoding=get_default_encoding()).stdout assert type(output) is str result = output.splitlines() assert type(result) is list From 7f187134dd743c955d26a6a58922e799cdbccffd Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:46:19 +0300 Subject: [PATCH 11/29] remote-tt: RemoteOperations::path_exists is updated --- src/remote_ops.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index a98edbe..938744b 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1135,38 +1135,34 @@ def path_exists(self, path: str) -> bool: command = "test -e " + __class__._quote_path(path) - exec_r = self.exec_command( + exec_r = self._transport_run( cmd=command, encoding=get_default_encoding(), - ignore_errors=True, - verbose=True, + check=False, ) - assert type(exec_r) is tuple - assert len(exec_r) == 3 - - exit_status, output, error = exec_r + assert type(exec_r) is __class__.tagTransportRunResult - assert type(exit_status) is int - assert type(output) is str - assert type(error) is str + assert type(exec_r.returncode) is int + assert type(exec_r.stdout) is str + assert type(exec_r.stderr) is str - if exit_status == 0: + if exec_r.returncode == 0: return True - if exit_status == 1: + if exec_r.returncode == 1: return False errMsg = "Test operation returns an unknown result code: {0}. Path is [{1}].".format( - exit_status, + exec_r.returncode, path) RaiseError.CommandExecutionError( cmd=command, - exit_code=exit_status, + exit_code=exec_r.returncode, message=errMsg, - error=error, - out=output + error=exec_r.stderr, + out=exec_r.stdout, ) @property From 9c8ab9b912c7417a8eb33cc67243ea71b15fa0b8 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:47:15 +0300 Subject: [PATCH 12/29] remote-tt: RemoteOperations::copytree is updated --- src/remote_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index 938744b..cb644bd 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1275,7 +1275,7 @@ def copytree(self, src: str, dst: str) -> str: __class__._quote_path(src), __class__._quote_path(abs_dst), ) - self.exec_command(cmd, encoding=get_default_encoding()) + self._transport_run(cmd, encoding=get_default_encoding()) return dst # Work with files From 391699810d7ca652e33f67dfdfa1e087c6d5bbe2 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:48:45 +0300 Subject: [PATCH 13/29] remote-tt: RemoteOperations::mkdtemp is updated --- src/remote_ops.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index cb644bd..9fab8d2 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1192,26 +1192,28 @@ def mkdtemp(self, prefix: typing.Optional[str] = None) -> str: command = " ".join(command_p) - exec_r = self.exec_command(command, verbose=True, encoding=get_default_encoding(), ignore_errors=True) - - assert type(exec_r) is tuple - assert len(exec_r) == 3 + exec_r = self._transport_run( + command, + encoding=get_default_encoding(), + check=False, + ) - exec_exitcode, exec_output, exec_error = exec_r + assert type(exec_r) is __class__.tagTransportRunResult - assert type(exec_exitcode) is int - assert type(exec_output) is str - assert type(exec_error) is str + assert type(exec_r.returncode) is int + assert type(exec_r.stdout) is str + assert type(exec_r.stderr) is str - if exec_exitcode != 0: + if exec_r.returncode != 0: RaiseError.CommandExecutionError( cmd=command, - exit_code=exec_exitcode, + exit_code=exec_r.returncode, message="Could not create temporary directory.", - error=exec_error, - out=exec_output) + error=exec_r.stderr, + out=exec_r.stdout, + ) - temp_dir = exec_output.strip() + temp_dir = exec_r.stdout.strip() return temp_dir def mkstemp(self, prefix: typing.Optional[str] = None) -> str: From 9713f11f0480691d7a044e968dcf57c0bb7f96c4 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:49:59 +0300 Subject: [PATCH 14/29] remote-tt: RemoteOperations::mkstemp is updated --- src/remote_ops.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index 9fab8d2..44a1f35 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1237,31 +1237,28 @@ def mkstemp(self, prefix: typing.Optional[str] = None) -> str: command = " ".join(command_p) - exec_r = self.exec_command( + exec_r = self._transport_run( command, - verbose=True, encoding=get_default_encoding(), - ignore_errors=True, + check=False, ) - assert type(exec_r) is tuple - assert len(exec_r) == 3 - - exec_exitcode, exec_output, exec_error = exec_r + assert type(exec_r) is __class__.tagTransportRunResult - assert type(exec_exitcode) is int - assert type(exec_output) is str - assert type(exec_error) is str + assert type(exec_r.returncode) is int + assert type(exec_r.stdout) is str + assert type(exec_r.stderr) is str - if exec_exitcode != 0: + if exec_r.returncode != 0: RaiseError.CommandExecutionError( cmd=command, - exit_code=exec_exitcode, + exit_code=exec_r.returncode, message="Could not create temporary file.", - error=exec_error, - out=exec_output) + error=exec_r.stderr, + out=exec_r.stdout, + ) - temp_file = exec_output.strip() + temp_file = exec_r.stdout.strip() return temp_file def copytree(self, src: str, dst: str) -> str: From c7a50516d91def16cde8ffd72ee5ccbfb2d6e927 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:51:42 +0300 Subject: [PATCH 15/29] remote-tt: RemoteOperations::write is updated --- src/remote_ops.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index 44a1f35..b6190fc 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1334,14 +1334,14 @@ def write( # 4. Execute ONE network request # Pass final_data to the stdin parameter of the exec_command method assert type(final_data) is bytes - self.exec_command( + self._transport_run( remote_cmd, input=final_data, # It does not touch our binary final_data (see PrepareProcessInput) # but allows to generate an error messages as text. encoding=get_default_encoding(), # Let it crash honestly if there are no rights or the disk is full - ignore_errors=False, + check=True, ) return From 65b02d0cf7641ef9fcb92e13f1286facc9964708 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:53:10 +0300 Subject: [PATCH 16/29] remote-tt: RemoteOperations::touch is updated --- src/remote_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index b6190fc..e01181e 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1374,7 +1374,7 @@ def touch(self, filename: str) -> None: cmd = "touch " + __class__._quote_path(filename) - self.exec_command(cmd, encoding=get_default_encoding()) + self._transport_run(cmd, encoding=get_default_encoding()) return def read( From c022265d706c69190c91fa1e6aa8f8270b0c34e0 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:56:33 +0300 Subject: [PATCH 17/29] remote-tt: RemoteOperations::_read__binary is updated --- src/remote_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index e01181e..451dfcb 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1413,7 +1413,7 @@ def _read__text_with_encoding(self, filename: str, encoding: str) -> str: def _read__binary(self, filename: str) -> bytes: assert type(filename) is str cmd = "cat " + __class__._quote_path(filename) - content = self.exec_command(cmd) + content = self._transport_run(cmd).stdout assert type(content) is bytes return content From 911fef4ee514322e75799cafb960cfe20ae2ba8e Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:58:00 +0300 Subject: [PATCH 18/29] remote-tt: RemoteOperations::readlines is updated --- src/remote_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index 451dfcb..2305e89 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1454,7 +1454,7 @@ def readlines( assert type(encoding) is str pass - result = self.exec_command(cmd, encoding=encoding) + result = self._transport_run(cmd, encoding=encoding).stdout assert result is not None if binary: From 6da42221cb703d87bd49ef68ae297eed32f99699 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 16:59:28 +0300 Subject: [PATCH 19/29] remote-tt: RemoteOperations::read_binary is updated --- src/remote_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index 2305e89..4b1724b 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1490,7 +1490,7 @@ def read_binary( cmd = " ".join(cmd_p) - r = self.exec_command(cmd) + r = self._transport_run(cmd).stdout assert type(r) is bytes return r From 5389ac93504a808afdeef69bb22545089182143d Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 17:00:11 +0300 Subject: [PATCH 20/29] remote-tt: RemoteOperations::isfile is updated --- src/remote_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index 4b1724b..c02520f 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1502,7 +1502,7 @@ def isfile(self, filename: str) -> bool: assert type(filename_q) is str cmd = "test -f {}; echo $?".format(filename_q) - stdout = self.exec_command(cmd) + stdout = self._transport_run(cmd).stdout assert type(stdout) is bytes result = int(stdout.strip()) return result == 0 From 6121bd2bede9a6ce5b44008c8e7170a7b0ec4382 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 17:02:34 +0300 Subject: [PATCH 21/29] remote-tt: RemoteOperations::isdir is updated --- src/remote_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index c02520f..e9ec783 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1514,7 +1514,7 @@ def isdir(self, dirname: str) -> bool: dirname_q = __class__._quote_path(dirname) cmd = "if [ -d {} ]; then echo True; else echo False; fi".format(dirname_q) - stdout = self.exec_command(cmd) + stdout = self._transport_run(cmd).stdout assert type(stdout) is bytes return stdout.strip() == b"True" From e35d7e0f1a92bf63e18a192e344c9aca9ac4a3d7 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 17:05:24 +0300 Subject: [PATCH 22/29] remote-tt: RemoteOperations::get_file_size is updated --- src/remote_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index e9ec783..df28580 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1528,7 +1528,7 @@ def get_file_size(self, filename: str) -> int: cmd = "stat -c %s " + filename_q # exec_command will throw ExecUtilException (e.g. with code 1) if the file does not exist - res = self.exec_command(cmd, encoding=get_default_encoding()) + res = self._transport_run(cmd, encoding=get_default_encoding()).stdout assert type(res) is str return int(res) From 830bf9e1cabe0e97bbfadee06fcacb8d756ee89c Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 17:07:05 +0300 Subject: [PATCH 23/29] remote-tt: remove_file, kill, get_pid are updated --- src/remote_ops.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index df28580..1008bb0 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1536,7 +1536,7 @@ def remove_file(self, filename: str) -> None: assert type(filename) is str assert filename != "" cmd = "rm " + __class__._quote_path(filename) - self.exec_command(cmd, encoding=get_default_encoding()) + self._transport_run(cmd, encoding=get_default_encoding()) return # Processes control @@ -1546,12 +1546,12 @@ def kill(self, pid: int, signal: T_OS_SIGNAL) -> None: assert type(signal) is int or type(signal) is os_signal.Signals assert int(signal) == signal cmd = "kill -{} {}".format(int(signal), pid) - self.exec_command(cmd, encoding=get_default_encoding()) + self._transport_run(cmd, encoding=get_default_encoding()) return def get_pid(self) -> int: # Get current process id - x = self.exec_command("echo $$", encoding=get_default_encoding()) + x = self._transport_run("echo $$", encoding=get_default_encoding()).stdout assert type(x) is str return int(x) From 9d8196dbea32cfc2fec29f6fe942a061ccc28521 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 17:09:09 +0300 Subject: [PATCH 24/29] remote-tt: RemoteOperations::get_process_children is updated --- src/remote_ops.py | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index 1008bb0..915afd4 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1558,29 +1558,22 @@ def get_pid(self) -> int: def get_process_children(self, pid: int) -> typing.List: assert type(pid) is int - exec_r = self.exec_command( + exec_r = self._transport_run( [ "sh", "-c", "[ -d /proc/{0} ] || exit 100; pgrep -P {0}".format(pid), ], encoding=get_default_encoding(), - verbose=True, - ignore_errors=True, + check=False, ) - assert type(exec_r) is tuple - assert len(exec_r) == 3 - assert type(exec_r[0]) is int - assert type(exec_r[1]) is str - assert type(exec_r[2]) is str - - exit_code, stdout, stderr = exec_r + assert type(exec_r) is __class__.tagTransportRunResult - assert type(exit_code) is int - assert type(stdout) is str - assert type(stderr) is str + assert type(exec_r.returncode) is int + assert type(exec_r.stdout) is str + assert type(exec_r.stderr) is str - if exit_code == 100: + if exec_r.returncode == 100: err_msg = "Failed to get process children. Reason: No such process with PID {}.".format( pid ) @@ -1590,8 +1583,8 @@ def get_process_children(self, pid: int) -> typing.List: exit_code=1, # ERR: NOT FOUND ) - if exit_code == 0: - stdout_clean = stdout.strip() + if exec_r.returncode == 0: + stdout_clean = exec_r.stdout.strip() if not stdout_clean: return [] return [ @@ -1599,19 +1592,19 @@ def get_process_children(self, pid: int) -> typing.List: for child_pid in stdout_clean.splitlines() ] - if exit_code == 1: - if not stderr.strip(): + if exec_r.returncode == 1: + if not exec_r.stderr.strip(): # pgrep returns 1 when no children are found return [] - error_msg = stderr.strip() or "command exited with code {}".format(exit_code) # noqa: E501 + error_msg = exec_r.stderr.strip() or "command exited with code {}".format(exec_r.returncode) # noqa: E501 raise ExecUtilException( "Failed to get process children for PID {}. Reason: {}".format( pid, error_msg, ), - exit_code=exit_code, + exit_code=exec_r.returncode, ) def is_port_free(self, number: int) -> bool: From 08a1d1714d20a1ab40d7a57038e6c911e94cb89e Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 17:10:41 +0300 Subject: [PATCH 25/29] remote-tt: RemoteOperations::is_port_free is updated --- src/remote_ops.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index 915afd4..a1e7107 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1631,34 +1631,30 @@ def is_port_free(self, number: int) -> bool: grep_cmd_s, ] - exec_r = self.exec_command( + exec_r = self._transport_run( cmd=cmd, encoding=get_default_encoding(), - ignore_errors=True, - verbose=True, + check=False, ) - assert type(exec_r) is tuple - assert len(exec_r) == 3 - - exit_status, output, error = exec_r + assert type(exec_r) is __class__.tagTransportRunResult # grep exit 0 -> port is busy - if exit_status == 0: + if exec_r.returncode == 0: return False # grep exit 1 -> port is free - if exit_status == 1: + if exec_r.returncode == 1: return True # any other code is an unexpected error - errMsg = f"grep returned unexpected exit code: {exit_status}" + errMsg = f"grep returned unexpected exit code: {exec_r.returncode}" raise RaiseError.CommandExecutionError( cmd=cmd, - exit_code=exit_status, + exit_code=exec_r.returncode, message=errMsg, - error=error, - out=output + error=exec_r.stderr, + out=exec_r.stdout, ) def get_tempdir(self) -> str: From 8edfe91723019fc58370350b3a9670f859d11a85 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 17:12:10 +0300 Subject: [PATCH 26/29] remote-tt: RemoteOperations::get_tempdir is updated --- src/remote_ops.py | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index a1e7107..1f43315 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1660,31 +1660,26 @@ def is_port_free(self, number: int) -> bool: def get_tempdir(self) -> str: command = ["mktemp", "-u", "-d"] - exec_r = self.exec_command( + exec_r = self._transport_run( command, - verbose=True, encoding=get_default_encoding(), - ignore_errors=True, + check=False, ) - assert type(exec_r) is tuple - assert len(exec_r) == 3 - - exec_exitcode, exec_output, exec_error = exec_r - - assert type(exec_exitcode) is int - assert type(exec_output) is str - assert type(exec_error) is str + assert type(exec_r.returncode) is int + assert type(exec_r.stdout) is str + assert type(exec_r.stderr) is str - if exec_exitcode != 0: + if exec_r.returncode != 0: RaiseError.CommandExecutionError( cmd=command, - exit_code=exec_exitcode, + exit_code=exec_r.returncode, message="Could not detect a temporary directory.", - error=exec_error, - out=exec_output) + error=exec_r.stderr, + out=exec_r.stdout, + ) - temp_subdir = exec_output.strip() + temp_subdir = exec_r.stdout.strip() assert type(temp_subdir) is str temp_dir = __class__._get_dirname(temp_subdir) assert type(temp_dir) is str From 11fba95392281a869673d3861f9753013ddd2c98 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 17:12:56 +0300 Subject: [PATCH 27/29] remote-tt: RemoteOperations::get_abs_path is updated --- src/remote_ops.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index 1f43315..f41397b 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1710,10 +1710,10 @@ def get_abs_path(self, path: str) -> str: # # "-m" is used to ignore not exist parts of path # - r = self.exec_command( + r = self._transport_run( cmd, encoding=get_default_encoding(), - ) + ).stdout assert type(r) is str r = __class__._strip_last_eol(r) assert type(r) is str From 0f447350d8c68aad274bada14a877108c12a52b4 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 17:18:29 +0300 Subject: [PATCH 28/29] remote-tt: RemoteOperations::get_file_stat is updated --- src/remote_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index f41397b..ef65dc7 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1730,7 +1730,7 @@ def get_file_stat(self, filename: str) -> OsOperations.T_FILE_STAT: cmd = "stat -c '%s|%Y' " + filename_q # exec_command will throw ExecUtilException (e.g. with code 1) if the file does not exist - res = self.exec_command(cmd, encoding=get_default_encoding()) + res = self._transport_run(cmd, encoding=get_default_encoding()).stdout assert type(res) is str parts = res.strip().split("|") From 17e027dc467b945c3801a8244bbc101efb366776 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Wed, 16 Sep 2026 17:23:37 +0300 Subject: [PATCH 29/29] remote-tt: RemoteOperations::create_file is updated --- src/remote_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote_ops.py b/src/remote_ops.py index ef65dc7..8b1e652 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -1769,7 +1769,7 @@ def create_file(self, filename: str) -> None: "(set -o noclobber; > {})".format(filename_q), ] - self.exec_command(cmd, encoding=get_default_encoding()) + self._transport_run(cmd, encoding=get_default_encoding()) return def _transport_popen(