objdict-bf


Nameobjdict-bf JSON
Version 0.1.24 PyPI version JSON
download
home_pagehttps://github.com/B4PT0R/objdict
SummaryA custom wrapper object around dict that allows attribute-style access to dictionary items and support for serialization of nested data.
upload_time2024-01-14 11:25:49
maintainer
docs_urlNone
authorBaptiste Ferrand
requires_python>=3.6
license
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # objdict-bf

`objdict-bf` is 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...

## Features

- Attribute-style access to dictionary items (e.g., `obj.key` instead of `obj['key']`).
- Synchronization with the original dictionary if passed at instantiation.
- Utility methods for recursive conversion of nested structures to and from `objdict` and `dict`.
- 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.

## Installation

```bash
pip install objdict-bf
```

## Signature of the constructor

```python
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 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 objdict


## Usage

```python
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')

#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 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](LICENSE) file for details.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/B4PT0R/objdict",
    "name": "objdict-bf",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.6",
    "maintainer_email": "",
    "keywords": "",
    "author": "Baptiste Ferrand",
    "author_email": "bferrand.maths@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/9e/46/6b4582171b00f79a8c40346cc8a23a782afad51b759814796590b3ad850a/objdict_bf-0.1.24.tar.gz",
    "platform": null,
    "description": "# objdict-bf\n\n`objdict-bf` is 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...\n\n## Features\n\n- Attribute-style access to dictionary items (e.g., `obj.key` instead of `obj['key']`).\n- Synchronization with the original dictionary if passed at instantiation.\n- Utility methods for recursive conversion of nested structures to and from `objdict` and `dict`.\n- Serialization and deserialization methods for both strings and files with json, toml, yaml and jsonpickle backend support.\n- Advanced default value attribution features for missing keys. \n- optional object-like behavior, by auto-passing the objdict instance as 'self' to callable attributes having 'self' in their signature.\n\n## Installation\n\n```bash\npip install objdict-bf\n```\n\n## Signature of the constructor\n\n```python\nobjdict(*args,_use_default=False,_default=None,_file=None,_backend=None,_auto_self=False,**kwargs)\n```\n\nParameters:\n- `*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.\n- `_use_default`: boolean, determines if a default value is attributed to missing keys\n- `_default`: can be any value or callable. If it is callable with adequate signature, this callable will be used to handle default values generation.\n- `_file`: reference to a file path for dumping (extension must match the backend used)\n- `_backend`: either 'json', 'toml','yaml' or 'jsonpickle'. Determines the backend used for serialization/deserialization when dumping/loading (None defaults to 'json').\n- `_auto_self`: boolean. Determines if the instance is auto-passed a 'self' to its callable attributes having 'self' in their signature (mocked object behavior).\n- `**kwargs`: key value pairs passed as kwargs to update the objdict\n\n\n## Usage\n\n```python\nfrom objdict_bf import objdict\n\n# Create an objdict with some initial data\ndata = objdict(\n    name='John',\n    age=30,\n    location='New York'\n)\n\n#Or synchronize with an existing dict\nd={'name': 'John', 'age': 30, 'location': 'New York'}\ndata = objdict(d)\n\n#Access data using attribute-style access\nprint(data.name)  # Output: John\nprint(data.age)   # Output: 30\n\n#Modify data\ndata.age = 31\n\n#Create a new key:value pair\ndata.job='developer'\n\n#Changes are reflected on the original dict\nprint(d['age']) #Ouput: 31\nprint(d['job']) #Ouput: 'developer'\n\n#Chaining attributes is supported for nested structures involving lists\nd={\n    'profile':{\n        'name':'John',\n        'hobbies':[\n            {'type':'sport','title':'tennis'},\n            {'type':'music','title':'guitar playing'}\n        ]\n    }\n}\ndata = objdict(d)\n\nprint(data) #Output: the repr of the above dict\nprint(data.profile.hobbies[1].title) #Output: guitar playing\n\n#Conversion of dict items to their objdict version is automatic upon access. \n#The created objdicts will inherit the parent objdict settings, namely: _backend,_use_default, _default, _auto_self).\n#The objdict being essentially a wrapper interface on the initial dict,  \n#this conversion is reflected in the initial dict content as well\n\nprint(isinstance(data.profile.hobbies[1],objdict)) #Output: True\nprint(isinstance(d['profile']['hobbies'][1],objdict)) #Output: True\n\n#to_dict returns the underlying dict, converting recursively all objdicts found in the nested structure back to dicts\nprint(d is data.to_dict()) #Ouptut: True\nprint(isinstance(d['profile']['hobbies'][1], dict)) #Output: True \n\n#-----------------------------Serialization-------------------------------\n\n# Serialize to JSON string\njson_string = data.dumps()\nprint(json_string)\n#or use another backend for serialization \ntoml_string=data.dumps(_backend='toml')\nprint(toml_string)\n\n#dump to a file\ndata.dump(\"my_json_file.json\")\n#or\ndata.dump(\"my_toml_file.toml\",_backend='toml')\n\n#make some more changes\ndata.email=\"dummy.email@gmail.com\"\n\n#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\ndata.dump()\n\n# Deserialize from a string (new instance keeping reference to the chosen backend)\ndata = objdict.loads(json_string)\n#or\ndata = objdict.loads(toml_string,_backend='toml')\n\n\n# Deserialize from a file (new instance keeping reference to the chosen file and backend)\ndata = objdict.load(\"my_json_file.json\")\n#or\ndata = objdict.load(\"my_toml_file.toml\",_backend='toml')\n\n#Mismatching file extension and backend will throw an exception \n\n#any class method creating a new instance can be passed parameters accepted in the objdict constructor to control the properties of the created instance:\ndata = objdict.loads(string,_backend='json',_use_default=True,_default=None,_auto_self=False)\ndata = objdict.load(file,_backend='toml',_use_default=False,_auto_self=True)\n\n#update data\ndata.email=\"dummy.email@gmail.com\"\ndata.user=\"dummy_username\"\n\n#dump changes to the file using the previously chosen backend \ndata.dump()\n\n#-------------------Working with default value generators-------------------\n\n\n#Default value (None) when accessing a missing key\nobj=objdict(_use_default=True)\n\n#Will set the value to None and won't raise a KeyError\nprint(obj.a) #Output: None\n\n#Or, choose a default value\nobj=objdict(_use_default=True,_default=3)\n#Missing key will be initialized to 3 and returned\nprint(obj.a) #Output: 3\n\n#Or pass a default value generator depending on the key (must have 'key' in its signature)\ndefault_gen=lambda key: f\"Missing key: {key}\" \nobj=objdict(_use_default=True,_default=default_gen)\nprint(obj.a) #Output: \"Missing key: a\"\nprint(obj.b) #Output: \"Missing key: b\"\n\n#Or pass a default value generator whose output depends on the current state/content of the objdict\n#Must have 'self' in its signature\n#Will use 'self' as the keyword refering to the current objdict instance\ndef default_gen(self):\n    if 'a' in self:\n        return self.a.value\n    else:\n        return objdict(value=5)\n        \nobj=objdict(_use_default=True,_default=default_gen)\nprint(obj.a) #Output: {'value':5}\nprint(obj.b) #Output: 5\n\n#Accepted signature of default value generators are () ; (self,); (key,) ; (self,key)\n#This allows implementing context-aware and key-dependant logic for default value attribution. \n#Any other signature will be considered invalid and will fall back to assign the callable itself as the default value for all keys.\n\n#Example: Using a default value generator to automatically create new child objdict instances inheriting the parent's settings when accessing missing keys\ndef child_instance(self):\n    return objdict(_use_default=True,_default=child_instance,_backend=self._backend,_auto_self=self._auto_self)\n\nobj=objdict(_use_default=True,_default=child_instance,_backend='toml',_auto_self=True)\nobj.a.b.c=3\nprint(obj) #Output: {'a':{'b':{'c':3}}}\n#child elements inherit the chosen parent properties\nprint(obj.a.b._backend) #Output: 'toml'\nprint(obj.a.b._auto_self) #Output: True\n\n#The child_instance generator hard-coded above is already implemented as the objdict.child_instance static method which you may pass as _default parameter\nobj=objdict(_use_default=True,_default=objdict.child_instance)\nobj.a.b.c=3\nprint(obj) #Output: {'a':{'b':{'c':3}}}\n\n#--------------------------------Mock objects-------------------------------\n\n#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.\n\nobj=objdict(_auto_self=True)\nobj.a=2\n\n#create a function with 'self' as first parameter (any other name won't receive the instance)\ndef add_to_a(self,b):\n    self.a+=b\n\n#attach the function as attribute\nobj.add_to_a=add_to_a\nobj.add_to_a(3)\nprint(obj.a) #output 5\n\n```\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "A custom wrapper object around dict that allows attribute-style access to dictionary items and support for serialization of nested data.",
    "version": "0.1.24",
    "project_urls": {
        "Homepage": "https://github.com/B4PT0R/objdict"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "5a87a4f262ca9b3045c3e92fd44117192bfc6ef7a876faaa23dd0f82b21172a1",
                "md5": "9484c01ec88866fb92eb283bb1778a7a",
                "sha256": "62baae1fe236cdec2f624588813322d97c2b48b6add5e13fdba2e83ed3662ca5"
            },
            "downloads": -1,
            "filename": "objdict_bf-0.1.24-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "9484c01ec88866fb92eb283bb1778a7a",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.6",
            "size": 10032,
            "upload_time": "2024-01-14T11:25:47",
            "upload_time_iso_8601": "2024-01-14T11:25:47.217882Z",
            "url": "https://files.pythonhosted.org/packages/5a/87/a4f262ca9b3045c3e92fd44117192bfc6ef7a876faaa23dd0f82b21172a1/objdict_bf-0.1.24-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "9e466b4582171b00f79a8c40346cc8a23a782afad51b759814796590b3ad850a",
                "md5": "0529be31fce42df98e2e361210f284f8",
                "sha256": "bd5c76495bb18fc6d36ee5c089ca4c17309356f64c01df3d9a61c336a24f69fe"
            },
            "downloads": -1,
            "filename": "objdict_bf-0.1.24.tar.gz",
            "has_sig": false,
            "md5_digest": "0529be31fce42df98e2e361210f284f8",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.6",
            "size": 11959,
            "upload_time": "2024-01-14T11:25:49",
            "upload_time_iso_8601": "2024-01-14T11:25:49.060213Z",
            "url": "https://files.pythonhosted.org/packages/9e/46/6b4582171b00f79a8c40346cc8a23a782afad51b759814796590b3ad850a/objdict_bf-0.1.24.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-01-14 11:25:49",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "B4PT0R",
    "github_project": "objdict",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "objdict-bf"
}
        
Elapsed time: 0.39371s