diff --git a/doc/reference/serialization.rst b/doc/reference/serialization.rst index eb9180b95..d5edf49b7 100644 --- a/doc/reference/serialization.rst +++ b/doc/reference/serialization.rst @@ -17,12 +17,12 @@ You can use the ``Serializer`` with classes of the following types: "Serializable", "ipv8/messaging/serialization.py", "Base class for all things serializable. Should support the instance method to_pack_list() and the class method from_unpack_list()." "Payload", "ipv8/messaging/payload.py", "Extension of the Serializable class with logic for pretty printing." "VariablePayload", "ipv8/messaging/lazy_payload.py", "Less verbose way to specify Payloads, at the cost of performance." - "dataclass_payload", "ipv8/messaging/payload_dataclass.py", "Use dataclasses to send messages, at the cost of control and performance." + "dataclass", "ipv8/messaging/payload_dataclass.py", "Use dataclasses to send messages, at the cost of control and performance." -Other than the ``dataclass_payload``, each of these serializable classes specifies a list of primitive data types it will serialize to and from. +Other than the ``dataclass``, each of these serializable classes specifies a list of primitive data types it will serialize to and from. The primitive data types are specified in the :ref:`data types` Section. -Each serializable class has to specify the following class members (``dataclass_payload`` does this automatically): +Each serializable class has to specify the following class members (``dataclass`` does this automatically): .. csv-table:: Serializable class members :header: "member", "description" @@ -34,7 +34,7 @@ Each serializable class has to specify the following class members (``dataclass_ As an example, we will now define four completely wire-format compatible messages using the four classes. Each of the messages will serialize to a (four byte) unsigned integer followed by an (two byte) unsigned short. -If the ``dataclass_payload`` had used normal ``int`` types, these would have been two signed 8-byte integers instead. +If the ``dataclass`` had used normal ``int`` types, these would have been two signed 8-byte integers instead. Each instance will have two fields: ``field1`` and ``field2`` corresponding to the integer and short. .. literalinclude:: serialization_1.py @@ -65,6 +65,20 @@ To show some of the differences, let's check out the output of the following scr | field1: 1 | field2: 2 + +Type hinting with the dataclass wrapper +--------------------------------------- + +For the ``@dataclass`` wrapper you need a special import construction to get type hinting: + +.. code-block:: python + + from dataclasses import dataclass + from ipv8.messaging.payload_dataclass import dataclass + +This will fool your IDE into providing type hints. + + .. _Datatypes Section: Datatypes @@ -162,4 +176,7 @@ It is recommended (but not obligatory) to have single payload messages store the :lines: 31,32,53,56 :dedent: 4 +If you are using the ``@dataclass`` wrapper you can specify the message identifier through an argument instead. +For example, ``@dataclass(msg_id=42)`` would set the message identifier to ``42``. + Of course, IPv8 also ships with various ``Community`` subclasses of its own, if you need inspiration. diff --git a/ipv8/messaging/payload_dataclass.py b/ipv8/messaging/payload_dataclass.py index e9a252354..fa2becbd3 100644 --- a/ipv8/messaging/payload_dataclass.py +++ b/ipv8/messaging/payload_dataclass.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass +from dataclasses import dataclass as ogdataclass from functools import partial from typing import TypeVar, Type, get_type_hints @@ -25,17 +25,16 @@ def type_map(t: Type) -> str: raise NotImplementedError(t, " unknown") -def dataclass_payload(cls=None, /, *, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, - msg_id=None): +def dataclass(cls=None, /, *, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, msg_id=None): """ Equivalent to ``@dataclass``, but also makes the wrapped class a ``VariablePayload``. See ``dataclasses.dataclass`` for argument descriptions. """ if cls is None: - return partial(dataclass_payload, init=init, repr=repr, eq=eq, order=order, unsafe_hash=unsafe_hash, + return partial(dataclass, init=init, repr=repr, eq=eq, order=order, unsafe_hash=unsafe_hash, frozen=frozen, msg_id=msg_id) - origin = dataclass(cls, init=init, repr=repr, eq=eq, order=order, unsafe_hash=unsafe_hash, frozen=frozen) + origin = ogdataclass(cls, init=init, repr=repr, eq=eq, order=order, unsafe_hash=unsafe_hash, frozen=frozen) class DataClassPayload(origin, VariablePayload): names = list(get_type_hints(origin).keys()) @@ -45,7 +44,8 @@ class DataClassPayload(origin, VariablePayload): setattr(DataClassPayload, "msg_id", msg_id) DataClassPayload.__name__ = origin.__name__ DataClassPayload.__qualname__ = origin.__qualname__ - return vp_compile(DataClassPayload) + out: ogdataclass = vp_compile(DataClassPayload) + return out -__all__ = ['dataclass_payload'] +__all__ = ['dataclass'] diff --git a/ipv8/test/messaging/test_payload_dataclass.py b/ipv8/test/messaging/test_payload_dataclass.py index 68d818da5..aa044ea54 100644 --- a/ipv8/test/messaging/test_payload_dataclass.py +++ b/ipv8/test/messaging/test_payload_dataclass.py @@ -1,55 +1,55 @@ -from dataclasses import dataclass, is_dataclass +from dataclasses import dataclass, dataclass as ogdataclass, is_dataclass from typing import List, TypeVar from ..base import TestBase -from ...messaging.payload_dataclass import dataclass_payload +from ...messaging.payload_dataclass import dataclass from ...messaging.serialization import default_serializer varlenH = TypeVar('varlenH') -@dataclass_payload +@dataclass class NativeBool: a: bool -@dataclass_payload +@dataclass class NativeInt: a: int -@dataclass_payload +@dataclass class NativeBytes: a: bytes -@dataclass_payload +@dataclass class NativeStr: a: str -@dataclass_payload +@dataclass class SerializerType: a: varlenH -@dataclass_payload +@dataclass class NestedType: a: NativeInt -@dataclass_payload +@dataclass class NestedListType: a: [NativeInt] -@dataclass_payload +@dataclass class NestedListTypeType: a: List[NativeInt] -@dataclass +@ogdataclass class Unknown: """ To whomever is reading this and wondering why dict is not supported: use a nested payload instead. @@ -57,32 +57,32 @@ class Unknown: a: dict -@dataclass_payload +@dataclass class A: a: int b: int -@dataclass_payload(eq=False) +@dataclass(eq=False) class FwdDataclass: a: int -@dataclass_payload +@dataclass class StripMsgId: a: int msg_id = 1 -@dataclass_payload(msg_id=1) +@dataclass(msg_id=1) class FwdMsgId: a: int -@dataclass_payload @dataclass +@ogdataclass class Everything: - @dataclass_payload + @dataclass class Item: a: bool @@ -294,7 +294,7 @@ def test_unknown_payload(self): """ Check if an unknown type raises an error. """ - self.assertRaises(NotImplementedError, dataclass_payload, Unknown) + self.assertRaises(NotImplementedError, dataclass, Unknown) def test_nestedlisttype_filled_payload(self): """