1 |
commit: 2857235f05980e0ad98351850c1422993305c6e6 |
2 |
Author: André Erdmann <dywi <AT> mailerd <DOT> de> |
3 |
AuthorDate: Thu May 31 18:22:45 2012 +0000 |
4 |
Commit: André Erdmann <dywi <AT> mailerd <DOT> de> |
5 |
CommitDate: Thu May 31 18:22:45 2012 +0000 |
6 |
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=2857235f |
7 |
|
8 |
roverlay, 2012-05-31: |
9 |
* basic logging |
10 |
** replaced some errors cases by log entries |
11 |
* started with the config module |
12 |
|
13 |
--- |
14 |
roverlay/__init__.py | 19 ++++ |
15 |
roverlay/config.py | 60 +++++++++++++ |
16 |
roverlay/descriptionfields.py | 121 ++++++++++++++++++++++++++ |
17 |
roverlay/ebuild.py | 96 +++++++++++++++------ |
18 |
roverlay/ebuildjob.py | 38 ++++++--- |
19 |
roverlay/fileio.py | 186 +++++++++++++++++++++-------------------- |
20 |
6 files changed, 394 insertions(+), 126 deletions(-) |
21 |
|
22 |
diff --git a/roverlay/__init__.py b/roverlay/__init__.py |
23 |
index 863ab2b..b1557ef 100644 |
24 |
--- a/roverlay/__init__.py |
25 |
+++ b/roverlay/__init__.py |
26 |
@@ -2,4 +2,23 @@ |
27 |
# Copyright 2006-2012 Gentoo Foundation |
28 |
# Distributed under the terms of the GNU General Public License v2 |
29 |
|
30 |
+import logging |
31 |
+ |
32 |
+logging.basicConfig ( |
33 |
+ level=logging.DEBUG, |
34 |
+ filename='roverlay.log', |
35 |
+ filemode='a', |
36 |
+ format='%(asctime)s %(levelname)-8s %(name)-14s -- %(message)s', |
37 |
+ datefmt='%F %H:%M:%S' |
38 |
+) |
39 |
+ |
40 |
+# add console output to the logger |
41 |
+ch = logging.StreamHandler() |
42 |
+ch.setLevel ( logging.INFO ) |
43 |
+ch.setFormatter ( |
44 |
+ logging.Formatter ( '%(levelname)-8s %(name)-14s -- %(message)s' ) |
45 |
+) |
46 |
+logging.getLogger().addHandler ( ch ) |
47 |
+del ch |
48 |
+ |
49 |
VERSION = "0.0-pre1" |
50 |
|
51 |
diff --git a/roverlay/config.py b/roverlay/config.py |
52 |
new file mode 100644 |
53 |
index 0000000..248fc22 |
54 |
--- /dev/null |
55 |
+++ b/roverlay/config.py |
56 |
@@ -0,0 +1,60 @@ |
57 |
+# R overlay -- config module |
58 |
+# Copyright 2006-2012 Gentoo Foundation |
59 |
+# Distributed under the terms of the GNU General Public License v2 |
60 |
+ |
61 |
+import sys |
62 |
+ |
63 |
+from roverlay import descriptionfields |
64 |
+ |
65 |
+try: |
66 |
+ import configparser |
67 |
+except ImportError: |
68 |
+ import ConfigParser as configparser |
69 |
+ |
70 |
+def access(): |
71 |
+ return ConfigTree() if ConfigTree.instance is None else ConfigTree.instance |
72 |
+ |
73 |
+class InitialLogger: |
74 |
+ |
75 |
+ def __init__ ( self ): |
76 |
+ self.debug = lambda x : sys.stderr.write ( "DBG " + str ( x ) + "\n" ) |
77 |
+ self.info = lambda x : sys.stderr.write ( "INFO " + str ( x ) + "\n" ) |
78 |
+ self.warning = lambda x : sys.stderr.write ( "WARN " + str ( x ) + "\n" ) |
79 |
+ self.error = lambda x : sys.stderr.write ( "ERR " + str ( x ) + "\n" ) |
80 |
+ self.critical = lambda x : sys.stderr.write ( "CRIT " + str ( x ) + "\n" ) |
81 |
+ self.exception = lambda x : sys.stderr.write ( "EXC! " + str ( x ) + "\n" ) |
82 |
+ |
83 |
+class ConfigTree: |
84 |
+ # static access to the first created ConfigTree |
85 |
+ instance = None |
86 |
+ |
87 |
+ def __init__ ( self ): |
88 |
+ if ConfigTree.instance is None: |
89 |
+ ConfigTree.instance = self |
90 |
+ |
91 |
+ self.logger = InitialLogger() |
92 |
+ |
93 |
+ self.parser = dict() |
94 |
+ |
95 |
+ |
96 |
+ def load_field_definition ( self, def_file, lenient=False ): |
97 |
+ if not 'field_def' in self.parser: |
98 |
+ self.parser ['field_def'] = configparser.SafeConfigParser ( allow_no_value=True ) |
99 |
+ |
100 |
+ try: |
101 |
+ self.logger.debug ( "Reading description field definition file " + def_file + "." ) |
102 |
+ if lenient: |
103 |
+ self.parser ['field_def'] . read ( def_file ) |
104 |
+ else: |
105 |
+ fh = open ( def_file, 'r' ) |
106 |
+ self.parser ['field_def'] . readfp ( fh ) |
107 |
+ if fh: |
108 |
+ fh.close() |
109 |
+ except IOError as err: |
110 |
+ self.logger.exception ( err ) |
111 |
+ raise |
112 |
+ except configparser.MissingSectionHeaderError as mshe: |
113 |
+ self.logger.exception ( mshe ) |
114 |
+ raise |
115 |
+ |
116 |
+ |
117 |
|
118 |
diff --git a/roverlay/descriptionfields.py b/roverlay/descriptionfields.py |
119 |
new file mode 100644 |
120 |
index 0000000..9c028c2 |
121 |
--- /dev/null |
122 |
+++ b/roverlay/descriptionfields.py |
123 |
@@ -0,0 +1,121 @@ |
124 |
+# R overlay -- description fields |
125 |
+# Copyright 2006-2012 Gentoo Foundation |
126 |
+# Distributed under the terms of the GNU General Public License v2 |
127 |
+ |
128 |
+ |
129 |
+# split from tmpconst / fileio to make configuration possible, but TODO |
130 |
+ |
131 |
+class DescriptionField: |
132 |
+ |
133 |
+ def __init__ ( self, name ): |
134 |
+ if not name: |
135 |
+ raise Exception ( "description field name is empty." ) |
136 |
+ |
137 |
+ self.name = name |
138 |
+ |
139 |
+ |
140 |
+ |
141 |
+ def get_name ( self ): |
142 |
+ return self.name |
143 |
+ |
144 |
+ def add_flag ( self, flag, lowercase=True ): |
145 |
+ if not hasattr ( self, flags ): |
146 |
+ self.flags = set () |
147 |
+ |
148 |
+ self.flags.add ( flag, flag.lower() if lowercase else flag ) |
149 |
+ |
150 |
+ return None |
151 |
+ |
152 |
+ |
153 |
+ def del_flag ( self, flag ): |
154 |
+ if hasattr ( self, flags ): |
155 |
+ self.flags.discard ( flag ) |
156 |
+ return None |
157 |
+ |
158 |
+ |
159 |
+ def add_alias ( self, alias, alias_type='withcase' ): |
160 |
+ if not hasattr ( self, aliases ): |
161 |
+ self.aliases = dict () |
162 |
+ |
163 |
+ to_add = dict ( |
164 |
+ withcase = alias, |
165 |
+ nocase = alias.lower(), |
166 |
+ ) [alias_type] |
167 |
+ |
168 |
+ |
169 |
+ if not alias_type in self.aliases: |
170 |
+ self.aliases [alias_type] = set () |
171 |
+ |
172 |
+ self.aliases [alias_type] . add ( to_add ) |
173 |
+ |
174 |
+ return None |
175 |
+ |
176 |
+ |
177 |
+ |
178 |
+ def add_simple_alias ( self, alias, withcase=True ): |
179 |
+ if withcase: |
180 |
+ return self.add_alias ( alias, alias_type='withcase' ) |
181 |
+ else: |
182 |
+ return self.add_alias ( alias, alias_type='nocase' ) |
183 |
+ |
184 |
+ |
185 |
+ |
186 |
+ def get_default_value ( self ): |
187 |
+ if hasattr ( self, 'default_value' ): |
188 |
+ return self.default_value |
189 |
+ else: |
190 |
+ return None |
191 |
+ |
192 |
+ |
193 |
+ def get ( self, key, fallback_value=None ): |
194 |
+ if hasattr ( self, key ): |
195 |
+ return self.key |
196 |
+ else: |
197 |
+ return fallback_value |
198 |
+ |
199 |
+ def matches ( self, field_identifier ): |
200 |
+ return bool ( self.name == field_identifier ) if field_identifier else False |
201 |
+ |
202 |
+ def matches_alias ( self, field_identifier ): |
203 |
+ |
204 |
+ if not field_identifier: |
205 |
+ return False |
206 |
+ if not hasattr ( self, aliases ): |
207 |
+ return False |
208 |
+ |
209 |
+ if 'withcase' in self.aliases: |
210 |
+ if field_identifier in self.aliases ['withcase']: |
211 |
+ return True |
212 |
+ |
213 |
+ if 'nocase' in self.aliases: |
214 |
+ field_id_lower = field_identifier.lower() |
215 |
+ if field_id_lower in self.aliases ['nocase']: |
216 |
+ return True |
217 |
+ |
218 |
+ def has_flag ( self, flag, lowercase=True ): |
219 |
+ if not hasattr ( self, flags ): |
220 |
+ return False |
221 |
+ |
222 |
+ return bool ( (flag.lower() if lowercase else flag) in self.flags ) |
223 |
+ |
224 |
+class DescriptionFields: |
225 |
+ |
226 |
+ def __init__ ( self ): |
227 |
+ fields = dict () |
228 |
+ |
229 |
+ def add ( self, desc_field ): |
230 |
+ if desc_field: |
231 |
+ if isinstance ( desc_field, DescriptionField ): |
232 |
+ fields [desc_field.get_name()] = desc_field |
233 |
+ return 1 |
234 |
+ elif isinstance ( desc_field, str ): |
235 |
+ fields [desc_field] = DescriptionField ( desc_field ) |
236 |
+ return 2 |
237 |
+ |
238 |
+ return 0 |
239 |
+ |
240 |
+ def get ( self, field_name ): |
241 |
+ return self.fields [field_name] if field_name in self.fields else None |
242 |
+ |
243 |
+ # ... TODO |
244 |
+ |
245 |
|
246 |
diff --git a/roverlay/ebuild.py b/roverlay/ebuild.py |
247 |
index 8c960a2..1634ef3 100644 |
248 |
--- a/roverlay/ebuild.py |
249 |
+++ b/roverlay/ebuild.py |
250 |
@@ -6,16 +6,31 @@ class Ebuild: |
251 |
# could move this to const |
252 |
EBUILD_INDENT = "\t" |
253 |
|
254 |
- def __init__ ( self ): |
255 |
+ ADD_REMAP = { |
256 |
+ # pkg vs package |
257 |
+ 'package_name' : 'pkg_name', |
258 |
+ 'package_version' : 'pkg_version', |
259 |
+ 'package_revision' : 'pkg_revision', |
260 |
+ # TITLE is in DESCRIPTION |
261 |
+ 'TITLE' : 'DESCRIPTION', |
262 |
+ } |
263 |
+ |
264 |
+ def __init__ ( self, logger ): |
265 |
"""Initializes an Ebuild. |
266 |
This is an abstraction layer between the verified + calculated data |
267 |
and the ebuild data, which can be written into a file / stdout / stdin. |
268 |
Most functions here assume that everything is fine when it reaches them. |
269 |
+ |
270 |
+ arguments: |
271 |
+ * logger -- logger for this Ebuild |
272 |
""" |
273 |
|
274 |
+ self.logger = logger |
275 |
+ |
276 |
# elements in ebuild_data are either a str or a list of str |
277 |
self._data = dict () |
278 |
self._ebuild_lines = None |
279 |
+ self._ebuild_name = None |
280 |
|
281 |
# --- end of __init__ (...) --- |
282 |
|
283 |
@@ -24,6 +39,8 @@ class Ebuild: |
284 |
This saves some memory but makes this Ebuild read-only. |
285 |
""" |
286 |
if self._ebuild_lines: |
287 |
+ # determine the ebuild name first |
288 |
+ self._ebuild_name = self.suggest_name() |
289 |
del self._data |
290 |
self._data = None |
291 |
|
292 |
@@ -56,12 +73,18 @@ class Ebuild: |
293 |
|
294 |
# --- end of prepare (...) --- |
295 |
|
296 |
+ def has_ebuild ( self ): |
297 |
+ """Returns True if this object has ebuild text lines else False.""" |
298 |
+ return bool ( self._ebuild_lines ) |
299 |
+ # --- end of has_ebuild (...) --- |
300 |
+ |
301 |
def add ( self, key, value, append=True ): |
302 |
"""Adds data to this Ebuild. |
303 |
|
304 |
arguments: |
305 |
* key -- identifier of the data (e.g. DEPEND). |
306 |
- May be remapped here (e.g. merging 'Title' and 'Description') |
307 |
+ May be remapped (e.g. merging 'Title' and 'Description') |
308 |
+ or even refused here |
309 |
* value -- |
310 |
* append -- whether to append values or overwrite existing ones, |
311 |
defaults to True. |
312 |
@@ -72,17 +95,22 @@ class Ebuild: |
313 |
# -- todo |
314 |
raise Exception ("Ebuild data are readonly.") |
315 |
|
316 |
- if append and key in self._data: |
317 |
- if not isinstance ( self._data [key], list ): |
318 |
- self._data [key] = [ self._data [key] ] |
319 |
- |
320 |
- if isinstance ( value, list ): |
321 |
- self._data [key].extend ( value ) |
322 |
- else: |
323 |
- self._data [key].append ( value ) |
324 |
+ _key = Ebuild.ADD_REMAP [key] if key in Ebuild.ADD_REMAP else key |
325 |
|
326 |
+ if _key is None: |
327 |
+ self.logger.debug ( "add (%s, %s): filtered key.", key, str ( value ) ) |
328 |
else: |
329 |
- self._data [key] = value |
330 |
+ if append and _key in self._data: |
331 |
+ if not isinstance ( self._data [_key], list ): |
332 |
+ self._data [_key] = [ self._data [_key] ] |
333 |
+ |
334 |
+ if isinstance ( value, list ): |
335 |
+ self._data [_key].extend ( value ) |
336 |
+ else: |
337 |
+ self._data [_key].append ( value ) |
338 |
+ |
339 |
+ else: |
340 |
+ self._data [_key] = value |
341 |
|
342 |
# --- end of add (...) --- |
343 |
|
344 |
@@ -99,14 +127,12 @@ class Ebuild: |
345 |
self.show ( fh ) |
346 |
fh.close() |
347 |
del fh |
348 |
- return True |
349 |
except IOError as err: |
350 |
- # ? todo |
351 |
+ self.logger.exception ( err ) |
352 |
raise |
353 |
|
354 |
else: |
355 |
- # todo log this |
356 |
- raise Exception ("cannot write ebuild") |
357 |
+ self.logger.warning ( "Cannot write ebuild - it's empty! (check with has_ebuild() before calling this method.)" ) |
358 |
|
359 |
# --- end of write (...) --- |
360 |
|
361 |
@@ -128,20 +154,39 @@ class Ebuild: |
362 |
|
363 |
# --- end of show (...) --- |
364 |
|
365 |
- def suggest_name ( self, fallback_name=None ): |
366 |
+ def suggest_dir_name ( self ): |
367 |
+ """Suggests a direcory name for this Ebuild.""" |
368 |
+ return self._data ['pkg_name'] if 'pkg_name' in self._data else self.suggest_name().partition ( '-' ) |
369 |
+ # --- end of suggest_dir_name (...) --- |
370 |
+ |
371 |
+ def suggest_name ( self, fallback_name='' ): |
372 |
"""Suggests a file name for the ebuild. This is calculated using |
373 |
pkg_name/version/revision. Returns a fallback_name if this is not |
374 |
possible. |
375 |
|
376 |
arguments: |
377 |
- fallback_name -- name to return if no suggestion available, defaults to None |
378 |
+ fallback_name -- name to return if no suggestion available, defaults to empty string |
379 |
""" |
380 |
|
381 |
- if 'pkg_name' in self._data and 'pkg_version' in self._data: |
382 |
- join = [ 'pkg_name' , 'pkg_version' ] |
383 |
- if 'pkg_revision' in self._data: join.append ('pkg_revision') |
384 |
+ if self._ebuild_name: |
385 |
+ return self._ebuild_name |
386 |
+ elif (not self._data is None) and 'pkg_name' in self._data: |
387 |
+ name_components = [ self._data ['pkg_name'] ] |
388 |
+ |
389 |
+ if 'pkg_version' in self._data: |
390 |
+ name_components.append ( self._data ['pkg_version'] ) |
391 |
+ else: |
392 |
+ # default ver |
393 |
+ name_components.append ( '1.0' ) |
394 |
+ |
395 |
+ if 'pkg_revision' in self._data: |
396 |
+ rev = self._data ['pkg_revision'] |
397 |
+ |
398 |
+ # omit rev == 0 and invalid revisions |
399 |
+ if isinstance ( rev, int ) and rev > 0: |
400 |
+ name_components.append ( 'r' + rev ) |
401 |
|
402 |
- return '-' . join ( [ self._data [c] for c in join ] ) |
403 |
+ return '-'.join ( name_components ) |
404 |
|
405 |
else: |
406 |
return fallback_name |
407 |
@@ -316,7 +361,7 @@ class Ebuild: |
408 |
if 'SRC_URI' in self._data: |
409 |
add_easyvar ( ebuild_lines, "SRC_URI" ) |
410 |
else: |
411 |
- # > calculate SRC_URI using self._data ['origin'] |
412 |
+ # > calculate SRC_URI using self._data ['origin'] -- either here or in eclass |
413 |
ebuild_lines.append ( make_var ( "SRC_URI" , None ) ) |
414 |
# (temporary, todo) setting restrict to fetch |
415 |
ebuild_lines.append ( make_var ( "RESTRICT" , "fetch" ) ) |
416 |
@@ -345,9 +390,8 @@ class Ebuild: |
417 |
del dep_and_use |
418 |
return remove_newlines ( ebuild_lines ) |
419 |
|
420 |
- except Exception as err: |
421 |
- # log this |
422 |
- ## .. |
423 |
- raise |
424 |
+ except ( ValueError, KeyError, NameError ) as err: |
425 |
+ self.logger.error ( "Cannot create ebuild text lines. The error message was %s.", str ( err ) ) |
426 |
+ return None |
427 |
|
428 |
# --- end of make_ebuild_lines (...) --- |
429 |
|
430 |
diff --git a/roverlay/ebuildjob.py b/roverlay/ebuildjob.py |
431 |
index 2d29643..0357e77 100644 |
432 |
--- a/roverlay/ebuildjob.py |
433 |
+++ b/roverlay/ebuildjob.py |
434 |
@@ -3,11 +3,15 @@ |
435 |
# Distributed under the terms of the GNU General Public License v2 |
436 |
|
437 |
import time |
438 |
+import logging |
439 |
+import re |
440 |
|
441 |
from roverlay.fileio import DescriptionReader |
442 |
from roverlay.ebuild import Ebuild |
443 |
|
444 |
class EbuildJob: |
445 |
+ LOGGER = logging.getLogger ( 'EbuildJob' ) |
446 |
+ |
447 |
# move this to const / config |
448 |
DEPENDENCY_FIELDS = { |
449 |
'R_SUGGESTS' : [ 'Suggests' ], |
450 |
@@ -43,10 +47,12 @@ class EbuildJob: |
451 |
dep resolver 'communication channel', status codes etc. |
452 |
""" |
453 |
|
454 |
- self.package_file = package_file |
455 |
+ #self.package_file = package_file |
456 |
self.dep_resolver = dep_resolver |
457 |
# get description reader from args? |
458 |
- self.description_reader = DescriptionReader() |
459 |
+ self.description_reader = DescriptionReader ( package_file ) |
460 |
+ |
461 |
+ self.logger = EbuildJob.LOGGER.getChild ( self.description_reader.get_log_name () ) |
462 |
|
463 |
self.ebuild = None |
464 |
|
465 |
@@ -92,23 +98,29 @@ class EbuildJob: |
466 |
""" |
467 |
|
468 |
# TODO move hardcoded entries to config/const |
469 |
+ # TODO metadata.xml creation |
470 |
|
471 |
try: |
472 |
|
473 |
# set status or return |
474 |
if not self._set_status ( 'BUSY', True ): return |
475 |
|
476 |
- read_data = self.description_reader.readfile ( self.package_file ) |
477 |
- |
478 |
- if read_data is None: |
479 |
- # set status accordingly |
480 |
+ desc = self.description_reader.get_desc ( True ) |
481 |
+ if desc is None: |
482 |
self._set_status ( 'FAIL' ) |
483 |
- return |
484 |
+ self.logger.info ( 'Cannot create an ebuild for this package.' ) |
485 |
+ |
486 |
|
487 |
- fileinfo = read_data ['fileinfo'] |
488 |
- desc = read_data ['description_data'] |
489 |
+ fileinfo = self.description_reader.get_fileinfo () |
490 |
+ |
491 |
+ ebuild = Ebuild ( self.logger.getChild ( "Ebuild" ) ) |
492 |
+ |
493 |
+ ebuild.add ( 'pkg_name', fileinfo ['package_name'] ) |
494 |
+ # TODO move regex to config/const |
495 |
+ ebuild.add ( 'pkg_version', |
496 |
+ re.sub ( '[-]{1,}', '.', fileinfo ['package_version'] ) |
497 |
+ ) |
498 |
|
499 |
- ebuild = Ebuild() |
500 |
|
501 |
have_description = False |
502 |
|
503 |
@@ -216,7 +228,7 @@ class EbuildJob: |
504 |
|
505 |
except Exception as any_exception: |
506 |
# any exception means failure |
507 |
- self.status = 'FAIL' |
508 |
+ self._set_status ( 'FAIL' ) |
509 |
raise |
510 |
|
511 |
# --- end of run (...) --- |
512 |
@@ -231,15 +243,19 @@ class EbuildJob: |
513 |
|
514 |
if new_status == 'FAIL': |
515 |
# always allowed |
516 |
+ self.logger.info ( "Entering status '%s'.", new_status ) |
517 |
self.status = new_status |
518 |
+ return True |
519 |
|
520 |
if new_status and new_status in EbuildJob.STATUS_LIST: |
521 |
# check if jumping from self.status to new_status is allowed |
522 |
if new_status in EbuildJob.STATUS_BRANCHMAP [self.status]: |
523 |
+ self.logger.debug ( "Entering status '%s'.", new_status ) |
524 |
self.status = new_status |
525 |
return True |
526 |
|
527 |
# default return |
528 |
+ self.logger.error ( "Cannot enter status '%s'.", new_status ) |
529 |
return False |
530 |
|
531 |
# --- end of _set_status (...) --- |
532 |
|
533 |
diff --git a/roverlay/fileio.py b/roverlay/fileio.py |
534 |
index 7c89356..5c01527 100644 |
535 |
--- a/roverlay/fileio.py |
536 |
+++ b/roverlay/fileio.py |
537 |
@@ -3,11 +3,10 @@ |
538 |
# Distributed under the terms of the GNU General Public License v2 |
539 |
|
540 |
import re |
541 |
-import os.path |
542 |
import tarfile |
543 |
+import logging |
544 |
+import os.path |
545 |
|
546 |
-# temporary import until logging is implemented |
547 |
-from sys import stderr as logging |
548 |
|
549 |
# temporary import until config and real constants are implemented |
550 |
from roverlay import tmpconst as const |
551 |
@@ -15,12 +14,70 @@ from roverlay import tmpconst as const |
552 |
class DescriptionReader: |
553 |
"""Description Reader""" |
554 |
|
555 |
- def __init__ ( self ): |
556 |
+ LOGGER = logging.getLogger ( 'DescriptionReader' ) |
557 |
+ |
558 |
+ def __init__ ( self, package_file, read_now=False ): |
559 |
"""Initializes a DESCRIPTION file reader.""" |
560 |
- pass |
561 |
+ |
562 |
+ self.fileinfo = self.make_fileinfo ( package_file ) |
563 |
+ self.logger = DescriptionReader.LOGGER.getChild ( self.get_log_name() ) |
564 |
+ self.desc_data = None |
565 |
+ |
566 |
+ |
567 |
+ if read_now: |
568 |
+ self.run() |
569 |
|
570 |
# --- end of __init__ (...) --- |
571 |
|
572 |
+ def get_log_name ( self ): |
573 |
+ try: |
574 |
+ return self.fileinfo ['filename'] |
575 |
+ except Exception as any_exception: |
576 |
+ return '__undef__' |
577 |
+ |
578 |
+ def get_desc ( self, run_if_unset=True ): |
579 |
+ if self.desc_data is None: |
580 |
+ self.run () |
581 |
+ |
582 |
+ return self.desc_data |
583 |
+ # --- end of get_desc (...) --- |
584 |
+ |
585 |
+ def get_fileinfo ( self ): |
586 |
+ return self.fileinfo |
587 |
+ # --- end of get_fileinfo (...) --- |
588 |
+ |
589 |
+ def make_fileinfo ( self, filepath ): |
590 |
+ """Returns some info about the given filepath as dict whose contents are |
591 |
+ the file path, the file name ([as package_file with suffix and] |
592 |
+ as filename with tarball suffix removed), the package name |
593 |
+ and the package_version. |
594 |
+ |
595 |
+ arguments: |
596 |
+ * filepath -- |
597 |
+ """ |
598 |
+ |
599 |
+ package_file = os.path.basename ( filepath ) |
600 |
+ |
601 |
+ filename = re.sub ( const.RPACKAGE_SUFFIX_REGEX + '$', '', package_file ) |
602 |
+ |
603 |
+ # todo move that separator to const |
604 |
+ package_name, sepa, package_version = filename.partition ( '_' ) |
605 |
+ |
606 |
+ if not sepa: |
607 |
+ # file name unexpected, tarball extraction will (probably) fail |
608 |
+ DescriptionReader.LOGGER.error ( "unexpected file name %s.'", filename ) |
609 |
+ |
610 |
+ return dict ( |
611 |
+ filepath = filepath, |
612 |
+ filename = filename, |
613 |
+ package_file = package_file, |
614 |
+ package_name = package_name, |
615 |
+ #package_origin = ?, |
616 |
+ package_version = package_version, |
617 |
+ ) |
618 |
+ |
619 |
+ # --- end of make_fileinfo (...) --- |
620 |
+ |
621 |
|
622 |
def _parse_read_data ( self, read_data ): |
623 |
"""Verifies and parses/fixes read data. |
624 |
@@ -91,44 +148,31 @@ class DescriptionReader: |
625 |
|
626 |
|
627 |
# check for fields that allow only certain values |
628 |
- unsuitable_fields = list() |
629 |
+ unsuitable_fields = dict() |
630 |
|
631 |
for field in read_data.keys(): |
632 |
- # skip _fileinfo |
633 |
- if field != '_fileinfo': |
634 |
- if 'allowed_values' in const.DESCRIPTION_FIELD_MAP [field]: |
635 |
- if not value_in_strlist ( read_data [field], |
636 |
- const.DESCRIPTION_FIELD_MAP [field] ['allowed_values'] |
637 |
- ): unsuitable_fields.append ( field ) |
638 |
+ if 'allowed_values' in const.DESCRIPTION_FIELD_MAP [field]: |
639 |
+ if not value_in_strlist ( |
640 |
+ read_data [field], |
641 |
+ const.DESCRIPTION_FIELD_MAP [field] ['allowed_values'] |
642 |
+ ): |
643 |
+ unsuitable_fields.append [field] = read_data [field] |
644 |
|
645 |
- valid = True |
646 |
- |
647 |
- if len ( missing_fields ): |
648 |
- valid = False |
649 |
- |
650 |
- logging.write ( |
651 |
- "Verification of mandatory fields failed, the result leading to this was: " + |
652 |
- str ( missing_fields ) + "\n" |
653 |
- ) |
654 |
- |
655 |
- #<raise custom exception> |
656 |
- raise Exception ("^^^look above") |
657 |
- |
658 |
- if len ( unsuitable_fields ): |
659 |
- valid = False |
660 |
- |
661 |
- logging.write ( |
662 |
- "Some fields have values that forbid further parsing, the result leading to this was: " + |
663 |
- str ( unsuitable_fields ) + "\n" |
664 |
- ) |
665 |
- |
666 |
- del missing_fields |
667 |
del field |
668 |
|
669 |
+ valid = not bool ( len ( missing_fields ) or len ( unsuitable_fields ) ) |
670 |
+ if not valid: |
671 |
+ self.logger.info ( "Cannot use R package" ) # name? |
672 |
+ if len ( missing_fields ): |
673 |
+ self.logger.debug ( "The following mandatory description fields are missing: %s.", str ( missing_fields ) ) |
674 |
+ if len ( unsuitable_fields ): |
675 |
+ self.logger.debug ( "The following fields have unsuitable values: %s.", str ( unsuitable_fields ) ) |
676 |
+ |
677 |
return valid |
678 |
+ |
679 |
# --- end of _parse_read_data (...) --- |
680 |
|
681 |
- def readfile ( self, filepath ): |
682 |
+ def run ( self ): |
683 |
"""Reads a DESCRIPTION file and returns the read data if successful, else None. |
684 |
|
685 |
arguments: |
686 |
@@ -145,40 +189,6 @@ class DescriptionReader: |
687 |
e.g. if OS_TYPE is not unix). |
688 |
""" |
689 |
|
690 |
- def get_fileinfo ( filepath ): |
691 |
- """Returns some info about the given filepath as dict whose contents are |
692 |
- the file path, the file name ([as package_file with suffix and] |
693 |
- as filename with tarball suffix removed), the package name |
694 |
- and the package_version. |
695 |
- |
696 |
- arguments: |
697 |
- * filepath -- |
698 |
- """ |
699 |
- |
700 |
- package_file = os.path.basename ( filepath ) |
701 |
- |
702 |
- filename = re.sub ( const.RPACKAGE_SUFFIX_REGEX + '$', '', package_file ) |
703 |
- |
704 |
- # todo move that separator to const |
705 |
- package_name, sepa, package_version = filename.partition ( '_' ) |
706 |
- |
707 |
- if not sepa: |
708 |
- # file name unexpected, tarball extraction will (probably) fail |
709 |
- #raise Exception ("file name unexpected") |
710 |
- logging.write ( "unexpected file name '" + filename + "'.\n" ) |
711 |
- |
712 |
- return dict ( |
713 |
- filepath = filepath, |
714 |
- filename = filename, |
715 |
- package_file = package_file, |
716 |
- package_name = package_name, |
717 |
- #package_origin = ?, |
718 |
- package_version = package_version, |
719 |
- ) |
720 |
- |
721 |
- # --- end of get_fileinfo (...) --- |
722 |
- |
723 |
- |
724 |
def make_values ( value_str, field_context=None ): |
725 |
"""Extracts relevant data from value_str and returns them as list. |
726 |
|
727 |
@@ -252,7 +262,7 @@ class DescriptionReader: |
728 |
file is read (<pkg_name>/DESCRIPTION) or a normal file. |
729 |
""" |
730 |
|
731 |
- logging.write ( "Starting to read file '" + str ( filepath ) + "' ...\n" ) |
732 |
+ self.logger.debug ( "Starting to read file '" + str ( filepath ) + "' ...\n" ) |
733 |
|
734 |
if not ( isinstance ( filepath, str ) and filepath ): |
735 |
raise Exception ( "bad usage" ) |
736 |
@@ -338,16 +348,19 @@ class DescriptionReader: |
737 |
# --- end of find_field (...) --- |
738 |
|
739 |
|
740 |
+ self.desc_data = None |
741 |
read_data = dict () |
742 |
- fileinfo = get_fileinfo ( filepath ) |
743 |
|
744 |
|
745 |
try: |
746 |
- desc_lines = get_desc_from_file ( filepath, fileinfo ['package_name'] ) |
747 |
+ desc_lines = get_desc_from_file ( |
748 |
+ self.fileinfo ['filepath'], |
749 |
+ self.fileinfo ['package_name'] |
750 |
+ ) |
751 |
|
752 |
except IOError as err: |
753 |
- # <todo> |
754 |
- raise |
755 |
+ self.logger.exception ( err ) |
756 |
+ return self.desc_data |
757 |
|
758 |
|
759 |
field_context = val = line = sline = None |
760 |
@@ -393,18 +406,15 @@ class DescriptionReader: |
761 |
|
762 |
else: |
763 |
# useless line, skip |
764 |
- logging.write ( |
765 |
- "Skipping a line, first line component (field identifier?) was: '" |
766 |
- + line_components [0] + "'\n" |
767 |
- ) |
768 |
+ self.logger.info ( "Skipped a description field: '%s'.", line_components [0] ) |
769 |
|
770 |
else: |
771 |
# reaching this branch means that |
772 |
# (a) line has no leading whitespace |
773 |
# (b) line has no separator (:) |
774 |
# this should not occur in description files (bad syntax?) |
775 |
- logging.write ( "***" + line_components [0] + "***\n") |
776 |
- raise Exception ( "bad file" ) |
777 |
+ self.logger.warning ( "Unexpected line in description file: '%s'.", line_components [0] ) |
778 |
+ |
779 |
|
780 |
del line_components |
781 |
|
782 |
@@ -412,14 +422,12 @@ class DescriptionReader: |
783 |
|
784 |
|
785 |
if self._parse_read_data ( read_data ): |
786 |
- #logging.write ( '## success ##\n' ) |
787 |
- #logging.write ( ( str ( read_data ) ) ) |
788 |
- return dict ( |
789 |
- fileinfo = fileinfo, |
790 |
- description_data = read_data |
791 |
- ) |
792 |
- else: |
793 |
- logging.write ( '## fail ##\n' ) |
794 |
- return None |
795 |
+ self.logger.debug ( "Successfully read file '%s' with data = %s.", |
796 |
+ self.fileinfo ['filepath'], str ( read_data ) |
797 |
+ ) |
798 |
+ self.desc_data = read_data |
799 |
+ |
800 |
+ # get_desc() is preferred, but this method returns the desc data, too |
801 |
+ return self.desc_data |
802 |
|
803 |
# --- end of readfile (...) --- |