Package: eventsourcing-orjsontranscoder
Welcome to the OrjsonTranscoder projectThis package provides aOrjsonTranscoderclass for use with
the Python eventsourcing library that uses theorjson
library.InstallationUse pip to install thestable distributionfrom the Python Package Index.$ pip install eventsourcing_orjsontranscoderIt is recommended to install Python packages into a Python virtual environment.This package uses Cython, so relevant build tools may need to be
installed before this package can be installed successfully.Synopsis>>>fromeventsourcing_orjsontranscoderimportOrjsonTranscoder,CTupleAsList>>>t=OrjsonTranscoder()>>>t.register(CTupleAsList())>>>d=t.encode((1,2,3))>>>db'{"_type_":"tuple_as_list","_data_":[1,2,3]}'>>>t.decode(d)(1,2,3)FeaturesMost importantly,OrjsonTranscodersupports custom transcoding of instances
oftupleand subclasses ofstr,int,dictandtuple. This is an
important improvement on the core library'sJSONTranscoderclass which convertstupletolistand loses type information for subclasses ofstr,int,dictandtuple.It is also faster thanJSONTranscoder, encoding approximately x3 faster
and decoding approximately x2 faster. This is less important than the preservation
of type information (see above) because latency in your application will
usually be dominated by database interactions. However, it's nice that it
is not slower.classencodedecodeOrjsonTranscoder6.8 μs13.8 μsJSON Transcoder20.1 μs25.7 μsThe above benchmark was performed on GitHub using the following object,
which is perhaps representative of the state of a domain event in an
event-sourced application.obj={"originator_id":uuid5(NAMESPACE_URL,"some_id"),"originator_version":123,"timestamp":DomainEvent.create_timestamp(),"a_str":"hello","b_int":1234567,"c_tuple":(1,2,3,4,5,6,7),"d_list":[1,2,3,4,5,6,7],"e_dict":{"a":1,"b":2,"c":3},"f_valueobj":CustomType2(CustomType1(UUID("b2723fe2c01a40d2875ea3aac6a09ff5"))),}Custom TranscodingsDefine custom transcodings for your custom value object types by subclassingCTranscoding. The prefixCis used to distinguish these classes from theTranscodingclasses provided by the core Python eventsourcing library.For example, consider the custom value objectMyIntbelow.classMyInt(int):def__repr__(self):returnf"{type(self).__name__}({super().__repr__()})"def__eq__(self,other):returntype(self)==type(other)andsuper().__eq__(other)You can define a custom transcoding forMyIntas a normal Python class in a
normal Python module (.pyfile) using theCTranscodingclass.classCMyIntAsInt(CTranscoding):deftype(self):returnMyIntdefname(self):return"myint_as_int"defencode(self,obj):returnint(obj)defdecode(self,data):returnMyInt(data)Alternatively for greater speed, you can define a custom transcoding forMyIntas a Cython extension type class in a Cython module (.pyxfile) using theCTranscodingextension type. See this project's Git repository for examples.from_eventsourcing_orjsontranscodercimportCTranscodingfrommy_domain_modelimportMyIntcdefclassCMyIntAsInt(CTranscoding):cpdefobjecttype(self):returnMyIntcpdefobjectname(self):return"myint_as_int"cpdefobjectencode(self,objectobj):returnint(obj)cpdefobjectdecode(self,objectdata):returnMyInt(data)If you define Cython modules, you will need to build them in-place before you
can use them. If you are distributing your code, you will also need to configure
your distribution to build the Cython module when your code is installed.$ cythonize -i my_transcodings.pyxSee the Cython documentation for more information about Cython.Using the OrjsonTranscoderTo use theOrjsonTranscoderclass in a Python eventsourcing application
object, override  theconstruct_transcoder()andregister_transcodings()methods.fromeventsourcing.applicationimportApplicationfromeventsourcing.domainimportAggregate,eventfromeventsourcing_orjsontranscoderimport(CDatetimeAsISO,CTupleAsList,CUUIDAsHex,OrjsonTranscoder,)classDogSchool(Application):defconstruct_transcoder(self):transcoder=OrjsonTranscoder()self.register_transcodings(transcoder)returntranscoderdefregister_transcodings(self,transcoder):transcoder.register(CDatetimeAsISO())transcoder.register(CTupleAsList())transcoder.register(CUUIDAsHex())transcoder.register(CMyIntAsInt())defregister_dog(self,name,age):dog=Dog(name,age)self.save(dog)returndog.iddefadd_trick(self,dog_id,trick):dog=self.repository.get(dog_id)dog.add_trick(trick)self.save(dog)defupdate_age(self,dog_id,age):dog=self.repository.get(dog_id)dog.update_age(age)self.save(dog)defget_dog(self,dog_id):dog=self.repository.get(dog_id)return{"name":dog.name,"tricks":tuple(dog.tricks),"age":dog.age}classDog(Aggregate):@event("Registered")def__init__(self,name,age):self.name=nameself.age=ageself.tricks=[]@event("TrickAdded")defadd_trick(self,trick):self.tricks.append(trick)@event("AgeUpdated")defupdate_age(self,age):self.age=agedeftest_dog_school():# Construct application object.school=DogSchool()# Evolve application state.dog_id=school.register_dog("Fido",MyInt(2))school.add_trick(dog_id,"roll over")school.add_trick(dog_id,"play dead")school.update_age(dog_id,MyInt(5))# Query application state.dog=school.get_dog(dog_id)assertdog["name"]=="Fido"assertdog["tricks"]==("roll over","play dead")assertdog["age"]==MyInt(5)# Select notifications.notifications=school.notification_log.select(start=1,limit=10)assertlen(notifications)==4See thelibrary docsfor more information about transcoding, but please note theCTranscoderuses a slightly
different API.DevelopersAfter cloning the repository, you can set up a virtual environment and
install dependencies by running the following command in the root
folder.$ make installAfter making changes, please run the tests.$ make testCheck the formatting of the code.$ make lintYou can automatically reformat the code by running the following command.$ make fmtIf the project dependencies change, you can update your packages by running
the following command.$ make update-packagesPlease submit changes for review by making a pull request.