Package: attrbox
attrboxAttribute-based data structures.Changelog-Issues-DocumentationWhy?I have common use cases where I want to improve python'sdictandlist:AttrDict: attribute-baseddictwith better merge and deep value accessAttrList:listthat broadcasts operations to its membersEnvironment: reading environment filesConfiguration: loading command-line arguments and configuration filesJSend: sending JSON responsesInstallpython-mpipinstallattrboxAttrDictAttrDictfeatures:Attribute Syntaxfordictsimilar toaccessing properties in JavaScript:thing.propmeansthing["prop"]for get / set / delete.NoKeyError: if a key is missing, just returnNone(likedict.get()).Deep Indexing: use a list of keys andintto get and set deeply nested values. This is similar tolodash.getexcept that only the array-like syntax is supported and you must use actualintto index acrosslistobjects.Deep Merge: combine twodictobjects by extending deeply-nested keys where possible. This is different than the newdictunion operator (PEP 584).fromattrboximportAttrDictitems=AttrDict(a=1,b=[{"c":{"d":5}}],e={"f":{"g":7}})items.a# => 1items.xisNone# => Trueitems.x=10items['x']# => 10items.get(["b",0,"c","d"])# => 5items<<={"e":{"f":{"g":20,"h":[30,40]}}}items.e.f.g# => 20items[['e','f','h',1]]# => 40Read more aboutAttrDictAttrListAttrListprovidesmember broadcast: performing operations on the list performs the operation on all the items in the list. I typically use this to achieve theComposite design pattern.fromattrboximportAttrDict,AttrListnumbers=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 aboutAttrListEnvironmentattrbox.envis similar topython-dotenv, but uses theAttrDictability to do deep indexing to allow for things like dotted variable names. Typically, you'll use it by callingattrbox.load_env()which will find the nearest.envfile and load it intoos.environ.Read more aboutattrbox.envConfigurationattrboxsupports loading configuration files from.json,.toml, and.envfiles. By default,load_config()looks for a keyimportsand will recursively import those files (relative to the current file) before loading the rest of the current file (data is merged usingAttrDict). 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 ofdocoptwith the flexibility ofAttrDict. By default,--configand<config>arguments will load the file using theload_config()"""Usage: prog.py [--help] [--version] [-c CONFIG] --file FILEOptions:--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"""defmain():args=parse_docopt(__doc__,version=__version__)args.file# has the value of --fileif__name__=="__main__":main()Building on top ofdocoptwe strip off leading dashes and convert them to underscores so that we can access the arguments asAttrDictattributes.Read more aboutattrbox.configJSendJSendis an approximate implementation of theJSendspecificationthat makes it easy to create standard JSON responses. The main difference is that I added anokattribute to make it easy to tell if there was a problem (failorerror).fromattrboximportJSenddefsome_function(arg1):result=JSend()# default is "success"ifnotis_good(arg1):# fail = controlled problemreturnresult.fail(message="You gone messed up.")try:result.success(data=process(arg1))exceptException:# error = uncontrolled problemreturnresult.error(message="We have a problem.")returnresultBecause theJSendobject is anAttrDict, it acts like adictin every other respect (e.g., it is JSON-serializable).Read more aboutJSendLicenseMIT License