Yaco

Yaco provides a dict like structure that can be serialized to & from yaml. Yaco objects behave as dictionaries but also allow attribute access (loosely based on this `recipe < http://code.activestate.com/recipes/473786/>`_). Sublevel dictionaries are automatically converted to Yaco objects, allowing sublevel attribute access, for example:

>>> x = Yaco()
>>> x.test = 1
>>> x.sub.test = 2
>>> x.sub.test
2

Note that sub-dictionaries do not need to be initialized. This has as a consequence that requesting uninitialized items automatically return an empty Yaco object (inherited from a dictionary).

Yaco can be found in the Python package index and is also part of the Moa source distribution

Autogenerating keys

An important feature (or annoyance) of Yaco is the auto generation of keys that are not present (yet). For example:

>>> x = Yaco()
>>> x.a.b.c.d = 1
>>> assert(x.a.b.c.d == 1)

works - a, b and c are assumed to be Yaco dictionaries and d is give value 1. This makes populating data structures easy.

It might also generate some confusion when querying for keys in the Yaco structure - if a key does not exists, it automatically comes back as an empy dict or Yaco object (renders as {}). This means that if it is easy to check if a certain ‘branch’ of a Yaco datastructure exists:

>>> x = Yaco()
>>> assert (not x.a.b)

but now the following works as well:

>>> assert('a' in x)
>>> assert('b' in x.a )

So, a safe way to test a data structure, without introducing extra branches is:

>>> x = Yaco()
>>> assert(not 'a' in x)

Todo: Need to find a more elegant way of testing without introducing data structures

class Yaco.PolyYaco(name='PY', files=[], pattern='*.config', leaf='')

A meta object that allows a composite Yaco object to be loaded from any number of different files which are kept as a stack of Yaco objects. If looking for a value, this object will check each of the layers in the stack and return the first value that it comes across.

Changes are only made to the toplevel object.

The goal is to have multiple configuration files, for example in:

/location/to/python/package/etc/config.yaml
/etc/APPLICATION.yaml
~/.config/APPLICATION/config.yaml

and have values in the latter file override those in the former. Saving changed values will also be done to the latter, but system and application wide settings can be maintained as well (manually for the time being).

load(leaf, files, pattern)
class Yaco.Yaco(data={}, leaf=None)
Originated from:
http://code.activestate.com/recipes/473786/
>>> v= Yaco()
>>> v.a = 1
>>> assert(v.a == 1)
>>> assert(v['a'] == 1)
>>> v= Yaco({'a':1})
>>> assert(v.a == 1)
>>> assert(v['a'] == 1)
get_data()

Prepare & parse data for export

>>> y = Yaco()
>>> y.a = 1
>>> y.b = 2
>>> y._c = 3
>>> assert(y._c == 3)
>>> d = y.get_data()
>>> assert('a' in d)
>>> assert('b' in d)
>>> assert(not 'c' in d)
>>> y._private = ['b']
>>> d = y.get_data()
>>> assert('a' in d)
>>> assert(not 'b' in d)
>>> assert(not '_c' in d)
load(from_file, leaf=None)

Load this dict from_file

Note - it can load the file into a leaf, instead of the root of this Yaco structure. Note - the leaf variable is a string, but may contain dots (which are automatically interpreted)

>>> import tempfile
>>> tf = tempfile.NamedTemporaryFile(delete=True)
>>> tf.close()
>>> x = Yaco({'a': [1,2,3, [1,2,3, {'d' : 4}]],
...           'b': 4, 'c': '5', 'uni' : "Aπ"})
>>> x.save(tf.name)
>>> y = Yaco()
>>> y.load(tf.name)
>>> assert(y.a[3][3].d == 4)
>>> assert(sys.version_info[0] == 2 or y.uni == "Aπ")
pretty()

Return data as a pprint.pformatted string

save(to_file, doNotSave=[])
simple()

return a simplified representation of this Yaco struct - remove Yaco from the equation - and all object reference. Leave only bool, float, str, lists, tuples and dicts

>>> x = Yaco()
>>> x.y.z = 1
>>> assert(isinstance(x.y, Yaco))
>>> s = x.simple()
>>> assert(s['y']['z'] == 1)
>>> assert(isinstance(s['y'], dict))
>>> assert(not isinstance(s['y'], Yaco))
soft_update(data)

As update - but only update keys that do not have a value.

Note - lists are completely

>>> d1 = {'a' : [1,2,3,{'b': 12}], 'd' : {'e': 72}}
>>> d2 = {'a' : [2,3,4,{'b': 12}], 'd' : {'e': 73, 'f': 18}, 'c' : 18}
>>> v = Yaco(d1)
>>> assert(v.a[2] == 3)
>>> assert(v.d.e == 72)
>>> v.soft_update(d2)
>>> assert(v.d.e == 72)
>>> assert(v.d.f == 18)
>>> assert(v.a[2] == 3)
update(data)
>>> v = Yaco({'a' : [1,2,3,{'b' : 12}]})
>>> assert(v.a[3].b == 12)
>>> v = Yaco({'a' : [1,2,3,[1,{'b' : 12}]]})
>>> assert(v.a[3][1].b == 12)
class Yaco.YacoDir(dirname, pattern='*.config')

As Yaco, but load all files in a directory on top of each other.

Order of loading is the alphanumerical sort of filenames

files in subdirectories are loaded into leaves e.g. a file in /tmp/test/sub/a.yaml with only (x=1) will end up as follows:

y = YacoDir(‘/tmp/test’) y.sub.x == 1

Note, YacoDir will try to cache itself in a .yacodir.cache file in the root of the dirname if the modification date of this file is the same as the directory - that will be loaded instead.

load(dirname, pattern)

Load from the defined directory

save()

Save is disabled.

class Yaco.YacoFile(filename)

As Yaco, but loads from a file - or returns an emtpy object if it cannot find the file

load()

Load from the defined filename

save()

Load from the defined filename