A custom wrapper object around dict that allows attribute-style access to dictionary items and support for nested JSON/TOML data.
Project description
objdict-bf
objdict-bf
is a Python module that provides a wrapper class to conveniently manipulate dictionaries or dict-based json/toml nested structures using attribute-like syntax. It is intended mostly to ease manipulation of json/toml data, web requests and responses, configuration files, dynamic prototyping...
Features
- Attribute-style access to dictionary items (e.g.,
obj.key
instead ofobj['key']
). - Synchronization with the original dictionary if passed at instantiation.
- Utility methods for recursive conversion of nested structures to and from
objdict
anddict
. - Serialization and deserialization methods for both strings and files with json, toml 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.
Installation
pip install objdict-bf
Signature of the constructor
objdict(*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 json file path for dumping_backend
: either 'json', 'toml' 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 callable with 'self' in their signature (mocked object behavior).**kwargs
: key value pairs passed as kwargs to update the objdict
Usage
from objdict_bf import objdict
# Create an objdict with some initial data
data = objdict(
name='John',
age=30,
location='New York'
)
#Or synchronize with an existing dict
d={'name': 'John', 'age': 30, 'location': 'New York'}
data = objdict(d)
#Access data using attribute-style access
print(data.name) # Output: John
print(data.age) # Output: 30
#Modify data
data.age = 31
#Create a new key:value pair
data.job='developer'
#Changes are reflected on the original dict
print(d['age']) #Ouput: 31
print(d['job']) #Ouput: 'developer'
#Chaining attributes is supported for nested structures involving lists
d={
'profile':{
'name':'John',
'hobbies':[
{'type':'sport','title':'tennis'},
{'type':'music','title':'guitar playing'}
]
}
}
data = objdict(d)
print(data) #Output: the repr of the above dict
print(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 well
print(isinstance(data.profile.hobbies[1],objdict)) #Output: True
print(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 dicts
print(d is data.to_dict()) #Ouptut: True
print(isinstance(d['profile']['hobbies'][1], dict)) #Output: True
#-----------------------------Serialization-------------------------------
# Serialize to JSON string
json_string = data.dumps()
print(json_string)
#or use another backend for serialization
toml_string=data.dumps(_backend='toml')
print(toml_string)
#dump to a file
data.dump("my_json_file.json")
#or
data.dump("my_toml_file.toml",_backend='toml')
#make some more changes
data.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 again
data.dump()
# Deserialize from a string (new instance keeping reference to the chosen backend)
data = objdict.loads(json_string)
#or
data = 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")
#or
data = objdict.load("my_toml_file.toml",_backend='toml')
#class methods creating new instances 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 data
data.email="dummy.email@gmail.com"
data.user="dummy_username"
#dump changes to the file using the previously chosen backend
data.dump()
#-------------------Working with default value generators-------------------
#Default value (None) when accessing a missing key
obj=objdict(_use_default=True)
#Will set the value to None and won't raise a KeyError
print(obj.a) #Output: None
#Or, choose a default value
obj=objdict(_use_default=True,_default=3)
#Missing key will be initialized to 3 and returned
print(obj.a) #Output: 3
#Or pass a default value generator depending on the key (must have 'key' in its signature)
default_gen=lambda key: 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 instance
def default_gen(self):
if 'a' in self:
return self.a.value
else:
return objdict(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 keys
def child_instance(self):
return objdict(_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=3
print(obj) #Output: {'a':{'b':{'c':3}}}
#child elements inherit the chosen parent properties
print(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 parameter
obj=objdict(_use_default=True,_default=objdict.child_instance)
obj.a.b.c=3
print(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)
def add_to_a(self,b):
self.a+=b
#attach the function as attribute
obj.add_to_a=add_to_a
obj.add_to_a(3)
print(obj.a) #output 5
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file objdict_bf-0.1.22.tar.gz
.
File metadata
- Download URL: objdict_bf-0.1.22.tar.gz
- Upload date:
- Size: 11.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.10.12
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 793b99bd3a57d7198fe065ad9f795f86770e90a181e1eddb4dd2f31a3fd6654e |
|
MD5 | b91ec911e227068b1ec8cd3335efddf8 |
|
BLAKE2b-256 | e55d635e0f47457fc5081bc32a57889db754c40990987745e880d285a0ffded9 |
File details
Details for the file objdict_bf-0.1.22-py3-none-any.whl
.
File metadata
- Download URL: objdict_bf-0.1.22-py3-none-any.whl
- Upload date:
- Size: 9.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.10.12
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | a3da28358aebe1c69ab9f9158fe9a3ea7b0b0ba5c34f437e3463f2597c9796f7 |
|
MD5 | 29c59b6454896f6ad6138760c703ae43 |
|
BLAKE2b-256 | f9de75808bbaeb73d736a8b408ceb7af3f331212669c50ebb113289ee60ce72a |