Package: yaycl
# YAYCL: YAML Configuration loader and cacheAll YAML files stored in the given directory are automatically parsed and loaded on request.The parsed files are exposed as object attributes based on the yaml file name.For example, consider a ``conf/config.yaml`` file:```yamldict_key:key1: value1key2: value2key3: value3list_key:- 1- 2- 3string_key: 'string value'```Now, you can interact with that yaml in python with minimal fuss:```pythonimport yayclconf = yaycl.Config('conf')# assuming config.yaml is valid yaml, this should work:assert 'key2' in conf.config.dict_key```Once loaded, the yaml contents are cached. The entire cache of a config object can be cleared,or a single config file's cache can be cleared:```pythonconf.clear()  # clears the entire cacheconf.config.clear() or conf['config'].clear()  # clears the cache only for config.yaml```Note that, as in the example above, yaml files loaded by yaycl (currently) must be a mapping type at the top level. Files containing more than one yaml document are (currently) unsupported.## .yaml vs. .ymlMany projects use the `.yml` file extension for YAML files. This is supported by passing the`extension` keyword argument to `yaycl.Config`. For this example, assume `conf/config.yaml`has been renamed to `conf/config.yml`:```pythonimport yayclconf = yaycl.Config('conf', extension='.yml')# Now this config will be loaded from conf/config.ymlassert 'key2' in conf.config.dict_key```## Module Impersonation`yaycl.Config` is indended to manage config files for an entire project. To facilitatethat goal, it supports acting as a module, making configurations importable.The module's name doesn't matter, as long as it can be imported by that name.In this example, we'll make a module called `conf.py`, with contents:```pythonimport sysfrom yaycl import Configsys.modules[__name__] = Config('/path/to/yaml/config/dir')```Now, the first time `conf` is imported, it will replace itself in conf with an instance of`yaycl.Config`, which will be what python imports thereafter. Once done, you can import configfiles directly. Here's the same example from before, but using the direct import method:```pythonfrom conf import configassert 'key2' in conf.config.dict_key```For brevity, following examples will use the module impersonation mechanism.## ShenanigansSpecial care has been taken to ensure that all objects are mutated, rather than replaced,so all names will reference the same config object.All objects representing config files (attributes or items accessed directly from the confmodule) will be some type of `AttrDict`. Attempting to make such a config object be anythingother than an `AttrDict` (see "Inherited methods section below)  will probably break everythingand should not be attempted, lest shenanigans be called.```python# Don't do this...conf.key = 'not a dict'# ...or this.conf['key'] = ['also', 'not', 'a', 'dict']```Generally speaking, with the exception of runtime overrides (see below), a `yaycl.Config` instanceshould be considered read-only.# Local Configuration OverridesIn addition to loading YAML files, the `yacl.Config` loader supports local overridefiles. This feature is useful for maintaining a shared set of config files for a team, whilestill allowing for local configuration.Take the following example YAML file, `config.local.yaml`:```yamlstring_key: 'new string value'```When loaded by the conf loader, the `string_key` will be automatically overridden by the valuein the local YAML file::```pythonfrom conf import configprint config.string_key```This will print: `new string value`, instead of the value in the base config, `string value`The existing keys (`dict_key` and `list_key` in this case) will not altered by the localconfig override.This allows for configurations to be stored in revision control, while still making it trivialto test new configs, override an existing config, or even create configs that only existlocally.```# .gitignore suggestion; adapt to your SCM of choice*.local.yaml```# Runtime OverridesSometimes writing to the config files is an inconvenient way to ensure that runtime changespersist through configuration cache clearing. These "runtime" changes can be stashed in theruntime overrides dict, allowing them to persist through a cache clear.The runtime overrides dictionary mimics the layout of the conf module itself, whereconfiguration file names are keys in the runtime overrides dictionary. So, for example, toupdate the base_url in a way that will persist clearing of the cache, the following will work:```pythonimport confconf.runtime['config']['string_key'] = 'overridden string key'print conf.config.string_key```That should print `overridden string key`## runtime.yamlIf you have a config file named 'runtime.yaml' that you'd like to load, or really any configname that interferes with python names ('get.yaml', for example), note that the configs arealways available via dictionary lookup; attribute lookup is supported for brevity, but dictitem lookup should always work.```pythonconf.runtime['runtime'] = {'shenanigans': True}assert conf['runtime']['shenanigans']```# Inherited methodsOnce loaded, all configs are instances of `AttrDict`, a very helpful class from the[layered-yaml-attrdict-config](https://pypi.python.org/pypi/layered-yaml-attrdict-config/)package. As such, all methods normally available to AttrDicts are available here.For example, `Config.save` and `Config`'s inheritance abilities rely on `AttrDict`'s`dump` and `rebase` methods, respectively.Of course, since `AttrDict` is a `dict` subclass, dictionary methods can also be used tomanipulate a `yaycl.Config` at runtime. The `clear` method is particularlyuseful as a means to trigger a reload of all config files by clearing yaycl's cache.# Thread safetyNo care whatsoever has been taken to ensure thread-safety, so if you're doing threadedthings with the conf module you should manage your own locking when making any confchanges. Since most config are loaded from the filesystem, generally this means thatany changes to the runtime overrides should be done under a lock.[![Coverage Status](https://coveralls.io/repos/seandst/yaycl/badge.svg?branch=master)](https://coveralls.io/r/seandst/yaycl?branch=master)[![Build Status](https://travis-ci.org/seandst/yaycl.svg?branch=master)](https://travis-ci.org/seandst/yaycl)