mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-23 19:44:20 +00:00
Rewrite package serialization (#53)
This commit is contained in:
parent
4d13ef47f5
commit
75e8fd774c
2 changed files with 94 additions and 14 deletions
|
@ -1,28 +1,44 @@
|
||||||
import pickle
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
|
import typing
|
||||||
|
|
||||||
|
|
||||||
class Package:
|
class Package:
|
||||||
"""A Royalnet package, the data type with which a :py:class:`royalnet.network.RoyalnetLink` communicates with a :py:class:`royalnet.network.RoyalnetServer` or another link. """
|
"""A Royalnet package, the data type with which a :py:class:`royalnet.network.RoyalnetLink` communicates with a :py:class:`royalnet.network.RoyalnetServer` or another link. """
|
||||||
|
|
||||||
def __init__(self, data, destination: str, source: str, *, source_conv_id: str = None, destination_conv_id: str = None):
|
def __init__(self,
|
||||||
|
data: typing.Union[None, int, float, str, list, dict],
|
||||||
|
*,
|
||||||
|
source: str,
|
||||||
|
destination: str,
|
||||||
|
source_conv_id: typing.Optional[str] = None,
|
||||||
|
destination_conv_id: typing.Optional[str] = None):
|
||||||
"""Create a Package.
|
"""Create a Package.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
data: The data that should be sent. Usually a :py:class:`royalnet.network.Message`.
|
data: The data that should be sent. Usually a :py:class:`royalnet.network.Message`.
|
||||||
destination: The ``link_type`` of the destination node, or alternatively, the ``nid`` of the node. Can also be the ``NULL`` value to send the message to nobody.
|
|
||||||
source: The ``nid`` of the node that created this Package.
|
source: The ``nid`` of the node that created this Package.
|
||||||
|
destination: The ``link_type`` of the destination node, or alternatively, the ``nid`` of the node. Can also be the ``NULL`` value to send the message to nobody.
|
||||||
source_conv_id: The conversation id of the node that created this package. Akin to the sequence number on IP packets.
|
source_conv_id: The conversation id of the node that created this package. Akin to the sequence number on IP packets.
|
||||||
destination_conv_id: The conversation id of the node that this Package is a reply to."""
|
destination_conv_id: The conversation id of the node that this Package is a reply to."""
|
||||||
# TODO: something is not right in these type hints. Check them.
|
# TODO: something is not right in these type hints. Check them.
|
||||||
self.data = data
|
self.data: typing.Union[None, int, float, str, list, dict] = data
|
||||||
self.destination: str = destination
|
|
||||||
self.source: str = source
|
self.source: str = source
|
||||||
self.source_conv_id: str = source_conv_id or str(uuid.uuid4())
|
self.source_conv_id: str = source_conv_id or str(uuid.uuid4())
|
||||||
self.destination_conv_id: str = destination_conv_id
|
self.destination: str = destination
|
||||||
|
self.destination_conv_id: typing.Optional[str] = destination_conv_id
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<Package to {self.destination}: {self.data.__class__.__name__}>"
|
return f"<Package {self.source} ({self.source_conv_id}) to {self.destination} ({self.destination_conv_id}>"
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if isinstance(other, Package):
|
||||||
|
return (self.data == other.data) and \
|
||||||
|
(self.source == other.source) and \
|
||||||
|
(self.destination == other.destination) and \
|
||||||
|
(self.source_conv_id == other.source_conv_id) and \
|
||||||
|
(self.destination_conv_id == other.destination_conv_id)
|
||||||
|
return False
|
||||||
|
|
||||||
def reply(self, data) -> "Package":
|
def reply(self, data) -> "Package":
|
||||||
"""Reply to this Package with another Package.
|
"""Reply to this Package with another Package.
|
||||||
|
@ -32,13 +48,63 @@ class Package:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The reply Package."""
|
The reply Package."""
|
||||||
return Package(data, self.source, self.destination,
|
return Package(data,
|
||||||
source_conv_id=str(uuid.uuid4()),
|
source=self.destination,
|
||||||
|
destination=self.source,
|
||||||
|
source_conv_id=self.destination_conv_id or str(uuid.uuid4()),
|
||||||
destination_conv_id=self.source_conv_id)
|
destination_conv_id=self.source_conv_id)
|
||||||
|
|
||||||
def pickle(self) -> bytes:
|
@staticmethod
|
||||||
""":py:mod:`pickle` this Package.
|
def from_dict(d) -> "Package":
|
||||||
|
"""Create a Package from a dictionary."""
|
||||||
|
if "source" not in d:
|
||||||
|
raise ValueError("Missing source field")
|
||||||
|
if "nid" not in d["source"]:
|
||||||
|
raise ValueError("Missing source.nid field")
|
||||||
|
if "conv_id" not in d["source"]:
|
||||||
|
raise ValueError("Missing source.conv_id field")
|
||||||
|
if "destination" not in d:
|
||||||
|
raise ValueError("Missing destination field")
|
||||||
|
if "nid" not in d["destination"]:
|
||||||
|
raise ValueError("Missing destination.nid field")
|
||||||
|
if "conv_id" not in d["destination"]:
|
||||||
|
raise ValueError("Missing destination.conv_id field")
|
||||||
|
if "data" not in d:
|
||||||
|
raise ValueError("Missing data field")
|
||||||
|
return Package(d["data"],
|
||||||
|
source=d["source"]["nid"],
|
||||||
|
destination=d["destination"]["nid"],
|
||||||
|
source_conv_id=d["source"]["conv_id"],
|
||||||
|
destination_conv_id=d["destination"]["conv_id"])
|
||||||
|
|
||||||
Returns:
|
def to_dict(self) -> dict:
|
||||||
The pickled package in form of bytes."""
|
"""Convert the Package into a dictionary."""
|
||||||
return pickle.dumps(self)
|
return {
|
||||||
|
"source": {
|
||||||
|
"nid": self.source,
|
||||||
|
"conv_id": self.source_conv_id
|
||||||
|
},
|
||||||
|
"destination": {
|
||||||
|
"nid": self.destination,
|
||||||
|
"conv_id": self.destination_conv_id
|
||||||
|
},
|
||||||
|
"data": self.data
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json_string(string: str) -> "Package":
|
||||||
|
"""Create a Package from a JSON string."""
|
||||||
|
return Package.from_dict(json.loads(string))
|
||||||
|
|
||||||
|
def to_json_string(self) -> str:
|
||||||
|
"""Convert the Package into a JSON string."""
|
||||||
|
return json.dumps(self.to_dict())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json_bytes(b: bytes) -> "Package":
|
||||||
|
"""Create a Package from UTF8-encoded JSON bytes."""
|
||||||
|
return Package.from_json_string(str(b, encoding="utf8"))
|
||||||
|
|
||||||
|
def to_json_bytes(self) -> bytes:
|
||||||
|
"""Convert the Package into UTF8-encoded JSON bytes."""
|
||||||
|
return bytes(self.to_json_string(), encoding="utf8")
|
||||||
|
|
14
tests/test_network.py
Normal file
14
tests/test_network.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import pytest
|
||||||
|
import uuid
|
||||||
|
from royalnet.network.packages import Package
|
||||||
|
|
||||||
|
|
||||||
|
def test_package_serialization():
|
||||||
|
pkg = Package("ciao",
|
||||||
|
source=str(uuid.uuid4()),
|
||||||
|
destination=str(uuid.uuid4()),
|
||||||
|
source_conv_id=str(uuid.uuid4()),
|
||||||
|
destination_conv_id=str(uuid.uuid4()))
|
||||||
|
assert pkg == Package.from_dict(pkg.to_dict())
|
||||||
|
assert pkg == Package.from_json_string(pkg.to_json_string())
|
||||||
|
assert pkg == Package.from_json_bytes(pkg.to_json_bytes())
|
Loading…
Reference in a new issue