summaryrefslogtreecommitdiff
path: root/python/skytools/config.py
blob: 3f2fa5fe44a771c921849e76c1131217d9c51aba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228

"""Nicer config class."""

import os, os.path, ConfigParser, socket

import skytools

__all__ = ['Config']

class Config(object):
    """Bit improved ConfigParser.

    Additional features:
     - Remembers section.
     - Accepts defaults in get() functions.
     - List value support.
    """
    def __init__(self, main_section, filename, sane_config = 1, user_defs = {}, override = {}, ignore_defs = False):
        """Initialize Config and read from file.

        @param sane_config:  chooses between ConfigParser/SafeConfigParser.
        """
        # use config file name as default job_name
        if filename:
            job_name = os.path.splitext(os.path.basename(filename))[0]
        else:
            job_name = main_section

        # initialize defaults, make them usable in config file
        if ignore_defs:
            self.defs = {}
        else:
            self.defs = {
                'job_name': job_name,
                'service_name': main_section,
                'host_name': socket.gethostname(),
            }
            if filename:
                self.defs['config_dir'] = os.path.dirname(filename)
                self.defs['config_file'] = filename
            self.defs.update(user_defs)

        self.main_section = main_section
        self.filename = filename
        self.sane_config = sane_config
        self.override = override
        if sane_config:
            self.cf = ConfigParser.SafeConfigParser()
        else:
            self.cf = ConfigParser.ConfigParser()

        if filename is None:
            self.cf.add_section(main_section)
        elif not os.path.isfile(filename):
            raise Exception('Config file not found: '+filename)

        self.reload()

    def reload(self):
        """Re-reads config file."""
        if self.filename:
            self.cf.read(self.filename)
        if not self.cf.has_section(self.main_section):
            raise Exception("Wrong config file, no section '%s'" % self.main_section)

        # apply default if key not set
        for k, v in self.defs.items():
            if not self.cf.has_option(self.main_section, k):
                self.cf.set(self.main_section, k, v)

        # apply overrides
        if self.override:
            for k, v in self.override.items():
                self.cf.set(self.main_section, k, v)

    def get(self, key, default=None):
        """Reads string value, if not set then default."""
        try:
            return self.cf.get(self.main_section, key)
        except ConfigParser.NoOptionError:
            if default == None:
                raise Exception("Config value not set: " + key)
            return default

    def getint(self, key, default=None):
        """Reads int value, if not set then default."""
        try:
            return self.cf.getint(self.main_section, key)
        except ConfigParser.NoOptionError:
            if default == None:
                raise Exception("Config value not set: " + key)
            return default

    def getboolean(self, key, default=None):
        """Reads boolean value, if not set then default."""
        try:
            return self.cf.getboolean(self.main_section, key)
        except ConfigParser.NoOptionError:
            if default == None:
                raise Exception("Config value not set: " + key)
            return default

    def getfloat(self, key, default=None):
        """Reads float value, if not set then default."""
        try:
            return self.cf.getfloat(self.main_section, key)
        except ConfigParser.NoOptionError:
            if default == None:
                raise Exception("Config value not set: " + key)
            return default

    def getlist(self, key, default=None):
        """Reads comma-separated list from key."""
        try:
            s = self.cf.get(self.main_section, key).strip()
            res = []
            if not s:
                return res
            for v in s.split(","):
                res.append(v.strip())
            return res
        except ConfigParser.NoOptionError:
            if default == None:
                raise Exception("Config value not set: " + key)
            return default

    def getdict(self, key, default=None):
        """Reads key-value dict from parameter.

        Key and value are separated with ':'.  If missing,
        key itself is taken as value.
        """
        try:
            s = self.cf.get(self.main_section, key).strip()
            res = {}
            if not s:
                return res
            for kv in s.split(","):
                tmp = kv.split(':', 1)
                if len(tmp) > 1:
                    k = tmp[0].strip()
                    v = tmp[1].strip()
                else:
                    k = kv.strip()
                    v = k
                res[k] = v
            return res
        except ConfigParser.NoOptionError:
            if default == None:
                raise Exception("Config value not set: " + key)
            return default

    def getfile(self, key, default=None):
        """Reads filename from config.

        In addition to reading string value, expands ~ to user directory.
        """
        fn = self.get(key, default)
        if fn == "" or fn == "-":
            return fn
        # simulate that the cwd is script location
        #path = os.path.dirname(sys.argv[0])
        #  seems bad idea, cwd should be cwd

        fn = os.path.expanduser(fn)

        return fn

    def getbytes(self, key, default=None):
        """Reads a size value in human format, if not set then default.

        Examples: 1, 2 B, 3K, 4 MB
        """
        try:
            s = self.cf.get(self.main_section, key)
        except ConfigParser.NoOptionError:
            if default is None:
                raise Exception("Config value not set: " + key)
            s = default
        return skytools.hsize_to_bytes(s)

    def get_wildcard(self, key, values=[], default=None):
        """Reads a wildcard property from conf and returns its string value, if not set then default."""

        orig_key = key
        keys = [key]

        for wild in values:
            key = key.replace('*', wild, 1)
            keys.append(key)
        keys.reverse()

        for key in keys:
            try:
                return self.cf.get(self.main_section, key)
            except ConfigParser.NoOptionError:
                pass

        if default == None:
            raise Exception("Config value not set: " + orig_key)
        return default

    def sections(self):
        """Returns list of sections in config file, excluding DEFAULT."""
        return self.cf.sections()

    def has_section(self, section):
        """Checks if section is present in config file, excluding DEFAULT."""
        return self.cf.has_section(section)

    def clone(self, main_section):
        """Return new Config() instance with new main section on same config file."""
        return Config(main_section, self.filename, self.sane_config)

    def options(self):
        """Return list of options in main section."""
        return self.cf.options(self.main_section)

    def has_option(self, opt):
        """Checks if option exists in main section."""
        return self.cf.has_option(self.main_section, opt)

    def items(self):
        """Returns list of (name, value) for each option in main section."""
        return self.cf.items(self.main_section)

    # define some aliases (short-cuts / backward compatibility cruft)
    getbool = getboolean