Server isolation and detailed errors
Isolated the JsonServer to server.py inside protocols/json Added specific errors for server startup timeout and server startup failed Refactored errors for Core to CoreError generic and refactored StateError to CoreStateMismatchError derived from CoreError
This commit is contained in:
@@ -2,85 +2,32 @@
|
||||
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import asyncio
|
||||
from asyncio import Server
|
||||
import threading
|
||||
from threading import Thread
|
||||
from typing import Literal
|
||||
|
||||
|
||||
from dynalab_core.config import CoreConfig
|
||||
from dynalab_core.constants import CORE_VERSION
|
||||
from dynalab_core.errors import StateError
|
||||
from dynalab_core.errors import CoreStateMismatchError
|
||||
from dynalab_core.protocols.json.common import VersionDescriptor
|
||||
|
||||
|
||||
class JsonServer:
|
||||
_thread: Thread | None = None
|
||||
_server: Server | None = None
|
||||
_config: CoreConfig
|
||||
_stop_event: threading.Event
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: CoreConfig,
|
||||
stop_event: threading.Event,
|
||||
) -> None:
|
||||
self._config = config
|
||||
self._stop_event = stop_event
|
||||
|
||||
def start(self) -> None:
|
||||
self._thread = Thread(
|
||||
target=self._json_server_thread_main, name="json_server_thread", daemon=True
|
||||
)
|
||||
self._thread.start()
|
||||
|
||||
def _json_server_thread_main(self) -> None:
|
||||
asyncio.run(self._run_json_server())
|
||||
|
||||
async def _run_json_server(self) -> None:
|
||||
try:
|
||||
self._server = await asyncio.start_server(
|
||||
self._handle_json_connection,
|
||||
host=self._config.host,
|
||||
port=self._config.port,
|
||||
)
|
||||
except OSError as error:
|
||||
print(
|
||||
f"Failed to start ingress server on {self._config.bind_str()}: {error}"
|
||||
)
|
||||
async with self._server:
|
||||
await asyncio.to_thread(self._stop_event.wait)
|
||||
|
||||
async def _handle_json_connection(
|
||||
self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter
|
||||
) -> None:
|
||||
print(f"Connected to: {writer.get_extra_info('peername')}")
|
||||
|
||||
try:
|
||||
await reader.readline()
|
||||
finally:
|
||||
writer.close()
|
||||
await writer.wait_closed()
|
||||
print("Disconnected")
|
||||
from dynalab_core.protocols.json.server import JsonServer
|
||||
|
||||
|
||||
class Core:
|
||||
_state: Literal["uninitd", "initd", "started", "stopping", "stopped"] = "unintid"
|
||||
_core_version: VersionDescriptor = CORE_VERSION
|
||||
_core_config: CoreConfig = CoreConfig()
|
||||
_stop_event: threading.Event = threading.Event()
|
||||
_json_server: JsonServer
|
||||
|
||||
def __init__(self, config: CoreConfig) -> None:
|
||||
self._core_config = config
|
||||
self._state = "initd"
|
||||
self._state: Literal["uninitd", "initd", "started", "stopping", "stopped"] = (
|
||||
"unintid"
|
||||
)
|
||||
self._core_version: VersionDescriptor = CORE_VERSION
|
||||
self._stop_event: threading.Event = threading.Event()
|
||||
self._core_config: CoreConfig = config
|
||||
self._json_server = JsonServer(self._core_config, self._stop_event)
|
||||
pass
|
||||
|
||||
self._state = "initd"
|
||||
|
||||
def start(self) -> None:
|
||||
if self._state != "initd":
|
||||
raise StateError(
|
||||
raise CoreStateMismatchError(
|
||||
f'Unable to start DynaLab Core, expected state to be "initd", found {self._state}'
|
||||
)
|
||||
self._json_server.start()
|
||||
|
||||
@@ -2,6 +2,27 @@
|
||||
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Core error declarations
|
||||
class CoreError(Exception):
|
||||
"""DynaLab Core error."""
|
||||
|
||||
class StateError(Exception):
|
||||
|
||||
class CoreStateMismatchError(CoreError):
|
||||
"""DynaLab Core state error."""
|
||||
|
||||
|
||||
# JsonServer error declarations
|
||||
class JsonServerError(Exception):
|
||||
"""Generic JsonServer Error"""
|
||||
|
||||
|
||||
class JsonServerTimeoutError(JsonServerError):
|
||||
"""JsonServer timed out"""
|
||||
|
||||
|
||||
class JsonServerStartupError(JsonServerError):
|
||||
"""JsonServer crashed on startup"""
|
||||
|
||||
def __init__(self, error: Exception) -> None:
|
||||
self.error = error
|
||||
super().__init__(f"JSON server failed to start: {error}")
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
# Copyright (C) 2026 Hector van der Aa <hector@h3cx.dev>
|
||||
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import asyncio
|
||||
from asyncio import Server
|
||||
import threading
|
||||
from threading import Thread
|
||||
|
||||
|
||||
from dynalab_core.config import CoreConfig
|
||||
from dynalab_core.errors import (
|
||||
JsonServerStartupError,
|
||||
JsonServerTimeoutError,
|
||||
)
|
||||
|
||||
|
||||
class JsonServer:
|
||||
def __init__(
|
||||
self,
|
||||
config: CoreConfig,
|
||||
stop_event: threading.Event,
|
||||
) -> None:
|
||||
self._thread: Thread | None = None
|
||||
self._server: Server | None = None
|
||||
self._started_event: threading.Event = threading.Event()
|
||||
self._startup_error: Exception | None = None
|
||||
self._debug_timeout_test: bool = False
|
||||
self._config = config
|
||||
self._global_stop_event = stop_event
|
||||
self._timeout: float = 10
|
||||
|
||||
def start(self) -> None:
|
||||
self._thread = Thread(
|
||||
target=self._json_server_thread_main, name="json_server_thread", daemon=True
|
||||
)
|
||||
self._thread.start()
|
||||
|
||||
res = self._started_event.wait(self._timeout)
|
||||
print(f"result: {res}")
|
||||
if not res:
|
||||
raise JsonServerTimeoutError
|
||||
|
||||
if self._startup_error is not None:
|
||||
raise JsonServerStartupError(self._startup_error)
|
||||
|
||||
def _json_server_thread_main(self) -> None:
|
||||
asyncio.run(self._run_json_server())
|
||||
|
||||
async def _run_json_server(self) -> None:
|
||||
try:
|
||||
self._server = await asyncio.start_server(
|
||||
self._handle_json_connection,
|
||||
host=self._config.host,
|
||||
port=self._config.port,
|
||||
)
|
||||
except OSError as error:
|
||||
print(
|
||||
f"Failed to start ingress server on {self._config.bind_str()}: {error}"
|
||||
)
|
||||
self._startup_error = error
|
||||
if not self._debug_timeout_test:
|
||||
self._started_event.set()
|
||||
|
||||
if not self._debug_timeout_test:
|
||||
self._started_event.set()
|
||||
async with self._server:
|
||||
await asyncio.to_thread(self._global_stop_event.wait)
|
||||
|
||||
async def _handle_json_connection(
|
||||
self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter
|
||||
) -> None:
|
||||
print(f"Connected to: {writer.get_extra_info('peername')}")
|
||||
|
||||
try:
|
||||
await reader.readline()
|
||||
finally:
|
||||
writer.close()
|
||||
await writer.wait_closed()
|
||||
print("Disconnected")
|
||||
Reference in New Issue
Block a user