Name | attrbox JSON |
Version |
0.1.5
JSON |
| download |
home_page | |
Summary | Attribute-based data structures. |
upload_time | 2023-06-05 11:37:16 |
maintainer | |
docs_url | None |
author | |
requires_python | >=3.8 |
license | MIT |
keywords |
attr
attributes
dict
list
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# attrbox
_Attribute-based data structures._
[![Build Status](https://img.shields.io/github/actions/workflow/status/metaist/attrbox/.github/workflows/ci.yaml?branch=main&style=for-the-badge)](https://github.com/metaist/attrbox/actions)
[![attrbox on PyPI](https://img.shields.io/pypi/v/attrbox.svg?color=blue&style=for-the-badge)](https://pypi.org/project/attrbox)
[![Supported Python versions](https://img.shields.io/pypi/pyversions/attrbox?style=for-the-badge)](https://pypi.org/project/attrbox)
[Changelog] - [Issues] - [Documentation]
[changelog]: https://github.com/metaist/attrbox/blob/main/CHANGELOG.md
[issues]: https://github.com/metaist/attrbox/issues
[documentation]: https://metaist.github.io/attrbox/
## Why?
I have common use cases where I want to improve python's `dict` and `list`:
- [`AttrDict`](#attrdict): attribute-based `dict` with better merge and deep value access
- [`AttrList`](#attrlist): `list` that broadcasts operations to its members
- [Environment](#environment): reading environment files
- [Configuration](#configuration): loading command-line arguments and configuration files
- [`JSend`](#jsend): sending JSON responses
## Install
```bash
python -m pip install attrbox
```
## AttrDict
`AttrDict` features:
- **Attribute Syntax** for `dict` similar to [accessing properties in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_objects#accessing_properties): `thing.prop` means `thing["prop"]` for get / set / delete.
- **No `KeyError`**: if a key is missing, just return `None` (like `dict.get()`).
- **Deep Indexing**: use a list of keys and `int` to get and set deeply nested values. This is similar to [lodash.get](https://lodash.com/docs/#get) except that only the array-like syntax is supported and you must use actual `int` to index across `list` objects.
- **Deep Merge**: combine two `dict` objects by extending deeply-nested keys where possible. This is different than the new `dict` union operator ([PEP 584](https://peps.python.org/pep-0584/)).
```python
from attrbox import AttrDict
items = AttrDict(a=1, b=[{"c": {"d": 5}}], e={"f": {"g": 7}})
items.a
# => 1
items.x is None
# => True
items.x = 10
items['x']
# => 10
items.get(["b", 0, "c", "d"])
# => 5
items <<= {"e": {"f": {"g": 20, "h": [30, 40]}}}
items.e.f.g
# => 20
items[['e', 'f', 'h', 1]]
# => 40
```
[Read more about `AttrDict`](https://metaist.github.io/attrbox/attrdict.html#attrbox.attrdict.AttrDict)
## AttrList
`AttrList` provides **member broadcast**: performing operations on the list performs the operation on all the items in the list. I typically use this to achieve the [Composite design pattern](https://en.wikipedia.org/wiki/Composite_pattern).
```python
from attrbox import AttrDict, AttrList
numbers = AttrList([complex(1, 2), complex(3, 4), complex(5, 6)])
numbers.real
# => [1.0, 3.0, 5.0]
words = AttrList(["Apple", "Bat", "Cat"])
words.lower()
# => ['apple', 'bat', 'cat']
items = AttrList([AttrDict(a=1, b=2), AttrDict(a=5)])
items.a
# => [1, 5]
items.b
# => [2, None]
```
[Read more about `AttrList`](https://metaist.github.io/attrbox/attrlist.html#attrbox.attrlist.AttrList)
## Environment
`attrbox.env` is similar to [python-dotenv](https://github.com/theskumar/python-dotenv), but uses the `AttrDict` ability to do deep indexing to allow for things like dotted variable names. Typically, you'll use it by calling `attrbox.load_env()` which will find the nearest <code>.env</code> file and load it into `os.environ`.
[Read more about `attrbox.env`](https://metaist.github.io/attrbox/env.html)
## Configuration
`attrbox` supports loading configuration files from `.json`, `.toml`, and `.env` files. By default, `load_config()` looks for a key `imports` and will recursively import those files (relative to the current file) before loading the rest of the current file (data is merged using `AttrDict`). This allows you to create templates or smaller configurations that build up to a larger configuration.
For CLI applications, `attrbox.parse_docopt()` let's you use the power of [`docopt`](https://github.com/docopt/docopt) with the flexibility of `AttrDict`. By default, `--config` and `<config>` arguments will load the file using the `load_config()`
```python
"""Usage: prog.py [--help] [--version] [-c CONFIG] --file FILE
Options:
--help show this message and exit
--version show the version number and exit
-c, --config CONFIG load this configuration file (supported: .toml, .json, .env)
--file FILE the file to process
"""
def main():
args = parse_docopt(__doc__, version=__version__)
args.file # has the value of --file
if __name__ == "__main__":
main()
```
Building on top of `docopt` we strip off leading dashes and convert them to underscores so that we can access the arguments as `AttrDict` attributes.
[Read more about `attrbox.config`](https://metaist.github.io/attrbox/config.html)
## JSend
`JSend` is an approximate implementation of the [`JSend` specification](https://labs.omniti.com/labs/jsend) that makes it easy to create standard JSON responses. The main difference is that I added an `ok` attribute to make it easy to tell if there was a problem (`fail` or `error`).
```python
from attrbox import JSend
def some_function(arg1):
result = JSend() # default is "success"
if not is_good(arg1):
# fail = controlled problem
return result.fail(message="You gone messed up.")
try:
result.success(data=process(arg1))
except Exception:
# error = uncontrolled problem
return result.error(message="We have a problem.")
return result
```
Because the `JSend` object is an `AttrDict`, it acts like a `dict` in every other respect (e.g., it is JSON-serializable).
[Read more about `JSend`](https://metaist.github.io/attrbox/jsend.html#attrbox.jsend.JSend)
## License
[MIT License](https://github.com/metaist/attrbox/blob/main/LICENSE.md)
Raw data
{
"_id": null,
"home_page": "",
"name": "attrbox",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "",
"keywords": "attr,attributes,dict,list",
"author": "",
"author_email": "Metaist LLC <metaist@metaist.com>",
"download_url": "https://files.pythonhosted.org/packages/87/f6/fe8d5f804a5fef520fa9e2f2e018827dcc0b267aa8449ca3322e4d27bbaa/attrbox-0.1.5.tar.gz",
"platform": null,
"description": "# attrbox\n\n_Attribute-based data structures._\n\n[![Build Status](https://img.shields.io/github/actions/workflow/status/metaist/attrbox/.github/workflows/ci.yaml?branch=main&style=for-the-badge)](https://github.com/metaist/attrbox/actions)\n[![attrbox on PyPI](https://img.shields.io/pypi/v/attrbox.svg?color=blue&style=for-the-badge)](https://pypi.org/project/attrbox)\n[![Supported Python versions](https://img.shields.io/pypi/pyversions/attrbox?style=for-the-badge)](https://pypi.org/project/attrbox)\n\n[Changelog] - [Issues] - [Documentation]\n\n[changelog]: https://github.com/metaist/attrbox/blob/main/CHANGELOG.md\n[issues]: https://github.com/metaist/attrbox/issues\n[documentation]: https://metaist.github.io/attrbox/\n\n## Why?\n\nI have common use cases where I want to improve python's `dict` and `list`:\n\n- [`AttrDict`](#attrdict): attribute-based `dict` with better merge and deep value access\n- [`AttrList`](#attrlist): `list` that broadcasts operations to its members\n- [Environment](#environment): reading environment files\n- [Configuration](#configuration): loading command-line arguments and configuration files\n- [`JSend`](#jsend): sending JSON responses\n\n## Install\n\n```bash\npython -m pip install attrbox\n```\n\n## AttrDict\n\n`AttrDict` features:\n\n- **Attribute Syntax** for `dict` similar to [accessing properties in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_objects#accessing_properties): `thing.prop` means `thing[\"prop\"]` for get / set / delete.\n\n- **No `KeyError`**: if a key is missing, just return `None` (like `dict.get()`).\n\n- **Deep Indexing**: use a list of keys and `int` to get and set deeply nested values. This is similar to [lodash.get](https://lodash.com/docs/#get) except that only the array-like syntax is supported and you must use actual `int` to index across `list` objects.\n\n- **Deep Merge**: combine two `dict` objects by extending deeply-nested keys where possible. This is different than the new `dict` union operator ([PEP 584](https://peps.python.org/pep-0584/)).\n\n```python\nfrom attrbox import AttrDict\n\nitems = AttrDict(a=1, b=[{\"c\": {\"d\": 5}}], e={\"f\": {\"g\": 7}})\nitems.a\n# => 1\nitems.x is None\n# => True\nitems.x = 10\nitems['x']\n# => 10\nitems.get([\"b\", 0, \"c\", \"d\"])\n# => 5\nitems <<= {\"e\": {\"f\": {\"g\": 20, \"h\": [30, 40]}}}\nitems.e.f.g\n# => 20\nitems[['e', 'f', 'h', 1]]\n# => 40\n```\n\n[Read more about `AttrDict`](https://metaist.github.io/attrbox/attrdict.html#attrbox.attrdict.AttrDict)\n\n## AttrList\n\n`AttrList` provides **member broadcast**: performing operations on the list performs the operation on all the items in the list. I typically use this to achieve the [Composite design pattern](https://en.wikipedia.org/wiki/Composite_pattern).\n\n```python\nfrom attrbox import AttrDict, AttrList\n\nnumbers = AttrList([complex(1, 2), complex(3, 4), complex(5, 6)])\nnumbers.real\n# => [1.0, 3.0, 5.0]\n\nwords = AttrList([\"Apple\", \"Bat\", \"Cat\"])\nwords.lower()\n# => ['apple', 'bat', 'cat']\n\nitems = AttrList([AttrDict(a=1, b=2), AttrDict(a=5)])\nitems.a\n# => [1, 5]\nitems.b\n# => [2, None]\n```\n\n[Read more about `AttrList`](https://metaist.github.io/attrbox/attrlist.html#attrbox.attrlist.AttrList)\n\n## Environment\n\n`attrbox.env` is similar to [python-dotenv](https://github.com/theskumar/python-dotenv), but uses the `AttrDict` ability to do deep indexing to allow for things like dotted variable names. Typically, you'll use it by calling `attrbox.load_env()` which will find the nearest <code>.env</code> file and load it into `os.environ`.\n\n[Read more about `attrbox.env`](https://metaist.github.io/attrbox/env.html)\n\n## Configuration\n\n`attrbox` supports loading configuration files from `.json`, `.toml`, and `.env` files. By default, `load_config()` looks for a key `imports` and will recursively import those files (relative to the current file) before loading the rest of the current file (data is merged using `AttrDict`). This allows you to create templates or smaller configurations that build up to a larger configuration.\n\nFor CLI applications, `attrbox.parse_docopt()` let's you use the power of [`docopt`](https://github.com/docopt/docopt) with the flexibility of `AttrDict`. By default, `--config` and `<config>` arguments will load the file using the `load_config()`\n\n```python\n\"\"\"Usage: prog.py [--help] [--version] [-c CONFIG] --file FILE\n\nOptions:\n --help show this message and exit\n --version show the version number and exit\n -c, --config CONFIG load this configuration file (supported: .toml, .json, .env)\n --file FILE the file to process\n\"\"\"\n\ndef main():\n args = parse_docopt(__doc__, version=__version__)\n args.file # has the value of --file\n\nif __name__ == \"__main__\":\n main()\n```\n\nBuilding on top of `docopt` we strip off leading dashes and convert them to underscores so that we can access the arguments as `AttrDict` attributes.\n\n[Read more about `attrbox.config`](https://metaist.github.io/attrbox/config.html)\n\n## JSend\n\n`JSend` is an approximate implementation of the [`JSend` specification](https://labs.omniti.com/labs/jsend) that makes it easy to create standard JSON responses. The main difference is that I added an `ok` attribute to make it easy to tell if there was a problem (`fail` or `error`).\n\n```python\nfrom attrbox import JSend\n\ndef some_function(arg1):\n result = JSend() # default is \"success\"\n\n if not is_good(arg1):\n # fail = controlled problem\n return result.fail(message=\"You gone messed up.\")\n\n try:\n result.success(data=process(arg1))\n except Exception:\n # error = uncontrolled problem\n return result.error(message=\"We have a problem.\")\n\n return result\n```\n\nBecause the `JSend` object is an `AttrDict`, it acts like a `dict` in every other respect (e.g., it is JSON-serializable).\n\n[Read more about `JSend`](https://metaist.github.io/attrbox/jsend.html#attrbox.jsend.JSend)\n\n## License\n\n[MIT License](https://github.com/metaist/attrbox/blob/main/LICENSE.md)\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Attribute-based data structures.",
"version": "0.1.5",
"project_urls": {
"Changelog": "https://github.com/metaist/attrbox/blob/main/CHANGELOG.md",
"Documentation": "https://metaist.github.io/attrbox/",
"Homepage": "https://github.com/metaist/attrbox",
"Repository": "https://github.com/metaist/attrbox.git"
},
"split_keywords": [
"attr",
"attributes",
"dict",
"list"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "c3334520765f25f1f4f9707a8b3244226b9cc63d2724054b22234f5f26aaf6bd",
"md5": "1c688948b0247d11c5cde01063c6c543",
"sha256": "ffea00298ecc7b4d109fc0fe71d9490ecd89b698abff152dc643a820ed8c5755"
},
"downloads": -1,
"filename": "attrbox-0.1.5-py3-none-any.whl",
"has_sig": false,
"md5_digest": "1c688948b0247d11c5cde01063c6c543",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 19161,
"upload_time": "2023-06-05T11:37:14",
"upload_time_iso_8601": "2023-06-05T11:37:14.587855Z",
"url": "https://files.pythonhosted.org/packages/c3/33/4520765f25f1f4f9707a8b3244226b9cc63d2724054b22234f5f26aaf6bd/attrbox-0.1.5-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "87f6fe8d5f804a5fef520fa9e2f2e018827dcc0b267aa8449ca3322e4d27bbaa",
"md5": "8de1a5ec074ca059f489e72e4e50af59",
"sha256": "7c5a898e176ad43bc7369caa1d6c737b2542e6cddb15fef38c032f44794b3d91"
},
"downloads": -1,
"filename": "attrbox-0.1.5.tar.gz",
"has_sig": false,
"md5_digest": "8de1a5ec074ca059f489e72e4e50af59",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 20715,
"upload_time": "2023-06-05T11:37:16",
"upload_time_iso_8601": "2023-06-05T11:37:16.349692Z",
"url": "https://files.pythonhosted.org/packages/87/f6/fe8d5f804a5fef520fa9e2f2e018827dcc0b267aa8449ca3322e4d27bbaa/attrbox-0.1.5.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-06-05 11:37:16",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "metaist",
"github_project": "attrbox",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "attrbox"
}