From 5feabb5ad5e3922866f10de932d63fc267d9828a Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Fri, 19 Dec 2025 19:56:14 +0300 Subject: [PATCH 1/5] fix: LocalOsOperation::kill uses os.kill, arg "expect_error" is removed Changes: - pid and signal must be int - argument "expected_error" is not supported anymore (unification with RemoteOsOperation) - it uses os.kill function instead "kill" command from OS. This commit brokes applications where expect_error is passed (testgres 1.12.0, for example). --- src/local_ops.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/local_ops.py b/src/local_ops.py index a55270b..499eb6d 100644 --- a/src/local_ops.py +++ b/src/local_ops.py @@ -568,10 +568,11 @@ def remove_file(self, filename): return os.remove(filename) # Processes control - def kill(self, pid, signal, expect_error=False): + def kill(self, pid: int, signal: int): # Kill the process - cmd = "kill -{} {}".format(signal, pid) - return self.exec_command(cmd, expect_error=expect_error) + assert type(pid) == int # noqa: E721 + assert type(signal) == int # noqa: E721 + os.kill(pid, signal) def get_pid(self): # Get current process id From 5503513558a915cb92edf1f6f722a09d7080e70a Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Fri, 19 Dec 2025 19:57:13 +0300 Subject: [PATCH 2/5] refactoring: OsOperation::kill(self, pid: int, signal: int) pid and signal must be int --- src/os_ops.py | 4 +++- src/remote_ops.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/os_ops.py b/src/os_ops.py index 4642226..f43f8c7 100644 --- a/src/os_ops.py +++ b/src/os_ops.py @@ -126,8 +126,10 @@ def remove_file(self, filename): raise NotImplementedError() # Processes control - def kill(self, pid, signal): + def kill(self, pid: int, signal: int): # Kill the process + assert type(pid) == int # noqa: E721 + assert type(signal) == int # noqa: E721 raise NotImplementedError() def get_pid(self): diff --git a/src/remote_ops.py b/src/remote_ops.py index 7fcc642..42eede9 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -658,8 +658,10 @@ def remove_file(self, filename): return self.exec_command(cmd) # Processes control - def kill(self, pid, signal): + def kill(self, pid: int, signal: int): # Kill the process + assert type(pid) == int # noqa: E721 + assert type(signal) == int # noqa: E721 cmd = "kill -{} {}".format(signal, pid) return self.exec_command(cmd) From b23a80bc429be7bfcd31c4a375bab01679137824 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Sat, 20 Dec 2025 02:01:25 +0300 Subject: [PATCH 3/5] os_ops.kill supports signal:typing.Union[int, os_signal.Signals] --- src/local_ops.py | 5 +++-- src/os_ops.py | 6 ++++-- src/remote_ops.py | 5 +++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/local_ops.py b/src/local_ops.py index 499eb6d..ea246bf 100644 --- a/src/local_ops.py +++ b/src/local_ops.py @@ -14,6 +14,7 @@ import typing import threading import copy +import signal as os_signal from .exceptions import ExecUtilException from .exceptions import InvalidOperationException @@ -568,10 +569,10 @@ def remove_file(self, filename): return os.remove(filename) # Processes control - def kill(self, pid: int, signal: int): + def kill(self, pid: int, signal: typing.Union[int, os_signal.Signals]): # Kill the process assert type(pid) == int # noqa: E721 - assert type(signal) == int # noqa: E721 + assert type(signal) in [int, os_signal.Signals] # noqa: E721 os.kill(pid, signal) def get_pid(self): diff --git a/src/os_ops.py b/src/os_ops.py index f43f8c7..e06f908 100644 --- a/src/os_ops.py +++ b/src/os_ops.py @@ -1,6 +1,8 @@ from __future__ import annotations import locale +import typing +import signal as os_signal class ConnectionParams: @@ -126,10 +128,10 @@ def remove_file(self, filename): raise NotImplementedError() # Processes control - def kill(self, pid: int, signal: int): + def kill(self, pid: int, signal: typing.Union[int, os_signal.Signals]): # Kill the process assert type(pid) == int # noqa: E721 - assert type(signal) == int # noqa: E721 + assert type(signal) in [int, os_signal.Signals] # noqa: E721 raise NotImplementedError() def get_pid(self): diff --git a/src/remote_ops.py b/src/remote_ops.py index 42eede9..e5f7279 100644 --- a/src/remote_ops.py +++ b/src/remote_ops.py @@ -11,6 +11,7 @@ import typing import copy import re +import signal as os_signal from .exceptions import ExecUtilException from .exceptions import InvalidOperationException @@ -658,10 +659,10 @@ def remove_file(self, filename): return self.exec_command(cmd) # Processes control - def kill(self, pid: int, signal: int): + def kill(self, pid: int, signal: typing.Union[int, os_signal.Signals]): # Kill the process assert type(pid) == int # noqa: E721 - assert type(signal) == int # noqa: E721 + assert type(signal) in [int, os_signal.Signals] # noqa: E721 cmd = "kill -{} {}".format(signal, pid) return self.exec_command(cmd) From 6354710c87ea378e13ca9372945bb35d7ce10c3f Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Sat, 20 Dec 2025 02:02:20 +0300 Subject: [PATCH 4/5] Tests for os_ops.kill are added --- tests/test_os_ops_common.py | 103 ++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/tests/test_os_ops_common.py b/tests/test_os_ops_common.py index 5a797d4..c84a9af 100644 --- a/tests/test_os_ops_common.py +++ b/tests/test_os_ops_common.py @@ -5,6 +5,7 @@ from tests.helpers.run_conditions import RunConditions import os +import sys import pytest import re @@ -14,6 +15,10 @@ import threading import typing import uuid +import subprocess +import psutil +import time +import signal as os_signal from src.exceptions import InvalidOperationException from src.exceptions import ExecUtilException @@ -1137,3 +1142,101 @@ class tadWorkerData: logging.info("Test is finished! Total error count is {}.".format(nErrors)) return + + T_KILL_SIGNAL_DESCR = typing.Tuple[ + str, + typing.Union[int, os_signal.Signals], + str + ] + + sm_kill_signal_ids: typing.List[T_KILL_SIGNAL_DESCR] = [ + ("SIGKILL", os_signal.SIGKILL, "9"), + ("SIGQUIT", os_signal.SIGQUIT, "3"), + ("9", 9, "9"), + ("3", 3, "3"), + ] + + @pytest.fixture( + params=sm_kill_signal_ids, + ids=["signal: {}".format(x[0]) for x in sm_kill_signal_ids], + ) + def kill_signal_id(self, request: pytest.FixtureRequest) -> T_KILL_SIGNAL_DESCR: + assert isinstance(request, pytest.FixtureRequest) + assert type(request.param) == tuple # noqa: E721 + return request.param + + def test_kill_signal( + self, + kill_signal_id: T_KILL_SIGNAL_DESCR, + ): + assert type(kill_signal_id) == tuple # noqa: E721 + assert "{}".format(kill_signal_id[1]) == kill_signal_id[2] + + def test_kill( + self, + os_ops: OsOperations, + kill_signal_id: T_KILL_SIGNAL_DESCR, + ): + """ + Test listdir for listing directory contents. + """ + assert isinstance(os_ops, OsOperations) + assert type(kill_signal_id) == tuple # noqa: E721 + + cmd = [ + sys.executable, + "-c", + "import time; print('ENTER');time.sleep(300);print('EXIT')" + ] + + logging.info("Local test process is creating ...") + proc = subprocess.Popen( + cmd, + text=True, + ) + + assert proc is not None + assert type(proc) == subprocess.Popen # noqa: E721 + proc_pid = proc.pid + assert type(proc_pid) == int # noqa: E721 + logging.info("Test process pid is {}".format(proc_pid)) + + logging.info("Get this test process ...") + p1 = psutil.Process(proc_pid) + assert p1 is not None + del p1 + + logging.info("Kill this test process ...") + os_ops.kill(proc_pid, kill_signal_id[1]) + + logging.info("Wait for finish ...") + proc.wait() + + logging.info("Try to get this test process ...") + + attempt = 0 + while True: + if attempt == 20: + raise RuntimeError("Process did not die,") + + attempt += 1 + + if attempt > 1: + logging.info("Sleep 1 seconds...") + time.sleep(1) + + try: + psutil.Process(proc_pid) + except psutil.ZombieProcess as e: + logging.info("Exception {}: {}".format( + type(e).__name__, + str(e), + )) + except psutil.NoSuchProcess: + logging.info("OK. Process died.") + break + + logging.info("Process is alive!") + continue + + return From 4d8647c6b6fcd66b982663d28ea73bebb6da0797 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Sat, 20 Dec 2025 11:58:40 +0300 Subject: [PATCH 5/5] test_kill is corrected (error message) --- tests/test_os_ops_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_os_ops_common.py b/tests/test_os_ops_common.py index c84a9af..140b1a9 100644 --- a/tests/test_os_ops_common.py +++ b/tests/test_os_ops_common.py @@ -1217,7 +1217,7 @@ def test_kill( attempt = 0 while True: if attempt == 20: - raise RuntimeError("Process did not die,") + raise RuntimeError("Process did not die.") attempt += 1