From 6e1cbd95abe999d9c3aab1fee778d91e997e0444 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Thu, 17 Sep 2026 12:25:28 +0300 Subject: [PATCH] OsProcessController::close is added It is an explicit variant of __exit__ method. --- src/local_ops.py | 4 +++ src/os_ops.py | 3 ++ src/remote_ops.py | 4 +++ tests/test_os_ops_common.py | 64 +++++++++++++++++++++++++------------ 4 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/local_ops.py b/src/local_ops.py index 384b9d2..20c4de4 100644 --- a/src/local_ops.py +++ b/src/local_ops.py @@ -102,6 +102,10 @@ def returncode(self) -> typing.Optional[int]: assert type(self._local_process) is subprocess.Popen return self._local_process.poll() + def close(self) -> None: + self.__exit__(None, None, None) + return + def communicate( self, input: typing.Optional[T_OS_EXEC_INPUT] = None, diff --git a/src/os_ops.py b/src/os_ops.py index c236cc8..1e59dae 100644 --- a/src/os_ops.py +++ b/src/os_ops.py @@ -75,6 +75,9 @@ def stderr(self) -> typing.Optional[T_OS_IO]: def returncode(self) -> typing.Optional[int]: RaiseError.PropertyIsNotImplemented(__class__, "get_returncode") + def close(self) -> None: + RaiseError.MethodIsNotImplemented(__class__, "close") + T_COMMUNICATE_RESULT = typing.Union[ typing.Tuple[bytes, bytes], typing.Tuple[str, str], diff --git a/src/remote_ops.py b/src/remote_ops.py index 424f674..faf2531 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -192,6 +192,10 @@ def stderr(self) -> typing.Optional[T_OS_IO]: def returncode(self) -> typing.Optional[int]: return self._poll() + def close(self) -> None: + self.__exit__(None, None, None) + return + def communicate( self, input: typing.Optional[T_OS_EXEC_INPUT] = None, diff --git a/tests/test_os_ops_common.py b/tests/test_os_ops_common.py index 6a6d7ef..1522ed5 100644 --- a/tests/test_os_ops_common.py +++ b/tests/test_os_ops_common.py @@ -4199,17 +4199,22 @@ def LOCAL_f(data): f.seek(0) return f - # Yes, it it is not good. I know. - tmp_stdin = LOCAL_f(popen_data.expected_result) + tmp_stdin: typing.Optional[typing.IO[typing.Any]] = None - controller = os_ops.popen( - cmd, - text=popen_data.param_text, - encoding=popen_data.param_encoding, - stdin=tmp_stdin, - ) + controller: typing.Optional[OsProcessController] = None + + try: + tmp_stdin = LOCAL_f( + popen_data.expected_result, + ) + + controller = os_ops.popen( + cmd, + text=popen_data.param_text, + encoding=popen_data.param_encoding, + stdin=tmp_stdin, + ) - with tmp_stdin, controller: assert isinstance(controller, OsProcessController) returncode = controller.wait() @@ -4224,7 +4229,12 @@ def LOCAL_f(data): assert controller.stderr is not None x = controller.stderr.read() assert len(x) == 0 - pass + finally: + if controller is not None: + controller.close() + + if tmp_stdin is not None: + tmp_stdin.close() return @@ -4355,18 +4365,21 @@ def LOCAL_f(): encoding=popen_data2.param_encoding, ) - # Yes, it it is not good. I know. - tmp_stderr = LOCAL_f() - tmp_stdout = LOCAL_f() - controller = os_ops.popen( - cmd, - text=popen_data2.param_text, - encoding=popen_data2.param_encoding, - stdout=tmp_stdout, - stderr=tmp_stderr, - ) + tmp_stderr: typing.Optional[typing.IO[typing.Any]] = None + tmp_stdout: typing.Optional[typing.IO[typing.Any]] = None + controller: typing.Optional[OsProcessController] = None + + try: + tmp_stderr = LOCAL_f() + tmp_stdout = LOCAL_f() + controller = os_ops.popen( + cmd, + text=popen_data2.param_text, + encoding=popen_data2.param_encoding, + stdout=tmp_stdout, + stderr=tmp_stderr, + ) - with tmp_stderr, tmp_stdout, controller: assert isinstance(controller, OsProcessController) assert controller.wait() == 0 assert controller.returncode == 0 @@ -4386,6 +4399,15 @@ def LOCAL_f(): assert len(v2) > 0 logging.info("stderr: {!r}".format(v2)) assert v2 == popen_data2.expected_result2 + finally: + if controller is not None: + controller.close() + + if tmp_stdout is not None: + tmp_stdout.close() + + if tmp_stderr is not None: + tmp_stderr.close() return