attrbox: attribute-based data structures
Why?
I have common use cases where I want to improve python's dict and list:
AttrDict: attribute-baseddictwith better merge and deep value accessAttrList:listthat broadcasts operations to its members- Environment: reading environment files
- Configuration: loading command-line arguments and configuration files
JSend: sending JSON responses
Install
python -m pip install attrbox
AttrDict
AttrDict features:
-
Attribute Syntax for
dictsimilar to accessing properties in JavaScript:thing.propmeansthing["prop"]for get / set / delete. -
No
KeyError: if a key is missing, just returnNone(likedict.get()). -
Deep Indexing: use a list of keys and
intto get and set deeply nested values. This is similar to lodash.get except that only the array-like syntax is supported and you must use actualintto index acrosslistobjects. -
Deep Merge: combine two
dictobjects by extending deeply-nested keys where possible. This is different than the newdictunion operator (PEP 584).
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
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.
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]
Environment
attrbox.env is similar to 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 .env file and load it into os.environ.
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 with the flexibility of AttrDict. By default, --config and <config> arguments will load the file using the load_config()
"""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
JSend
JSend is an approximate implementation of the JSend specification 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).
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).
License
The vendorized version of docopt-ng is also licensed under the MIT License.
Metadata
Release files for attrbox 0.1.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| attrbox-0.1.6.tar.gz | 32.3 kB | Without provenance | Details for attrbox-0.1.6.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| attrbox-0.1.6-py3-none-any.whl | Python 3 | none | any | Details for attrbox-0.1.6-py3-none-any.whl |
Total release size: 63.2 kB
Release files / attrbox-0.1.6.tar.gz
| Download URL | attrbox-0.1.6.tar.gz |
|---|---|
| Size | 32.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
499098f61f32209b46e82cc885c1d3a2e078fbf3c2b7381b7980510f22ff05c9
|
|
BLAKE2b-256 checksum How to use checksums |
4f647b33735c69bb07086383e8c6a05ad222f21bfb129ce494e8d5f74c35f236
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.2
|
Release files / attrbox-0.1.6-py3-none-any.whl
| Download URL | attrbox-0.1.6-py3-none-any.whl |
|---|---|
| Size | 30.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d935533c3057bcc90bc9a68c54c455a930e22780e0679b30cafe28bee0140a24
|
|
BLAKE2b-256 checksum How to use checksums |
cdb9f3e7a821647bff14e416a2da6130cdbff14c4f3be3c9657db1f06e71d2c2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.2
|
Security for attrbox 0.1.6
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.