Source code for confire.config

# confire.conf
# A simple configuration module for Confire
#
# Author:   Benjamin Bengfort <ben@cobrain.com>
# Created:  Tue May 20 22:19:11 2014 -0400
#
# Copyright (C) 2013 Cobrain Company
# For license information, see LICENSE.txt
#
# ID: conf.py [] ben@cobrain.com $

"""
Confire class for specifying Confire specific optional items via a YAML
configuration file format. The main configuration class provides utilities
for loading the configuration from disk and iterating across all the
settings. Subclasses of the Configuration specify defaults that can be
updated via the configuration files.

General usage:

    from confire.conf import settings
    mysetting = settings.get('mysetting', default)

You can also get settings via a dictionary like access:

    mysetting = settings['mysetting']

However, this will raise an exception if the setting is not found.

Note: Keys are CASE insensitive

Note: Settings can be modified directly by settings.mysetting = newsetting
however, this is not recommended, and settings should be fetched via the
dictionary-like access.
"""

##########################################################################
## Imports
##########################################################################

import os
import yaml
import warnings

from copy import deepcopy
from .exceptions import ImproperlyConfigured, ConfigurationMissing

##########################################################################
## Environment helper function
##########################################################################

[docs]def environ_setting(name, default=None, required=True): """ Fetch setting from the environment. The bahavior of the setting if it is not in environment is as follows: 1. If it is required and the default is None, raise Exception 2. If it is requried and a default exists, return default 3. If it is not required and default is None, return None 4. If it is not required and default exists, return default """ if name not in os.environ and default is None: message = "The {0} ENVVAR is not set.".format(name) if required: raise ImproperlyConfigured(message) else: warnings.warn(ConfigurationMissing(message)) return os.environ.get(name, default)
########################################################################## ## Configuration Base Class ##########################################################################
[docs]class Configuration(object): """ Base configuration class specifies how configurations should be handled and provides helper methods for iterating through options and configuring the base class. Subclasses should provide defaults for the various configurations as directly set class level properties. Note, however, that ANY directive set in a configuration file (whether or not it has a default) will be added to the configuration. Example: class MyConfig(Configuration): mysetting = True logpath = "/var/log/myapp.log" appname = "MyApp" The configuration is then loaded via the classmethod `load`: settings = MyConfig.load() Access to properties is done two ways: settings['mysetting'] settings.get('mysetting', True) Note: None settings are not allowed! """ CONF_PATHS = [ '/etc/confire.yaml', # The global configuration os.path.expanduser('~/.confire.yaml'), # User specific configuration os.path.abspath('conf/confire.yaml') # Local directory configuration ] @classmethod
[docs] def load(klass): """ Insantiates the configuration by attempting to load the configuration from YAML files specified by the CONF_PATH module variable. This should be the main entry point for configuration. """ config = klass() for path in klass.CONF_PATHS: if os.path.exists(path): with open(path, 'r') as conf: config.configure(yaml.load(conf)) return config
[docs] def configure(self, conf={}): """ Allows updating of the configuration via a dictionary of configuration terms or a configuration object. Generally speaking, this method is utilized to configure the object from a JSON or YAML parsing. """ if not conf: return if isinstance(conf, Configuration): conf = dict(conf.options()) for key, value in conf.items(): opt = self.get(key, None) if isinstance(opt, Configuration): opt.configure(value) else: self.__dict__[key] = value
[docs] def options(self): """ Returns an iterable of sorted option names in order to loop through all the configuration directives specified in the class. """ keys = self.__class__.__dict__.copy() keys.update(self.__dict__) keys = sorted(keys.keys()) for opt in keys: val = self.get(opt) if val is not None: yield opt, val
[docs] def get(self, key, default=None): """ Fetches a key from the configuration without raising a KeyError exception if the key doesn't exist in the config, instead it returns the default (None). """ try: return self[key] except KeyError: return default
def __getitem__(self, key): """ Main configuration access method. Performs a case insensitive lookup of the key on the class, filtering methods and pseudo private properties. Raises KeyError if not found. Note, this makes all properties that are uppercase invisible to the options. """ key = key.lower() if hasattr(self, key): attr = getattr(self, key) if not callable(attr) and not key.startswith('_'): return attr raise KeyError("%s has no configuration '%s'" % (self.__class__.__name__, key)) def __repr__(self): return str(self) def __str__(self): s = "" for opt, val in self.options(): r = repr(val) r = " ".join(r.split()) wlen = 76-max(len(opt),10) if len(r) > wlen: r = r[:wlen-3]+"..." s += "%-10s = %s\n" % (opt, r) return s[:-1]