mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-04-21 08:40:03 +02:00
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:
commit
2e9ced4e5e
19
poetry.lock
generated
19
poetry.lock
generated
@ -1536,6 +1536,23 @@ pytest = ">=4.6"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
|
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]]
|
[[package]]
|
||||||
name = "python-dateutil"
|
name = "python-dateutil"
|
||||||
version = "2.8.2"
|
version = "2.8.2"
|
||||||
@ -2012,4 +2029,4 @@ h11 = ">=0.9.0,<1"
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.10"
|
python-versions = "^3.10"
|
||||||
content-hash = "409dcbb9f27079667033d1111bdc1ca99436c0e28568562dece75b590aa80e08"
|
content-hash = "b2763b13a2e00c8094a0c30f6157e72c118bab47ab2d9a0357345b33693e6afa"
|
||||||
|
@ -35,6 +35,7 @@ flake8-pep604 = "^0.1.0"
|
|||||||
flake8-eradicate = "^1.4.0"
|
flake8-eradicate = "^1.4.0"
|
||||||
matplotlib = "^3.7.2"
|
matplotlib = "^3.7.2"
|
||||||
pyright = "^1.1.323"
|
pyright = "^1.1.323"
|
||||||
|
pytest-mock = "^3.11.1"
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.group.docs.dependencies]
|
[tool.poetry.group.docs.dependencies]
|
||||||
|
@ -358,20 +358,16 @@ class Server:
|
|||||||
# Signals can only be listened to from the main thread.
|
# Signals can only be listened to from the main thread.
|
||||||
return
|
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:
|
for sig in HANDLED_SIGNALS:
|
||||||
signal.signal(sig, self.handle_exit)
|
signal.signal(sig, self.handle_exit)
|
||||||
|
|
||||||
def handle_exit(self, sig: int = 0, frame: Optional[FrameType] = None) -> None:
|
def handle_exit(self, sig: int = 0, frame: Optional[FrameType] = None) -> None:
|
||||||
logger.info("Handling exit")
|
|
||||||
if self.should_exit and sig == signal.SIGINT:
|
if self.should_exit and sig == signal.SIGINT:
|
||||||
self.force_exit = True
|
logger.warning(f"Received signal {sig}, forcing exit...")
|
||||||
|
os._exit(1)
|
||||||
else:
|
else:
|
||||||
self.should_exit = True
|
self.should_exit = True
|
||||||
|
logger.warning(f"Received signal {sig}, exiting...")
|
||||||
|
|
||||||
def custom_exception_handler(
|
def custom_exception_handler(
|
||||||
self, loop: asyncio.AbstractEventLoop, context: dict[str, Any]
|
self, loop: asyncio.AbstractEventLoop, context: dict[str, Any]
|
||||||
|
35
tests/server/test_server.py
Normal file
35
tests/server/test_server.py
Normal 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)
|
Loading…
x
Reference in New Issue
Block a user