Package: objdict-bf
objdict-bfobjdict-bfis a Python module that provides a wrapper class to conveniently manipulate dictionaries or dict-based nested structures using attribute-like syntax. It is intended mostly to ease manipulation of serialized data, web requests and responses, configuration files, dynamic prototyping...FeaturesAttribute-style access to dictionary items (e.g.,obj.keyinstead ofobj['key']).Synchronization with the original dictionary if passed at instantiation.Utility methods for recursive conversion of nested structures to and fromobjdictanddict.Serialization and deserialization methods for both strings and files with json, toml, yaml and jsonpickle backend support.Advanced default value attribution features for missing keys.optional object-like behavior, by auto-passing the objdict instance as 'self' to callable attributes having 'self' in their signature.Installationpipinstallobjdict-bfSignature of the constructorobjdict(*args,_use_default=False,_default=None,_file=None,_backend=None,_auto_self=False,**kwargs)Parameters:*args: either dicts, objdicts or iterables on key:value pairs. If the first arg is a dict, it will serve as the internal _data_dict of the objdict instance._use_default: boolean, determines if a default value is attributed to missing keys_default: can be any value or callable. If it is callable with adequate signature, this callable will be used to handle default values generation._file: reference to a file path for dumping (extension must match the backend used)_backend: either 'json', 'toml','yaml' or 'jsonpickle'. Determines the backend used for serialization/deserialization when dumping/loading (None defaults to 'json')._auto_self: boolean. Determines if the instance is auto-passed a 'self' to its callable attributes having 'self' in their signature (mocked object behavior).**kwargs: key value pairs passed as kwargs to update the objdictUsagefromobjdict_bfimportobjdict# Create an objdict with some initial datadata=objdict(name='John',age=30,location='New York')#Or synchronize with an existing dictd={'name':'John','age':30,'location':'New York'}data=objdict(d)#Access data using attribute-style accessprint(data.name)# Output: Johnprint(data.age)# Output: 30#Modify datadata.age=31#Create a new key:value pairdata.job='developer'#Changes are reflected on the original dictprint(d['age'])#Ouput: 31print(d['job'])#Ouput: 'developer'#Chaining attributes is supported for nested structures involving listsd={'profile':{'name':'John','hobbies':[{'type':'sport','title':'tennis'},{'type':'music','title':'guitar playing'}]}}data=objdict(d)print(data)#Output: the repr of the above dictprint(data.profile.hobbies[1].title)#Output: guitar playing#Conversion of dict items to their objdict version is automatic upon access.#The created objdicts will inherit the parent objdict settings, namely: _backend,_use_default, _default, _auto_self).#The objdict being essentially a wrapper interface on the initial dict,#this conversion is reflected in the initial dict content as wellprint(isinstance(data.profile.hobbies[1],objdict))#Output: Trueprint(isinstance(d['profile']['hobbies'][1],objdict))#Output: True#to_dict returns the underlying dict, converting recursively all objdicts found in the nested structure back to dictsprint(disdata.to_dict())#Ouptut: Trueprint(isinstance(d['profile']['hobbies'][1],dict))#Output: True#-----------------------------Serialization-------------------------------# Serialize to JSON stringjson_string=data.dumps()print(json_string)#or use another backend for serializationtoml_string=data.dumps(_backend='toml')print(toml_string)#dump to a filedata.dump("my_json_file.json")#ordata.dump("my_toml_file.toml",_backend='toml')#make some more changesdata.email="dummy.email@gmail.com"#the reference to the file and backend preference from the last dump is kept in the objdict instance so you don't have to pass them againdata.dump()# Deserialize from a string (new instance keeping reference to the chosen backend)data=objdict.loads(json_string)#ordata=objdict.loads(toml_string,_backend='toml')# Deserialize from a file (new instance keeping reference to the chosen file and backend)data=objdict.load("my_json_file.json")#ordata=objdict.load("my_toml_file.toml",_backend='toml')#Mismatching file extension and backend will throw an exception#any class method creating a new instance can be passed parameters accepted in the objdict constructor to control the properties of the created instance:data=objdict.loads(string,_backend='json',_use_default=True,_default=None,_auto_self=False)data=objdict.load(file,_backend='toml',_use_default=False,_auto_self=True)#update datadata.email="dummy.email@gmail.com"data.user="dummy_username"#dump changes to the file using the previously chosen backenddata.dump()#-------------------Working with default value generators-------------------#Default value (None) when accessing a missing keyobj=objdict(_use_default=True)#Will set the value to None and won't raise a KeyErrorprint(obj.a)#Output: None#Or, choose a default valueobj=objdict(_use_default=True,_default=3)#Missing key will be initialized to 3 and returnedprint(obj.a)#Output: 3#Or pass a default value generator depending on the key (must have 'key' in its signature)default_gen=lambdakey:f"Missing key:{key}"obj=objdict(_use_default=True,_default=default_gen)print(obj.a)#Output: "Missing key: a"print(obj.b)#Output: "Missing key: b"#Or pass a default value generator whose output depends on the current state/content of the objdict#Must have 'self' in its signature#Will use 'self' as the keyword refering to the current objdict instancedefdefault_gen(self):if'a'inself:returnself.a.valueelse:returnobjdict(value=5)obj=objdict(_use_default=True,_default=default_gen)print(obj.a)#Output: {'value':5}print(obj.b)#Output: 5#Accepted signature of default value generators are () ; (self,); (key,) ; (self,key)#This allows implementing context-aware and key-dependant logic for default value attribution.#Any other signature will be considered invalid and will fall back to assign the callable itself as the default value for all keys.#Example: Using a default value generator to automatically create new child objdict instances inheriting the parent's settings when accessing missing keysdefchild_instance(self):returnobjdict(_use_default=True,_default=child_instance,_backend=self._backend,_auto_self=self._auto_self)obj=objdict(_use_default=True,_default=child_instance,_backend='toml',_auto_self=True)obj.a.b.c=3print(obj)#Output: {'a':{'b':{'c':3}}}#child elements inherit the chosen parent propertiesprint(obj.a.b._backend)#Output: 'toml'print(obj.a.b._auto_self)#Output: True#The child_instance generator hard-coded above is already implemented as the objdict.child_instance static method which you may pass as _default parameterobj=objdict(_use_default=True,_default=objdict.child_instance)obj.a.b.c=3print(obj)#Output: {'a':{'b':{'c':3}}}#--------------------------------Mock objects-------------------------------#Using the objdict as a mocked object with context aware methods thanks to the _auto_self parameter which automatically passes the objdict instance as 'self' to callable attributes having 'self' as first parameter in their signature.obj=objdict(_auto_self=True)obj.a=2#create a function with 'self' as first parameter (any other name won't receive the instance)defadd_to_a(self,b):self.a+=b#attach the function as attributeobj.add_to_a=add_to_aobj.add_to_a(3)print(obj.a)#output 5LicenseThis project is licensed under the MIT License - see theLICENSEfile for details.