1 |
commit: 85bca473d98e6c1e9217b299d54fa3499b407e3c |
2 |
Author: André Erdmann <dywi <AT> mailerd <DOT> de> |
3 |
AuthorDate: Tue Jun 5 17:18:34 2012 +0000 |
4 |
Commit: André Erdmann <dywi <AT> mailerd <DOT> de> |
5 |
CommitDate: Tue Jun 5 17:18:34 2012 +0000 |
6 |
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=85bca473 |
7 |
|
8 |
config: comments and config entry map |
9 |
modified: roverlay/config.py |
10 |
|
11 |
--- |
12 |
roverlay/config.py | 133 +++++++++++++++++++++++++++++++++++++++++++++------- |
13 |
1 files changed, 116 insertions(+), 17 deletions(-) |
14 |
|
15 |
diff --git a/roverlay/config.py b/roverlay/config.py |
16 |
index ab6dfec..424e69f 100644 |
17 |
--- a/roverlay/config.py |
18 |
+++ b/roverlay/config.py |
19 |
@@ -2,7 +2,6 @@ |
20 |
# Copyright 2006-2012 Gentoo Foundation |
21 |
# Distributed under the terms of the GNU General Public License v2 |
22 |
|
23 |
-import copy |
24 |
import os.path |
25 |
import re |
26 |
import sys |
27 |
@@ -89,13 +88,21 @@ class ConfigTree: |
28 |
), |
29 |
ebuild_header = dict ( |
30 |
value_type = 'fs_file', |
31 |
- ) |
32 |
+ ), |
33 |
+ overlay_dir = dict ( |
34 |
+ value_type = 'fs_dir', |
35 |
+ ), |
36 |
+ distfiles_dir = dict ( |
37 |
+ value_type = 'fs_dir', |
38 |
+ ), |
39 |
|
40 |
) |
41 |
|
42 |
+ # often used regexes |
43 |
DEFAULT_LIST_REGEX = re.compile ( '\s*[,;]{1}\s*' ) |
44 |
WHITESPACE = re.compile ( '\s+' ) |
45 |
|
46 |
+ |
47 |
def __init__ ( self, import_const=True ): |
48 |
"""Initializes an ConfigTree, which is a container for options/config values. |
49 |
values can be stored directly (such as the field_definitions) or in a |
50 |
@@ -122,6 +129,17 @@ class ConfigTree: |
51 |
|
52 |
|
53 |
def _findpath ( self, path, root=None, create=False, value=None ): |
54 |
+ """All-in-one method that searches for a config path. |
55 |
+ It is able to create the path if non-existent and to assign a |
56 |
+ value to it. |
57 |
+ |
58 |
+ arguments: |
59 |
+ * path -- config path as path list ([a,b,c]) or as path str (a.b.c) |
60 |
+ * root -- config root (dict expected). Uses self._config if None (the default) |
61 |
+ * create -- create path if nonexistent |
62 |
+ * value -- assign value to the last path element |
63 |
+ an empty dict will be created if this is None and create is True |
64 |
+ """ |
65 |
if path is None: |
66 |
return root |
67 |
elif isinstance ( path, str ): |
68 |
@@ -166,10 +184,36 @@ class ConfigTree: |
69 |
# --- end of get (...) --- |
70 |
|
71 |
def _add_entry ( self, option, value=None, config_root=None ): |
72 |
+ """Adds an option to the config. |
73 |
+ |
74 |
+ arguments: |
75 |
+ * option -- name of the option as it appears in the (main) config file |
76 |
+ * value -- value to assign, defaults to None |
77 |
+ * config_root -- root of the config (a dict), defaults to None which is |
78 |
+ later understood as self._config |
79 |
+ """ |
80 |
|
81 |
def make_and_verify_value ( value_type, value, entryconfig_ref ): |
82 |
+ """Prepares the value of a config option so that it can be used |
83 |
+ in the ConfigTree. |
84 |
+ |
85 |
+ arguments: |
86 |
+ * value_type -- type of the value, look above for explanation concerning this |
87 |
+ * value -- value to verify and transform |
88 |
+ * entryconfig_ref -- reference to the config entry config |
89 |
+ """ |
90 |
|
91 |
def to_int ( val, fallback_value=-1 ): |
92 |
+ """Tries to convert val to an int, returning a fallback value |
93 |
+ on any error. |
94 |
+ |
95 |
+ arguments: |
96 |
+ * val -- |
97 |
+ * fallback_value -- |
98 |
+ |
99 |
+ catches: ValueError in case of an unsuccesful int conversion |
100 |
+ raises: nothing |
101 |
+ """ |
102 |
try: |
103 |
ret = int ( val ) |
104 |
return ret |
105 |
@@ -178,6 +222,13 @@ class ConfigTree: |
106 |
# --- end of to_int (...) --- |
107 |
|
108 |
def yesno ( val ): |
109 |
+ """Tries to canonize an yes or no value to its integer |
110 |
+ representation. Returns 1 if val means 'yes', 0 if 'no' and |
111 |
+ -1 otherwise. |
112 |
+ |
113 |
+ arguments: |
114 |
+ * val -- |
115 |
+ """ |
116 |
if not val is None: |
117 |
to_check = str ( val ) . lower () |
118 |
if to_check in [ 'y', 'yes', '1', 'true', 'enabled', 'on' ]: |
119 |
@@ -190,10 +241,21 @@ class ConfigTree: |
120 |
# --- end of yesno (...) --- |
121 |
|
122 |
def fs_path ( val ): |
123 |
+ """val is a filesystem path - returns expanded path (~ -> HOME). |
124 |
+ |
125 |
+ arguments: |
126 |
+ * val -- |
127 |
+ """ |
128 |
return os.path.expanduser ( val ) if val else None |
129 |
# --- end of fs_path (...) --- |
130 |
|
131 |
def fs_file ( val ): |
132 |
+ """"val is a file - returns expanded path if it is an existent |
133 |
+ file or it does not exist. |
134 |
+ |
135 |
+ arguments: |
136 |
+ * val -- |
137 |
+ """ |
138 |
if val: |
139 |
retval = os.path.expanduser ( val ) |
140 |
if os.path.isfile ( retval ) or not os.path.exists ( retval ): |
141 |
@@ -202,8 +264,25 @@ class ConfigTree: |
142 |
return None |
143 |
# --- end of fs_file (...) --- |
144 |
|
145 |
+ def fs_dir ( val ): |
146 |
+ """val is a directory -- returns expanded path if it is an existent |
147 |
+ dir or it does not exist. |
148 |
+ |
149 |
+ arguments: |
150 |
+ * val -- |
151 |
+ """ |
152 |
+ if val: |
153 |
+ retval = os.path.expanduser ( val ) |
154 |
+ if os.path.isdir ( retval ) or not os.path.exists ( retval ): |
155 |
+ return retval |
156 |
+ |
157 |
+ return None |
158 |
+ # --- end of fs_dir (...) --- |
159 |
+ |
160 |
+ # replace whitespace with a single ' ' |
161 |
value = ConfigTree.WHITESPACE.sub ( ' ', value ) |
162 |
|
163 |
+ # convert value_type into a list of value types |
164 |
if not value_type: |
165 |
return value |
166 |
elif isinstance ( value_type, list ): |
167 |
@@ -227,7 +306,6 @@ class ConfigTree: |
168 |
# dofunc ( function f, <list or str> v) calls f(x) for every str in v |
169 |
dofunc = lambda f, v : [ f(x) for x in v ] if isinstance ( v, list ) else f(v) |
170 |
|
171 |
- |
172 |
retval = value |
173 |
|
174 |
for vtype in vtypes: |
175 |
@@ -236,26 +314,43 @@ class ConfigTree: |
176 |
else: |
177 |
self.logger.warning ( "unknown value type '" + vtype + "'." ) |
178 |
|
179 |
- |
180 |
return retval |
181 |
# --- end of make_and_verify_value (...) --- |
182 |
|
183 |
|
184 |
real_option = option |
185 |
low_option = option.lower() |
186 |
+ |
187 |
+ # known option? |
188 |
if option and low_option in ConfigTree.CONFIG_ENTRY_MAP: |
189 |
- cref = ConfigTree.CONFIG_ENTRY_MAP [low_option] |
190 |
|
191 |
- if isinstance ( cref, str ) and cref in ConfigTree.CONFIG_ENTRY_MAP: |
192 |
- option = low_option = cref |
193 |
- cref = ConfigTree.CONFIG_ENTRY_MAP [cref] |
194 |
+ original_cref = cref = ConfigTree.CONFIG_ENTRY_MAP [low_option] |
195 |
+ cref_level = 0 |
196 |
+ |
197 |
+ # check if cref is a link to another entry in CONFIG_ENTRY_MAP |
198 |
+ while isinstance ( cref, str ): |
199 |
+ if cref == original_cref and cref_level: |
200 |
+ self.logger.critical ( "CONFIG_ENTRY_MAP is invalid! circular cref detected." ) |
201 |
+ raise Exception ( "CONFIG_ENTRY_MAP is invalid!" ) |
202 |
+ |
203 |
+ elif cref in ConfigTree.CONFIG_ENTRY_MAP: |
204 |
+ option = low_option = cref |
205 |
+ cref = ConfigTree.CONFIG_ENTRY_MAP [cref] |
206 |
+ cref_level += 1 |
207 |
+ else: |
208 |
+ self.logger.critical ( |
209 |
+ "CONFIG_ENTRY_MAP is invalid! last cref = " + option + |
210 |
+ ", current cref = " + cref + "." |
211 |
+ ) |
212 |
+ raise Exception ( "CONFIG_ENTRY_MAP is invalid!" ) |
213 |
|
214 |
+ # check if config entry is disabled |
215 |
if cref is None: |
216 |
# deftly ignored |
217 |
return True |
218 |
|
219 |
|
220 |
- |
221 |
+ # determine the config path |
222 |
path = None |
223 |
if 'path' in cref: |
224 |
path = cref ['path'] |
225 |
@@ -264,12 +359,14 @@ class ConfigTree: |
226 |
for n in range ( len ( path ) - 1 ): |
227 |
path [n] = path [n].upper() |
228 |
|
229 |
- |
230 |
+ # need a valid path |
231 |
if path: |
232 |
|
233 |
+ # verify and convert value if value_type is set |
234 |
if 'value_type' in cref: |
235 |
value = make_and_verify_value ( cref ['value_type'], value, cref ) |
236 |
|
237 |
+ # need a valid value |
238 |
if value: |
239 |
|
240 |
self.logger.debug ( |
241 |
@@ -278,6 +375,7 @@ class ConfigTree: |
242 |
" and value " + str ( value ) + "." |
243 |
) |
244 |
|
245 |
+ # add option/value to the config |
246 |
self._findpath ( path, config_root, True, value ) |
247 |
|
248 |
return True |
249 |
@@ -286,7 +384,11 @@ class ConfigTree: |
250 |
"Option '" + str ( real_option ) + |
251 |
"' has an unusable value '" + str ( value ) + "'." |
252 |
) |
253 |
+ return False |
254 |
# --- |
255 |
+ |
256 |
+ self.logger.error ( "Option '" + str ( real_option ) + "' is unusable..." ) |
257 |
+ return False |
258 |
# --- |
259 |
|
260 |
self.logger.warning ( "Option '" + str ( real_option ) + "' is unknown." ) |
261 |
@@ -313,10 +415,11 @@ class ConfigTree: |
262 |
# load file |
263 |
|
264 |
try: |
265 |
- fh = open ( config_file, 'r' ) |
266 |
- reader = shlex.shlex ( fh ) |
267 |
+ reader.wordchars += ' ./$()[]:+-@*~' |
268 |
+ fh = open ( config_file, 'r' ) |
269 |
+ reader = shlex.shlex ( fh ) |
270 |
reader.whitespace_split = False |
271 |
- reader.wordchars += ' ./$()[]:+-@*~' |
272 |
+ |
273 |
|
274 |
nextline = lambda : ( reader.get_token() for n in range (3) ) |
275 |
|
276 |
@@ -333,13 +436,9 @@ class ConfigTree: |
277 |
|
278 |
option, equal, value = nextline () |
279 |
|
280 |
- |
281 |
- |
282 |
if fh: |
283 |
fh.close () |
284 |
|
285 |
- # <TODO> |
286 |
- |
287 |
except IOError as ioerr: |
288 |
raise |