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: Fri, 29 Jun 2012 22:49:19
Message-Id: 1341009393.f4fe2f55298624cd522b554aa57b505591a4138a.dywi@gentoo
1 commit: f4fe2f55298624cd522b554aa57b505591a4138a
2 Author: André Erdmann <dywi <AT> mailerd <DOT> de>
3 AuthorDate: Fri Jun 29 22:36:33 2012 +0000
4 Commit: André Erdmann <dywi <AT> mailerd <DOT> de>
5 CommitDate: Fri Jun 29 22:36:33 2012 +0000
6 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=f4fe2f55
7
8 dependency resolution: 'fuzzy' rules
9
10 These extended simple rules match various dependency strings
11 with a single rule, e.g. a fuzzy "R" rule matches
12 "R (>= 2.10)" as ">=dev-lang/R-2.10" and "R <2.15" as "<dev-lang/R-2.15".
13
14 Also added easier syntax for R package dependencies (sound -> sci-R/sound)
15 in the dep rule file(s), which can now be written as 'sound' instead of
16 a category-hardcoded 'sci-R/sound :: sound' statement.
17
18 new file: roverlay/depres/abstractsimpledeprule.py
19 modified: roverlay/depres/deprule.py
20 modified: roverlay/depres/simpledeprule.py
21
22 ---
23 roverlay/depres/abstractsimpledeprule.py | 204 ++++++++++++++++++++++++
24 roverlay/depres/deprule.py | 11 +-
25 roverlay/depres/simpledeprule.py | 254 ++++++++++++++----------------
26 3 files changed, 325 insertions(+), 144 deletions(-)
27
28 diff --git a/roverlay/depres/abstractsimpledeprule.py b/roverlay/depres/abstractsimpledeprule.py
29 new file mode 100644
30 index 0000000..b03ce63
31 --- /dev/null
32 +++ b/roverlay/depres/abstractsimpledeprule.py
33 @@ -0,0 +1,204 @@
34 +import logging
35 +
36 +from roverlay.depres import deprule
37 +
38 +TMP_LOGGER = logging.getLogger ('simpledeps')
39 +
40 +class SimpleRule ( deprule.DependencyRule ):
41 + """A dependency rule that represents an ignored package in portage."""
42 +
43 + def __init__ ( self,
44 + dep_str=None, priority=50, resolving_package=None,
45 + logger_name='simple_rule'
46 + ):
47 + """Initializes a SimpleIgnoreDependencyRule.
48 +
49 + arguments:
50 + * dep_str -- a dependency string that this rule is able to resolve
51 + * priority -- priority of this rule
52 + """
53 + super ( SimpleRule, self ) . __init__ ( priority )
54 + self.dep_alias = list()
55 +
56 + self.logger = TMP_LOGGER.getChild ( logger_name )
57 +
58 + self.resolving_package = resolving_package
59 +
60 + self.prepare_lowercase_alias = True
61 +
62 + if not dep_str is None:
63 + self.dep_alias.append ( dep_str )
64 +
65 + self.logger.debug ( "new rule (%s) for %s" % ( self.__class__.__name__, self.resolving_package ) )
66 +
67 + # --- end of __init__ (...) ---
68 +
69 + def done_reading ( self ):
70 + self.dep_alias = frozenset ( self.dep_alias )
71 + if self.prepare_lowercase_alias:
72 + self.dep_alias_low = frozenset ( x.lower() for x in self.dep_alias )
73 +
74 + def add_resolved ( self, dep_str ):
75 + """Adds an dependency string that should be matched by this rule.
76 +
77 + arguments:
78 + * dep_str --
79 + """
80 + self.dep_alias.append ( dep_str )
81 + # --- end of add_resolved (...) ---
82 +
83 + def _find ( self, dep_str, lowercase ):
84 + if lowercase:
85 + if hasattr ( self, 'dep_alias_low' ):
86 + if dep_str in self.dep_alias_low:
87 + return True
88 + else:
89 + if dep_str in ( alias.lower() for alias in self.dep_alias ):
90 + return True
91 +
92 + return dep_str in self.dep_alias
93 + # --- end of _find (...) ---
94 +
95 + def matches ( self, dep_env, lowercase=True ):
96 + """Returns True if this rule matches the given DepEnv, else False.
97 +
98 + arguments:
99 + * dep_env --
100 + * lowercase -- if True: be case-insensitive when iterating over all
101 + stored dep_strings
102 + """
103 +
104 + if self._find (
105 + dep_env.dep_str_low if lowercase else dep_env.dep_str, lowercase
106 + ):
107 + self.logger.debug (
108 + "matches %s with score %i and priority %i."
109 + % ( dep_env.dep_str, self.max_score, self.priority )
110 + )
111 + return ( self.max_score, self.resolving_package )
112 +
113 + return None
114 + # --- end of matches (...) ---
115 +
116 + def export_rule ( self, resolving_to=None ):
117 + """Returns this rule as a list of text lines that can be written into
118 + a file.
119 + An empty list will be returned if dep_alias has zero length.
120 +
121 + arguments:
122 + * resolving_to -- portage package that the exported rule should
123 + resolve to, defaults to self.resolving_package or
124 + an ignore keyword such as '!'.
125 + """
126 +
127 + alias_count = len ( self.dep_alias )
128 +
129 + retlist = []
130 +
131 + if alias_count:
132 + if resolving_to is None:
133 + if hasattr ( self, 'resolving_package'):
134 + resolving_package = self.resolving_package
135 + else:
136 + resolving_package = '!'
137 + else:
138 + resolving_package = resolving_to
139 +
140 + # todo hardcoded rule format here
141 + if alias_count > 1:
142 +
143 + retlist = [ resolving_package + ' {\n' ] + \
144 + [ "\t%s\n" % alias for alias in self.dep_alias ] + \
145 + [ '}\n' ]
146 + else:
147 + retlist = [
148 + "%s :: %s\n" % ( resolving_package, self.dep_alias [0] )
149 + ]
150 +
151 + # -- if
152 +
153 + return retlist
154 + # --- end of export_rule (...) ---
155 +
156 +class FuzzySimpleRule ( SimpleRule ):
157 +
158 + def __init__ ( self, *args, **kw ):
159 + super ( FuzzySimpleRule, self ) . __init__ ( *args, **kw )
160 + self.prepare_lowercase_alias = True
161 +
162 + # 0 : version with modifier, 1 : version w/o mod, 2 : name only, 3 : std
163 + self.fuzzy_score = ( 1250, 1000, 750, 500 )
164 + self.max_score = max ( self.fuzzy_score )
165 +
166 + def matches ( self, dep_env ):
167 + if self._find ( dep_env.dep_str_low, lowercase=True ):
168 + # non-fuzzy match
169 + self.logger.debug (
170 + "matches %s with score %i and priority %i."
171 + % ( dep_env.dep_str, self.max_score, self.priority )
172 + )
173 + return ( self.fuzzy_score[3], self.resolving_package )
174 +
175 + elif hasattr ( dep_env, 'fuzzy' ):
176 + for fuzzy in dep_env.fuzzy:
177 + if 'name' in fuzzy:
178 + if self._find ( fuzzy ['name'], lowercase=True ):
179 + # fuzzy match found
180 +
181 + if self.resolving_package is None:
182 + # ignore rule
183 + res = None
184 + score = self.fuzzy_score [2]
185 +
186 +
187 + elif 'version' in fuzzy:
188 +
189 + ver_pkg = '-'.join ( (
190 + self.resolving_package, fuzzy ['version']
191 + ) )
192 +
193 + if 'version_modifier' in fuzzy:
194 + vmod = fuzzy ['version_modifier']
195 + if '!' in vmod:
196 + # package matches, but specific version is forbidden
197 + # ( !<package>-<specific verion> <package> )
198 + res = '( !=%s %s )' % (
199 + ver_pkg,
200 + self.resolving_package
201 + )
202 +
203 + else:
204 + # std vmod: >=, <=, =, <, >
205 + res = vmod + ver_pkg
206 +
207 + score = self.fuzzy_score[0]
208 +
209 + else:
210 + # version required, but no modifier: defaults to '>='
211 +
212 + res = '>=' + ver_pkg
213 + score = self.fuzzy_score[1]
214 +
215 + else:
216 + # substring match
217 + # currently not possible (see DepEnv's regexes)
218 + score = fuzzy[2]
219 + res = self.resolving_package
220 + # --- if resolving... elif version ... else
221 +
222 +
223 + self.logger.debug (
224 + "fuzzy-match: %s resolved as '%s' with score=%i."
225 + % ( dep_env.dep_str, res, score )
226 + )
227 + return ( score, res )
228 + # --- if find (=match found)
229 + # --- if name in
230 + # --- for fuzzy
231 + # --- elif hasattr
232 +
233 + return None
234 + # --- end of matches (...) ---
235 +
236 +
237 +
238
239 diff --git a/roverlay/depres/deprule.py b/roverlay/depres/deprule.py
240 index b02213c..72e706d 100644
241 --- a/roverlay/depres/deprule.py
242 +++ b/roverlay/depres/deprule.py
243 @@ -16,8 +16,9 @@ class DependencyRule ( object ):
244 # --- end of __init__ (...) ---
245
246 def matches ( self, dep_env ):
247 - """Returns an int > 0 if this rule matches the given DepEnv."""
248 - return 0
249 + """Returns a tuple ( score ::= int > 0, matching dep ::= str )
250 + if this rule matches the given DepEnv, else None"""
251 + return None
252 # --- end of matches (...) ---
253
254 # --- end of DependencyRule ---
255 @@ -97,12 +98,12 @@ class DependencyRulePool ( object ):
256 order.reverse()
257
258 for index in order:
259 - score = self.rules [index].matches ( dep_env )
260 - if score:
261 + result = self.rules [index].matches ( dep_env )
262 + if result is not None and result [0] > 0:
263 if skipped < skip_matches:
264 skipped += 1
265 else:
266 - return ( score, self.rules [index].get_dep () )
267 + return result
268
269
270 return None
271
272 diff --git a/roverlay/depres/simpledeprule.py b/roverlay/depres/simpledeprule.py
273 index ee40045..ce4ba10 100644
274 --- a/roverlay/depres/simpledeprule.py
275 +++ b/roverlay/depres/simpledeprule.py
276 @@ -5,122 +5,23 @@
277 import re
278 import logging
279
280 +from roverlay import config
281 from roverlay.depres import deprule
282 +from roverlay.depres.abstractsimpledeprule import SimpleRule, FuzzySimpleRule
283
284 TMP_LOGGER = logging.getLogger ('simpledeps')
285
286 -class SimpleIgnoreDependencyRule ( deprule.DependencyRule ):
287 - """A dependency rule that represents an ignored package in portage."""
288 -
289 - def __init__ ( self, dep_str=None, priority=50 ):
290 - """Initializes a SimpleIgnoreDependencyRule.
291 -
292 - arguments:
293 - * dep_str -- a dependency string that this rule is able to resolve
294 - * priority -- priority of this rule
295 - """
296 - super ( SimpleIgnoreDependencyRule, self ) . __init__ ( priority )
297 - self.dep_alias = set ()
298 -
299 - self.logger = TMP_LOGGER.getChild ( 'IGNORE_DEPS' )
300 -
301 - if not dep_str is None:
302 - self.dep_alias.add ( dep_str )
303 -
304 - # --- end of __init__ (...) ---
305 -
306 - def add_resolved ( self, dep_str ):
307 - """Adds an dependency string that should be matched by this rule.
308 -
309 - arguments:
310 - * dep_str --
311 - """
312 - self.dep_alias.add ( dep_str )
313 - # --- end of add_resolved (...) ---
314 -
315 - def matches ( self, dep_env, lowercase=True ):
316 - """Returns True if this rule matches the given DepEnv, else False.
317 -
318 - arguments:
319 - * dep_env --
320 - * lowercase -- if True: be case-insensitive when iterating over all
321 - stored dep_strings
322 - """
323 -
324 - def logmatch ( score=self.max_score ):
325 - """Wrapper function that logs a successful match and
326 - returns its score.
327 -
328 - arguments:
329 - * score -- score of this match, defaults to self.max_score
330 - """
331 -
332 - self.logger.debug ( "matches %s with score %i and priority %i." %
333 - ( dep_env.dep_str, score, self.priority )
334 - )
335 - return score
336 - # --- end of logmatch (...) ---
337 -
338 - if lowercase:
339 - #lower_dep_str = dep_env.dep_str.lower()
340 - for alias in self.dep_alias:
341 - if alias.lower() == dep_env.dep_str_low:
342 - return logmatch ()
343 - elif dep_env.dep_str in self.dep_alias:
344 - return logmatch ()
345 -
346 - return 0
347 - # --- end of matches (...) ---
348 -
349 - def get_dep ( self ):
350 - """Returns the textual portage package representation of this rule,
351 - which is None 'cause this is an ignored dependency.
352 - """
353 - return None
354 - # --- end of get_dep (...) ---
355 -
356 - def export_rule ( self, resolving_to=None ):
357 - """Returns this rule as a list of text lines that can be written into
358 - a file.
359 - An empty list will be returned if dep_alias has zero length.
360 -
361 - arguments:
362 - * resolving_to -- portage package that the exported rule should
363 - resolve to, defaults to self.resolving_package or
364 - an ignore keyword such as '!'.
365 - """
366 -
367 - alias_count = len ( self.dep_alias )
368 -
369 - retlist = []
370 -
371 - if alias_count:
372 - if resolving_to is None:
373 - if hasattr ( self, 'resolving_package'):
374 - resolving_package = self.resolving_package
375 - else:
376 - resolving_package = '!'
377 - else:
378 - resolving_package = resolving_to
379 -
380 - # todo hardcoded rule format here
381 - if alias_count > 1:
382 -
383 - retlist = [ resolving_package + ' {\n' ] + \
384 - [ "\t%s\n" % alias for alias in self.dep_alias ] + \
385 - [ '}\n' ]
386 - else:
387 - retlist = [
388 - "%s :: %s\n" % ( resolving_package, self.dep_alias [0] )
389 - ]
390 -
391 - # -- if
392 -
393 - return retlist
394 - # --- end of export_rule (...) ---
395 +class SimpleIgnoreDependencyRule ( SimpleRule ):
396
397 + def __init__ ( self, dep_str=None, priority=50, resolving_package=None ):
398 + super ( SimpleIgnoreDependencyRule, self ) . __init__ (
399 + dep_str=dep_str,
400 + priority=priority,
401 + resolving_package=None,
402 + logger_name = 'IGNORE_DEPS',
403 + )
404
405 -class SimpleDependencyRule ( SimpleIgnoreDependencyRule ):
406 +class SimpleDependencyRule ( SimpleRule ):
407
408 def __init__ ( self, resolving_package, dep_str=None, priority=70 ):
409 """Initializes a SimpleDependencyRule. This is
410 @@ -132,21 +33,32 @@ class SimpleDependencyRule ( SimpleIgnoreDependencyRule ):
411 * priority --
412 """
413 super ( SimpleDependencyRule, self ) . __init__ (
414 - dep_str=dep_str, priority=priority
415 + dep_str=dep_str,
416 + priority=priority,
417 + resolving_package=resolving_package,
418 + logger_name=resolving_package
419 )
420
421 - self.resolving_package = resolving_package
422 + # --- end of __init__ (...) ---
423
424 - self.logger = TMP_LOGGER.getChild ( resolving_package )
425 +class SimpleFuzzyIgnoreDependencyRule ( FuzzySimpleRule ):
426
427 - # --- end of __init__ (...) ---
428 + def __init__ ( self, dep_str=None, priority=51, resolving_package=None ):
429 + super ( SimpleFuzzyIgnoreDependencyRule, self ) . __init__ (
430 + dep_str=dep_str,
431 + priority=priority,
432 + resolving_package=None,
433 + logger_name = 'FUZZY.IGNORE_DEPS',
434 + )
435
436 - def get_dep ( self ):
437 - """Returns the textual portage package representation of this rule,
438 - e.g. 'dev-lang/R'.
439 - """
440 - return self.resolving_package
441 - # --- end of get_dep (...) ---
442 +class SimpleFuzzyDependencyRule ( FuzzySimpleRule ):
443 + def __init__ ( self, resolving_package, dep_str=None, priority=71 ):
444 + super ( SimpleFuzzyDependencyRule, self ) . __init__ (
445 + dep_str=dep_str,
446 + priority=priority,
447 + resolving_package=resolving_package,
448 + logger_name = 'FUZZY.' + resolving_package,
449 + )
450
451
452 class SimpleDependencyRulePool ( deprule.DependencyRulePool ):
453 @@ -175,7 +87,7 @@ class SimpleDependencyRulePool ( deprule.DependencyRulePool ):
454 arguments:
455 * rule --
456 """
457 - if isinstance ( rule, SimpleIgnoreDependencyRule ):
458 + if isinstance ( rule, SimpleRule ):
459 self.rules.append ( rule )
460 else:
461 raise Exception ( "bad usage (simple dependency rule expected)." )
462 @@ -220,8 +132,13 @@ class SimpleDependencyRuleReader ( object ):
463 multiline_start = '{'
464 multiline_stop = '}'
465 comment_chars = "#;"
466 +
467 # todo: const/config?
468 - package_ignore = [ '!' ]
469 + package_ignore = '!'
470 + fuzzy = '~'
471 + fuzzy_ignore = '%'
472 +
473 + BREAK_PARSING = frozenset (( '#! NOPARSE', '#! BREAK' ))
474
475
476 def __init__ ( self ):
477 @@ -229,6 +146,12 @@ class SimpleDependencyRuleReader ( object ):
478 pass
479 # --- end of __init__ (...) ---
480
481 +
482 + def _make_rule ( self, rule_str ):
483 + CLS = self.__class__
484 +
485 +
486 +
487 def read_file ( self, filepath ):
488 """Reads a file that contains simple dependency rules
489 (SimpleIgnoreDependencyRules/SimpleDependencyRules).
490 @@ -244,6 +167,8 @@ class SimpleDependencyRuleReader ( object ):
491 logging.debug ( "Reading simple dependency rule file %s." % filepath )
492 fh = open ( filepath, 'r' )
493
494 + CLS = self.__class__
495 +
496 # the list of read rules
497 rules = list ()
498
499 @@ -258,47 +183,95 @@ class SimpleDependencyRuleReader ( object ):
500 # empty
501 pass
502
503 + elif line in CLS.BREAK_PARSING:
504 + # stop reading here
505 + break
506 +
507 elif not next_rule is None:
508 # in a multiline rule
509
510 - if line [0] == SimpleDependencyRuleReader.multiline_stop:
511 + if line [0] == CLS.multiline_stop:
512 # end of a multiline rule,
513 # add rule to rules and set next_rule to None
514 + next_rule.done_reading()
515 rules.append ( next_rule )
516 next_rule = None
517 else:
518 # new resolved str
519 next_rule.add_resolved ( line )
520
521 - elif line [0] in SimpleDependencyRuleReader.comment_chars:
522 + elif line [0] in CLS.comment_chars:
523 # comment
524 # it is intented that multi line rules cannot contain comments
525 pass
526
527 - elif line [-1] == SimpleDependencyRuleReader.multiline_start:
528 + elif line [-1] == CLS.multiline_start:
529 # start of a multiline rule
530 portpkg = line [:-1].rstrip()
531 - if portpkg in SimpleDependencyRuleReader.package_ignore:
532 +
533 + if portpkg == CLS.fuzzy_ignore:
534 + next_rule = SimpleFuzzyIgnoreDependencyRule ( None )
535 + elif portpkg == CLS.fuzzy:
536 + next_rule = SimpleFuzzyDependencyRule ( portpkg[1:], None )
537 + elif portpkg == CLS.package_ignore:
538 next_rule = SimpleIgnoreDependencyRule ( None, 60 )
539 else:
540 next_rule = SimpleDependencyRule ( portpkg, None, 70 )
541
542 else:
543 - # single line rule?
544 - rule_str = \
545 - SimpleDependencyRuleReader.one_line_separator.split (line, 1)
546 + # single line rule, either selfdep,
547 + # e.g. '~zoo' -> fuzzy sci-R/zoo :: zoo
548 + # or normal rule 'dev-lang/R :: R'
549 + # selfdeps are always single line statements (!)
550 + rule_str = CLS.one_line_separator.split (line, 1)
551 +
552 + new_rule = None
553 + rule_class = None
554 + resolving = None
555 +
556 + first_char = rule_str [0][0] if len ( rule_str [0] ) else ''
557 +
558 + if first_char == CLS.fuzzy:
559 + rule_class = SimpleFuzzyDependencyRule
560 + resolving = rule_str [0] [1:]
561 +
562 + elif rule_str [0] == CLS.fuzzy_ignore:
563 + rule_class = SimpleFuzzyIgnoreDependencyRule
564 + resolving = None
565 +
566 + elif rule_str [0] == CLS.package_ignore:
567 + rule_class = SimpleIgnoreDependencyRule
568 +
569 + else:
570 + rule_class = SimpleDependencyRule
571 + resolving = rule_str [0]
572
573 if len ( rule_str ) == 2:
574 - # is a single line rule
575 -
576 - if rule_str [0] in SimpleDependencyRuleReader.package_ignore:
577 - rules.append (
578 - SimpleIgnoreDependencyRule ( rule_str [1], 40 )
579 - )
580 - else:
581 - rules.append (
582 - SimpleDependencyRule ( rule_str [0], rule_str [1], 50 )
583 - )
584 + # normal rule
585 + new_rule = rule_class (
586 + resolving_package=resolving,
587 + dep_str=rule_str [1]
588 + )
589 +
590 + elif resolving is not None:
591 + # selfdep
592 + dep_str = resolving
593 + resolving = '/'.join ( (
594 + config.get_or_fail ( 'OVERLAY.category' ),
595 + resolving
596 + ) )
597 + new_rule = rule_class (
598 + resolving_package=resolving,
599 + dep_str=dep_str
600 + )
601 +
602 + # else: error
603 +
604 +
605 + if new_rule:
606 + new_rule.done_reading()
607 + rules.append ( new_rule )
608 +
609 else:
610 logging.error (
611 "In %s, line %i : cannot use this line." %
612 @@ -308,6 +281,9 @@ class SimpleDependencyRuleReader ( object ):
613
614 if fh: fh.close ()
615
616 + if next_rule is not None:
617 + logging.warning ( "Multi line rule does not end at EOF - ignored" )
618 +
619 logging.info (
620 "%s: read %i dependency rules (in %i lines)" %
621 ( filepath, len ( rules ), lineno )