Gentoo Archives: gentoo-commits

From: "André Erdmann" <dywi@×××××××.de>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/R_overlay:master commit in: roverlay/depres/
Date: Wed, 06 Jun 2012 19:53:26
Message-Id: 1339011890.1202ca5c63080cf066a6997709a23088996322ef.dywi@gentoo
1 commit: 1202ca5c63080cf066a6997709a23088996322ef
2 Author: André Erdmann <dywi <AT> mailerd <DOT> de>
3 AuthorDate: Wed Jun 6 19:44:50 2012 +0000
4 Commit: André Erdmann <dywi <AT> mailerd <DOT> de>
5 CommitDate: Wed Jun 6 19:44:50 2012 +0000
6 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=1202ca5c
7
8 dependency resolution
9
10 * can now resolve dependencies using dictionary-like 'SimpleDependencyRules'
11
12 new file: roverlay/depres/communication.py
13 new file: roverlay/depres/depenv.py
14 modified: roverlay/depres/depresolver.py
15 modified: roverlay/depres/deprule.py
16 deleted: roverlay/depres/rulereader.py
17 new file: roverlay/depres/simpledeprule.py
18
19 ---
20 roverlay/depres/communication.py | 115 +++++++++++++++++++++++++
21 roverlay/depres/depenv.py | 47 +++++++++++
22 roverlay/depres/depresolver.py | 161 +++++++++++++++++++++++++++++-------
23 roverlay/depres/deprule.py | 90 +++++----------------
24 roverlay/depres/rulereader.py | 88 --------------------
25 roverlay/depres/simpledeprule.py | 170 ++++++++++++++++++++++++++++++++++++++
26 6 files changed, 482 insertions(+), 189 deletions(-)
27
28 diff --git a/roverlay/depres/communication.py b/roverlay/depres/communication.py
29 new file mode 100644
30 index 0000000..65050f1
31 --- /dev/null
32 +++ b/roverlay/depres/communication.py
33 @@ -0,0 +1,115 @@
34 +# R overlay --
35 +# Copyright 2006-2012 Gentoo Foundation
36 +# Distributed under the terms of the GNU General Public License v2
37 +
38 +import logging
39 +
40 +from roverlay.depres.depenv import DepEnv
41 +
42 +class DependencyResolverListener:
43 +
44 + def __init__ ( self ):
45 + self.ident = id ( self )
46 +
47 + def notify ( event_type, pkg_env=None, **extra ):
48 + """Notify this listener about an event."""
49 + pass
50 +
51 +
52 +class DependencyResolverChannel ( DependencyResolverListener ):
53 +
54 + def __init__ ( self, main_resolver, *args ):
55 + super ( DependencyResolverChannel, self ) . __init__ ()
56 + self._depres_master = main_resolver
57 +
58 + def close ( self ):
59 + """Closes this channel."""
60 + pass
61 +
62 + def enabled ( self ):
63 + return True
64 +
65 +
66 +class EbuildJobChannel ( DependencyResolverChannel ):
67 + # this channel has a strict control flow:
68 + # add deps -> while not done(): "wait" -> if satisfy_request(): collect/lookup
69 +
70 + def __init__ ( self, main_resolver=None, name=None ):
71 + super ( EbuildJobChannel, self ) . __init__ ( main_resolver )
72 + self.dep_env_list = list ()
73 + self._done = False
74 + self._collected_deps = None
75 +
76 + if not name is None:
77 + self.name = name
78 +
79 + def get_name ( self ):
80 + return self.name if hasattr ( self, 'name' ) else None
81 +
82 +
83 +
84 + def depcount ( self ):
85 + if isinstance ( self.dep_env_list, list ):
86 + return len ( self.dep_env_list )
87 + else:
88 + return -1
89 +
90 +
91 + def done ( self ):
92 + if not self._done:
93 + self._done = self._depres_master.done (
94 + self.ident, len ( self.dep_env_list )
95 + )
96 + return self._done
97 +
98 + def add_dependency ( self, dep_str ):
99 + if self._done:
100 + raise Exception ( "This channel is 'done', it doesn't accept new dependencies." )
101 + else:
102 + dep_env = DepEnv ( dep_str )
103 + self.dep_env_list.append ( dep_env )
104 + self._depres_master.enqueue ( dep_env, self.ident )
105 +
106 + return None
107 +
108 + def add_dependencies ( self, dep_list ):
109 + for dep_str in dep_list: self.add_dependency ( dep_str )
110 + return None
111 +
112 + def satisfy_request ( self, delete_list_if_unresolvable=True ):
113 + if self._done:
114 + if self.dep_env_list is None:
115 + return False
116 +
117 + dep_collected = list()
118 +
119 + for dep_env in self.dep_env_list:
120 + if dep_env.is_resolved ():
121 + dep_collected.append ( dep_env.get_result() [1] )
122 + else:
123 + if delete_list_if_unresolvable:
124 + del self.dep_env_list
125 + self.dep_env_list = None
126 + return False
127 +
128 + self._collected_deps = dep_collected
129 + return True
130 +
131 + return False
132 +
133 + def lookup ( self, dep_str ):
134 + """Looks up a specific dep str. Use collect_dependencies() for getting
135 + all dependencies if order doesn't matter.
136 + """
137 + raise Exception ( "TODO" )
138 + return None
139 +
140 + def collect_dependencies ( self ):
141 + """Returns a tuple ( dep str, resolved by )."""
142 + if self._done:
143 + logging.critical ( str ( self._collected_deps ) )
144 + return self._collected_deps
145 + raise Exception ( "cannot do that" )
146 +
147 + def trigger_run ( self ):
148 + self._depres_master.run()
149
150 diff --git a/roverlay/depres/depenv.py b/roverlay/depres/depenv.py
151 new file mode 100644
152 index 0000000..b6c945c
153 --- /dev/null
154 +++ b/roverlay/depres/depenv.py
155 @@ -0,0 +1,47 @@
156 +# R overlay --
157 +# Copyright 2006-2012 Gentoo Foundation
158 +# Distributed under the terms of the GNU General Public License v2
159 +
160 +
161 +class DepEnv:
162 +
163 + STATUS_UNDONE = 1
164 + STATUS_RESOLVED = 2
165 + STATUS_UNRESOLVABLE = 4
166 +
167 + def __init__ ( self, dep_str ):
168 + self.ident = id ( self )
169 + self.dep_str = dep_str
170 + self.status = DepEnv.STATUS_UNDONE
171 + self.resolved_by = None
172 +
173 + # TODO: analyze dep_str: extract dep name, dep version, useless comments,...
174 +
175 +
176 + def set_resolved ( self, resolved_by, append=False ):
177 + if self.resolved_by is None:
178 + self.resolved_by = resolved_by
179 + elif append:
180 + raise Exception ( "appending is not supported..." ) #TODO
181 + #self.resolved_by.append ( resolved_by )
182 + else:
183 + raise Exception ( "dependency is already resolved and append is disabled." )
184 +
185 + self.status |= DepEnv.STATUS_RESOLVED
186 +
187 + def set_unresolvable ( self, force=False ):
188 + if force or not self.status & DepEnv.STATUS_RESOLVED:
189 + self.resolved_by = None
190 + self.status |= DepEnv.STATUS_UNRESOLVABLE
191 + else:
192 + raise Exception ("dependency is already marked as resolved." )
193 +
194 + def zap ( self ):
195 + self.status = DepEnv.STATUS_UNDONE
196 + self.resolved_by = None
197 +
198 + def is_resolved ( self ):
199 + return bool ( self.status & DepEnv.STATUS_RESOLVED )
200 +
201 + def get_result ( self ):
202 + return ( self.dep_str, self.resolved_by )
203
204 diff --git a/roverlay/depres/depresolver.py b/roverlay/depres/depresolver.py
205 index ebb88cd..c8b4729 100644
206 --- a/roverlay/depres/depresolver.py
207 +++ b/roverlay/depres/depresolver.py
208 @@ -2,14 +2,53 @@
209 # Copyright 2006-2012 Gentoo Foundation
210 # Distributed under the terms of the GNU General Public License v2
211
212 +import logging
213 +
214 +from collections import deque
215 +
216 +#from roverlay.depres import deprule
217 +from roverlay.depres import simpledeprule, communication
218 +#from roverlay.depres.depenv import DepEnv
219 +
220 +
221
222 class DependencyResolver:
223
224 - def __init__ ( self ):
225 - self.logger = None
226 - self.channels = list ()
227 - self.listeners = list ()
228 + LOGGER = logging.getLogger ( "DependencyResolver" )
229
230 + def __init__ ( self ):
231 + # these loggers are temporary helpers
232 + self.logger = DependencyResolver.LOGGER
233 + self.logger_unresolvable = self.logger.getChild ( "UNRESOLVABLE" )
234 + self.logger_resolved = self.logger.getChild ( "RESOLVED" )
235 + self.channels = dict ()
236 + self.listeners = dict ()
237 +
238 + self._depqueue = deque ()
239 + self._depqueue_failed = deque ()
240 + self._depdone = dict ()
241 +
242 + self.static_rule_pools = list ()
243 +
244 + def log_unresolvable ( self, dep_env ):
245 + msg = "'" + dep_env.dep_str + "'"
246 + if not dep_env.dep_str:
247 + self.logger_unresolvable.warning ( msg + " empty? fix that!" )
248 + else:
249 + self.logger_unresolvable.info ( msg )
250 +
251 + def log_resolved ( self, dep_env ):
252 + msg = "'" + dep_env.dep_str + "' as '" + str ( dep_env.resolved_by ) + "'"
253 + self.logger_resolved.info ( msg )
254 +
255 + def sort ( self ):
256 + for pool in self.static_rule_pools: pool.sort()
257 + self.static_rule_pools.sort ( key = lambda pool : ( pool.priority, pool.rule_weight ) )
258 +
259 + def add_rulepool ( self, rulepool, pool_type=None ):
260 + self.static_rule_pools.append ( rulepool )
261 + self.sort()
262 + return None
263
264 def _report_event ( self, event, pkg_env=None, **extra ):
265 """Reports an event to the log, channels and listeners."""
266 @@ -26,7 +65,7 @@ class DependencyResolver:
267 """
268 pass
269
270 -
271 + # def add_listener ( self, listener ): FIXME
272 def add_listener ( self ):
273 """Adds a listener, which listens to events such as
274 "dependency is unresolvable". Possible use cases include redirecting
275 @@ -35,11 +74,13 @@ class DependencyResolver:
276 arguments:
277 * listener_type
278 """
279 + # broken due to module "outsourcing"
280 new_listener = DependencyResolverListener()
281 # register the new listener
282 self.listeners [new_listener.ident] = new_listener
283 return new_listener
284
285 + # FIXME get_channel is not useful when using various channel types
286 def get_channel ( self, readonly=False ):
287 """Returns a communication channel to the DependencyResolver.
288 This channel can be used to _talk_, e.g. queue dependencies for resolution
289 @@ -48,50 +89,108 @@ class DependencyResolver:
290 arguments:
291 * readonly -- whether this channel has write access or not
292 """
293 - new channel = DependencyResolverChannel ( self )
294 + # broken due to module "outsourcing"
295 + new_channel = DependencyResolverChannel ( self )
296 self.channels [new_channel.ident] = new_channel
297 return new_channel
298
299 + def register_channel ( self, channel ):
300 + if channel in self.channels:
301 + raise Exception ( "channel is already registered." )
302
303 + if channel._depres_master is None:
304 + channel._depres_master = self
305
306 -class DependencyResolverListener:
307 + self.channels [channel.ident] = channel
308
309 - def __init__ ( self ):
310 - self.ident = id ( self )
311 + return channel
312
313 - def notify ( event_type, pkg_env=None, **extra ):
314 - """Notify this listener about an event."""
315 - pass
316
317 + def get_worker ( self, max_dep_resolve=0 ):
318 + """Returns a dep resolver worker (thread).
319 + -- Threading is not implemented, this method is just a reminder.
320
321 -class DependencyResolverChannel ( DependencyResolverListener ):
322 + arguments:
323 + * max_dep_resolve -- if > 0 : worker stops after resolving max_dep_resolve deps
324 + if 0 : worker stops when queue is empty
325 + else : worker does not stop unless told to do so
326 + """
327 + raise Exception ( "DependencyResolver.get_worker(...) is TODO!" )
328
329 - def __init__ ( self, main_resolver, *args ):
330 - super ( DependencyResolverChannel, self ) . __init__ ()
331 - self._depres_master = main_resolver
332 + def _queue_previously_failed( self ):
333 + queue_again = self._depqueue_failed
334 + self._depqueue_failed = deque()
335 + self._depqueue.extend ( queue_again )
336
337 - def close ( self ):
338 - """Closes this channel."""
339 - pass
340
341 + def run ( self ):
342 + """Resolves dependencies."""
343 + # TODO: this method has to be replaced when using threads
344
345 -class EbuildJobChannel ( DependencyResolverChannel ):
346 - def __init__ ( self, main_resolver, *args ):
347 - super ( EbuildJobChannel, self ) . __init__ ( main_resolver )
348
349 + while len ( self._depqueue ):
350
351 - def done ( self ):
352 - pass
353 + to_resolve = self._depqueue.popleft()
354 + channel_id, dep_env = to_resolve
355
356 - def add_dependency ( self, dep_str ):
357 - pass
358 + self.logger.debug ( "Trying to resolve '" + dep_env.dep_str + "'." )
359
360 - def add_dependencies ( self, dep_list ):
361 - pass
362 + if not channel_id in self.channels:
363 + # channel has been closed but did not request a cleanup
364 + raise Exception ( "dirty queue!" )
365
366 - def satisfy_request ( self ):
367 - pass
368 + #have_new_rule = False
369 + resolved = None
370 +
371 + for rulepool in self.static_rule_pools:
372 + result = rulepool.matches ( dep_env )
373 + if not result is None and result [0] > 0:
374 + resolved = result [1]
375 + break
376 +
377 +
378 +
379 + if resolved:
380 + dep_env.set_resolved ( resolved, append=False )
381 + self.log_resolved ( dep_env )
382 + self._depdone [channel_id] +=1
383 + else:
384 + self._depqueue_failed.append ( to_resolve )
385 +
386 + """
387 + ## only useful if new rules can be created
388 + # new rule found, queue all previously failed dependency searches again
389 + if have_new_rule:
390 + self._queue_previously_failed
391 + """
392 + # --- end while
393 +
394 + # iterate over _depqueue_failed and report unresolved
395 +
396 + while len ( self._depqueue_failed ):
397 +
398 + channel_id, dep_env = self._depqueue_failed.popleft()
399 + dep_env.set_unresolvable()
400 + self._depdone [channel_id] += 1
401 +
402 + # TODO: temporary log
403 + self.log_unresolvable ( dep_env )
404
405 - def lookup ( self, dep_str ):
406 return None
407
408 +
409 + def enqueue ( self, dep_env, channel_id ):
410 + self._depqueue.append ( ( channel_id, dep_env ) )
411 + if not channel_id in self._depdone:
412 + # allocate a result counter in depdone
413 + self._depdone [channel_id] = 0
414 +
415 +
416 + def done ( self, channel_id, numdeps ):
417 +
418 + if channel_id in self._depdone:
419 + return bool ( self._depdone [channel_id] >= numdeps )
420 + else:
421 + return False
422 +
423 +
424
425 diff --git a/roverlay/depres/deprule.py b/roverlay/depres/deprule.py
426 index b5d1116..83cff66 100644
427 --- a/roverlay/depres/deprule.py
428 +++ b/roverlay/depres/deprule.py
429 @@ -6,60 +6,26 @@ class DependencyRule:
430 def __init__ ( self ):
431 self.max_score = 1000
432
433 - def matches ( dep_str ):
434 + def matches ( self, dep_env ):
435 return 0
436
437 -class SimpleDependencyRule ( DependencyRule ):
438 -
439 - def __init__ ( self, resolving_package, dep_str=None, priority=100 ):
440 - super ( SimpleDependencyRule, self ) . __init__ ( )
441 - self.dep_alias = set ()
442 - if dep_str:
443 - self.dep_alias.add ( dep_str )
444 -
445 - self.resolving_package = resolving_package
446 -
447 - self.priority = priority
448 - # --- end of __init__ (...) ---
449 -
450 - def add_resolved ( self, dep_str ):
451 - self.dep_alias.add ( dep_str )
452 - # --- end of add_resolved (...) ---
453 -
454 - def matches ( self, dep_str, lowercase=True ):
455 - if lowercase:
456 - lower_dep_str = dep_str
457 - for alias in self.dep_alias:
458 - if alias.lower() == lower_dep_str:
459 - return self.max_score
460 - elif dep_str in self.dep_alias:
461 - return self.max_score
462 -
463 - return 0
464 - # --- end of matches (...) ---
465 -
466 - def get_dep ( self ):
467 - return self.resolving_package
468 -
469 - def export_rule ( self ):
470 - pass
471 -
472 class DependencyRulePool:
473
474 - def __init__ ( self, name ):
475 - self.rules = list ()
476 - self.name = name
477 - self.priority = 0
478 + def __init__ ( self, name, priority ):
479 + self.rules = list ()
480 + self.name = name
481 + self.priority = priority
482 + # the "rule weight" is the sum of the rules' priorities - it's used to
483 + # compare/sort dependency pools with the same priority (lesser weight is better)
484 + self.rule_weight = 0
485 # --- end of __init__ (...) ---
486
487 def sort ( self ):
488 self.rules.sort ( key=lambda rule : rule.priority )
489
490 - priority_sum = 0
491 - for r in self.rules:
492 - priority_sum += r.priority
493 -
494 - self.priority = int ( priority_sum / len ( self.rules ) ) if len ( self.rules ) else 0
495 + rule_priority_sum = 0
496 + for r in self.rules: rule_priority_sum += r.priority
497 + self.rule_weight = rule_priority_sum
498
499 return None
500 # --- end of _sort_rules (...) ---
501 @@ -73,13 +39,15 @@ class DependencyRulePool:
502 return None
503 # --- end of add (...) ---
504
505 - def matches ( self, dep_str, skip_matches=0 ):
506 + def matches ( self, dep_env, skip_matches=0 ):
507 """Tries to find a match in this dependency rule pool.
508 The first match is immediatly returned unless skip_matches is != 0, in
509 which case the first (>0) / last (<0) skip_matches matches are skipped.
510 + Returns a tuple ( score, portage dependency ),
511 + e.g. ( 1000, 'sys-apps/which' ), if match found, else None.
512
513 arguments:
514 - * dep_str -- dependency to look up
515 + * dep_env -- dependency to look up
516 * skip_matches --
517 """
518
519 @@ -89,41 +57,23 @@ class DependencyRulePool:
520
521 else:
522 skipped = 0
523 - order = range ( len ( self.rules ) )
524 + # python3 requires list ( range ... )
525 + order = list ( range ( len ( self.rules ) ) )
526
527 if skip_matches < 1:
528 skip_matches *= -1
529 order.reverse()
530
531 for index in order:
532 - score = self.rules [index].matches ( dep_str )
533 + score = self.rules [index].matches ( dep_env )
534 if score:
535 if skipped < skip_matches:
536 skipped += 1
537 else:
538 - return score, self.rules [index].get_dep ()
539 -
540 + return ( score, self.rules [index].get_dep () )
541
542 - return 0, None
543
544 + return None
545
546 -class SimpleDependencyRulePool ( DependencyRulePool )
547
548 - def __init__ ( self, name ):
549 - super ( SimpleDependencyRulePool, self ) . __init__ ( name )
550 - # --- end of __init__ (...) ---
551 -
552 - def add ( self, rile ):
553 - if isinstance ( rule, SimpleDependencyRule ):
554 - self.rules.append ( rule )
555 - else:
556 - raise Exception ( "bad usage (simple dependency rule expected)." )
557 - # --- end of add (...) ---
558
559 - def export_rules ( self, fh ):
560 - for rule in self.rules:
561 - to_write = fh.export_rule()
562 - if isinstance ( to_write, str ):
563 - fh.write ( to_write )
564 - else:
565 - fh.writelines ( to_write )
566
567 diff --git a/roverlay/depres/rulereader.py b/roverlay/depres/rulereader.py
568 deleted file mode 100644
569 index 6998cb2..0000000
570 --- a/roverlay/depres/rulereader.py
571 +++ /dev/null
572 @@ -1,88 +0,0 @@
573 -
574 -import re
575 -import logging
576 -
577 -from roverlay.depres.deprule import SimpleDependencyRule
578 -
579 -class SimpleDependencyRuleReader:
580 -
581 - one_line_separator = re.compile ( '\s+::\s+' )
582 - multiline_start = '{'
583 - multiline_stop = '}'
584 - comment_chars = list ( '#;' )
585 -
586 -
587 - def __init__ ( self ):
588 - pass
589 - # --- end of __init__ (...) ---
590 -
591 - def read_file ( self, filepath ):
592 - """Reads a file that contains SimpleDescriptionRules.
593 -
594 - arguments:
595 - * filepath -- file to read
596 - """
597 -
598 - lineno = 0
599 -
600 - try:
601 - logging.debug ( "Reading simple dependency rule file " + filepath + "." )
602 - fh = open ( filepath, 'r' )
603 -
604 - next_rule = None
605 - rules = list ()f
606 -
607 - for line in fh.readlines():
608 - lineno += 1
609 - line = line.strip()
610 -
611 - if not line:
612 - pass
613 -
614 - elif not next_rule is None:
615 - # in a multiline rule
616 - if line [0] == SimpleDependencyRuleReader.multiline_stop:
617 - # end of a multiline rule
618 - rules.append ( next_rule )
619 - next_rule = None
620 - else:
621 - # new resolved str
622 - next_rule.add_resolved ( line )
623 -
624 - elif line [0] in SimpleDependencyRuleReader.comment_chars:
625 - # comment
626 - pass
627 -
628 - elif line [-1] == SimpleDependencyRuleReader.multiline_start:
629 - # start of multiline rule
630 - next_rule = SimpleDependencyRule ( line [:-1].rstrip(), None, 100 )
631 -
632 - else:
633 - # one line rule?
634 - rule_str = SimpleDependencyRuleReader.one_line_separator.split ( line, 1 )
635 -
636 - if len ( rule_str ) == 2:
637 - rules.append (
638 - SimpleDependencyRule ( rule_str [0], rule_str [1], 90 )
639 - )
640 - else:
641 - logging.error (
642 - "In " + filepath + ", line " + str ( lineno ) + ": cannot use this line."
643 - )
644 - # ---
645 -
646 - if fh: fh.close ()
647 -
648 - logging.info ( filepath + ": read " + str ( len ( rules ) ) + " dependency rules." )
649 -
650 - return rules
651 -
652 - except IOError as ioerr:
653 - if lineno:
654 - logging.error ( "Failed to read file " + filepath + " after " + str ( lineno ) " lines."
655 - else:
656 - logging.error ( "Could not read file " + filepath + "." )
657 - raise
658 -
659 - # --- end of read_file (...) ---
660 -
661
662 diff --git a/roverlay/depres/simpledeprule.py b/roverlay/depres/simpledeprule.py
663 new file mode 100644
664 index 0000000..0f916ec
665 --- /dev/null
666 +++ b/roverlay/depres/simpledeprule.py
667 @@ -0,0 +1,170 @@
668 +
669 +import re
670 +import logging
671 +
672 +
673 +
674 +from roverlay.depres import deprule
675 +
676 +class SimpleDependencyRule ( deprule.DependencyRule ):
677 +
678 + TMP_LOGGER = logging.getLogger ( 'SimpleDependencyRule' )
679 +
680 + def __init__ ( self, resolving_package, dep_str=None, priority=100 ):
681 + super ( SimpleDependencyRule, self ) . __init__ ( )
682 + self.dep_alias = set ()
683 +
684 + self.logger = SimpleDependencyRule.TMP_LOGGER.getChild ( resolving_package )
685 + self.logger.debug ( "new rule" )
686 +
687 + if dep_str:
688 + self.logger.debug ( "resolves '" + dep_str + "' now." )
689 + self.dep_alias.add ( dep_str )
690 +
691 + self.resolving_package = resolving_package
692 +
693 + self.priority = priority
694 +
695 +
696 + # --- end of __init__ (...) ---
697 +
698 + def add_resolved ( self, dep_str ):
699 + self.dep_alias.add ( dep_str )
700 + # --- end of add_resolved (...) ---
701 +
702 + def matches ( self, dep_env, lowercase=True ):
703 +
704 + if lowercase:
705 + lower_dep_str = dep_env.dep_str.lower()
706 + for alias in self.dep_alias:
707 + if alias.lower() == lower_dep_str:
708 + self.logger.debug ( "matches '" + lower_dep_str + "'" )
709 + return self.max_score
710 + elif dep_env.dep_str in self.dep_alias:
711 + self.logger.debug ( "matches '" + dep_env.dep_str + "'" )
712 + return self.max_score
713 +
714 + return 0
715 + # --- end of matches (...) ---
716 +
717 + def get_dep ( self ):
718 + return self.resolving_package
719 +
720 + def export_rule ( self ):
721 + pass
722 +
723 +class SimpleDependencyRulePool ( deprule.DependencyRulePool ):
724 +
725 + def __init__ ( self, name, priority=70, filepath=None ):
726 + super ( SimpleDependencyRulePool, self ) . __init__ ( name, priority )
727 +
728 + if not filepath is None:
729 + self.load_rule_file ( filepath )
730 + # --- end of __init__ (...) ---
731 +
732 + def add ( self, rule ):
733 + if isinstance ( rule, SimpleDependencyRule ):
734 + self.rules.append ( rule )
735 + else:
736 + raise Exception ( "bad usage (simple dependency rule expected)." )
737 + # --- end of add (...) ---
738 +
739 + def load_rule_file ( self, filepath ):
740 + reader = SimpleDependencyRuleReader()
741 +
742 + new_rules = reader.read_file ( filepath )
743 + for rule in new_rules:
744 + self.add ( rule )
745 +
746 + def export_rules ( self, fh ):
747 + for rule in self.rules:
748 + to_write = fh.export_rule()
749 + if isinstance ( to_write, str ):
750 + fh.write ( to_write )
751 + else:
752 + fh.writelines ( to_write )
753 +
754 +
755 +
756 +class SimpleDependencyRuleReader:
757 +
758 + one_line_separator = re.compile ( '\s+::\s+' )
759 + multiline_start = '{'
760 + multiline_stop = '}'
761 + comment_chars = list ( '#;' )
762 +
763 +
764 + def __init__ ( self ):
765 + pass
766 + # --- end of __init__ (...) ---
767 +
768 + def read_file ( self, filepath ):
769 + """Reads a file that contains SimpleDescriptionRules.
770 +
771 + arguments:
772 + * filepath -- file to read
773 + """
774 +
775 + lineno = 0
776 +
777 + try:
778 + logging.debug ( "Reading simple dependency rule file " + filepath + "." )
779 + fh = open ( filepath, 'r' )
780 +
781 + next_rule = None
782 + rules = list ()
783 +
784 + for line in fh.readlines():
785 + lineno += 1
786 + line = line.strip()
787 +
788 + if not line:
789 + pass
790 +
791 + elif not next_rule is None:
792 + # in a multiline rule
793 + if line [0] == SimpleDependencyRuleReader.multiline_stop:
794 + # end of a multiline rule
795 + rules.append ( next_rule )
796 + next_rule = None
797 + else:
798 + # new resolved str
799 + next_rule.add_resolved ( line )
800 +
801 + elif line [0] in SimpleDependencyRuleReader.comment_chars:
802 + # comment
803 + pass
804 +
805 + elif line [-1] == SimpleDependencyRuleReader.multiline_start:
806 + # start of multiline rule
807 + next_rule = SimpleDependencyRule ( line [:-1].rstrip(), None, 100 )
808 +
809 + else:
810 + # one line rule?
811 + rule_str = SimpleDependencyRuleReader.one_line_separator.split ( line, 1 )
812 +
813 + if len ( rule_str ) == 2:
814 + rules.append (
815 + SimpleDependencyRule ( rule_str [0], rule_str [1], 90 )
816 + )
817 + else:
818 + logging.error (
819 + "In " + filepath + ", line " + str ( lineno ) + ": cannot use this line."
820 + )
821 + # ---
822 +
823 + if fh: fh.close ()
824 +
825 + logging.info ( filepath + ": read " + str ( len ( rules ) ) + " dependency rules." )
826 +
827 + return rules
828 +
829 + except IOError as ioerr:
830 + if lineno:
831 + logging.error ( "Failed to read file " + filepath + " after " + str ( lineno ) + " lines." )
832 + else:
833 + logging.error ( "Could not read file " + filepath + "." )
834 + raise
835 +
836 + # --- end of read_file (...) ---
837 +