Package: packify
PackifyThis is a simple package that allows one to serialize and deserialize
practically any data structure to and from bytes.InstallationpipinstallpackifyUsageUsage is simple: import the package and call the pack/unpack functions.frompackifyimportpack,unpackfromdecimalimportDecimaldata={123:432.1,"abc":"cba",b"abc":b"cba","None":None,'list':['123',123,b'123',],'tuple':('123',123,b'123',),'set':{'123',123,b'123',},'Decimal':Decimal('123.321'),}packed=pack(data)unpacked=unpack(packed)asserttype(packed)isbytesassertunpacked==dataThe following types are supported:intfloatDecimalstrbytesbytearrayNoneTypelisttuplesetdictAdditionally, a simple duck-type interface/protocol,Packable, is included.
Any more complex data structure can be handled if it implements thePackableinterface.Packableis defined as follows:@runtime_checkableclassPackable(Protocol):defpack(self)->bytes:"""Packs the instance into bytes."""...@classmethoddefunpack(cls,data:bytes,/,*,inject:dict={})->Packable:"""Unpacks an instance from bytes. Must accept dependencyinjection to unpack other Packable types."""...If a class that implementsPackableis used, then it needs to be included in
theinjectparameter for calls tounpack. For example:fromdataclassesimportdataclass,fieldfrompackifyimportpack,unpack@dataclassclassThing:data:str=field()amount:int=field()fraction:float=field()parts:list=field()def__eq__(self,other)->bool:returntype(self)istype(other)andself.pack()==other.pack()defpack(self)->bytes:returnpack((self.data,self.amount,self.fraction,self.parts))@classmethoddefunpack(cls,data:bytes,/,*,inject:dict={}):returncls(*unpack(data,inject={**globals(),**inject}))thing=Thing("hello world",123,420.69,['a',b'b',3])packed=pack(thing)unpacked=unpack(packed,inject={'Thing':Thing})assertunpacked==thing# alternately, the easier but less specific method is to copy globalsunpacked=unpack(packed,inject={**globals()})assertunpacked==thingAs long as the class implements thePackableprotocol, it can be included in
lists, sets, tuples, and dicts (assuming it is hashable for set or to be used as
a dict key), and it will just work.Technically, monkey-patching is also possible:importpackifypackify.SomeClass=SomeClasspacked=packify.pack(SomeClass())unpacked=packify.unpack(packed)This is not encouraged, but it is possible if you do not want to passinjectparameters. (Code should be easier to test when using theinjectparameter
instead of monkey-patching.)Thepackfunction will raise aUsageErrorif the data is not serializable,
and theunpackfunction will raise aUsageErrorif it is unable to find aPackableclass to unpack the relevant item.For convenience/use in annotations, aSerializableTypeis exported which
includes the above type information.Full documentation can be found indox.md, which was
generated automagically byautodox.TestsSince it is a simple package, there are only 7 tests, and they are mostly e2e
tests of both thepackandunpackfunctions. To run the tests, clone the
repository and use the following:pythontest/test_serialization.pyLicenseCopyleft (c) 2023 k98kurzPermission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyleft notice and this permission notice appear in
all copies.THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.