16 | 16 |
###
|
17 | 17 |
from __future__ import print_function
|
18 | 18 |
|
19 | |
__VERSION__ = (2, 9, 9, 20190910)
|
|
19 |
__VERSION__ = (2, 9, 0, 20190910)
|
20 | 20 |
|
21 | 21 |
import os
|
22 | 22 |
import sys
|
|
25 | 25 |
|
26 | 26 |
from string import Template
|
27 | 27 |
|
28 | |
from thesaurus import thes, thesext, key_treepath
|
|
28 |
from thesaurus import thes, thesext
|
29 | 29 |
|
30 | 30 |
class cfg_types (object):
|
31 | 31 |
class __EXAMPLE (object):
|
|
60 | 60 |
return ''
|
61 | 61 |
s = ''
|
62 | 62 |
for i in val:
|
63 | |
s += '"%s" ' % (i.replace('"','\\"').replace("'","\\'"))
|
|
63 |
s += '"%s" ' % (i.replace('"','\\"'))
|
64 | 64 |
return s[:-1]
|
65 | 65 |
class str_to_bool (object):
|
66 | 66 |
""""Support 3 values: True, False, None"""
|
67 | 67 |
def set (self, key, val, curval):
|
68 | 68 |
if val in (True, False, None):
|
69 | 69 |
return val
|
70 | |
raise TypeError(s)
|
|
70 |
raise ValueError(s)
|
71 | 71 |
def parse (self, key, val):
|
72 | 72 |
s = val.lower()
|
73 | 73 |
if s in ('true', 'yes', 'on', '1'):
|
|
76 | 76 |
return False
|
77 | 77 |
if s in ('none', ''):
|
78 | 78 |
return None
|
79 | |
raise TypeError(s)
|
|
79 |
raise ValueError(s)
|
80 | 80 |
def dump (self, key, val):
|
81 | 81 |
if val == None:
|
82 | 82 |
return 'None'
|
83 | 83 |
return bool(val)
|
84 | |
|
|
84 |
class ipv4 (object):
|
|
85 |
def set (self, key, val, curval):
|
|
86 |
return self.parse(key, val)
|
|
87 |
def parse (self, key, val):
|
|
88 |
l = val.split('.')
|
|
89 |
if len(l) != 4:
|
|
90 |
raise ValueError(val)
|
|
91 |
for i in l:
|
|
92 |
try:
|
|
93 |
i = int(i)
|
|
94 |
except:
|
|
95 |
raise ValueError(val)
|
|
96 |
if i < 0 or i > 255:
|
|
97 |
raise ValueError(val)
|
|
98 |
return val
|
|
99 |
|
85 | 100 |
class configparser (object):
|
86 | 101 |
def __init__(self, *args, **kwargs):
|
87 | 102 |
self._thes_type = thesext
|
|
90 | 105 |
self._value_rstrip = True
|
91 | 106 |
for k,v in kwargs.items():
|
92 | 107 |
setattr(self, '_'+k, v)
|
|
108 |
def split_key (self, key):
|
|
109 |
lex = shlex.shlex(key, posix=True)
|
|
110 |
lex.whitespace_split = True
|
|
111 |
lex.whitespace = '.'
|
|
112 |
return list(lex)
|
93 | 113 |
def read(self, filename=None, t=None):
|
94 | 114 |
if t == None: t = self._thes_type()
|
95 | 115 |
t = self._thes_type()
|
|
98 | 118 |
def readfp (self, fp=None, t=None):
|
99 | 119 |
if t == None: t = self._thes_type()
|
100 | 120 |
return self.parse(t, fp.read())
|
101 | |
def parse (self, t=None, s='', func_map={}, set_map={}, parse_map={}, dump_map={}):
|
102 | |
"""Original trimmed text is returned, sutain for input back to parse"""
|
|
121 |
def parse (self, t=None, s='', func_map={}):
|
|
122 |
"""Original trimmed text is returned, sustain for input back to parse"""
|
103 | 123 |
def full_key (root_key, key):
|
104 | 124 |
if root_key != '':
|
105 | 125 |
return root_key+'.'+key
|
|
143 | 163 |
# FIX ME suport empty root sections without []
|
144 | 164 |
if line.startswith('[') and line.rstrip().endswith(']'):
|
145 | 165 |
root_key = line.rstrip()[1:-1].strip() # '[]' section resets root_key
|
146 | |
if root_key != '' and root_key not in t:
|
147 | |
t.set_path(root_key, self._thes_type()) # FIX me use parent class
|
|
166 |
root_keypath = self.split_key(root_key)
|
|
167 |
if root_key != '' and root_keypath not in t:
|
|
168 |
t.set_path(root_keypath, self._thes_type()) # FIX me use parent class
|
148 | 169 |
continue
|
149 | 170 |
|
150 | 171 |
key,value = re_sep.split(line, 1)
|
|
166 | 187 |
key = full_key(root_key, key.strip())
|
167 | 188 |
funcname = funcvar[:-1].strip()
|
168 | 189 |
|
|
190 |
|
|
191 |
keypath = self.split_key(key)
|
|
192 |
if len(keypath) > 1 and keypath[:-1] not in t:
|
|
193 |
t.set_path(keypath, self._thes_type())
|
|
194 |
o = t[keypath[:-1]]
|
|
195 |
key = keypath[-1]
|
|
196 |
|
169 | 197 |
if funcname and funcname in func_map:
|
170 | 198 |
if hasattr(func_map[funcname], 'parse'):
|
171 | |
parse_map[key] = func_map[funcname]().parse
|
172 | |
if key in parse_map:
|
173 | |
value = parse_map[key](key, value)
|
174 | |
t.set_path(key, value) # Apply parse_map prior to set. NOTE: addition applicaiton WILL applied set_map
|
|
199 |
o._ThesConf__parse_map[key] = func_map[funcname]().parse
|
|
200 |
if key in o._ThesConf__parse_map:
|
|
201 |
value = o._ThesConf__parse_map[key](key, value)
|
|
202 |
o[key] = value
|
175 | 203 |
|
176 | 204 |
if funcname and funcname in func_map:
|
177 | 205 |
if hasattr(func_map[funcname], 'set'):
|
178 | |
set_map[key] = func_map[funcname]().set
|
|
206 |
o._ThesConf__set_map[key] = func_map[funcname]().set
|
179 | 207 |
if hasattr(func_map[funcname], 'dump'):
|
180 | |
dump_map[key] = func_map[funcname]().dump
|
|
208 |
o._ThesConf__dump_map[key] = func_map[funcname]().dump
|
181 | 209 |
|
182 | 210 |
return t
|
183 | 211 |
def write (self, fp=None, t=None):
|
184 | 212 |
if t == None: t = self._thes_type()
|
185 | 213 |
return self.dump(t, fp.read())
|
186 | 214 |
|
187 | |
def dump(self, t, sections=0, empty_sect=True, dump_map={}):
|
188 | |
def recursedict(d, sections, empty_sect, dump_map, key_tree=[], depth=0, s='', r=''):
|
|
215 |
def dump(self, t, sections=0, empty_sect=True):
|
|
216 |
def recursedict(d, sections, empty_sect, key_tree=[], depth=0, s='', r=''):
|
189 | 217 |
for k,v in iter(d.items()):
|
190 | 218 |
if hasattr(v, 'keys'):
|
191 | |
key_tree.append(k)
|
192 | |
r = recursedict(v, sections, empty_sect, dump_map, key_tree, depth+1)
|
|
219 |
if '.' in k:
|
|
220 |
key_tree.append("'"+k+"'")
|
|
221 |
else:
|
|
222 |
key_tree.append(k)
|
|
223 |
r = recursedict(v, sections, empty_sect, key_tree, depth+1)
|
193 | 224 |
if sections and depth < sections:
|
194 | 225 |
if r or empty_sect:
|
195 | 226 |
if s != '': s += '\n' # Not at top of output
|
|
201 | 232 |
else:
|
202 | 233 |
if sections and depth == 0 and r: # More root level values after recursing
|
203 | 234 |
s += '\n[]\n'; r = '' # Reset section so not absorbed by previous
|
204 | |
if k in dump_map:
|
205 | |
v = dump_map[k](k, v)
|
|
235 |
|
|
236 |
if k in d._ThesConf__dump_map:
|
|
237 |
v = d._ThesConf__dump_map[k](k, v)
|
|
238 |
|
206 | 239 |
if '\n' in str(v):
|
207 | 240 |
v = ':\n\t%s\n' % (v.replace('\n','\n\t'))
|
208 | 241 |
else:
|
|
212 | 245 |
else:
|
213 | 246 |
s += key_treepath(key_tree[sections:], k) + v
|
214 | 247 |
return s
|
215 | |
return recursedict(t, sections, empty_sect, dump_map)
|
|
248 |
return recursedict(t, sections, empty_sect)
|
216 | 249 |
|
217 | 250 |
class ThesConf (object):
|
218 | 251 |
def __init__(self, *args, **kwargs):
|
|
225 | 258 |
return self.parse(f.read(), *args, **kwargs)
|
226 | 259 |
def parse (self, data, *args, **kwargs):
|
227 | 260 |
func_map=mapper(cfg_types())
|
228 | |
return configparser(thes_type=self.__class__).parse(self, data, func_map=func_map,
|
229 | |
set_map=self.__set_map, parse_map=self.__parse_map, dump_map=self.__dump_map)
|
|
261 |
return configparser(thes_type=self.__class__).parse(self, data, func_map=func_map)
|
230 | 262 |
def write (self, fp, *args, **kwargs):
|
231 | 263 |
fp.write(self.dump(*args, **kwargs))
|
232 | 264 |
def dump (self, sections=0, empty_sect=True):
|
233 | |
return configparser(thes_type=self.__class__).dump(self, sections, empty_sect, dump_map=self.__dump_map)
|
|
265 |
return configparser(thes_type=self.__class__).dump(self, sections, empty_sect)
|
234 | 266 |
|
235 | 267 |
|
236 | 268 |
class ThesaurusCfg (thesext, ThesConf):
|
|
363 | 395 |
try:
|
364 | 396 |
f.close()
|
365 | 397 |
except: pass
|
366 | |
|
|
398 |
|
|
399 |
def key_treepath (key_tree, *keys):
|
|
400 |
"""''.join keeps playing with my emotions"""
|
|
401 |
return '.'.join(key_tree+list(keys))
|
367 | 402 |
|
368 | 403 |
#Define prefered import name
|
369 | 404 |
thescfg = ThesaurusCfg
|