Completed
Push — master ( 3d8115...ca2ec2 )
by Kenny
01:12
created

Conf   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 102
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 102
rs 10
c 0
b 0
f 0
wmc 21

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __str__() 0 5 1
A defaults() 0 12 4
B get() 0 28 6
A set_conf() 0 16 4
A __repr__() 0 5 1
B __init__() 0 18 5
1
# -*- coding: utf-8 -*-
2
"""Simple configuration file helpers for small configurations in yaml.
3
4
.. moduleauthor:: Kenny Freeman <[email protected]>
5
6
"""
7
__author__ = 'Kenny Freeman'
8
__email__ = '[email protected]'
9
__license__     = "ISCL"
10
__docformat__   = 'reStructuredText'
11
12
import fnmatch
13
import os.path
14
15
import yaml # pip install pyaml
16
17
import plumd
18
19
20
def find(dname, ext):
21
    """Helper to yield a recursive list of the yaml files in a directory.
22
23
    :param dname: Path to list files in
24
    :type name: str
25
    :param ext: File extension to search for
26
    :type name: str
27
    :rtype: list -- a list of of the filenames found or []
28
    """
29
    if os.path.isdir(dname):
30
        try:
31
            for rdir, sdirs, fnames in os.walk(dname, topdown=True,
32
                                               onerror=None, followlinks=False):
33
                for fname in fnmatch.filter(fnames, "*.{0}".format(ext)):
34
                    yield os.path.join(rdir, fname)
35
        except AttributeError as e:
36
            raise plumd.ConfigError("Invalid path: {0} : {1}".format(dname, e))
37
        except OSError as e:
38
            raise plumd.ConfigError("OSError: {0} : {1}".format(dname, e))
39
    else:
40
        yield None
41
42
43
class Conf(object):
44
    """Simple key/value configuration helper class for small yaml files.
45
46
    :param cfile: Full path to the configuration file to load
47
    :type name: str
48
    :raises: ConfigError: if the specified file does not exist
49
    """
50
51
    def __init__(self, cfile):
52
        """conf constructor.
53
54
        :param cfile: Full path to the configuration file to load
55
        :type name: str
56
        :rtype: conf -- an instance of conf
57
        :raises: ConfigError: if the specified file does not exist
58
        """
59
        self.conf = {}
60
        self.path = cfile
61
        if (not cfile) or (not os.path.isfile(cfile)):
62
            raise plumd.ConfigError("file {0} does not exist".format(cfile))
63
        with open(cfile) as fd:
64
            try:
65
                self.conf = yaml.safe_load(fd)
66
            except yaml.scanner.ScannerError as e:
67
                msg = "invalid: {0} : {1}".format(cfile, e)
68
                raise plumd.ConfigError(msg)
69
70
71
    def __str__(self):
72
        """Return a nicely formatted string of our config.
73
        :rtype: str
74
        """
75
        return yaml.dump(self.conf,allow_unicode=False,default_flow_style=False)
76
77
78
    def __repr__(self):
79
        """Return a nicely formatted string of our config.
80
        :rtype: str
81
        """
82
        return yaml.dump(self.conf,allow_unicode=True,default_flow_style=False)
83
84
85
    def get(self, name, exception=False, default=None, rtype=None):
86
        """Returns the requested value, the provided default or None.
87
88
        raises:
89
            ConfigError if exception=True and the requested key is not found.
90
91
        :param name: The configuration value to lookup and return
92
        :type name: str
93
        :param exception: If True raise a ConfigError if the vlaue is not present
94
        :type exception: bool
95
        :param default: The default value to return if value is not found
96
        :rtype: value -- the configured value for the requested name or default
97
        :raises: ConfigError
98
        """
99
        val = default
100
        if name in self.conf and self.conf[name] is not None:
101
            val = self.conf[name]
102
        elif exception:
103
            msg = "missing {0} in: {1}".format(name, self.path)
104
            raise plumd.ConfigError(msg)
105
        if rtype:
106
            try:
107
                val = rtype(val)
108
            except (ValueError, TypeError) as e:
109
                msg = "{0} from file: {1} is not a(n) {2}, exception: {3}"
110
                rname = rtype.__name__
111
                raise plumd.ConfigError(msg.format(name, self.path, rname, e))
112
        return val
113
114
115
    def set_conf(self, name, val, overwrite=False):
116
        """Sets the named configuration to the provided value.
117
118
        If overwrite is False and the value exists it will be left unchanged.
119
120
        :param name: The configuration key to set
121
        :type name: str
122
        :param val: The configuration key is set to this value
123
        :rtype: self -- this object - allows chaining calls
124
        """
125
        name = name.strip()
126
        if name in self.conf and overwrite:
127
            self.conf[name] = val
128
        elif name not in self.conf:
129
            self.conf[name] = val
130
        return self
131
132
133
    def defaults(self, defconf):
134
        """Updates any missing configurations with values from defconf.
135
136
        :param defconf: dict of default values to set
137
        :type defconf: dict
138
        :rtype: self -- this object - allows chaining calls
139
        """
140
        for key, val in defconf.items():
141
            key = key.strip()
142
            if key not in self.conf or self.conf[key] is None:
143
                self.conf[key] = val
144
        return self
145