1 |
commit: ac0b226fd7d16dad7a4b8d30b4293fe401406c1c |
2 |
Author: André Erdmann <dywi <AT> mailerd <DOT> de> |
3 |
AuthorDate: Fri Jun 1 14:56:09 2012 +0000 |
4 |
Commit: André Erdmann <dywi <AT> mailerd <DOT> de> |
5 |
CommitDate: Fri Jun 1 14:56:09 2012 +0000 |
6 |
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=ac0b226f |
7 |
|
8 |
2012-01-06: |
9 |
* description field definition is now configurable |
10 |
|
11 |
modified: roverlay/__init__.py |
12 |
modified: roverlay/config.py |
13 |
new file: roverlay/const.py |
14 |
modified: roverlay/descriptionfields.py |
15 |
renamed: roverlay/fileio.py -> roverlay/descriptionreader.py |
16 |
modified: roverlay/ebuild.py |
17 |
modified: roverlay/ebuildjob.py |
18 |
deleted: roverlay/tmpconst.py |
19 |
|
20 |
--- |
21 |
roverlay/__init__.py | 5 + |
22 |
roverlay/config.py | 164 ++++++++++++- |
23 |
roverlay/const.py | 51 ++++ |
24 |
roverlay/descriptionfields.py | 343 +++++++++++++++++++++++--- |
25 |
roverlay/{fileio.py => descriptionreader.py} | 248 ++++++------------- |
26 |
roverlay/ebuild.py | 5 +- |
27 |
roverlay/ebuildjob.py | 15 +- |
28 |
roverlay/tmpconst.py | 108 -------- |
29 |
8 files changed, 615 insertions(+), 324 deletions(-) |
30 |
|
31 |
diff --git a/roverlay/__init__.py b/roverlay/__init__.py |
32 |
index b1557ef..f50cec9 100644 |
33 |
--- a/roverlay/__init__.py |
34 |
+++ b/roverlay/__init__.py |
35 |
@@ -4,6 +4,11 @@ |
36 |
|
37 |
import logging |
38 |
|
39 |
+ |
40 |
+from roverlay import config |
41 |
+ |
42 |
+config.access().load_field_definition ( 'description_fields.conf' ) |
43 |
+ |
44 |
logging.basicConfig ( |
45 |
level=logging.DEBUG, |
46 |
filename='roverlay.log', |
47 |
|
48 |
diff --git a/roverlay/config.py b/roverlay/config.py |
49 |
index 248fc22..b0bf7a3 100644 |
50 |
--- a/roverlay/config.py |
51 |
+++ b/roverlay/config.py |
52 |
@@ -3,20 +3,53 @@ |
53 |
# Distributed under the terms of the GNU General Public License v2 |
54 |
|
55 |
import sys |
56 |
- |
57 |
-from roverlay import descriptionfields |
58 |
+import shlex |
59 |
|
60 |
try: |
61 |
import configparser |
62 |
-except ImportError: |
63 |
+except ImportError as running_python2: |
64 |
+ # configparser is named ConfigParser in python2 |
65 |
import ConfigParser as configparser |
66 |
|
67 |
+ |
68 |
+ |
69 |
+ |
70 |
+from roverlay import descriptionfields |
71 |
+from roverlay import const |
72 |
+ |
73 |
+ |
74 |
+ |
75 |
+ |
76 |
def access(): |
77 |
+ """Returns the ConfigTree.""" |
78 |
return ConfigTree() if ConfigTree.instance is None else ConfigTree.instance |
79 |
+# --- end of access (...) --- |
80 |
+ |
81 |
+ |
82 |
+def get ( key, fallback_value=None ): |
83 |
+ """Searches for key in the ConfigTree and returns its value if possible, |
84 |
+ else fallback_value. |
85 |
+ 'key' is a config path [<section>[.<subsection>*]]<option name>. |
86 |
+ |
87 |
+ arguments: |
88 |
+ * key -- |
89 |
+ * fallback_value -- |
90 |
+ """ |
91 |
+ if fallback_value: |
92 |
+ return access().get ( key, fallback_value ) |
93 |
+ else: |
94 |
+ return access().get ( key ) |
95 |
+# --- end of get (...) --- |
96 |
+ |
97 |
|
98 |
class InitialLogger: |
99 |
|
100 |
def __init__ ( self ): |
101 |
+ """Initializes an InitialLogger. |
102 |
+ It implements the debug/info/warning/error/critical/exception methods |
103 |
+ known from the logging module and its output goes directly to sys.stderr. |
104 |
+ This can be used until the real logging has been configured. |
105 |
+ """ |
106 |
self.debug = lambda x : sys.stderr.write ( "DBG " + str ( x ) + "\n" ) |
107 |
self.info = lambda x : sys.stderr.write ( "INFO " + str ( x ) + "\n" ) |
108 |
self.warning = lambda x : sys.stderr.write ( "WARN " + str ( x ) + "\n" ) |
109 |
@@ -24,11 +57,23 @@ class InitialLogger: |
110 |
self.critical = lambda x : sys.stderr.write ( "CRIT " + str ( x ) + "\n" ) |
111 |
self.exception = lambda x : sys.stderr.write ( "EXC! " + str ( x ) + "\n" ) |
112 |
|
113 |
+ # --- end of __init__ (...) --- |
114 |
+ |
115 |
class ConfigTree: |
116 |
# static access to the first created ConfigTree |
117 |
instance = None |
118 |
|
119 |
- def __init__ ( self ): |
120 |
+ def __init__ ( self, import_const=True ): |
121 |
+ """Initializes an ConfigTree, which is a container for options/config values. |
122 |
+ values can be stored directly (such as the field_definitions) or in a |
123 |
+ tree-like { section -> subsection[s] -> option = value } structure. |
124 |
+ Config keys cannot contain dots because they're used as config path |
125 |
+ separator. |
126 |
+ |
127 |
+ arguments: |
128 |
+ * import_const -- whether to deepcopy constants into the config tree or |
129 |
+ not. Copying allows faster lookups. |
130 |
+ """ |
131 |
if ConfigTree.instance is None: |
132 |
ConfigTree.instance = self |
133 |
|
134 |
@@ -36,8 +81,52 @@ class ConfigTree: |
135 |
|
136 |
self.parser = dict() |
137 |
|
138 |
+ self._config = const.clone() if import_const else None |
139 |
+ self._const_imported = import_const |
140 |
+ self._field_definitions = None |
141 |
+ |
142 |
+ # --- end of __init__ (...) --- |
143 |
+ |
144 |
+ |
145 |
+ def get ( self, key, fallback_value=None ): |
146 |
+ """Searches for key in the ConfigTree and returns its value. |
147 |
+ Searches in const if ConfigTree does not contain the requested key and |
148 |
+ returns the fallback_value if key not found. |
149 |
+ |
150 |
+ arguments: |
151 |
+ * key -- |
152 |
+ * fallback_value -- |
153 |
+ """ |
154 |
+ if self._config: |
155 |
+ config_path = key.split ( '.' ) |
156 |
+ config_path.reverse () |
157 |
+ |
158 |
+ config_position = self._config |
159 |
+ while len ( config_path ) and config_position: |
160 |
+ next_key = config_path.pop () |
161 |
+ if next_key in config_position: |
162 |
+ config_position = config_position [next_key] |
163 |
+ else: |
164 |
+ config_position = None |
165 |
+ |
166 |
+ if config_position: |
167 |
+ return config_position |
168 |
+ |
169 |
+ if self._const_imported: |
170 |
+ return fallback_value |
171 |
+ else: |
172 |
+ return const.lookup ( key, fallback_value ) |
173 |
+ |
174 |
+ # --- end of get (...) --- |
175 |
|
176 |
def load_field_definition ( self, def_file, lenient=False ): |
177 |
+ """Loads a field definition file. Please see the example file for format |
178 |
+ details. |
179 |
+ |
180 |
+ arguments: |
181 |
+ * def_file -- file (str) to read, this can be a list of str if lenient is True |
182 |
+ * lenient -- if True: do not fail if a file cannot be read; defaults to False |
183 |
+ """ |
184 |
if not 'field_def' in self.parser: |
185 |
self.parser ['field_def'] = configparser.SafeConfigParser ( allow_no_value=True ) |
186 |
|
187 |
@@ -57,4 +146,71 @@ class ConfigTree: |
188 |
self.logger.exception ( mshe ) |
189 |
raise |
190 |
|
191 |
+ # --- end of load_field_definition (...) --- |
192 |
+ |
193 |
+ |
194 |
+ def get_field_definition ( self, force_update=False ): |
195 |
+ """Gets the field definition stored in this ConfigTree. |
196 |
+ |
197 |
+ arguments: |
198 |
+ * force_update -- enforces recreation of the field definition data. |
199 |
+ """ |
200 |
+ if force_update or not self._field_definitions: |
201 |
+ self._field_definitions = self._make_field_definition () |
202 |
+ |
203 |
+ return self._field_definitions |
204 |
+ |
205 |
+ # --- end of get_field_definition (...) --- |
206 |
+ |
207 |
+ |
208 |
+ def _make_field_definition ( self ): |
209 |
+ """Creates and returns field definition data. Please see the example |
210 |
+ field definition config file for details. |
211 |
+ """ |
212 |
+ |
213 |
+ def get_list ( value_str ): |
214 |
+ if value_str is None: |
215 |
+ return [] |
216 |
+ else: |
217 |
+ l = value_str.split ( ', ' ) |
218 |
+ return [ e for e in l if e.strip() ] |
219 |
+ |
220 |
+ if not 'field_def' in self.parser: |
221 |
+ return None |
222 |
+ |
223 |
+ fdef = descriptionfields.DescriptionFields () |
224 |
+ |
225 |
+ for field_name in self.parser ['field_def'].sections(): |
226 |
+ field = descriptionfields.DescriptionField ( field_name ) |
227 |
+ for option, value in self.parser ['field_def'].items ( field_name, 1 ): |
228 |
+ |
229 |
+ if option == 'alias' or option == 'alias_withcase': |
230 |
+ for alias in get_list ( value ): |
231 |
+ field.add_simple_alias ( alias, True ) |
232 |
+ |
233 |
+ elif option == 'alias_nocase': |
234 |
+ for alias in get_list ( value ): |
235 |
+ field.add_simple_alias ( alias, False ) |
236 |
+ |
237 |
+ elif option == 'default_value': |
238 |
+ field.set_default_value ( value ) |
239 |
+ |
240 |
+ elif option == 'allowed_value': |
241 |
+ field.add_allowed_value ( value ) |
242 |
+ |
243 |
+ elif option == 'allowed_values': |
244 |
+ for item in get_list ( value ): |
245 |
+ field.add_allowed_value ( item ) |
246 |
+ |
247 |
+ elif option == 'flags': |
248 |
+ for flag in get_list ( value ): |
249 |
+ field.add_flag ( flag ) |
250 |
+ else: |
251 |
+ # treat option as flag |
252 |
+ field.add_flag ( option ) |
253 |
+ |
254 |
+ fdef.add ( field ) |
255 |
+ |
256 |
+ return fdef |
257 |
|
258 |
+ # --- end of _make_field_definition (...) --- |
259 |
|
260 |
diff --git a/roverlay/const.py b/roverlay/const.py |
261 |
new file mode 100644 |
262 |
index 0000000..32630cf |
263 |
--- /dev/null |
264 |
+++ b/roverlay/const.py |
265 |
@@ -0,0 +1,51 @@ |
266 |
+# R Overlay -- constants |
267 |
+# Copyright 2006-2012 Gentoo Foundation |
268 |
+# Distributed under the terms of the GNU General Public License v2 |
269 |
+ |
270 |
+import copy |
271 |
+import time |
272 |
+ |
273 |
+_CONSTANTS = dict ( |
274 |
+ DESCRIPTION = dict ( |
275 |
+ field_separator = ':', |
276 |
+ comment_char = '#', |
277 |
+ list_split_regex = '\s*[,;]{1}\s*', |
278 |
+ file_name = 'DESCRIPTION', |
279 |
+ ), |
280 |
+ R_PACKAGE = dict ( |
281 |
+ suffix_regex = '[.](tgz|tbz2|tar|(tar[.](gz|bz2)))', |
282 |
+ name_ver_separator = '_', |
283 |
+ ), |
284 |
+ EBUILD = dict ( |
285 |
+ indent = '\t', |
286 |
+ default_header = [ '# Copyright 1999-' + str ( time.gmtime() [0] ) + ' Gentoo Foundation', |
287 |
+ '# Distributed under the terms of the GNU General Public License v2', |
288 |
+ '# $Header: $', |
289 |
+ '', |
290 |
+ 'EAPI=4', |
291 |
+ '', |
292 |
+ 'inherit R-packages' |
293 |
+ ], |
294 |
+ ) |
295 |
+) |
296 |
+ |
297 |
+def lookup ( key, fallback_value=None ): |
298 |
+ path = key.split ( '.' ) |
299 |
+ path.reverse () |
300 |
+ |
301 |
+ const_position = _CONSTANTS |
302 |
+ |
303 |
+ while len ( path ) and const_position: |
304 |
+ next_key = path.pop () |
305 |
+ if next_key in const_position: |
306 |
+ const_position = const_position [next_key] |
307 |
+ else: |
308 |
+ const_position = None |
309 |
+ |
310 |
+ if const_position: |
311 |
+ return const_position |
312 |
+ else: |
313 |
+ return fallback_value |
314 |
+ |
315 |
+def clone ( ): |
316 |
+ return copy.deepcopy ( _CONSTANTS ) |
317 |
|
318 |
diff --git a/roverlay/descriptionfields.py b/roverlay/descriptionfields.py |
319 |
index 9c028c2..72175cb 100644 |
320 |
--- a/roverlay/descriptionfields.py |
321 |
+++ b/roverlay/descriptionfields.py |
322 |
@@ -2,39 +2,95 @@ |
323 |
# Copyright 2006-2012 Gentoo Foundation |
324 |
# Distributed under the terms of the GNU General Public License v2 |
325 |
|
326 |
- |
327 |
-# split from tmpconst / fileio to make configuration possible, but TODO |
328 |
- |
329 |
class DescriptionField: |
330 |
+ """Configuration for a field in the R package description file.""" |
331 |
|
332 |
def __init__ ( self, name ): |
333 |
+ """Initializes a DescriptionField with a valid(!) name. |
334 |
+ |
335 |
+ arguments: |
336 |
+ * name -- name of the field, has to be True (neither empty nor None) |
337 |
+ |
338 |
+ raises: Exception if name not valid |
339 |
+ """ |
340 |
+ |
341 |
if not name: |
342 |
raise Exception ( "description field name is empty." ) |
343 |
|
344 |
self.name = name |
345 |
|
346 |
+ # --- end of __init__ (...) --- |
347 |
|
348 |
|
349 |
def get_name ( self ): |
350 |
+ """Returns the name of this DescriptionField.""" |
351 |
return self.name |
352 |
|
353 |
- def add_flag ( self, flag, lowercase=True ): |
354 |
- if not hasattr ( self, flags ): |
355 |
+ # --- end of get_name (...) --- |
356 |
+ |
357 |
+ |
358 |
+ def add_flag ( self, flag ): |
359 |
+ """Adds a flag to this DescriptionField. Flags are always stored in |
360 |
+ their lowercase form. |
361 |
+ |
362 |
+ arguments: |
363 |
+ * flag -- name of the flag |
364 |
+ """ |
365 |
+ if not hasattr ( self, 'flags' ): |
366 |
self.flags = set () |
367 |
|
368 |
- self.flags.add ( flag, flag.lower() if lowercase else flag ) |
369 |
+ self.flags.add ( flag.lower() ) |
370 |
|
371 |
return None |
372 |
|
373 |
+ # --- end of add_flag (...) --- |
374 |
+ |
375 |
+ |
376 |
+ def add_allowed_value ( self, value ): |
377 |
+ """Adds an allowed value to this DescriptionField, which creates a |
378 |
+ value whitelist for it. You can later check if a value is allowed using |
379 |
+ value_allowed (<value> [, <case insensitive?>]). |
380 |
+ |
381 |
+ arguments: |
382 |
+ * value -- allowed value |
383 |
+ """ |
384 |
+ |
385 |
+ if not hasattr ( self, 'allowed_values' ): |
386 |
+ self.allowed_values = set () |
387 |
+ |
388 |
+ self.allowed_values.add ( value ) |
389 |
+ |
390 |
+ return None |
391 |
+ |
392 |
+ # --- end of add_allowed_value (...) --- |
393 |
+ |
394 |
|
395 |
def del_flag ( self, flag ): |
396 |
- if hasattr ( self, flags ): |
397 |
- self.flags.discard ( flag ) |
398 |
+ """Removes a flag from this DescriptionField. Does nothing if the flag |
399 |
+ does not exist. |
400 |
+ """ |
401 |
+ if hasattr ( self, 'flags' ): |
402 |
+ self.flags.discard ( flag.lower() ) |
403 |
return None |
404 |
|
405 |
+ # --- end of del_flag (...) --- |
406 |
+ |
407 |
|
408 |
def add_alias ( self, alias, alias_type='withcase' ): |
409 |
- if not hasattr ( self, aliases ): |
410 |
+ """Adds an alias for this DescriptionField's name. This can also be used |
411 |
+ to combine different fields ('Description' and 'Title') or to fix |
412 |
+ typos ('Depend' -> 'Depends'). |
413 |
+ |
414 |
+ arguments: |
415 |
+ * alias -- alias name |
416 |
+ * alias_type -- type of the alias; currently this is limited to |
417 |
+ 'withcase' : alias is case sensitive, |
418 |
+ 'nocase' : alias is case insensitive |
419 |
+ any other type leads to an error |
420 |
+ |
421 |
+ raises: KeyError if alias_type unknown. |
422 |
+ """ |
423 |
+ if not hasattr ( self, 'aliases' ): |
424 |
self.aliases = dict () |
425 |
|
426 |
to_add = dict ( |
427 |
@@ -50,37 +106,83 @@ class DescriptionField: |
428 |
|
429 |
return None |
430 |
|
431 |
+ # --- end of add_alias (...) --- |
432 |
|
433 |
|
434 |
def add_simple_alias ( self, alias, withcase=True ): |
435 |
- if withcase: |
436 |
- return self.add_alias ( alias, alias_type='withcase' ) |
437 |
- else: |
438 |
- return self.add_alias ( alias, alias_type='nocase' ) |
439 |
+ """Adds an alias to this DescriptionField. Its type is either withcase |
440 |
+ or nocase. See add_alias (...) for details. |
441 |
+ |
442 |
+ arguments: |
443 |
+ alias -- |
444 |
+ withcase -- if True (the default): alias_type is withcase, else nocase |
445 |
+ |
446 |
+ raises: KeyError (passed from add_alias (...)) |
447 |
+ """ |
448 |
+ return self.add_alias ( alias, ( 'withcase' if withcase else 'nocase' ) ) |
449 |
|
450 |
+ # --- end of add_simple_alias (...) --- |
451 |
|
452 |
|
453 |
def get_default_value ( self ): |
454 |
- if hasattr ( self, 'default_value' ): |
455 |
- return self.default_value |
456 |
- else: |
457 |
- return None |
458 |
+ """Returns the default value for this DescriptionField if it exists, |
459 |
+ else None. |
460 |
+ """ |
461 |
+ return self.default_value if hasattr ( self, 'default_value' ) else None |
462 |
|
463 |
+ # --- end of get_default_value (...) --- |
464 |
+ |
465 |
+ |
466 |
+ def set_default_value ( self, value ): |
467 |
+ """Sets the default value for this this DescriptionField. |
468 |
+ |
469 |
+ arguments: |
470 |
+ * value -- new default value |
471 |
+ """ |
472 |
+ self.default_value = value |
473 |
+ |
474 |
+ # --- end of set_default_value (...) --- |
475 |
+ |
476 |
+ |
477 |
+ def get_flags ( self ): |
478 |
+ """Returns the flags of this DescriptionField or an empty list (=no flags).""" |
479 |
+ return self.flags if hasattr ( self, 'flags' ) else [] |
480 |
+ |
481 |
+ # --- end of get_flags (...) --- |
482 |
+ |
483 |
+ |
484 |
+ def get_allowed_values ( self ): |
485 |
+ """Returns the allowed values of this DescriptionField or an empty list, |
486 |
+ which should be interpreted as 'no value restriction'. |
487 |
+ """ |
488 |
+ return self.allowed_values if hasattr ( self, 'allowed_values' ) else [] |
489 |
+ |
490 |
+ # --- end of get_allowed_values (...) --- |
491 |
|
492 |
- def get ( self, key, fallback_value=None ): |
493 |
- if hasattr ( self, key ): |
494 |
- return self.key |
495 |
- else: |
496 |
- return fallback_value |
497 |
|
498 |
def matches ( self, field_identifier ): |
499 |
+ """Returns whether field_identifier equals the name of this DescriptionField. |
500 |
+ |
501 |
+ arguments: |
502 |
+ * field_identifier -- |
503 |
+ """ |
504 |
return bool ( self.name == field_identifier ) if field_identifier else False |
505 |
|
506 |
+ # --- end of matches (...) --- |
507 |
+ |
508 |
+ |
509 |
def matches_alias ( self, field_identifier ): |
510 |
+ """Returns whether field_identifier equals any alias of this DescriptionField. |
511 |
+ |
512 |
+ arguments: |
513 |
+ * field_identifier -- |
514 |
+ """ |
515 |
|
516 |
if not field_identifier: |
517 |
+ # bad identifier |
518 |
return False |
519 |
- if not hasattr ( self, aliases ): |
520 |
+ elif not hasattr ( self, aliases ): |
521 |
+ # no aliases |
522 |
return False |
523 |
|
524 |
if 'withcase' in self.aliases: |
525 |
@@ -92,30 +194,211 @@ class DescriptionField: |
526 |
if field_id_lower in self.aliases ['nocase']: |
527 |
return True |
528 |
|
529 |
- def has_flag ( self, flag, lowercase=True ): |
530 |
- if not hasattr ( self, flags ): |
531 |
+ # --- end of matches_alias (...) --- |
532 |
+ |
533 |
+ |
534 |
+ def has_flag ( self, flag ): |
535 |
+ """Returns whether this DescriptionField has the given flag. |
536 |
+ |
537 |
+ arguments: |
538 |
+ * flag -- |
539 |
+ """ |
540 |
+ if not hasattr ( self, 'flags' ): |
541 |
return False |
542 |
|
543 |
- return bool ( (flag.lower() if lowercase else flag) in self.flags ) |
544 |
+ return bool ( flag.lower() in self.flags ) |
545 |
+ |
546 |
+ def value_allowed ( self, value, nocase=True ): |
547 |
+ """Returns whether value is allowed for this DescriptionField. |
548 |
+ |
549 |
+ arguments: |
550 |
+ * value -- value to check |
551 |
+ * nocase -- if True (the default): be case insensitive |
552 |
+ """ |
553 |
+ allowed_values = self.get_allowed_values () |
554 |
+ |
555 |
+ if not allowed_values: |
556 |
+ return True |
557 |
+ elif nocase: |
558 |
+ lowval = value.lower() |
559 |
+ for allowed in allowed_values: |
560 |
+ if allowed.lower() == lowval: |
561 |
+ return True |
562 |
+ |
563 |
+ else: |
564 |
+ return bool ( value in allowed_values ) |
565 |
+ |
566 |
+ return False |
567 |
+ |
568 |
+ # --- end of has_flag (...) --- |
569 |
+ |
570 |
+# --- end of DescriptionField --- |
571 |
+ |
572 |
|
573 |
class DescriptionFields: |
574 |
+ """DescriptionFields stores several instances of DescriptionField and provides |
575 |
+ 'search in all' methods such as get_fields_with_flag (<flag>). |
576 |
+ """ |
577 |
|
578 |
def __init__ ( self ): |
579 |
- fields = dict () |
580 |
+ """Initializes an DescriptionFields object.""" |
581 |
+ self.fields = dict () |
582 |
+ # result 'caches' |
583 |
+ ## flag -> [<fields>] |
584 |
+ self._fields_by_flag = None |
585 |
+ ## option -> [<fields>] |
586 |
+ self._fields_by_option = None |
587 |
+ |
588 |
+ # --- end of __init__ (...) --- |
589 |
+ |
590 |
|
591 |
def add ( self, desc_field ): |
592 |
+ """Adds an DescriptionField. Returns 1 desc_field was a DescriptionField |
593 |
+ and has been added as obj ref, 2 if a new DescriptionField with |
594 |
+ name=desc_field has been created and added and 0 if this was not |
595 |
+ possible. |
596 |
+ |
597 |
+ arguments: |
598 |
+ * desc_field -- this can either be a DescriptionField or a name. |
599 |
+ """ |
600 |
if desc_field: |
601 |
if isinstance ( desc_field, DescriptionField ): |
602 |
- fields [desc_field.get_name()] = desc_field |
603 |
+ self.fields [desc_field.get_name()] = desc_field |
604 |
return 1 |
605 |
elif isinstance ( desc_field, str ): |
606 |
- fields [desc_field] = DescriptionField ( desc_field ) |
607 |
+ self.fields [desc_field] = DescriptionField ( desc_field ) |
608 |
return 2 |
609 |
|
610 |
return 0 |
611 |
|
612 |
+ # --- end of add (...) --- |
613 |
+ |
614 |
+ |
615 |
def get ( self, field_name ): |
616 |
+ """Returns the DescriptionField to which field_name belongs to. |
617 |
+ This method does, unlike others in DescriptionFields, return a |
618 |
+ reference to the matching DescriptionField object, not the field name! |
619 |
+ Returns None if field_name not found. |
620 |
+ |
621 |
+ arguments: |
622 |
+ * field_name -- |
623 |
+ """ |
624 |
+ |
625 |
return self.fields [field_name] if field_name in self.fields else None |
626 |
|
627 |
- # ... TODO |
628 |
+ # --- end of get (...) --- |
629 |
+ |
630 |
+ |
631 |
+ def find_field ( self, field_name ): |
632 |
+ """Determines the name of the DescriptionField to which field_name belongs |
633 |
+ to. Returns the name of the matching field or None. |
634 |
+ |
635 |
+ arguments: |
636 |
+ * field_name -- |
637 |
+ """ |
638 |
+ |
639 |
+ field = get ( field_name ) |
640 |
+ if field is None: |
641 |
+ for field in self.fields: |
642 |
+ if field.matches_alias ( field_name ): |
643 |
+ return field.get_name () |
644 |
+ else: |
645 |
+ return field.get_name () |
646 |
+ |
647 |
+ # --- end of find_field (...) --- |
648 |
+ |
649 |
+ |
650 |
+ def _field_search ( self ): |
651 |
+ """Scans all stored DescriptionField(s) and creates fast-accessible |
652 |
+ data to be used in get_fields_with_<sth> (...). |
653 |
+ """ |
654 |
+ flagmap = dict () |
655 |
+ optionmap = dict ( |
656 |
+ defaults = dict (), |
657 |
+ allowed_values = set () |
658 |
+ ) |
659 |
+ |
660 |
+ for field_name in self.fields.keys(): |
661 |
+ |
662 |
+ d = self.fields [field_name].get_default_value() |
663 |
+ if not d is None: |
664 |
+ optionmap ['defaults'] [field_name] = d |
665 |
+ |
666 |
+ if self.fields [field_name].get_allowed_values(): |
667 |
+ optionmap ['allowed_values'].add ( field_name ) |
668 |
+ |
669 |
+ for flag in self.fields [field_name].get_flags(): |
670 |
+ if not flag in flagmap: |
671 |
+ flagmap [flag] = set () |
672 |
+ flagmap [flag].add ( field_name ) |
673 |
+ |
674 |
+ self._fields_by_flag = flagmap |
675 |
+ self._fields_by_option = optionmap |
676 |
+ return None |
677 |
+ |
678 |
+ # --- end of _field_search (...) --- |
679 |
+ |
680 |
+ |
681 |
+ def get_fields_with_flag ( self, flag, force_update=False ): |
682 |
+ """Returns the names of the fields that have the given flag. |
683 |
+ |
684 |
+ arguments: |
685 |
+ * flag -- |
686 |
+ * force_update -- force recreation of data |
687 |
+ """ |
688 |
+ if force_update or self._fields_by_flag is None: |
689 |
+ self._field_search () |
690 |
+ |
691 |
+ flag = flag.lower() |
692 |
+ |
693 |
+ if flag in self._fields_by_flag: |
694 |
+ return self._fields_by_flag [flag] |
695 |
+ else: |
696 |
+ return [] |
697 |
+ |
698 |
+ # --- end of get_fields_with_flag (...) --- |
699 |
+ |
700 |
+ |
701 |
+ def get_fields_with_option ( self, option, force_update=False ): |
702 |
+ """Returns a struct with fields that have the given option. The actual |
703 |
+ data type depends on the requested option. |
704 |
+ |
705 |
+ arguments: |
706 |
+ * option -- |
707 |
+ * force_update -- force recreation of data |
708 |
+ """ |
709 |
+ if force_update or self._fields_by_option is None: |
710 |
+ self._field_search () |
711 |
+ |
712 |
+ if option in self._fields_by_option: |
713 |
+ return self._fields_by_option [option] |
714 |
+ else: |
715 |
+ return [] |
716 |
+ |
717 |
+ # --- end of get_field_with_option (...) --- |
718 |
+ |
719 |
+ |
720 |
+ def get_fields_with_default_value ( self, force_update=False ): |
721 |
+ """Returns a dict { '<field name>' -> '<default value>' } for all |
722 |
+ fields that have a default value. |
723 |
+ |
724 |
+ arguments: |
725 |
+ * force_update -- force recreation of data |
726 |
+ """ |
727 |
+ return self.get_fields_with_option ( 'defaults', force_update ) |
728 |
+ |
729 |
+ # --- end of get_fields_with_default_value (...) --- |
730 |
+ |
731 |
+ |
732 |
+ def get_fields_with_allowed_values ( self, force_update=False ): |
733 |
+ """Returns a set { <field name> } for all fields that allow only |
734 |
+ certain values. |
735 |
+ |
736 |
+ arguments: |
737 |
+ * force_update -- force recreation of data |
738 |
+ """ |
739 |
+ return self.get_fields_with_option ( 'allowed_values', force_update ) |
740 |
+ |
741 |
+ # --- end of get_fields_with_allowed_values (...) --- |
742 |
|
743 |
+# --- end of DescriptionFields --- |
744 |
|
745 |
diff --git a/roverlay/fileio.py b/roverlay/descriptionreader.py |
746 |
similarity index 54% |
747 |
rename from roverlay/fileio.py |
748 |
rename to roverlay/descriptionreader.py |
749 |
index 5c01527..2af4372 100644 |
750 |
--- a/roverlay/fileio.py |
751 |
+++ b/roverlay/descriptionreader.py |
752 |
@@ -1,4 +1,4 @@ |
753 |
-# R Overlay -- file in/out |
754 |
+# R Overlay -- description reader |
755 |
# Copyright 2006-2012 Gentoo Foundation |
756 |
# Distributed under the terms of the GNU General Public License v2 |
757 |
|
758 |
@@ -7,21 +7,25 @@ import tarfile |
759 |
import logging |
760 |
import os.path |
761 |
|
762 |
- |
763 |
-# temporary import until config and real constants are implemented |
764 |
-from roverlay import tmpconst as const |
765 |
+from roverlay import config |
766 |
+from roverlay import descriptionfields |
767 |
|
768 |
class DescriptionReader: |
769 |
"""Description Reader""" |
770 |
|
771 |
LOGGER = logging.getLogger ( 'DescriptionReader' ) |
772 |
|
773 |
+ |
774 |
def __init__ ( self, package_file, read_now=False ): |
775 |
"""Initializes a DESCRIPTION file reader.""" |
776 |
|
777 |
- self.fileinfo = self.make_fileinfo ( package_file ) |
778 |
- self.logger = DescriptionReader.LOGGER.getChild ( self.get_log_name() ) |
779 |
- self.desc_data = None |
780 |
+ if not config.access().get_field_definition(): |
781 |
+ raise Exception ( "Field definition is missing, cannot initialize DescriptionReader." ) |
782 |
+ |
783 |
+ self.field_definition = config.access().get_field_definition() |
784 |
+ self.fileinfo = self.make_fileinfo ( package_file ) |
785 |
+ self.logger = DescriptionReader.LOGGER.getChild ( self.get_log_name() ) |
786 |
+ self.desc_data = None |
787 |
|
788 |
|
789 |
if read_now: |
790 |
@@ -30,10 +34,13 @@ class DescriptionReader: |
791 |
# --- end of __init__ (...) --- |
792 |
|
793 |
def get_log_name ( self ): |
794 |
+ """Returns a logging name that can be used in other modules.""" |
795 |
try: |
796 |
return self.fileinfo ['filename'] |
797 |
except Exception as any_exception: |
798 |
return '__undef__' |
799 |
+ # --- end of get_log_name (...) --- |
800 |
+ |
801 |
|
802 |
def get_desc ( self, run_if_unset=True ): |
803 |
if self.desc_data is None: |
804 |
@@ -58,10 +65,11 @@ class DescriptionReader: |
805 |
|
806 |
package_file = os.path.basename ( filepath ) |
807 |
|
808 |
- filename = re.sub ( const.RPACKAGE_SUFFIX_REGEX + '$', '', package_file ) |
809 |
+ filename = re.sub ( config.get ( 'R_PACKAGE.suffix_regex' ) + '$', '', package_file ) |
810 |
|
811 |
- # todo move that separator to const |
812 |
- package_name, sepa, package_version = filename.partition ( '_' ) |
813 |
+ package_name, sepa, package_version = filename.partition ( |
814 |
+ config.get ( 'R_PACKAGE.name_ver_separator', '_' ) |
815 |
+ ) |
816 |
|
817 |
if not sepa: |
818 |
# file name unexpected, tarball extraction will (probably) fail |
819 |
@@ -78,7 +86,6 @@ class DescriptionReader: |
820 |
|
821 |
# --- end of make_fileinfo (...) --- |
822 |
|
823 |
- |
824 |
def _parse_read_data ( self, read_data ): |
825 |
"""Verifies and parses/fixes read data. |
826 |
|
827 |
@@ -86,80 +93,42 @@ class DescriptionReader: |
828 |
* read_data -- data from file, will be modified |
829 |
""" |
830 |
|
831 |
- def get_fields_with_flag ( flag, foce_update=False ): |
832 |
- |
833 |
- matching_fields = [] |
834 |
- |
835 |
- field = None |
836 |
- for field in const.DESCRIPTION_FIELD_MAP.keys(): |
837 |
- if flag is None: |
838 |
- matching_fields.append ( field ) |
839 |
- |
840 |
- elif 'flags' in const.DESCRIPTION_FIELD_MAP [field]: |
841 |
- if flag in const.DESCRIPTION_FIELD_MAP [field] ['flags']: |
842 |
- matching_fields.append ( field ) |
843 |
- |
844 |
- del field |
845 |
- return matching_fields |
846 |
- |
847 |
- # --- end of get_fields_with_flag (...) --- |
848 |
- |
849 |
- def value_in_strlist ( _val, _list, case_insensitive=True ): |
850 |
- """Returns true if value is in the given list.""" |
851 |
- el = None |
852 |
- if case_insensitive: |
853 |
- lowval = _val.lower() |
854 |
- for el in _list: |
855 |
- if el.lower() == lowval: |
856 |
- return True |
857 |
- del lowval |
858 |
- else: |
859 |
- for el in _list: |
860 |
- if el == _val: |
861 |
- return True |
862 |
- |
863 |
- del el |
864 |
- return False |
865 |
- # --- end of value_in_strlist (...) --- |
866 |
- |
867 |
- field = None |
868 |
|
869 |
# insert default values |
870 |
- for field in const.DESCRIPTION_FIELD_MAP.keys(): |
871 |
- if not field in read_data and 'default_value' in const.DESCRIPTION_FIELD_MAP [field]: |
872 |
- read_data [field] = const.DESCRIPTION_FIELD_MAP [field] ['default_value'] |
873 |
+ default_values = self.field_definition.get_fields_with_default_value() |
874 |
+ for field_name in default_values.keys(): |
875 |
+ if not field_name in read_data: |
876 |
+ read_data [field_name] = default_values [field_name] |
877 |
+ |
878 |
|
879 |
# join values to a single string |
880 |
- for field in get_fields_with_flag ( 'joinValues' ): |
881 |
- if field in read_data.keys(): |
882 |
- read_data [field] = ' ' . join ( read_data [field] ) |
883 |
+ for field_name in self.field_definition.get_fields_with_flag ( 'joinValues' ): |
884 |
+ print ( "?, ".join ( [ field_name, 'join', str ( read_data ) ] ) ) |
885 |
+ if field_name in read_data: |
886 |
+ read_data [field_name] = ' ' . join ( read_data [field_name] ) |
887 |
|
888 |
# ensure that all mandatory fields are set |
889 |
- missing_fields = list() |
890 |
+ missing_fields = set () |
891 |
|
892 |
- for field in get_fields_with_flag ( 'mandatory' ): |
893 |
- if field in read_data: |
894 |
- if not len (read_data [field]): |
895 |
- missing_fields.append ( field ) |
896 |
+ for field_name in self.field_definition.get_fields_with_flag ( 'mandatory' ): |
897 |
+ if field_name in read_data: |
898 |
+ if read_data [field_name] is None or len ( read_data [field_name] ) < 1: |
899 |
+ missing_fields.add ( field_name ) |
900 |
+ #else: ok |
901 |
else: |
902 |
- missing_fields.append ( field ) |
903 |
- |
904 |
- |
905 |
+ missing_fields.add ( field_name ) |
906 |
|
907 |
|
908 |
# check for fields that allow only certain values |
909 |
- unsuitable_fields = dict() |
910 |
+ unsuitable_fields = set() |
911 |
|
912 |
- for field in read_data.keys(): |
913 |
- if 'allowed_values' in const.DESCRIPTION_FIELD_MAP [field]: |
914 |
- if not value_in_strlist ( |
915 |
- read_data [field], |
916 |
- const.DESCRIPTION_FIELD_MAP [field] ['allowed_values'] |
917 |
- ): |
918 |
- unsuitable_fields.append [field] = read_data [field] |
919 |
- |
920 |
- del field |
921 |
+ restricted_fields = self.field_definition.get_fields_with_allowed_values() |
922 |
+ for field_name in restricted_fields: |
923 |
+ if field_name in read_data: |
924 |
+ if not self.field_definition.get ( field_name ).value_allowed ( read_data [field_name] ): |
925 |
+ unsuitable_fields.add ( field_name ) |
926 |
|
927 |
+ # summarize results |
928 |
valid = not bool ( len ( missing_fields ) or len ( unsuitable_fields ) ) |
929 |
if not valid: |
930 |
self.logger.info ( "Cannot use R package" ) # name? |
931 |
@@ -200,27 +169,6 @@ class DescriptionReader: |
932 |
multiple values arranged in a list (dep0, dep1 [, depK]*). |
933 |
""" |
934 |
|
935 |
- def check_fieldflag ( field, flag_to_check=None ): |
936 |
- """Checks if the given field has the specified flag and returns a bool. |
937 |
- |
938 |
- arguments: |
939 |
- * field -- name of the field that should be checked |
940 |
- * flag_to_check -- name of the flag to check; optional, defaults to None |
941 |
- |
942 |
- This method acts as 'field has any flags?' if flag_to_check is None (its default value). |
943 |
- """ |
944 |
- |
945 |
- if field in const.DESCRIPTION_FIELD_MAP: |
946 |
- if 'flags' in const.DESCRIPTION_FIELD_MAP [field]: |
947 |
- if flag_to_check in const.DESCRIPTION_FIELD_MAP [field] ['flags']: |
948 |
- return True |
949 |
- elif flag_to_check is None: |
950 |
- # 'flags' exist, return true |
951 |
- return True |
952 |
- |
953 |
- return False |
954 |
- # --- end of check_fieldflag (...) --- |
955 |
- |
956 |
svalue_str = value_str.strip() |
957 |
|
958 |
if not svalue_str: |
959 |
@@ -231,16 +179,17 @@ class DescriptionReader: |
960 |
# default return if no context given |
961 |
return [ svalue_str ] |
962 |
|
963 |
- elif check_fieldflag ( field_context ): |
964 |
- # value str is not empty and have flags for field_context, check these |
965 |
- |
966 |
- if check_fieldflag ( field_context, 'isList' ): |
967 |
- # split up this list (that is separated by commata and/or semicolons) |
968 |
- return re.split (const.DESCRIPTION_LIST_SPLIT_REGEX, svalue_str, 0) |
969 |
+ elif field_context in self.field_definition.get_fields_with_flag ( 'isList' ): |
970 |
+ # split up this list (that is separated by commata and/or semicolons) |
971 |
+ return re.split ( |
972 |
+ config.get ( 'DESCRIPTION.list_split_regex' ), |
973 |
+ svalue_str, |
974 |
+ 0 |
975 |
+ ) |
976 |
|
977 |
- elif check_fieldflag ( field_context, 'isWhitespaceList' ): |
978 |
- # split up this list (that is separated whitespace) |
979 |
- return re.split ( '\s+', svalue_str, 0 ) |
980 |
+ elif field_context in self.field_definition.get_fields_with_flag ( 'isWhitespaceList' ): |
981 |
+ # split up this list (that is separated whitespace) |
982 |
+ return re.split ( '\s+', svalue_str, 0 ) |
983 |
|
984 |
|
985 |
# default return |
986 |
@@ -275,9 +224,12 @@ class DescriptionReader: |
987 |
# filepath is a tarball, open tar handle + file handle |
988 |
th = tarfile.open ( filepath, 'r' ) |
989 |
if pkg_name: |
990 |
- fh = th.extractfile ( os.path.join ( pkg_name, const.DESCRIPTION_FILE_NAME ) ) |
991 |
+ fh = th.extractfile ( os.path.join ( |
992 |
+ pkg_name, |
993 |
+ config.get ( 'DESCRIPTION.file_name' ) |
994 |
+ ) ) |
995 |
else: |
996 |
- fh = th.extractfile ( const.DESCRIPTION_FILE_NAME ) |
997 |
+ fh = th.extractfile ( config.get ( 'DESCRIPTION.file_name' ) ) |
998 |
|
999 |
# have to decode the lines |
1000 |
read = lambda lines : [ line.decode().rstrip() for line in lines ] |
1001 |
@@ -298,60 +250,9 @@ class DescriptionReader: |
1002 |
|
1003 |
# --- end of get_desc_from_file (...) --- |
1004 |
|
1005 |
- def find_field ( field_identifier ): |
1006 |
- """Determines the real name of a field. |
1007 |
- |
1008 |
- arguments: |
1009 |
- * field_identifier -- name of the field as it appears in the DESCRIPTION file |
1010 |
- |
1011 |
- At first, it is checked whether field_identifier matches the name of |
1012 |
- a field listed in DESCRIPTION_FIELD_MAP (any match results in immediate return). |
1013 |
- Then, a new iteration over the field map compares field_identifier |
1014 |
- with all aliases until the first case-(in)sensitive match (-> immediate return). |
1015 |
- None will be returned if none of the above searches succeed. |
1016 |
- |
1017 |
- In other words: this method decides whether a field_identifier will be used and if so, |
1018 |
- with which name. |
1019 |
- """ |
1020 |
- |
1021 |
- # save some time by prevent searching if field_id is empty |
1022 |
- if not field_identifier: |
1023 |
- return None |
1024 |
- |
1025 |
- # search for real field names first |
1026 |
- for field in const.DESCRIPTION_FIELD_MAP.keys(): |
1027 |
- if field_identifier == field: |
1028 |
- return field |
1029 |
- |
1030 |
- field_id_lower = field_identifier.lower() |
1031 |
- |
1032 |
- for field in const.DESCRIPTION_FIELD_MAP.keys(): |
1033 |
- |
1034 |
- # does extra information (-> alias(es)) for this field exist? |
1035 |
- if 'alias' in const.DESCRIPTION_FIELD_MAP [field]: |
1036 |
- |
1037 |
- if 'withcase' in const.DESCRIPTION_FIELD_MAP [field] ['alias']: |
1038 |
- for alias in const.DESCRIPTION_FIELD_MAP [field] ['alias'] ['withcase']: |
1039 |
- if field_identifier == alias: |
1040 |
- return field |
1041 |
- |
1042 |
- if 'nocase' in const.DESCRIPTION_FIELD_MAP [field] ['alias']: |
1043 |
- for alias in const.DESCRIPTION_FIELD_MAP [field] ['alias'] ['nocase']: |
1044 |
- if field_id_lower == alias.lower(): |
1045 |
- return field |
1046 |
- |
1047 |
- #if 'other_alias_type' in const.DESCRIPTION_FIELD_MAP [field] ['alias']: |
1048 |
- |
1049 |
- # returning None if no valid field identifier matches |
1050 |
- return None |
1051 |
- |
1052 |
- # --- end of find_field (...) --- |
1053 |
- |
1054 |
- |
1055 |
self.desc_data = None |
1056 |
read_data = dict () |
1057 |
|
1058 |
- |
1059 |
try: |
1060 |
desc_lines = get_desc_from_file ( |
1061 |
self.fileinfo ['filepath'], |
1062 |
@@ -362,14 +263,15 @@ class DescriptionReader: |
1063 |
self.logger.exception ( err ) |
1064 |
return self.desc_data |
1065 |
|
1066 |
+ field_context = None |
1067 |
|
1068 |
- field_context = val = line = sline = None |
1069 |
for line in desc_lines: |
1070 |
+ field_context_ref = None |
1071 |
|
1072 |
# using s(tripped)line whenever whitespace doesn't matter |
1073 |
sline = line.lstrip() |
1074 |
|
1075 |
- if (not sline) or (line [0] == const.DESCRIPTION_COMMENT_CHAR): |
1076 |
+ if (not sline) or (line [0] == config.get ( 'DESCRIPTION.comment_char' ) ): |
1077 |
# empty line or comment |
1078 |
pass |
1079 |
|
1080 |
@@ -389,14 +291,26 @@ class DescriptionReader: |
1081 |
# line introduces a new field context, forget last one |
1082 |
field_context = None |
1083 |
|
1084 |
- line_components = sline.partition ( const.DESCRIPTION_FIELD_SEPARATOR ) |
1085 |
+ line_components = sline.partition ( config.get ( 'DESCRIPTION.field_separator' ) ) |
1086 |
|
1087 |
if line_components [1]: |
1088 |
# line contains a field separator, set field context |
1089 |
- field_context = find_field ( line_components [0] ) |
1090 |
+ field_context_ref = self.field_definition.get ( line_components [0] ) |
1091 |
+ |
1092 |
+ if field_context_ref is None: |
1093 |
+ # useless line, skip |
1094 |
+ self.logger.info ( "Skipped a description field: '%s'.", line_components [0] ) |
1095 |
+ elif field_context_ref.has_flag ( 'ignore' ): |
1096 |
+ # field ignored |
1097 |
+ self.logger.debug ( "Ignored field '%s'.", field_context ) |
1098 |
+ |
1099 |
+ else: |
1100 |
+ field_context = field_context_ref.get_name() |
1101 |
+ |
1102 |
+ if not field_context: |
1103 |
+ raise Exception ( "Field name is not valid! This should've already been catched in DescriptionField..." ) |
1104 |
|
1105 |
- if field_context: |
1106 |
- # create a new empty list for field_context |
1107 |
+ # create a new empty list for this field_context |
1108 |
read_data [field_context] = [] |
1109 |
|
1110 |
# add values to read_data |
1111 |
@@ -404,9 +318,7 @@ class DescriptionReader: |
1112 |
for val in make_values ( line_components [2], field_context ): |
1113 |
read_data [field_context] . append ( val ) |
1114 |
|
1115 |
- else: |
1116 |
- # useless line, skip |
1117 |
- self.logger.info ( "Skipped a description field: '%s'.", line_components [0] ) |
1118 |
+ |
1119 |
|
1120 |
else: |
1121 |
# reaching this branch means that |
1122 |
@@ -415,11 +327,7 @@ class DescriptionReader: |
1123 |
# this should not occur in description files (bad syntax?) |
1124 |
self.logger.warning ( "Unexpected line in description file: '%s'.", line_components [0] ) |
1125 |
|
1126 |
- |
1127 |
- del line_components |
1128 |
- |
1129 |
- del sline, line, val, field_context |
1130 |
- |
1131 |
+ # -- end for -- |
1132 |
|
1133 |
if self._parse_read_data ( read_data ): |
1134 |
self.logger.debug ( "Successfully read file '%s' with data = %s.", |
1135 |
@@ -430,4 +338,4 @@ class DescriptionReader: |
1136 |
# get_desc() is preferred, but this method returns the desc data, too |
1137 |
return self.desc_data |
1138 |
|
1139 |
- # --- end of readfile (...) --- |
1140 |
+ # --- end of run (...) --- |
1141 |
|
1142 |
diff --git a/roverlay/ebuild.py b/roverlay/ebuild.py |
1143 |
index 1634ef3..4493bb7 100644 |
1144 |
--- a/roverlay/ebuild.py |
1145 |
+++ b/roverlay/ebuild.py |
1146 |
@@ -2,9 +2,10 @@ |
1147 |
# Copyright 2006-2012 Gentoo Foundation |
1148 |
# Distributed under the terms of the GNU General Public License v2 |
1149 |
|
1150 |
+import roverlay.config |
1151 |
+ |
1152 |
class Ebuild: |
1153 |
- # could move this to const |
1154 |
- EBUILD_INDENT = "\t" |
1155 |
+ EBUILD_INDENT = roverlay.config.get ( 'EBUILD.indent', '\t' ) |
1156 |
|
1157 |
ADD_REMAP = { |
1158 |
# pkg vs package |
1159 |
|
1160 |
diff --git a/roverlay/ebuildjob.py b/roverlay/ebuildjob.py |
1161 |
index 0357e77..b6c9456 100644 |
1162 |
--- a/roverlay/ebuildjob.py |
1163 |
+++ b/roverlay/ebuildjob.py |
1164 |
@@ -2,16 +2,18 @@ |
1165 |
# Copyright 2006-2012 Gentoo Foundation |
1166 |
# Distributed under the terms of the GNU General Public License v2 |
1167 |
|
1168 |
-import time |
1169 |
import logging |
1170 |
import re |
1171 |
|
1172 |
-from roverlay.fileio import DescriptionReader |
1173 |
+from roverlay.descriptionreader import DescriptionReader |
1174 |
from roverlay.ebuild import Ebuild |
1175 |
+from roverlay import config |
1176 |
|
1177 |
class EbuildJob: |
1178 |
LOGGER = logging.getLogger ( 'EbuildJob' ) |
1179 |
|
1180 |
+ DEFAULT_EBUILD_HEADER = config.get ( 'EBUILD.default_header' ) |
1181 |
+ |
1182 |
# move this to const / config |
1183 |
DEPENDENCY_FIELDS = { |
1184 |
'R_SUGGESTS' : [ 'Suggests' ], |
1185 |
@@ -142,14 +144,7 @@ class EbuildJob: |
1186 |
|
1187 |
## default ebuild header, could use some const here (eclass name,..) |
1188 |
ebuild.add ( 'ebuild_header', |
1189 |
- [ '# Copyright 1999-' + str ( time.gmtime() [0] ) + ' Gentoo Foundation', |
1190 |
- '# Distributed under the terms of the GNU General Public License v2', |
1191 |
- '# $Header: $', |
1192 |
- '', |
1193 |
- 'EAPI=4', |
1194 |
- '', |
1195 |
- 'inherit R-packages' |
1196 |
- ], |
1197 |
+ EbuildJob.DEFAULT_EBUILD_HEADER, |
1198 |
False |
1199 |
) |
1200 |
|
1201 |
|
1202 |
diff --git a/roverlay/tmpconst.py b/roverlay/tmpconst.py |
1203 |
deleted file mode 100644 |
1204 |
index 8519aad..0000000 |
1205 |
--- a/roverlay/tmpconst.py |
1206 |
+++ /dev/null |
1207 |
@@ -1,108 +0,0 @@ |
1208 |
-# R overlay -- constants (temporary file) |
1209 |
-# Copyright 2006-2012 Gentoo Foundation |
1210 |
-# Distributed under the terms of the GNU General Public License v2 |
1211 |
- |
1212 |
-# matches .tgz .tbz2 .tar .tar.gz .tar.bz2 |
1213 |
-RPACKAGE_SUFFIX_REGEX = '[.](tgz|tbz2|tar|(tar[.](gz|bz2)))' |
1214 |
- |
1215 |
-PACKAGE_CATEGORY = 'sci-R' |
1216 |
- |
1217 |
-DESCRIPTION_FIELD_SEPARATOR = ':' |
1218 |
- |
1219 |
-DESCRIPTION_COMMENT_CHAR = '#' |
1220 |
- |
1221 |
-DESCRIPTION_LIST_SPLIT_REGEX = '\s*[,;]{1}\s*' |
1222 |
- |
1223 |
-DESCRIPTION_FILE_NAME = 'DESCRIPTION' |
1224 |
- |
1225 |
-# moved to <field> -> 'allowed_values' |
1226 |
-##DESCRIPTION_VALID_OS_TYPES = [ "unix" ] |
1227 |
- |
1228 |
- |
1229 |
-# note for 2012-05-25: make this struct more organized, assign real values |
1230 |
-"""The map of used fields in the DESCRIPTION file |
1231 |
- |
1232 |
- stores the real field name as well as field flags and aliases |
1233 |
- that can be case-sensitive (withcase) or not (nocase) |
1234 |
- |
1235 |
- access to these values is |
1236 |
- * for aliases |
1237 |
- DESCRIPTION_FIELD_MAP [<field name>] [alias] [case sensitive ? withcase : nocase] [<index>] |
1238 |
- |
1239 |
- * for flags |
1240 |
- DESCRIPTION_FIELD_MAP [<field name>] [flags] [<index>] |
1241 |
- |
1242 |
- * default values |
1243 |
- DESCRIPTION_FIELD_MAP [<field name>] [default_value] |
1244 |
- |
1245 |
- notable flags: |
1246 |
- * isList : indicates that this field has several values that are |
1247 |
- separated by commata/semicolons =:<DESCRIPTION_LIST_SPLIT_REGEX> |
1248 |
- this disables isWhitespaceList |
1249 |
- |
1250 |
- * isWhitespaceList : indicates that this field has several values separated |
1251 |
- by whitespace |
1252 |
- |
1253 |
- * joinValues : indicates that the values of this field should be concatenated |
1254 |
- after reading them (with a ' ' as separator) |
1255 |
- (this implies that the read values are one string) |
1256 |
- |
1257 |
- * mandatory : cannot proceed if a file does not contain this field (implies ignoring default values) |
1258 |
- |
1259 |
-""" |
1260 |
- |
1261 |
-DESCRIPTION_FIELD_MAP = { |
1262 |
- 'Description' : { |
1263 |
- 'flags' : [ 'joinValues' ], |
1264 |
- }, |
1265 |
- 'Title' : { |
1266 |
- 'flags' : [ 'joinValues' ], |
1267 |
- }, |
1268 |
- 'Package' : { |
1269 |
- 'flags' : [ 'joinValues' ], |
1270 |
- }, |
1271 |
- 'License' : { |
1272 |
- 'flags' : [ 'isList' ], |
1273 |
- }, |
1274 |
- 'Version' : { |
1275 |
- 'flags' : [ 'mandatory', 'joinValues' ] |
1276 |
- }, |
1277 |
- 'Suggests' : { |
1278 |
- 'alias' : { |
1279 |
- 'nocase' : [ 'Suggests', 'Suggest', |
1280 |
- '%Suggests', 'Suggets', 'Recommends' ] |
1281 |
- }, |
1282 |
- }, |
1283 |
- 'Depends' : { |
1284 |
- 'alias' : { |
1285 |
- 'nocase' : [ 'Depends', 'Dependencies', 'Dependes', |
1286 |
- '%Depends', 'Depents', 'Require', 'Requires' ], |
1287 |
- }, |
1288 |
- 'flags' : [ 'isList' ], |
1289 |
- 'default_value' : '', |
1290 |
- }, |
1291 |
- 'Imports' : { |
1292 |
- 'alias' : { |
1293 |
- 'nocase' : [ 'Imports', 'Import' ] |
1294 |
- }, |
1295 |
- }, |
1296 |
- 'LinkingTo' : { |
1297 |
- 'alias' : { |
1298 |
- 'nocase' : [ 'LinkingTo', 'LinkingdTo' ] |
1299 |
- }, |
1300 |
- }, |
1301 |
- 'SystemRequirements' : { |
1302 |
- 'alias' : { |
1303 |
- 'nocase' : [ 'SystemRequirements', 'SystemRequirement' ] |
1304 |
- }, |
1305 |
- }, |
1306 |
- 'OS_Type' : { |
1307 |
- 'alias' : { |
1308 |
- 'nocase' : [ 'OS_TYPE' ] |
1309 |
- }, |
1310 |
- 'allowed_values' : [ 'unix' ], |
1311 |
- }, |
1312 |
- 'test-default' : { |
1313 |
- 'default_value' : 'some default value' |
1314 |
- } |
1315 |
-} |