Merge pull request #43 from tiqi-group/42-enhanced-signal-handling-for-asyncio-loop

Enhances signal handling, adds force exit capability
This commit is contained in:
Mose Müller 2023-10-19 11:14:06 +02:00 committed by GitHub
commit 2e9ced4e5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 10 deletions

19
poetry.lock generated
View File

@ -1536,6 +1536,23 @@ pytest = ">=4.6"
[package.extras]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
[[package]]
name = "pytest-mock"
version = "3.11.1"
description = "Thin-wrapper around the mock package for easier use with pytest"
optional = false
python-versions = ">=3.7"
files = [
{file = "pytest-mock-3.11.1.tar.gz", hash = "sha256:7f6b125602ac6d743e523ae0bfa71e1a697a2f5534064528c6ff84c2f7c2fc7f"},
{file = "pytest_mock-3.11.1-py3-none-any.whl", hash = "sha256:21c279fff83d70763b05f8874cc9cfb3fcacd6d354247a976f9529d19f9acf39"},
]
[package.dependencies]
pytest = ">=5.0"
[package.extras]
dev = ["pre-commit", "pytest-asyncio", "tox"]
[[package]]
name = "python-dateutil"
version = "2.8.2"
@ -2012,4 +2029,4 @@ h11 = ">=0.9.0,<1"
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "409dcbb9f27079667033d1111bdc1ca99436c0e28568562dece75b590aa80e08"
content-hash = "b2763b13a2e00c8094a0c30f6157e72c118bab47ab2d9a0357345b33693e6afa"

View File

@ -35,6 +35,7 @@ flake8-pep604 = "^0.1.0"
flake8-eradicate = "^1.4.0"
matplotlib = "^3.7.2"
pyright = "^1.1.323"
pytest-mock = "^3.11.1"
[tool.poetry.group.docs.dependencies]

View File

@ -358,20 +358,16 @@ class Server:
# Signals can only be listened to from the main thread.
return
try:
for sig in HANDLED_SIGNALS:
self._loop.add_signal_handler(sig, self.handle_exit, sig, None)
except NotImplementedError:
# Windows
for sig in HANDLED_SIGNALS:
signal.signal(sig, self.handle_exit)
def handle_exit(self, sig: int = 0, frame: Optional[FrameType] = None) -> None:
logger.info("Handling exit")
if self.should_exit and sig == signal.SIGINT:
self.force_exit = True
logger.warning(f"Received signal {sig}, forcing exit...")
os._exit(1)
else:
self.should_exit = True
logger.warning(f"Received signal {sig}, exiting...")
def custom_exception_handler(
self, loop: asyncio.AbstractEventLoop, context: dict[str, Any]

View File

@ -0,0 +1,35 @@
import signal
from pytest_mock import MockerFixture
import pydase
def test_signal_handling(mocker: MockerFixture):
# Mock os._exit and signal.signal
mock_exit = mocker.patch("os._exit")
mock_signal = mocker.patch("signal.signal")
class MyService(pydase.DataService):
pass
# Instantiate your server object
server = pydase.Server(MyService())
# Call the method to install signal handlers
server.install_signal_handlers()
# Check if the signal handlers were registered correctly
assert mock_signal.call_args_list == [
mocker.call(signal.SIGINT, server.handle_exit),
mocker.call(signal.SIGTERM, server.handle_exit),
]
# Simulate receiving a SIGINT signal for the first time
server.handle_exit(signal.SIGINT, None)
assert server.should_exit # assuming should_exit is public
mock_exit.assert_not_called()
# Simulate receiving a SIGINT signal for the second time
server.handle_exit(signal.SIGINT, None)
mock_exit.assert_called_once_with(1)