This repository has been archived by the owner on Feb 2, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 119
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #32 from bcj/v2.0
V2.0
- Loading branch information
Showing
23 changed files
with
1,965 additions
and
1,389 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,3 +4,4 @@ MANIFEST | |
build/* | ||
dist/* | ||
.tox | ||
attrdict.egg-info/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,9 +6,8 @@ AttrDict | |
.. image:: https://coveralls.io/repos/bcj/AttrDict/badge.png?branch=master | ||
:target: https://coveralls.io/r/bcj/AttrDict?branch=master | ||
|
||
|
||
AttrDict is a 2.6, 2.7, 3-compatible dictionary that allows its elements | ||
to be accessed both as keys and as attributes:: | ||
AttrDict is an MIT-licensed library that provides mapping objects that allow | ||
their elements to be accessed both as keys and as attributes:: | ||
|
||
> from attrdict import AttrDict | ||
> a = AttrDict({'foo': 'bar'}) | ||
|
@@ -17,18 +16,15 @@ to be accessed both as keys and as attributes:: | |
> a['foo'] | ||
'bar' | ||
|
||
With this, you can easily create convenient, hierarchical settings | ||
objects. | ||
|
||
:: | ||
Attribute access makes it easy to create convenient, hierarchical settings | ||
objects:: | ||
|
||
with open('settings.yaml', 'r') as fileobj: | ||
with open('settings.yaml') as fileobj: | ||
settings = AttrDict(yaml.safe_load(fileobj)) | ||
|
||
cursor = connect(**settings.db.credentials).cursor() | ||
|
||
cursor.execute("SELECT column FROM table"); | ||
|
||
cursor.execute("SELECT column FROM table;") | ||
|
||
Installation | ||
============ | ||
|
@@ -42,80 +38,110 @@ Or from Github:: | |
$ cd AttrDict | ||
$ python setup.py install | ||
|
||
Documentation | ||
============= | ||
|
||
Documentation is available at https://github.com/bcj/AttrDict | ||
|
||
Usage | ||
===== | ||
Creation | ||
-------- | ||
An empty AttrDict can be created with:: | ||
Basic Usage | ||
=========== | ||
AttrDict comes with three different classes, `AttrMap`, `AttrDict`, and | ||
`AttrDefault`. They are all fairly similar, as they all are MutableMappings ( | ||
read: dictionaries) that allow creating, accessing, and deleting key-value | ||
pairs as attributes. | ||
|
||
Valid Names | ||
----------- | ||
Any key can be used as an attribute as long as: | ||
|
||
#. The key represents a valid attribute (i.e., it is a string comprised only of | ||
alphanumeric characters and underscores that doesn't start with a number) | ||
#. The key represents a public attribute (i.e., it doesn't start with an | ||
underscore). This is done (in part) so that implementation changes between | ||
minor and micro versions don't force major version changes. | ||
#. The key does not shadow a class attribute (e.g., get). | ||
|
||
Attributes vs. Keys | ||
------------------- | ||
There is a minor difference between accessing a value as an attribute vs. | ||
accessing it as a key, is that when a dict is accessed as an attribute, it will | ||
automatically be converted to an Attr object. This allows you to recursively | ||
access keys:: | ||
|
||
> attr = AttrDict({'foo': {'bar': 'baz'}}) | ||
> attr.foo.bar | ||
'baz' | ||
|
||
a = AttrDict() | ||
Relatedly, by default, sequence types that aren't `bytes`, `str`, or `unicode` | ||
(e.g., lists, tuples) will automatically be converted to tuples, with any | ||
mappings converted to Attrs:: | ||
|
||
Or, you can pass an existing ``dict`` (or other type of ``Mapping`` object):: | ||
> attr = AttrDict({'foo': [{'bar': 'baz'}, {'bar': 'qux'}]}) | ||
> for sub_attr in attr.foo: | ||
> print(subattr.foo) | ||
'baz' | ||
'qux' | ||
|
||
a = AttrDict({'foo': 'bar'}) | ||
To get this recursive functionality for keys that cannot be used as attributes, | ||
you can replicate the behavior by calling the Attr object:: | ||
|
||
NOTE: Unlike ``dict``, AttrDict will not clone on creation. AttrDict's | ||
internal dictionary will be the same instance as the dict passed in. | ||
> attr = AttrDict({1: {'two': 3}}) | ||
> attr(1).two | ||
3 | ||
|
||
Access | ||
------ | ||
AttrDict can be used *exactly* like a normal dict:: | ||
Classes | ||
------- | ||
AttrDict comes with three different objects, `AttrMap`, `AttrDict`, and | ||
`AttrDefault`. | ||
|
||
> a = AttrDict() | ||
> a['foo'] = 'bar' | ||
> a['foo'] | ||
'bar' | ||
> '{foo}'.format(**a) | ||
'bar' | ||
> del a['foo'] | ||
> a.get('foo', 'default') | ||
'default' | ||
AttrMap | ||
^^^^^^^ | ||
The most basic implementation. Use this if you want to limit the number of | ||
invalid keys, or otherwise cannot use `AttrDict` | ||
|
||
AttrDict can also have it's keys manipulated as attributes to the object:: | ||
AttrDict | ||
^^^^^^^^ | ||
An Attr object that subclasses `dict`. You should be able to use this | ||
absolutely anywhere you can use a `dict`. While this is probably the class you | ||
want to use, there are a few caveats that follow from this being a `dict` under | ||
the hood. | ||
|
||
> a = AttrDict() | ||
> a.foo = 'bar' | ||
> a.foo | ||
'bar' | ||
> del a.foo | ||
The `copy` method (which returns a shallow copy of the mapping) returns a | ||
`dict` instead of an `AttrDict`. | ||
|
||
Both methods operate on the same underlying object, so operations are | ||
interchangeable. The only difference between the two methods is that | ||
where dict-style access would return a dict, attribute-style access will | ||
return an AttrDict. This allows recursive attribute-style access:: | ||
Recursive attribute access results in a shallow copy, so recursive assignment | ||
will fail (as you will be writing to a copy of that dictionary):: | ||
|
||
> a = AttrDict({'foo': {'bar': 'baz'}}) | ||
> a.foo.bar | ||
'baz' | ||
> a['foo'].bar | ||
AttributeError: 'dict' object has no attribute 'bar' | ||
> attr = AttrDict('foo': {}) | ||
> attr.foo.bar = 'baz' | ||
> attr.foo | ||
{} | ||
|
||
There are some valid keys that cannot be accessed as attributes. To be | ||
accessed as an attribute, a key must: | ||
Assignment as keys will still work:: | ||
|
||
* be a string | ||
> attr = AttrDict('foo': {}) | ||
> attr['foo']['bar'] = 'baz' | ||
> attr.foo | ||
{'bar': 'baz'} | ||
|
||
* start with an alphabetic character | ||
If either of these caveats are deal-breakers, or you don't need your object to | ||
be a `dict`, consider using `AttrMap` instead. | ||
|
||
* be comprised solely of alphanumeric characters and underscores | ||
AttrDefault | ||
^^^^^^^^^^^ | ||
At Attr object that behaves like a `defaultdict`. This allows on-the-fly, | ||
automatic key creation:: | ||
|
||
* not map to an existing attribute name (e.g., get, items) | ||
> attr = AttrDefault(int, {}) | ||
> attr.foo += 1 | ||
> attr.foo | ||
1 | ||
|
||
To access these attributes while retaining an AttrDict wrapper (or to | ||
dynamically access any key as an attribute):: | ||
AttrDefault also has a `pass_key` option that passes the supplied key to the | ||
`default_factory`:: | ||
|
||
> a = AttrDict({'_foo': {'bar': 'baz'}}) | ||
> a('_foo').bar | ||
'baz' | ||
> attr = AttrDefault(sorted, {}, pass_key=True) | ||
> attr.banana | ||
['a', 'a', 'a', 'b', 'n', 'n'] | ||
|
||
Merging | ||
------- | ||
AttrDicts can be merged with each other or other dict objects using the | ||
All three Attr classes can be merged with eachother or other Mappings using the | ||
``+`` operator. For conflicting keys, the right dict's value will be | ||
preferred, but in the case of two dictionary values, they will be | ||
recursively merged:: | ||
|
@@ -164,54 +190,6 @@ When merging an AttrDict with another mapping, this behavior will be disabled | |
if at least one of the merged items is an AttrDict that has set ``recursive`` | ||
to ``False``. | ||
|
||
DefaultDict | ||
=========== | ||
|
||
AttrDict supports defaultdict-style automatic creation of attributes:: | ||
|
||
> adict = AttrDict(default_factory=list) | ||
> adict.foo | ||
[] | ||
|
||
Furthermore, if ``pass_key=True``, then the key will be passed to the function | ||
used when creating the value:: | ||
|
||
> adict = AttrDict(default_factory=lambda value: value.upper(), pass_key=True) | ||
> adict.foo | ||
'FOO' | ||
|
||
load | ||
==== | ||
A common usage for AttrDict is to use it in combination with settings files to | ||
create hierarchical settings. attrdict comes with a load function to make this | ||
easier:: | ||
|
||
from attrdict import load | ||
|
||
settings = load('settings.json') | ||
|
||
By default, ``load`` uses ``json.load`` to load the settings file, but this can | ||
be overridden by passing ``load_function=YOUR_LOAD_FUNCTION``. | ||
|
||
``load`` supports loading from multiple files at once. This allows for | ||
overriding of default settings, e.g.:: | ||
|
||
from attrdict import load | ||
from yaml import safe_load | ||
|
||
# config.yaml = | ||
# emergency: | ||
# email: [email protected] | ||
# message: Something went wrong | ||
# | ||
# user.yaml = | ||
# emergency: | ||
# email: [email protected] | ||
settings = load('config.yaml', 'user.yaml', load_function=safe_load) | ||
|
||
assert settings.email == '[email protected]' | ||
assert settings.message == 'Something went wrong' | ||
|
||
License | ||
======= | ||
AttrDict is released under a MIT license. |
Oops, something went wrong.