Optimized pydantic use

All protocol classes have now been duplicated in order to minimize the
use of pydantic validation

The "pure" classes are named what the previous validated classes were,
now each transport has its transport name prefixed on the class.

For example before we had Heartbeat, now we have Heartbeat and
JsonHeartbeat

This new architecture allows us to reduce the number of useless backend
validation steps created when copying and processing data, reducing CPU
stress by roughly 10-20% in real world testing
This commit is contained in:
h3cx committed 2026-09-17 22:22:42 +02:00
1 parent 207a91811e
commit 2e8155142e
11 files changed
+359 -88

No files matched your search

+6 -10
View File
@@ -5,23 +5,19 @@
from uuid import uuid4 from uuid import uuid4
from dynalab_core.protocols.common import VersionDescriptor from dynalab_core.protocols.common import VersionDescriptor
from dynalab_core.protocols.constants import PROTOCOL_VERSION from dynalab_core.protocols.constants import PROTOCOL_VERSION
from dynalab_core.protocols.packets.handshake import ConnectorHello, DynaLabHello from dynalab_core.protocols.packets.handshake import (
ConnectorHello,
DynaLabHello,
JsonDynaLabHello,
)
CORE_VERSION = VersionDescriptor(type="alpha", major=0, minor=0, patch=1) CORE_VERSION = VersionDescriptor(type="alpha", major=0, minor=0, patch=1)
HELLO_PACKET = DynaLabHello( HELLO_PACKET = JsonDynaLabHello(
instance_id=uuid4(), instance_id=uuid4(),
core_version=CORE_VERSION, core_version=CORE_VERSION,
protocol_version=PROTOCOL_VERSION, protocol_version=PROTOCOL_VERSION,
heartbeat_interval_ms=1000, heartbeat_interval_ms=1000,
heartbeat_timeout_ms=5000, heartbeat_timeout_ms=5000,
) )
INTERNAL_CONNECTOR_HELLO = ConnectorHello(
connector_uuid=uuid4(),
protocol_version=PROTOCOL_VERSION,
connector_name="INTERNAL_LOOPBACK",
connector_version=CORE_VERSION.get_version(),
signals=[],
)
+2 -27
View File
@@ -10,7 +10,7 @@ from time import monotonic, sleep
from uuid import UUID from uuid import UUID
from dynalab_core.types import CoreModeState from dynalab_core.types import CoreModeState
from dynalab_core.constants import HELLO_PACKET, INTERNAL_CONNECTOR_HELLO from dynalab_core.constants import HELLO_PACKET
from dynalab_core.errors import ConnectorError from dynalab_core.errors import ConnectorError
from dynalab_core.protocols.packets import ProtocolMessage from dynalab_core.protocols.packets import ProtocolMessage
from dynalab_core.protocols.packets.data import ValueBatch, ValueDescriptor from dynalab_core.protocols.packets.data import ValueBatch, ValueDescriptor
@@ -486,21 +486,10 @@ class ConnectorEndpoint:
class ConnectorRegistry: class ConnectorRegistry:
def __init__(self, core_input_queue: Queue, core_mode: CoreModeState) -> None: def __init__(self, core_input_queue: Queue, core_mode: CoreModeState) -> None:
self._endpoints: dict[UUID, ConnectorEndpoint] = {} self._endpoints: dict[UUID, ConnectorEndpoint] = {}
self._internal_connector: ConnectorHello = INTERNAL_CONNECTOR_HELLO
self._lock = RLock() self._lock = RLock()
self._core_input_queue = core_input_queue self._core_input_queue = core_input_queue
self._core_mode = core_mode self._core_mode = core_mode
def add_internal_signal(self, signal: SignalDescriptor) -> None:
with self._lock:
if not any(s.id == signal.id for s in self._internal_connector.signals):
self._internal_connector.signals.append(signal)
def remove_internal_signal(self, signal: SignalDescriptor) -> None:
with self._lock:
if not any(s.id == signal.id for s in self._internal_connector.signals):
self._internal_connector.signals.remove(signal)
def register( def register(
self, self,
hello: ConnectorHello, hello: ConnectorHello,
@@ -612,14 +601,7 @@ class ConnectorRegistry:
def get(self, connector_uuid: UUID) -> ConnectorEndpoint | None: def get(self, connector_uuid: UUID) -> ConnectorEndpoint | None:
with self._lock: with self._lock:
endpoint = self._endpoints.get(connector_uuid) return self._endpoints.get(connector_uuid)
if not endpoint:
return endpoint
if self._internal_connector.connector_uuid == connector_uuid:
return self._internal_connector
return None
def get_signal_descriptor(self, signal_id: UUID) -> SignalDescriptor | None: def get_signal_descriptor(self, signal_id: UUID) -> SignalDescriptor | None:
with self._lock: with self._lock:
@@ -630,10 +612,6 @@ class ConnectorRegistry:
if signal is not None: if signal is not None:
return signal return signal
for signal in self._internal_connector.signals:
if signal.id == signal_id:
return signal
return None return None
def get_all_signal_descriptors(self) -> list[SignalDescriptor]: def get_all_signal_descriptors(self) -> list[SignalDescriptor]:
@@ -646,9 +624,6 @@ class ConnectorRegistry:
for signal in endpoint_signals: for signal in endpoint_signals:
signals.append(signal) signals.append(signal)
for signal in self._internal_connector.signals:
signals.append(signal)
return signals return signals
def stop(self) -> bool: def stop(self) -> bool:
+11 -4
View File
@@ -17,7 +17,12 @@ from dynalab_core.constants import HELLO_PACKET
from dynalab_core.protocols.endpoint import ConnectorEndpoint, ConnectorRegistry from dynalab_core.protocols.endpoint import ConnectorEndpoint, ConnectorRegistry
from dynalab_core.errors import ConnectorError, JsonServerError from dynalab_core.errors import ConnectorError, JsonServerError
from dynalab_core.protocols.json.wire import read_message, write_message from dynalab_core.protocols.json.wire import read_message, write_message
from dynalab_core.protocols.packets.handshake import ConnectorHello, HandshakeAccepted from dynalab_core.protocols.packets.data import JsonValueBatch, JsonValueDescriptor
from dynalab_core.protocols.packets.handshake import (
ConnectorHello,
HandshakeAccepted,
JsonConnectorHello,
)
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -193,6 +198,7 @@ class JsonServer:
) )
while not self._stop_event.is_set(): while not self._stop_event.is_set():
message = await read_message(reader) message = await read_message(reader)
message = message.to_pure()
endpoint.put_ingress_packet(message) endpoint.put_ingress_packet(message)
async def _output_task( async def _output_task(
@@ -216,7 +222,8 @@ class JsonServer:
except Empty: except Empty:
await asyncio.sleep(0.01) await asyncio.sleep(0.01)
continue continue
await write_message(writer, message) json_message = message.to_json()
await write_message(writer, json_message)
if isinstance(message, HandshakeAccepted): if isinstance(message, HandshakeAccepted):
log.info( log.info(
"Connector %s connected from %s", "Connector %s connected from %s",
@@ -309,7 +316,7 @@ class JsonServer:
) )
return return
if not isinstance(message, ConnectorHello): if not isinstance(message, JsonConnectorHello):
reason = "unexpected_message" reason = "unexpected_message"
log.warning( log.warning(
"Connection %s sent %s instead of connector_hello", "Connection %s sent %s instead of connector_hello",
@@ -324,7 +331,7 @@ class JsonServer:
) )
return return
connector_hello = message connector_hello = message.to_pure()
context["connector_uuid"] = str(connector_hello.connector_uuid) context["connector_uuid"] = str(connector_hello.connector_uuid)
timeout_event = threading.Event() timeout_event = threading.Event()
reject_event = threading.Event() reject_event = threading.Event()
+4 -4
View File
@@ -5,19 +5,19 @@
from asyncio import StreamReader, StreamWriter from asyncio import StreamReader, StreamWriter
from pydantic import TypeAdapter from pydantic import TypeAdapter
from dynalab_core.protocols.packets import ProtocolMessage from dynalab_core.protocols.packets import JsonProtocolMessage
_MESSAGE_ADAPTER = TypeAdapter(ProtocolMessage) _MESSAGE_ADAPTER = TypeAdapter(JsonProtocolMessage)
async def write_message(writer: StreamWriter, message: ProtocolMessage) -> None: async def write_message(writer: StreamWriter, message: JsonProtocolMessage) -> None:
writer.write(message.model_dump_json().encode("utf-8") + b"\n") writer.write(message.model_dump_json().encode("utf-8") + b"\n")
await writer.drain() await writer.drain()
async def read_message( async def read_message(
reader: StreamReader, *, max_frame_bytes: int = 65_536 reader: StreamReader, *, max_frame_bytes: int = 65_536
) -> ProtocolMessage: ) -> JsonProtocolMessage:
line = await reader.readline() line = await reader.readline()
if not line: if not line:
+29 -16
View File
@@ -2,27 +2,40 @@
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com> # Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from typing import Annotated from dynalab_core.protocols.packets.data import (
JsonValueBatch,
from pydantic import Field JsonValueDescriptor,
ValueBatch,
from dynalab_core.protocols.packets.data import ValueBatch, ValueDescriptor ValueDescriptor,
)
from dynalab_core.protocols.packets.handshake import ( from dynalab_core.protocols.packets.handshake import (
ConnectorHello, ConnectorHello,
DynaLabHello, DynaLabHello,
HandshakeAccepted, HandshakeAccepted,
HandshakeRejected, HandshakeRejected,
JsonConnectorHello,
JsonDynaLabHello,
JsonHandshakeAccepted,
JsonHandshakeRejected,
) )
from dynalab_core.protocols.packets.heartbeat import Heartbeat from dynalab_core.protocols.packets.heartbeat import Heartbeat, JsonHeartbeat
from dynalab_core.protocols.packets.types import JsonProtocolMessage, ProtocolMessage
__all__ = [
ProtocolMessage = Annotated[ "ConnectorHello",
DynaLabHello "DynaLabHello",
| ConnectorHello "HandshakeAccepted",
| HandshakeAccepted "HandshakeRejected",
| HandshakeRejected "Heartbeat",
| Heartbeat "JsonConnectorHello",
| ValueDescriptor "JsonDynaLabHello",
| ValueBatch, "JsonHandshakeAccepted",
Field(discriminator="type"), "JsonHandshakeRejected",
"JsonHeartbeat",
"JsonProtocolMessage",
"JsonValueBatch",
"JsonValueDescriptor",
"ProtocolMessage",
"ValueBatch",
"ValueDescriptor",
] ]
+41 -4
View File
@@ -2,18 +2,55 @@
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com> # Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from dataclasses import dataclass
from typing import Literal from typing import Literal
from uuid import UUID from uuid import UUID
from pydantic import BaseModel from pydantic import BaseModel, ConfigDict
@dataclass(slots=True)
class ValueDescriptor:
signal_id: UUID
value: float
timestamp: int
def __init__(self, signal_id: UUID, value: float, timestamp: int) -> None:
self.signal_id: UUID = signal_id
self.value: float = value
self.timestamp: int = timestamp
def to_json(self) -> JsonValueDescriptor:
return JsonValueDescriptor.model_validate(self)
class ValueDescriptor(BaseModel): @dataclass(slots=True)
class ValueBatch:
values: list[ValueDescriptor]
def __init__(self, values: list[ValueDescriptor]) -> None:
self.values: list[ValueDescriptor] = values
def to_json(self) -> JsonValueBatch:
return JsonValueBatch.model_validate(self)
class JsonValueDescriptor(BaseModel):
model_config = ConfigDict(from_attributes=True)
type: Literal["value_descriptor"] = "value_descriptor" type: Literal["value_descriptor"] = "value_descriptor"
signal_id: UUID signal_id: UUID
value: float value: float
timestamp: int timestamp: int
def to_pure(self) -> ValueDescriptor:
return ValueDescriptor(self.signal_id, self.value, self.timestamp)
class JsonValueBatch(BaseModel):
model_config = ConfigDict(from_attributes=True)
class ValueBatch(BaseModel):
type: Literal["value_batch"] = "value_batch" type: Literal["value_batch"] = "value_batch"
values: list[ValueDescriptor] values: list[JsonValueDescriptor]
def to_pure(self) -> ValueBatch:
return ValueBatch([value.to_pure() for value in self.values])
+163 -9
View File
@@ -2,15 +2,129 @@
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com> # Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from dataclasses import dataclass
from typing import Literal from typing import Literal
from uuid import UUID from uuid import UUID
from pydantic import BaseModel, Field from pydantic import BaseModel, ConfigDict, Field
from dynalab_core.protocols.common import VersionDescriptor from dynalab_core.protocols.common import VersionDescriptor
from dynalab_core.protocols.constants import PROTOCOL_VERSION from dynalab_core.protocols.constants import PROTOCOL_VERSION
@dataclass(slots=True)
class SignalDescriptor:
id: UUID
name: str
type: Literal["number", "binary"]
min_value: float | None
max_value: float | None
unit: str | None
timeout_ms: int
origin: Literal["source", "derived"]
def __init__(
self,
id: UUID,
name: str,
type: Literal["number", "binary"],
min_value: float | None = None,
max_value: float | None = None,
unit: str | None = None,
timeout_ms: int = 2000,
origin: Literal["source", "derived"] = "source",
) -> None:
self.id: UUID = id
self.name: str = name
self.type: Literal["number", "binary"] = type
self.min_value: float | None = min_value
self.max_value: float | None = max_value
self.unit: str | None = unit
self.timeout_ms: int = timeout_ms
self.origin: Literal["source", "derived"] = origin
def to_json(self) -> JsonSignalDescriptor:
return JsonSignalDescriptor.model_validate(self)
class SignalDescriptor(BaseModel): @dataclass(slots=True)
class DynaLabHello:
instance_id: UUID
core_version: VersionDescriptor
protocol_version: VersionDescriptor
heartbeat_interval_ms: int
heartbeat_timeout_ms: int
def __init__(
self,
instance_id: UUID,
core_version: VersionDescriptor,
protocol_version: VersionDescriptor = PROTOCOL_VERSION,
heartbeat_interval_ms: int = 1000,
heartbeat_timeout_ms: int = 5000,
) -> None:
self.instance_id: UUID = instance_id
self.core_version: VersionDescriptor = core_version
self.protocol_version: VersionDescriptor = protocol_version
self.heartbeat_interval_ms: int = heartbeat_interval_ms
self.heartbeat_timeout_ms: int = heartbeat_timeout_ms
def to_json(self) -> JsonDynaLabHello:
return JsonDynaLabHello.model_validate(self)
@dataclass(slots=True)
class ConnectorHello:
connector_uuid: UUID
protocol_version: VersionDescriptor
connector_name: str
connector_version: str
signals: list[SignalDescriptor]
def __init__(
self,
connector_uuid: UUID,
connector_name: str,
connector_version: str,
signals: list[SignalDescriptor],
protocol_version: VersionDescriptor = PROTOCOL_VERSION,
) -> None:
self.connector_uuid: UUID = connector_uuid
self.protocol_version: VersionDescriptor = protocol_version
self.connector_name: str = connector_name
self.connector_version: str = connector_version
self.signals: list[SignalDescriptor] = signals
def to_json(self) -> JsonConnectorHello:
return JsonConnectorHello.model_validate(self)
@dataclass(slots=True)
class HandshakeAccepted:
accepted_signals: list[UUID]
def __init__(self, accepted_signals: list[UUID]) -> None:
self.accepted_signals: list[UUID] = accepted_signals
def to_json(self) -> JsonHandshakeAccepted:
return JsonHandshakeAccepted.model_validate(self)
@dataclass(slots=True)
class HandshakeRejected:
reason: str
def __init__(self, reason: str) -> None:
self.reason: str = reason
def to_json(self) -> JsonHandshakeRejected:
return JsonHandshakeRejected.model_validate(self)
class JsonSignalDescriptor(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: UUID id: UUID
name: str name: str
type: Literal["number", "binary"] type: Literal["number", "binary"]
@@ -20,18 +134,41 @@ class SignalDescriptor(BaseModel):
timeout_ms: int = 2000 timeout_ms: int = 2000
origin: Literal["source", "derived"] = "source" origin: Literal["source", "derived"] = "source"
def to_pure(self) -> SignalDescriptor:
return SignalDescriptor(
self.id,
self.name,
self.type,
self.min_value,
self.max_value,
self.unit,
self.timeout_ms,
self.origin,
)
class DynaLabHello(BaseModel):
class JsonDynaLabHello(BaseModel):
model_config = ConfigDict(from_attributes=True)
type: Literal["dynalab_hello"] = "dynalab_hello" type: Literal["dynalab_hello"] = "dynalab_hello"
instance_id: UUID instance_id: UUID
core_version: VersionDescriptor core_version: VersionDescriptor
protocol_version: VersionDescriptor = PROTOCOL_VERSION protocol_version: VersionDescriptor = PROTOCOL_VERSION
heartbeat_interval_ms: int = Field(default_factory=1000, ge=0) heartbeat_interval_ms: int = Field(default=1000, ge=0)
heartbeat_timeout_ms: int = Field(default_factory=5000, ge=0) heartbeat_timeout_ms: int = Field(default=5000, ge=0)
def to_pure(self) -> DynaLabHello:
return DynaLabHello(
self.instance_id,
self.core_version,
self.protocol_version,
self.heartbeat_interval_ms,
self.heartbeat_timeout_ms,
)
class ConnectorHello(BaseModel): class JsonConnectorHello(BaseModel):
model_config = ConfigDict(from_attributes=True)
type: Literal["connector_hello"] = "connector_hello" type: Literal["connector_hello"] = "connector_hello"
connector_uuid: UUID connector_uuid: UUID
protocol_version: VersionDescriptor = PROTOCOL_VERSION protocol_version: VersionDescriptor = PROTOCOL_VERSION
@@ -39,14 +176,31 @@ class ConnectorHello(BaseModel):
connector_name: str connector_name: str
connector_version: str connector_version: str
signals: list[SignalDescriptor] signals: list[JsonSignalDescriptor]
def to_pure(self) -> ConnectorHello:
return ConnectorHello(
connector_uuid=self.connector_uuid,
connector_name=self.connector_name,
connector_version=self.connector_version,
signals=[signal.to_pure() for signal in self.signals],
protocol_version=self.protocol_version,
)
class HandshakeAccepted(BaseModel): class JsonHandshakeAccepted(BaseModel):
model_config = ConfigDict(from_attributes=True)
type: Literal["handshake_accepted"] = "handshake_accepted" type: Literal["handshake_accepted"] = "handshake_accepted"
accepted_signals: list[UUID] accepted_signals: list[UUID]
def to_pure(self) -> HandshakeAccepted:
return HandshakeAccepted(self.accepted_signals)
class HandshakeRejected(BaseModel):
class JsonHandshakeRejected(BaseModel):
model_config = ConfigDict(from_attributes=True)
type: Literal["handshake_rejected"] = "handshake_rejected" type: Literal["handshake_rejected"] = "handshake_rejected"
reason: str reason: str
def to_pure(self) -> HandshakeRejected:
return HandshakeRejected(self.reason)
@@ -2,13 +2,35 @@
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com> # Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from dataclasses import dataclass
from typing import Literal from typing import Literal
from pydantic import BaseModel from pydantic import BaseModel, ConfigDict
@dataclass(slots=True)
class Heartbeat:
sequence: int
send_timestamp: int
return_timestamp: int | None
def __init__(
self, sequence: int, send_timestamp: int, return_timestamp: int | None = None
) -> None:
self.sequence: int = sequence
self.send_timestamp: int = send_timestamp
self.return_timestamp: int | None = return_timestamp
def to_json(self) -> JsonHeartbeat:
return JsonHeartbeat.model_validate(self)
class Heartbeat(BaseModel): class JsonHeartbeat(BaseModel):
model_config = ConfigDict(from_attributes=True)
type: Literal["heartbeat"] = "heartbeat" type: Literal["heartbeat"] = "heartbeat"
sequence: int sequence: int
send_timestamp: int send_timestamp: int
return_timestamp: int | None = None return_timestamp: int | None = None
def to_pure(self) -> Heartbeat:
return Heartbeat(self.sequence, self.send_timestamp, self.return_timestamp)
@@ -0,0 +1,55 @@
# 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
"""Aggregate packet types used at package boundaries.
Leaf packet modules deliberately do not import this module. Keeping the
aggregate unions here prevents the package re-export layer from becoming part
of a packet model's dependency graph.
"""
from typing import Annotated
from pydantic import Field
from dynalab_core.protocols.packets.data import (
JsonValueBatch,
JsonValueDescriptor,
ValueBatch,
ValueDescriptor,
)
from dynalab_core.protocols.packets.handshake import (
ConnectorHello,
DynaLabHello,
HandshakeAccepted,
HandshakeRejected,
JsonConnectorHello,
JsonDynaLabHello,
JsonHandshakeAccepted,
JsonHandshakeRejected,
)
from dynalab_core.protocols.packets.heartbeat import Heartbeat, JsonHeartbeat
ProtocolMessage = Annotated[
DynaLabHello
| ConnectorHello
| HandshakeAccepted
| HandshakeRejected
| Heartbeat
| ValueDescriptor
| ValueBatch,
Field(discriminator="type"),
]
JsonProtocolMessage = Annotated[
JsonDynaLabHello
| JsonConnectorHello
| JsonHandshakeAccepted
| JsonHandshakeRejected
| JsonHeartbeat
| JsonValueDescriptor
| JsonValueBatch,
Field(discriminator="type"),
]
+13 -6
View File
@@ -13,6 +13,13 @@ from rich.logging import RichHandler
from dynalab_core.protocols.common import VersionDescriptor from dynalab_core.protocols.common import VersionDescriptor
from dynalab_core.protocols.constants import PROTOCOL_VERSION from dynalab_core.protocols.constants import PROTOCOL_VERSION
from dynalab_core.protocols.json.wire import read_message, write_message from dynalab_core.protocols.json.wire import read_message, write_message
from dynalab_core.protocols.packets import (
JsonConnectorHello,
JsonDynaLabHello,
JsonHeartbeat,
JsonValueBatch,
JsonValueDescriptor,
)
from dynalab_core.protocols.packets.data import ValueBatch, ValueDescriptor from dynalab_core.protocols.packets.data import ValueBatch, ValueDescriptor
from dynalab_core.protocols.packets.handshake import ( from dynalab_core.protocols.packets.handshake import (
ConnectorHello, ConnectorHello,
@@ -124,13 +131,13 @@ def value_sender_thread(
tau * SINE_FREQUENCY_HZ * (perf_counter() - sine_start_time) tau * SINE_FREQUENCY_HZ * (perf_counter() - sine_start_time)
) )
timestamp = monotonic_ns() timestamp = monotonic_ns()
message = ValueBatch(values=[]) message = JsonValueBatch(values=[])
value1 = ValueDescriptor( value1 = JsonValueDescriptor(
signal_id=signal_id_1, signal_id=signal_id_1,
value=2.0 * sine_value, value=2.0 * sine_value,
timestamp=timestamp, timestamp=timestamp,
) )
value2 = ValueDescriptor( value2 = JsonValueDescriptor(
signal_id=signal_id_2, signal_id=signal_id_2,
value=sine_value, value=sine_value,
timestamp=timestamp, timestamp=timestamp,
@@ -203,7 +210,7 @@ async def perform_handshake(
except TimeoutError as exc: except TimeoutError as exc:
raise TimeoutError("Timed out waiting for server hello") from exc raise TimeoutError("Timed out waiting for server hello") from exc
if not isinstance(message, DynaLabHello): if not isinstance(message, JsonDynaLabHello):
raise RuntimeError(f"Expected DynaLabHello, received {type(message).__name__}") raise RuntimeError(f"Expected DynaLabHello, received {type(message).__name__}")
log.debug( log.debug(
@@ -211,7 +218,7 @@ async def perform_handshake(
message.instance_id, message.instance_id,
) )
connector_hello = ConnectorHello( connector_hello = JsonConnectorHello(
connector_uuid=uuid4(), connector_uuid=uuid4(),
protocol_version=PROTOCOL_VERSION, protocol_version=PROTOCOL_VERSION,
connector_name="Test connector", connector_name="Test connector",
@@ -252,7 +259,7 @@ async def receive_messages(
while True: while True:
message = await read_message(reader) message = await read_message(reader)
if isinstance(message, Heartbeat): if isinstance(message, JsonHeartbeat):
message.return_timestamp = monotonic_ms() message.return_timestamp = monotonic_ms()
await send_message( await send_message(
+10 -5
View File
@@ -10,7 +10,12 @@ from dynalab_core.config import CoreConfig
from dynalab_core.errors import JsonServerError from dynalab_core.errors import JsonServerError
from dynalab_core.protocols.constants import PROTOCOL_VERSION from dynalab_core.protocols.constants import PROTOCOL_VERSION
from dynalab_core.protocols.common import VersionDescriptor from dynalab_core.protocols.common import VersionDescriptor
from dynalab_core.protocols.packets.handshake import ConnectorHello, SignalDescriptor from dynalab_core.protocols.packets import JsonConnectorHello
from dynalab_core.protocols.packets.handshake import (
ConnectorHello,
JsonSignalDescriptor,
SignalDescriptor,
)
from test.common import find_available_port from test.common import find_available_port
@@ -141,12 +146,12 @@ def test_rejected_handshake_is_not_logged_as_connected(
port = find_available_port(8765) port = find_available_port(8765)
core = Core(CoreConfig(port=port)) core = Core(CoreConfig(port=port))
connector_uuid = uuid4() connector_uuid = uuid4()
connector_hello = ConnectorHello( connector_hello = JsonConnectorHello(
connector_uuid=connector_uuid, connector_uuid=connector_uuid,
protocol_version=VersionDescriptor(type="alpha", major=999, minor=0, patch=0), protocol_version=VersionDescriptor(type="alpha", major=999, minor=0, patch=0),
connector_name="Incompatible connector", connector_name="Incompatible connector",
connector_version="0.1.0-test", connector_version="0.1.0-test",
signals=[SignalDescriptor(id=uuid4(), name="Dummy signal", type="number")], signals=[JsonSignalDescriptor(id=uuid4(), name="Dummy signal", type="number")],
) )
with caplog.at_level(logging.DEBUG, logger="dynalab_core"): with caplog.at_level(logging.DEBUG, logger="dynalab_core"):
@@ -176,12 +181,12 @@ def test_json_server_waits_for_connection_handlers_on_stop(
port = find_available_port(8765) port = find_available_port(8765)
core = Core(CoreConfig(port=port)) core = Core(CoreConfig(port=port))
connector_uuid = uuid4() connector_uuid = uuid4()
connector_hello = ConnectorHello( connector_hello = JsonConnectorHello(
connector_uuid=connector_uuid, connector_uuid=connector_uuid,
protocol_version=PROTOCOL_VERSION, protocol_version=PROTOCOL_VERSION,
connector_name="Test connector", connector_name="Test connector",
connector_version="0.1.0-test", connector_version="0.1.0-test",
signals=[SignalDescriptor(id=uuid4(), name="Dummy signal", type="number")], signals=[JsonSignalDescriptor(id=uuid4(), name="Dummy signal", type="number")],
) )
with caplog.at_level(logging.DEBUG, logger="dynalab_core"): with caplog.at_level(logging.DEBUG, logger="dynalab_core"):