Structure Serializer

Network packet serializer module based on structures.

class easynetwork.serializers.struct.StructSerializer

Bases: AbstractStructSerializer[tuple[Any, …], tuple[Any, …]]

Generic class to handle a tuple of data with a struct.Struct object.

Example

>>> s = StructSerializer(">ii")
>>> data = s.serialize((10, 20))
>>> data
b'\x00\x00\x00\n\x00\x00\x00\x14'
>>> s.deserialize(data)
(10, 20)

Note

If the endianness is not specified, the network byte-order is used:

>>> s = StructSerializer("qq")
>>> s.struct.format
'!qq'
final iter_values(packet: tuple[Any, ...], /) tuple[Any, ...]

Returns an object suitable for struct.Struct.pack().

See serialize() for details.

Parameters:

packet (tuple[Any, ...]) – The Python object to serialize.

Returns:

an iterable object yielding the structure values

Return type:

tuple[Any, …]

final from_tuple(packet_tuple: tuple[Any, ...], /) tuple[Any, ...]

Finishes the packet deserialization by parsing the tuple obtained by struct.Struct.unpack().

See deserialize() for details.

Parameters:

packet_tuple (tuple[Any, ...]) – A tuple of each elements extracted from the structure.

Returns:

the deserialized Python object.

Return type:

tuple[Any, …]

easynetwork.serializers.struct._T_NamedTuple

Type:    TypeVar

Invariant TypeVar bound to typing.NamedTuple.

class easynetwork.serializers.struct.NamedTupleStructSerializer

Bases: AbstractStructSerializer[_T_NamedTuple, _T_NamedTuple]

Generic class to handle a named tuple with a struct.Struct object.

Accepts classes created directly from collections.namedtuple() factory:

>>> import collections
>>> Point = collections.namedtuple("Point", ("x", "y"))

…or declared with typing.NamedTuple:

from typing import NamedTuple

class Point(NamedTuple):
    x: int
    y: int

They are used like this:

>>> s = NamedTupleStructSerializer(Point, {"x": "i", "y": "i"}, format_endianness=">")
>>> s.struct.format
'>ii'
>>> data = s.serialize(Point(x=10, y=20))
>>> data
b'\x00\x00\x00\n\x00\x00\x00\x14'
>>> s.deserialize(data)
Point(x=10, y=20)

Note

If the endianness is not specified, the network byte-order is used:

>>> s = NamedTupleStructSerializer(Point, {"x": "i", "y": "i"})
>>> s.struct.format
'!ii'
__init__(namedtuple_cls: type[_T_NamedTuple], field_formats: SupportsKeysAndGetItem[str, str], format_endianness: str = '', encoding: str | None = 'utf-8', unicode_errors: str = 'strict', strip_string_trailing_nul_bytes: bool = True, *, debug: bool = False) None
Parameters:
  • namedtuple_cls (type[_T_NamedTuple]) – A named tuple type.

  • field_formats (SupportsKeysAndGetItem[str, str]) – A mapping of string format for the namedtuple_cls fields.

  • format_endianness (str) – The endianness character. Defaults to empty string.

  • encoding (str | None) – String fields encoding. Can be disabled by setting it to None.

  • unicode_errors (str) – Controls how encoding errors are handled. Ignored if encoding is set to None.

  • strip_string_trailing_nul_bytes (bool) – If True (the default), removes \0 characters at the end of a string field.

  • debug (bool) – If True, add information to DeserializeError via the error_info attribute.

final iter_values(packet: _T_NamedTuple) _T_NamedTuple

Returns the named tuple to pack using struct.Struct.pack().

In most case, this method will directly return packet.

If there are string fields and encoding is not None, this method will return a shallow copy of the named tuple with the encoded strings.

Example

The named tuple:

>>> from typing import NamedTuple
>>> class Person(NamedTuple):
...     name: str
...     age: int

In application:

>>> s = NamedTupleStructSerializer(Person, {"name": "10s", "age": "I"})
>>> s.iter_values(Person(name="John", age=20))
Person(name=b'John', age=20)
Parameters:

packet (_T_NamedTuple) – The namedtuple_cls instance.

Returns:

a namedtuple_cls instance.

Return type:

_T_NamedTuple

final from_tuple(packet_tuple: tuple[Any, ...], /) _T_NamedTuple

Constructs the named tuple from the given tuple.

If there are string fields and encoding is not None, their values will be decoded.

If strip_string_trailing_nul_bytes was set to True, the "\0" characters at the end of the string fields, added for padding, will be removed.

Example

The named tuple:

>>> from typing import NamedTuple
>>> class Person(NamedTuple):
...     name: str
...     age: int

In application:

>>> s = NamedTupleStructSerializer(Person, {"name": "10s", "age": "I"})
>>> t = s.struct.unpack(b'John\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14')
>>> t
(b'John\x00\x00\x00\x00\x00\x00', 20)
>>> s.from_tuple(t)
Person(name='John', age=20)
Parameters:

packet_tuple (tuple[Any, ...]) – A tuple of each elements extracted from the structure.

Returns:

a namedtuple_cls instance.

Return type:

_T_NamedTuple

Abstract Structure Interface

class easynetwork.serializers.struct.AbstractStructSerializer

Bases: FixedSizePacketSerializer[_T_SentDTOPacket, _T_ReceivedDTOPacket]

A base class for structured data.

To use the serializer directly without additional layers, it is possible to create a subclass with the minimal requirements:

>>> class MyStructSerializer(AbstractStructSerializer):
...     __slots__ = ()
...     def iter_values(self, packet):
...         return packet
...     def from_tuple(self, packet_tuple):
...         return packet_tuple
...

And then:

>>> s = MyStructSerializer(">ii")
>>> data = s.serialize((10, 20))
>>> data
b'\x00\x00\x00\n\x00\x00\x00\x14'
>>> s.deserialize(data)
(10, 20)

This is an abstract class in order to allow you to include fancy structures like ctypes.Structure subclasses.

Note

If the endianness is not specified, the network byte-order is used:

>>> s = MyStructSerializer("qq")
>>> s.struct.format
'!qq'

See also

StructSerializer

Default implementation without additional layers.

NamedTupleStructSerializer

Specialization for named tuple instances.

__init__(format: str, *, debug: bool = False) None
Parameters:
abstractmethod iter_values(packet: _T_SentDTOPacket, /) Iterable[Any]

Returns an object suitable for struct.Struct.pack().

See serialize() for details.

Parameters:

packet (_T_SentDTOPacket) – The Python object to serialize.

Returns:

an iterable object yielding the structure values

Return type:

Iterable[Any]

abstractmethod from_tuple(packet_tuple: tuple[Any, ...], /) _T_ReceivedDTOPacket

Finishes the packet deserialization by parsing the tuple obtained by struct.Struct.unpack().

See deserialize() for details.

Parameters:

packet_tuple (tuple[Any, ...]) – A tuple of each elements extracted from the structure.

Returns:

the deserialized Python object.

Return type:

_T_ReceivedDTOPacket

final serialize(packet: _T_SentDTOPacket) bytes

Returns the structured data representation of the Python object packet.

Roughly equivalent to:

def serialize(self, packet):
    to_pack = self.iter_values(packet)
    return self.struct.pack(*to_pack)
Parameters:

packet (_T_SentDTOPacket) – The Python object to serialize.

Returns:

a byte sequence.

Return type:

bytes

final deserialize(data: bytes) _T_ReceivedDTOPacket

Creates a Python object representing the structure from data.

Roughly equivalent to:

def deserialize(self, data):
    unpacked_data = self.struct.unpack(data)
    return self.from_tuple(unpacked_data)
Parameters:

data (bytes) – The byte sequence to deserialize.

Raises:

DeserializeError – A struct.error have been raised.

Returns:

the deserialized Python object.

Return type:

_T_ReceivedDTOPacket

property struct: struct.Struct

The underlying struct.Struct instance. Read-only attribute.