Skip to content

Commit

Permalink
Added composite serializers (#328)
Browse files Browse the repository at this point in the history
  • Loading branch information
francis-clairicia authored Jul 20, 2024
1 parent 2a6e31f commit b161843
Show file tree
Hide file tree
Showing 9 changed files with 682 additions and 2 deletions.
24 changes: 24 additions & 0 deletions docs/source/api/serializers/composite.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
*********************
Composite Serializers
*********************

.. automodule:: easynetwork.serializers.composite

.. autoclass:: StapledPacketSerializer
:members:
:inherited-members:

.. autoclass:: StapledIncrementalPacketSerializer
:members:
:inherited-members:

.. autoclass:: StapledBufferedIncrementalPacketSerializer
:members:
:inherited-members:

-----

.. seealso::

:doc:`/howto/advanced/serializer_composition`
Describes where and when a :term:`composite serializer` is used.
1 change: 1 addition & 0 deletions docs/source/api/serializers/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Serializers
:caption: Built-In Serializers

abc
composite
json
pickle
line
Expand Down
7 changes: 6 additions & 1 deletion docs/source/glossary.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ Glossary

.. seealso:: :class:`.StapledPacketConverter` class.

composite serializer
A :term:`serializer wrapper` that processes different data formats in input and output.

.. seealso:: :class:`.StapledPacketSerializer` class.

converter
An interface responsible for bridging the gap between the Python objects manipulated by the application/software
and the :term:`data transfer objects <data transfer object>` handled by the :term:`serializer`.
Expand Down Expand Up @@ -97,7 +102,7 @@ Glossary
if supported by the underlying transport layer.

serializer wrapper
A :term:`serializer` that transforms data coming from another :term:`serializer`.
A :term:`serializer` that (potentially) transforms data coming from another :term:`serializer`.

Example:

Expand Down
1 change: 1 addition & 0 deletions docs/source/howto/advanced/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ Advanced Guide
serializers
buffered_serializers
serializer_combinations
serializer_composition
standalone_servers
2 changes: 1 addition & 1 deletion docs/source/howto/advanced/serializer_combinations.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
********************************
How-to — Serializer combinations
How-to — Serializer Combinations
********************************

.. contents:: Table of Contents
Expand Down
70 changes: 70 additions & 0 deletions docs/source/howto/advanced/serializer_composition.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
*******************************
How-to — Serializer Composition
*******************************

.. contents:: Table of Contents
:local:

------

The Basics
==========

A :term:`composite serializer` fulfills the same need as a :term:`composite converter`,
which is to handle two disjoint formats between sent and received packet data.

This is typically done using the :class:`.StapledPacketSerializer`:

>>> import pickle
>>> from easynetwork.serializers import *
>>> from easynetwork.serializers.composite import *
>>> s = StapledPacketSerializer(sent_packet_serializer=PickleSerializer(), received_packet_serializer=JSONSerializer())
>>> s.deserialize(b'{"data": 42}')
{'data': 42}
>>> data = s.serialize({"data": 42})
>>> pickle.loads(data)
{'data': 42}

:class:`.StapledPacketSerializer` will return the correct implementation according to
the base class of ``sent_packet_serializer`` and ``received_packet_serializer``:

>>> from easynetwork.serializers.abc import *
>>>
>>> StapledPacketSerializer(sent_packet_serializer=PickleSerializer(), received_packet_serializer=JSONSerializer())
StapledPacketSerializer(...)
>>> isinstance(_, (AbstractIncrementalPacketSerializer, BufferedIncrementalPacketSerializer))
False
>>>
>>> StapledPacketSerializer(sent_packet_serializer=StringLineSerializer(), received_packet_serializer=JSONSerializer())
StapledIncrementalPacketSerializer(...)
>>> isinstance(_, AbstractIncrementalPacketSerializer)
True
>>>
>>> StapledPacketSerializer(sent_packet_serializer=JSONSerializer(), received_packet_serializer=StringLineSerializer())
StapledBufferedIncrementalPacketSerializer(...)
>>> isinstance(_, BufferedIncrementalPacketSerializer)
True


Use Case: Different Structure Between A Request And A Response
==============================================================

>>> from typing import NamedTuple
>>> from easynetwork.serializers import NamedTupleStructSerializer
>>> from easynetwork.serializers.composite import StapledPacketSerializer
>>> class Request(NamedTuple):
... type: int
... data: bytes
...
>>> class Response(NamedTuple):
... rc: int
... message: str
...
>>> s = StapledPacketSerializer(
... sent_packet_serializer=NamedTupleStructSerializer(Request, {"type": "B", "data": "1024s"}, encoding=None),
... received_packet_serializer=NamedTupleStructSerializer(Response, {"rc": "h", "message": "10s"}, encoding="utf8"),
... )
>>> s.serialize(Request(type=42, data=b"some data to send"))
b'*some data to send\x00\x00\x00\x00\x00...'
>>> s.deserialize(b"\x00\xc8OK\x00\x00\x00\x00\x00\x00\x00\x00")
Response(rc=200, message='OK')
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ ignore_missing_imports = true
[tool.pytest.ini_options]
asyncio_mode = "strict" # Avoid some unwanted behaviour
addopts = "--dist=worksteal --strict-markers -p 'no:anyio' -p 'no:benchmark'" # hatch CLI dependencies installs anyio
doctest_optionflags = "ELLIPSIS"
minversion = "7.1.2"
testpaths = ["tests"]
norecursedirs = ["scripts"]
Expand Down
Loading

0 comments on commit b161843

Please sign in to comment.