1 |
commit: d72284c4978f84bbdcdc54ce0c4b4d66b6d91ad3 |
2 |
Author: André Erdmann <dywi <AT> mailerd <DOT> de> |
3 |
AuthorDate: Sun Jan 26 16:37:44 2014 +0000 |
4 |
Commit: André Erdmann <dywi <AT> mailerd <DOT> de> |
5 |
CommitDate: Sun Jan 26 16:37:44 2014 +0000 |
6 |
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=d72284c4 |
7 |
|
8 |
roverlay/config/, ConfigTree: get options by name |
9 |
|
10 |
Added get_by_name() to the ConfigTree, which is similar to get(), but takes an |
11 |
option name (used in config files, e.g. OVERLAY_DIR) instead of a config path |
12 |
(used in the code, e.g. ['OVERLAY','dir']) as key. |
13 |
query_by_name() can be used to get multiple options and return them as dict. |
14 |
|
15 |
Might be useful for scripting. |
16 |
|
17 |
--- |
18 |
roverlay/config/entryutil.py | 43 +++++++++++++++----- |
19 |
roverlay/config/exceptions.py | 40 +++++++++++++++++++ |
20 |
roverlay/config/tree.py | 92 ++++++++++++++++++++++++++++++++++++++++--- |
21 |
3 files changed, 159 insertions(+), 16 deletions(-) |
22 |
|
23 |
diff --git a/roverlay/config/entryutil.py b/roverlay/config/entryutil.py |
24 |
index cd5f230..716c166 100644 |
25 |
--- a/roverlay/config/entryutil.py |
26 |
+++ b/roverlay/config/entryutil.py |
27 |
@@ -1,6 +1,6 @@ |
28 |
# R overlay -- config package, entryutil |
29 |
# -*- coding: utf-8 -*- |
30 |
-# Copyright (C) 2012 André Erdmann <dywi@×××××××.de> |
31 |
+# Copyright (C) 2012-2014 André Erdmann <dywi@×××××××.de> |
32 |
# Distributed under the terms of the GNU General Public License; |
33 |
# either version 2 of the License, or (at your option) any later version. |
34 |
|
35 |
@@ -11,9 +11,11 @@ __all__ = [ 'list_entries', ] |
36 |
import re |
37 |
import textwrap |
38 |
|
39 |
+import roverlay.config.exceptions |
40 |
from roverlay.config.entrymap import CONFIG_ENTRY_MAP |
41 |
|
42 |
def deref_entry ( name ): |
43 |
+ # COULDFIX: raise ConfigOptionNotFound |
44 |
entry_name = name.lower() |
45 |
entry_next = CONFIG_ENTRY_MAP [entry_name] |
46 |
while isinstance ( entry_next, str ): |
47 |
@@ -25,29 +27,50 @@ def deref_entry ( name ): |
48 |
def deref_entry_safe ( name ): |
49 |
visited = set() |
50 |
entry_name = name.lower() |
51 |
- entry_next = CONFIG_ENTRY_MAP [entry_name] |
52 |
+ try: |
53 |
+ entry_next = CONFIG_ENTRY_MAP [entry_name] |
54 |
+ except KeyError: |
55 |
+ # entry does not exist |
56 |
+ raise roverlay.config.exceptions.ConfigOptionNotFound ( name ) |
57 |
|
58 |
while isinstance ( entry_next, str ): |
59 |
visited.add ( entry_name ) |
60 |
entry_name = entry_next |
61 |
- entry_next = CONFIG_ENTRY_MAP [entry_name] |
62 |
+ try: |
63 |
+ entry_next = CONFIG_ENTRY_MAP [entry_name] |
64 |
+ except KeyError: |
65 |
+ raise roverlay.config.exceptions.ConfigEntryMapException ( |
66 |
+ "dangling config map entry {!r} (<- {!r})".format ( |
67 |
+ entry_name, name |
68 |
+ ) |
69 |
+ ) |
70 |
|
71 |
if entry_name in visited: |
72 |
- raise Exception ( |
73 |
- "cyclic config entry detected for {!r}!".format ( name ) |
74 |
- ) |
75 |
+ raise roverlay.config.exceptions.ConfigEntryMapException ( |
76 |
+ "cyclic config entry detected for {!r}!".format ( name ) |
77 |
+ ) |
78 |
|
79 |
return ( entry_name, entry_next ) |
80 |
# --- end of deref_entry_safe (...) --- |
81 |
|
82 |
def find_config_path ( name ): |
83 |
entry_name, entry = deref_entry_safe ( name ) |
84 |
- try: |
85 |
- return entry ['path'] |
86 |
- except KeyError: |
87 |
- return entry_name.split ( '_' ) |
88 |
+ if entry: |
89 |
+ try: |
90 |
+ return entry ['path'] |
91 |
+ except KeyError: |
92 |
+ return entry_name.split ( '_' ) |
93 |
+ else: |
94 |
+ # hidden entry |
95 |
+ raise roverlay.config.exceptions.ConfigOptionNotFound ( name ) |
96 |
# --- end of find_config_path (...) --- |
97 |
|
98 |
+def iter_config_keys(): |
99 |
+ for key, entry in CONFIG_ENTRY_MAP.items(): |
100 |
+ if isinstance ( entry, dict ): |
101 |
+ yield key |
102 |
+# --- end of iter_config_keys (...) --- |
103 |
+ |
104 |
def _iter_entries(): |
105 |
"""Iterates through all entries in CONFIG_ENTRY_MAP and yields config |
106 |
entry information (entry name, description). |
107 |
|
108 |
diff --git a/roverlay/config/exceptions.py b/roverlay/config/exceptions.py |
109 |
new file mode 100644 |
110 |
index 0000000..715d9eb |
111 |
--- /dev/null |
112 |
+++ b/roverlay/config/exceptions.py |
113 |
@@ -0,0 +1,40 @@ |
114 |
+# R overlay -- config package, exceptions |
115 |
+# -*- coding: utf-8 -*- |
116 |
+# Copyright (C) 2014 André Erdmann <dywi@×××××××.de> |
117 |
+# Distributed under the terms of the GNU General Public License; |
118 |
+# either version 2 of the License, or (at your option) any later version. |
119 |
+ |
120 |
+__all__ = [ |
121 |
+ 'ConfigException', 'ConfigEntryMapException', |
122 |
+ 'ConfigKeyError', 'ConfigOptionNotFound', |
123 |
+ 'ConfigTreeUsageError', |
124 |
+] |
125 |
+ |
126 |
+ |
127 |
+class ConfigException ( Exception ): |
128 |
+ pass |
129 |
+# --- end of ConfigException --- |
130 |
+ |
131 |
+class ConfigEntryMapException ( ConfigException ): |
132 |
+ pass |
133 |
+# --- end of ConfigEntryMapException --- |
134 |
+ |
135 |
+ |
136 |
+class ConfigKeyError ( ConfigException ): |
137 |
+ # or inherit KeyError |
138 |
+ def __init__ ( self, config_key ): |
139 |
+ super ( ConfigKeyError, self ).__init__ ( |
140 |
+ "config key {!r} not found but required.".format ( config_key ) |
141 |
+ ) |
142 |
+# --- end of ConfigKeyError --- |
143 |
+ |
144 |
+class ConfigOptionNotFound ( ConfigException ): |
145 |
+ pass |
146 |
+# --- end of ConfigOptionNotFound --- |
147 |
+ |
148 |
+class ConfigTreeUsageError ( ConfigException ): |
149 |
+ def __init__ ( self, message=None ): |
150 |
+ super ( ConfigTreeUsageError, self ).__init__ ( |
151 |
+ "bad usage" if message is None else message |
152 |
+ ) |
153 |
+# --- end of ConfigTreeUsageError --- |
154 |
|
155 |
diff --git a/roverlay/config/tree.py b/roverlay/config/tree.py |
156 |
index d9f1900..0aeb86c 100644 |
157 |
--- a/roverlay/config/tree.py |
158 |
+++ b/roverlay/config/tree.py |
159 |
@@ -1,6 +1,6 @@ |
160 |
# R overlay -- config package, tree |
161 |
# -*- coding: utf-8 -*- |
162 |
-# Copyright (C) 2012 André Erdmann <dywi@×××××××.de> |
163 |
+# Copyright (C) 2012-2014 André Erdmann <dywi@×××××××.de> |
164 |
# Distributed under the terms of the GNU General Public License; |
165 |
# either version 2 of the License, or (at your option) any later version. |
166 |
|
167 |
@@ -20,9 +20,11 @@ __all__ = [ 'ConfigTree', ] |
168 |
|
169 |
import logging |
170 |
|
171 |
-from roverlay.config import const |
172 |
-from roverlay.config.loader import ConfigLoader |
173 |
-from roverlay.config.util import get_config_path |
174 |
+import roverlay.config.exceptions |
175 |
+from roverlay.config import const |
176 |
+from roverlay.config.loader import ConfigLoader |
177 |
+from roverlay.config.util import get_config_path |
178 |
+from roverlay.config.entryutil import find_config_path |
179 |
|
180 |
CONFIG_INJECTION_IS_BAD = True |
181 |
|
182 |
@@ -115,7 +117,7 @@ class ConfigTree ( object ): |
183 |
pass |
184 |
|
185 |
else: |
186 |
- raise Exception ( "bad usage" ) |
187 |
+ raise roverlay.config.exceptions.ConfigUsageError() |
188 |
|
189 |
# --- end of merge_with (...) --- |
190 |
|
191 |
@@ -207,6 +209,9 @@ class ConfigTree ( object ): |
192 |
* key -- |
193 |
* fallback_value -- |
194 |
* fail_if_unset -- fail if key is neither in config nor const |
195 |
+ |
196 |
+ raises: |
197 |
+ * ConfigKeyError -- key does not exist and fail_if_unset is True |
198 |
""" |
199 |
|
200 |
config_value = self._findpath ( key ) |
201 |
@@ -219,7 +224,7 @@ class ConfigTree ( object ): |
202 |
config_value = fallback |
203 |
|
204 |
if config_value is None and fail_if_unset: |
205 |
- raise Exception ( "config key '%s' not found but required." % key ) |
206 |
+ raise roverlay.config.exceptions.ConfigKeyError ( key ) |
207 |
|
208 |
return config_value |
209 |
|
210 |
@@ -230,6 +235,81 @@ class ConfigTree ( object ): |
211 |
return self.get ( key, fail_if_unset=True ) |
212 |
# --- end of get_or_fail --- |
213 |
|
214 |
+ def get_by_name ( self, option_name, *args, **kwargs ): |
215 |
+ """Searches for an option referenced by name (e.g. OVERLAY_DIR) |
216 |
+ and returns its value. See ConfigTree.get() for details. |
217 |
+ |
218 |
+ This is an inefficient operation meant for setup/query scripts. |
219 |
+ Use get() where possible. |
220 |
+ |
221 |
+ arguments: |
222 |
+ * option_name |
223 |
+ * *args, **kwargs -- passed to get() |
224 |
+ |
225 |
+ raises: |
226 |
+ * ConfigOptionNotFound -- option_name is unknown or hidden |
227 |
+ * ConfigEntryMapException -- config entry is broken |
228 |
+ * ConfigKeyError -- key does not exist and fail_if_unset is True |
229 |
+ """ |
230 |
+ return self.get ( find_config_path ( option_name ), *args, **kwargs ) |
231 |
+ # --- end of get_by_name (...) --- |
232 |
+ |
233 |
+ def get_by_name_or_fail ( self, option_name ): |
234 |
+ """Alias to self.get_by_name ( key, fail_if_unset=True ).""" |
235 |
+ return self.get_by_name ( option_name, fail_if_unset=True ) |
236 |
+ # --- end of get_by_name_or_fail (...) --- |
237 |
+ |
238 |
+ def query_by_name ( self, |
239 |
+ request, empty_missing=False, convert_value=None |
240 |
+ ): |
241 |
+ """Creates a dict<var_name,value> of config options, referenced by name |
242 |
+ |
243 |
+ Returns: 2-tuple ( # of missing options, var dict ). |
244 |
+ |
245 |
+ arguments: |
246 |
+ * request -- an iterable containing strings |
247 |
+ or 2-tuples(option_name,var_name) |
248 |
+ * empty_missing -- whether to create empty entries for missing options |
249 |
+ or not. Defaults to False. |
250 |
+ * convert_value -- if set and not None: convert config values using |
251 |
+ this function before adding them to the resulting |
252 |
+ dict |
253 |
+ """ |
254 |
+ num_missing = 0 |
255 |
+ retvars = dict() |
256 |
+ |
257 |
+ for k in request: |
258 |
+ if ( |
259 |
+ not isinstance ( k, str ) and hasattr ( k, '__iter__' ) |
260 |
+ and len ( k ) > 1 |
261 |
+ ): |
262 |
+ |
263 |
+ option_name = k[0] |
264 |
+ var_name = k[1] |
265 |
+ else: |
266 |
+ option_name = str(k) |
267 |
+ var_name = option_name |
268 |
+ # -- end if <set option_name/var_name> |
269 |
+ |
270 |
+ try: |
271 |
+ value = self.get_by_name_or_fail ( option_name ) |
272 |
+ except ( |
273 |
+ roverlay.config.exceptions.ConfigOptionNotFound, |
274 |
+ roverlay.config.exceptions.ConfigKeyError |
275 |
+ ): |
276 |
+ num_missing += 1 |
277 |
+ if empty_missing: |
278 |
+ retvars [var_name] = "" |
279 |
+ else: |
280 |
+ if convert_value is not None: |
281 |
+ retvars [var_name] = convert_value ( value ) |
282 |
+ else: |
283 |
+ retvars [var_name] = value |
284 |
+ # -- end for <request> |
285 |
+ |
286 |
+ return ( num_missing, retvars ) |
287 |
+ # --- end of query_by_name (...) --- |
288 |
+ |
289 |
def get_field_definition ( self, force_update=False ): |
290 |
"""Gets the field definition stored in this ConfigTree. |