Package: kadet
kadetEasily define and reuse complex Python objects that serialize into JSON or YAML.ExamplefromkadetimportBaseObjfrompprintimportpprintships=BaseObj()ships.root.type.container=["panamax","suezmax","post-panamax"]ships.root.type.carrier=["conventional","geared","gearless"]ships.root.type.tanker=BaseObj.from_yaml("tankers.yml")pprint(ships.root)# output{'type':{'carrier':['conventional','geared','gearless'],'container':['panamax','suezmax','post-panamax'],'tanker':['oil','liquified-gas','chemical']}}InstallationInstall usingpip install kadet.OverviewBaseObjBaseObj implements the basic object that serializes into JSON or YAML.
Setting keys inself.rootmeans they will be serialized. Keys can be set as an hierarchy of attributes.Theself.body()method is reserved for setting self.root on instantiation.The example below:classMyApp(BaseObj):defbody(self):self.root.name="myapp"self.root.inner.foo="bar"self.root.list=[1,2,3]yaml.dump(MyApp().dump())serializes into:---name:myappinner:foo:barlist:-1-2-3Theself.new()method can be used to define a basic constructor.self.need()checks if a key is set and errors if it isn't (with an optional custom error message).self.optional()sets a key as optional. Usedefaultkeyword to set default value when not set.Bothself.new()andself.body()method accept theistypekeyword to validate value type on runtime.
Supportstypingtypes.kwargsthat are passed onto a new instance of BaseObj are always accessible viaself.kwargsself.new_with()is an utility method to callsuper().new()while passing kwargs to the super class.In this example, MyApp needsnameandfooto be passed as kwargs.classMyApp(BaseObj):defnew(self):self.need("name")self.need("foo",msg="please provide a value for foo")self.optional("baz")defbody(self):self.root.name=self.kwargs.nameself.root.inner.foo=self.kwargs.fooself.root.list=[1,2,3]obj=MyApp(name="myapp",foo="bar")Setting a skeletonDefining a large body with Python can be quite hard and repetitive to read and write.Theself.root_file()method allows importing a YAML/JSON file to setself.root.MyApp's skeleton can be set instead like this:#skel.yml---name:myappinner:foo:barlist:-1-2-3classMyApp(BaseObj):defnew(self):self.need("name")self.need("foo",msg="please provide a value for foo")self.root_file("path/to/skel.yml")Extending a MyApp's skeleton is possible just by implementingself.body():classMyApp(BaseObj):defnew(self):self.need("name")self.need("foo",msg="please provide a value for foo")self.root_file("path/to/skel.yml")defbody(self):self.set_replicas()self.root.metadata.labels={"app":"mylabel"}defset_replicas(self):self.root.spec.replicas=5InheritancePython inheritance will work as expected:classMyOtherApp(MyApp):defnew(self):super().new()# MyApp's new()self.need("size")defbody(self):super().body()#  we want to extend MyApp's bodyself.root.size=self.kwargs.sizedelself.root.list# get rid of "list"obj=MyOtherApp(name="otherapp1",foo="bar2",size=3)yaml.dump(obj.dump())serializes to:---name:otherapp1inner:foo:bar2replicas:5size:3BaseModelBaseModel integrates Kadet semantics withPydantic's BaseModel together with powerful data validation and type hinting features.
Just like in BaseObj, keys inself.rootwill be serialized, but kwargs is no longer necessary as BaseModel's parameters are set as attributes inself.Theself.body()method is reserved for setting self.root on instantiation.The example below:classBoat(BaseModel):name:str# Requiredlength:int# Requireddescription:str="I am a boat"# Default descriptiondefbody(self):self.root.name=self.nameself.root.details.length=self.lengthself.root.details.description=self.descriptionprint(yaml.dump(Boat(name="Boaty",length=600).dump()))---details:description:Iamaboatlength:600name:Boaty