Completed
Push — master ( 53a941...3a6f3a )
by Kenny
01:19
created

Conf.write()   A

Complexity

Conditions 2

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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