Package: pynary
Pynary – binary representation of python objectsPynaryis a simple library that allows you to represent python objects as binary data. The only module it uses isstructfrom the standard library.Unlike thepicklemodule, Pynary doesn't allow arbitraty code execution (by default). This though means that only
a small subset of python objects is supported (by default). Currently this
includes:NoneTypeboolint– u32 by defaultfloat– 64 bit (default) and 32 bitstrtuplelistsetdictAdditional types can be supported with acustom decoder and encoder.Basic usage# Import the default decoder and encoder set:frompynaryimportpyn# Prepare the object you want to encodeyour_object:object=...# Encode the objectencoded_object:bytes=pyn.dump(your_object)# Decode the objectdecoded_object:object=pyn.load(encoded_object)If your input data cannot be parsed, apynary.PYNEncoder.TypeMissmatchis
raised.Modifying the Decoder and EncoderWARNING:Modifying the decoder and the encoder can introduce security risks.
Just be aware of that and act accordingly.Adding types to the default parserIf you don't want to change the behaviour of existing types you can simply
use theadd_typemethod of thePYNEncoderandPYNDecoder.Here is an example for adding a custom class:frompynary.pynimport_pynclassMyClass:x:intdef_encode_my_object(enc:dict,my_object:MyClass)->bytes:returnenc[MyClass]["tag"]+int.to_bytes(my_object.x,4,"big")def_decode_my_object(_,b:bytes)->(MyClass,int):my_object=MyClass()my_object.x=int.from_bytes(b[:4],"big")returnmy_object,4_pyn.encoder.add_type(MyClass,_encode_my_object)_pyn.decoder.add_type(_decode_my_object)The encode function (which doesn't have to be named like this) always takes two
arguments and has to return abytesobject. The first one is the encoding
table provided by thePYNEncoder (_pyn.encoder)and the second one is the object
that is to be encoded.The decode function works similarly. It needs an encoding table from thePYNDecoder (_pyn.decoder)as it's first argument. We only need that if we need
to identify other types that are contained in our object (e.g. like in a list), so
we can omit it here. The second argument is thebytesobject which contains the
data we that corresponds to our object. Instead of only returning one thing here,
we have to return two: the decoded python object and how much space it took in the
encoded data in bytes. Since we gave our x a size of 4 bytes in the encoding function we can just return that.After that we add out encoding function to_pyn.encoderand our decoding function to_pyn.decoderwith theadd_typemethod. Note that the encoder's
method also requires us to add the type of the objects we want to encode, which in
this case isMyClass. For an object to be able to be parsed,type(object)must
returnMyClass. Objects of classes that inherit fromMyClasswill raise an
error if not handled seperately.It might also be a good idea to define a custom magic byte sequence to ensure
that data that is valid for our new format. This can be any sequence of bytes.magic:bytes=b"MyMagic"_pyn.encoder.magic=magic_pyn.decoder.magic=magicTo load or dump data use_pyn.encoder.dump()or_pyn.decoder.load(). If you
think that's a bit complicated you can create adumpand aloadvariable and
make them equal to their respective_pynfunction.dump:callable=_pyn.encoder.dumpload:callable=_pyn.decoder.loadNow you can use your custom parser just like you would use the default.importmy_pynmy_object=MyClass()my_object.x=12my_pyn.dump(my_object)>>>b'\x07MyMagic\t\x00\x00\x00\x0c'my_pyn.load(my_pyn.dump(my_object))>>><__main__.MyClassobjectat0x...>Change the default behaviourDetailed guide soon. If you want to know how now, just take a look at the source code. It's not well documented though...