# 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 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]