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/packagerules/, roverlay/packagerules/abstract/, ...
Date: Wed, 30 Jan 2013 20:16:35
Message-Id: 1359576451.2333b3c2779ae8e832db338af93f82f320457d24.dywi@gentoo
1 commit: 2333b3c2779ae8e832db338af93f82f320457d24
2 Author: André Erdmann <dywi <AT> mailerd <DOT> de>
3 AuthorDate: Wed Jan 30 20:07:31 2013 +0000
4 Commit: André Erdmann <dywi <AT> mailerd <DOT> de>
5 CommitDate: Wed Jan 30 20:07:31 2013 +0000
6 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=2333b3c2
7
8 Introducing package rules
9
10 A PackageRule matches zero or more PackageInfo instances and manipulates them,
11 e.g by filtering it out ("do not process") or adding ebuild variables
12 ("KEYWORDS='amd64 -86', ...).
13
14 This commit adds the abstract layout of such rules,
15 each rule consists of acceptors (Acceptor objects) that determine if the rule
16 matches a package, and actions (PackageRuleAction objects) that manipulate the
17 PackageInfo instance.
18 NestedPackageRules add the ability to add subordinate rules to a rule.
19
20 Acceptors can also be part of boolean expressions,
21 e.g. "match package if name == 'seewave' or repo == 'CRAN'" (just an example).
22
23 What's missing is the creation of such rules from file (PackageRulesLoader),
24 so the added code doesn't do much (currently).
25
26 ---
27 roverlay/packagerules/__init__.py | 11 ++
28 roverlay/packagerules/abstract/__init__.py | 5 +
29 roverlay/packagerules/abstract/acceptors.py | 157 +++++++++++++++++++++++++++
30 roverlay/packagerules/abstract/actions.py | 32 ++++++
31 roverlay/packagerules/abstract/rules.py | 149 +++++++++++++++++++++++++
32 roverlay/packagerules/actions/__init__.py | 5 +
33 roverlay/packagerules/actions/evar.py | 60 ++++++++++
34 roverlay/packagerules/loader.py | 33 ++++++
35 roverlay/packagerules/rules.py | 54 +++++++++
36 9 files changed, 506 insertions(+), 0 deletions(-)
37
38 diff --git a/roverlay/packagerules/__init__.py b/roverlay/packagerules/__init__.py
39 new file mode 100644
40 index 0000000..100cd2f
41 --- /dev/null
42 +++ b/roverlay/packagerules/__init__.py
43 @@ -0,0 +1,11 @@
44 +# R overlay -- package rules
45 +# -*- coding: utf-8 -*-
46 +# Copyright (C) 2013 André Erdmann <dywi@×××××××.de>
47 +# Distributed under the terms of the GNU General Public License;
48 +# either version 2 of the License, or (at your option) any later version.
49 +
50 +# TODO:
51 +# * load rules from a file (make them accessible)
52 +# -> syntax
53 +# * logging
54 +# * doc/rst
55
56 diff --git a/roverlay/packagerules/abstract/__init__.py b/roverlay/packagerules/abstract/__init__.py
57 new file mode 100644
58 index 0000000..2434c5d
59 --- /dev/null
60 +++ b/roverlay/packagerules/abstract/__init__.py
61 @@ -0,0 +1,5 @@
62 +# R overlay -- abstract package rules
63 +# -*- coding: utf-8 -*-
64 +# Copyright (C) 2013 André Erdmann <dywi@×××××××.de>
65 +# Distributed under the terms of the GNU General Public License;
66 +# either version 2 of the License, or (at your option) any later version.
67
68 diff --git a/roverlay/packagerules/abstract/acceptors.py b/roverlay/packagerules/abstract/acceptors.py
69 new file mode 100644
70 index 0000000..fa28dd0
71 --- /dev/null
72 +++ b/roverlay/packagerules/abstract/acceptors.py
73 @@ -0,0 +1,157 @@
74 +# R overlay -- abstract package rules, acceptors
75 +# -*- coding: utf-8 -*-
76 +# Copyright (C) 2013 André Erdmann <dywi@×××××××.de>
77 +# Distributed under the terms of the GNU General Public License;
78 +# either version 2 of the License, or (at your option) any later version.
79 +
80 +import roverlay.util
81 +
82 +__all__ = [
83 + 'Acceptor',
84 + 'Acceptor_AND', 'Acceptor_OR', 'Acceptor_XOR1', 'Acceptor_NOR',
85 +]
86 +
87 +class EmptyAcceptor ( ValueError ):
88 + """
89 + Exception for "empty" Acceptors (Acceptor represents a boolean
90 + expression, but has no Acceptors attached).
91 + """
92 + pass
93 +# --- end of EmptyAcceptor ---
94 +
95 +
96 +class Acceptor ( object ):
97 + """An Acceptor is able to determine whether it matches a PackageInfo
98 + instance or not.
99 + """
100 +
101 + def __init__ ( self, priority ):
102 + super ( Acceptor, self ).__init__()
103 + self.priority = priority
104 + # --- end of __init__ (...) ---
105 +
106 + def prepare ( self ):
107 + """Prepare the Acceptor for usage (typically used after loading
108 + it from a file).
109 + """
110 + pass
111 + # --- end of prepare (...) ---
112 +
113 + def accepts ( self, p_info ):
114 + """Returns True if this Acceptor matches the given PackageInfo, else
115 + False.
116 +
117 + arguments:
118 + * p_info --
119 + """
120 + raise NotImplementedError()
121 + # --- end of accepts (...) ---
122 +
123 +# --- end of Acceptor ---
124 +
125 +class _AcceptorCompound ( Acceptor ):
126 + """The base class for Acceptors that represent a boolean expression."""
127 +
128 + def __init__ ( self, priority ):
129 + super ( _AcceptorCompound, self ).__init__ ( priority )
130 + self._acceptors = list()
131 + # --- end of __init__ (...) ---
132 +
133 + def prepare ( self ):
134 + """Prepare the Acceptor for usage (typically used after loading
135 + it from a file).
136 +
137 + Sorts all Acceptors according to their priority.
138 +
139 + Raises: EmptyAcceptor
140 + """
141 + if len ( self._acceptors ) > 0:
142 + self._acceptors = roverlay.util.priosort ( self._acceptors )
143 + for acceptor in self._acceptors:
144 + acceptor.prepare()
145 + else:
146 + raise EmptyAcceptor()
147 + # --- end of prepare (...) ---
148 +
149 + def add_acceptor ( self, acceptor ):
150 + """Adds an Acceptor.
151 +
152 + arguments:
153 + * acceptor --
154 + """
155 + self._acceptors.append ( acceptor )
156 + # --- end of add_acceptor (...) ---
157 +
158 +# --- end of _AcceptorCompound ---
159 +
160 +class Acceptor_OR ( _AcceptorCompound ):
161 + """OR( <Acceptors> )"""
162 +
163 + def accepts ( self, p_info ):
164 + """Returns True if any attached Acceptor returns True, else False.
165 +
166 + arguments:
167 + * p_info --
168 + """
169 + for acceptor in self._acceptors:
170 + if acceptor.accepts ( p_info ):
171 + return True
172 + return False
173 + # --- end of accepts (...) ---
174 +
175 +# --- end of Acceptor_OR ---
176 +
177 +class Acceptor_AND ( _AcceptorCompound ):
178 + """AND( <Acceptors> )"""
179 +
180 + def accepts ( self, p_info ):
181 + """Returns True if all acceptors accept p_info, else False.
182 +
183 + arguments:
184 + * p_info --
185 + """
186 + for acceptor in self._acceptors:
187 + if not acceptor.accepts ( p_info ):
188 + return False
189 + return True
190 + # --- end of accepts (...) ---
191 +
192 +# --- end of Acceptor_AND ---
193 +
194 +class Acceptor_XOR1 ( _AcceptorCompound ):
195 + """XOR( <Acceptors> )"""
196 +
197 + # XOR := odd number of matches
198 + # XOR1 := exactly one match
199 +
200 + def accepts ( self, p_info ):
201 + """Returns True if exactly one acceptor accepts p_info, else False.
202 +
203 + arguments:
204 + * p_info --
205 + """
206 + any_true = False
207 + for acceptor in self._acceptors:
208 + if acceptor.accepts ( p_info ):
209 + if any_true:
210 + return False
211 + else:
212 + any_true = True
213 + return any_true
214 + # --- end of accepts (...) ---
215 +
216 +# --- end of Acceptor_XOR1 ---
217 +
218 +class Acceptor_NOR ( Acceptor_OR ):
219 + """NOR( <Acceptors> )"""
220 +
221 + def accepts ( self, p_info ):
222 + """Returns True if no acceptor accepts p_info, else False.
223 +
224 + arguments:
225 + * p_info --
226 + """
227 + return not super ( Acceptor_NOR, self ).accepts ( p_info )
228 + # --- end of accepts (...) ---
229 +
230 +# --- end of Acceptor_NOR ---
231
232 diff --git a/roverlay/packagerules/abstract/actions.py b/roverlay/packagerules/abstract/actions.py
233 new file mode 100644
234 index 0000000..1204f53
235 --- /dev/null
236 +++ b/roverlay/packagerules/abstract/actions.py
237 @@ -0,0 +1,32 @@
238 +# R overlay -- abstract package rules, actions
239 +# -*- coding: utf-8 -*-
240 +# Copyright (C) 2013 André Erdmann <dywi@×××××××.de>
241 +# Distributed under the terms of the GNU General Public License;
242 +# either version 2 of the License, or (at your option) any later version.
243 +
244 +__all__ = [ 'PackageRuleAction', ]
245 +
246 +class PackageRuleAction ( object ):
247 + """PackageRuleActions manipulate PackageInfo instances."""
248 +
249 + def __init__ ( self, priority=1000 ):
250 + super ( PackageRuleAction, self ).__init__()
251 + self.priority = priority
252 + # --- end of __init__ (...) ---
253 +
254 + def apply_action ( self, p_info ):
255 + """Applies the action to the given PackageInfo.
256 +
257 + Returns False if the package should be filtered out.
258 + Any other value, especially None, should be interpreted as
259 + "successfully processed"
260 + (In constrast to the PackageRule.apply_actions(), where any false value
261 + means "package should be filtered out".)
262 +
263 + arguments:
264 + * p_info --
265 + """
266 + raise NotImplementedError()
267 + # --- end of apply_action (...) ---
268 +
269 +# --- end of PackageRuleAction ---
270
271 diff --git a/roverlay/packagerules/abstract/rules.py b/roverlay/packagerules/abstract/rules.py
272 new file mode 100644
273 index 0000000..b9e057c
274 --- /dev/null
275 +++ b/roverlay/packagerules/abstract/rules.py
276 @@ -0,0 +1,149 @@
277 +# R overlay -- abstract package rules, rules
278 +# -*- coding: utf-8 -*-
279 +# Copyright (C) 2013 André Erdmann <dywi@×××××××.de>
280 +# Distributed under the terms of the GNU General Public License;
281 +# either version 2 of the License, or (at your option) any later version.
282 +
283 +import roverlay.util
284 +
285 +__all__ = [ 'PackageRule', 'NestedPackageRule', 'IgnorePackageRule', ]
286 +
287 +class PackageRule ( object ):
288 + """A package rule is able to determine whether it matches
289 + a given PackageInfo instance (using Acceptor instances)
290 + and applies zero or more actions (using PackageAction instances) to the
291 + package info.
292 + """
293 +
294 + def __init__ ( self, priority=1000 ):
295 + super ( PackageRule, self ).__init__()
296 + self.priority = priority
297 + self._actions = list()
298 + self._acceptors = list()
299 + # --- end of __init__ (...) ---
300 +
301 + def prepare ( self ):
302 + """
303 + Prepares this rule for usage. Has to be called after adding actions.
304 + """
305 + self._actions = roverlay.util.priosort ( self._actions )
306 + self._acceptors = roverlay.util.priosort ( self._acceptors )
307 + for acceptor in self._acceptors:
308 + acceptor.prepare()
309 + # --- end of prepare (...) ---
310 +
311 + def accepts ( self, p_info ):
312 + """Returns True if this rule matches the given PackageInfo else False.
313 +
314 + arguments:
315 + * p_info --
316 + """
317 + for acceptor in self._acceptors:
318 + if not acceptor.accepts ( p_info ):
319 + return False
320 + return True
321 + # --- end of accepts (...) ---
322 +
323 + def apply_actions ( self, p_info ):
324 + """Applies all actions to the given PackageInfo.
325 +
326 + The return value indicates whether the package has been filtered out
327 + (do not process it any longer -> False) or not (True).
328 +
329 + arguments:
330 + * p_info -- PackageInfo object that will be modified
331 + """
332 + for action in self._actions:
333 + # "is False" - see ./actions.py
334 + if action.apply_action ( p_info ) is False:
335 + return False
336 + return True
337 + # --- end of apply_actions (...) ---
338 +
339 + def add_action ( self, action ):
340 + """Adds an action to this rule.
341 +
342 + arguments:
343 + * action --
344 + """
345 + self._actions.append ( action )
346 + # --- end of add_action (...) ---
347 +
348 + def add_acceptor ( self, acceptor ):
349 + """Adds an acceptor to this rule. Such objects are used to match
350 + PackageInfo instances (in self.accepts()).
351 +
352 + arguments:
353 + * acceptor
354 + """
355 + self._acceptors.append ( acceptor )
356 + # --- end of add_acceptor (...) ---
357 +
358 +# --- end of PackageRule ---
359 +
360 +
361 +class IgnorePackageRule ( PackageRule ):
362 + """A rule that has only one action: filter packages."""
363 +
364 + def __init__ ( self, priority=100 ):
365 + super ( PackageRule, self ).__init__( priority )
366 + # --- end of __init__ (...) ---
367 +
368 + def apply_actions ( self, p_info ):
369 + """Ignores a PackageInfo by returning False.
370 +
371 + arguments:
372 + * p_info --
373 + """
374 + return False
375 + # --- end of apply_actions (...) ---
376 +
377 +# --- end of IgnorePackageRule ---
378 +
379 +
380 +class NestedPackageRule ( PackageRule ):
381 + """A rule that consists of zero or more subordinate rules."""
382 +
383 + def __init__ ( self, priority=2000 ):
384 + super ( NestedPackageRule, self ).__init__ ( priority )
385 + self._rules = list()
386 + # --- end of __init__ (...) ---
387 +
388 + def prepare ( self ):
389 + """
390 + Prepares this rule for usage. Has to be called after adding actions.
391 + """
392 + super ( NestedPackageRule, self ).prepare()
393 + self._rules = roverlay.util.priosort ( self._rules )
394 + for rule in self._rules:
395 + rule.prepare()
396 + # --- end of prepare (...) ---
397 +
398 + def apply_actions ( self, p_info ):
399 + """Applies all actions to the given PackageInfo.
400 +
401 + The return value indicates whether the package has been filtered out
402 + (do not process it any longer -> False) or not (True).
403 +
404 + arguments:
405 + * p_info -- PackageInfo object that will be modified
406 + """
407 + if super ( NestedPackageRule, self ).apply_actions ( p_info ):
408 + for rule in self._rules:
409 + if rule.accepts ( p_info ) and not rule.apply_actions ( p_info ):
410 + return False
411 + return True
412 + else:
413 + return False
414 + # --- end of apply_actions (...) ---
415 +
416 + def add_rule ( self, rule ):
417 + """Adds a rule.
418 +
419 + arguments:
420 + * rule --
421 + """
422 + self._rules.append ( rule )
423 + # --- end of add_rule (...) ---
424 +
425 +# --- end of NestedPackageRule ---
426
427 diff --git a/roverlay/packagerules/actions/__init__.py b/roverlay/packagerules/actions/__init__.py
428 new file mode 100644
429 index 0000000..1a6cb02
430 --- /dev/null
431 +++ b/roverlay/packagerules/actions/__init__.py
432 @@ -0,0 +1,5 @@
433 +# R overlay -- package rules, actions
434 +# -*- coding: utf-8 -*-
435 +# Copyright (C) 2013 André Erdmann <dywi@×××××××.de>
436 +# Distributed under the terms of the GNU General Public License;
437 +# either version 2 of the License, or (at your option) any later version.
438
439 diff --git a/roverlay/packagerules/actions/evar.py b/roverlay/packagerules/actions/evar.py
440 new file mode 100644
441 index 0000000..d2a4580
442 --- /dev/null
443 +++ b/roverlay/packagerules/actions/evar.py
444 @@ -0,0 +1,60 @@
445 +# R overlay -- package rule actions, add/set ebuild variables
446 +# -*- coding: utf-8 -*-
447 +# Copyright (C) 2013 André Erdmann <dywi@×××××××.de>
448 +# Distributed under the terms of the GNU General Public License;
449 +# either version 2 of the License, or (at your option) any later version.
450 +
451 +import roverlay.packagerules.abstract.actions
452 +
453 +import roverlay.ebuild.evars
454 +
455 +__all__ = [
456 + 'EvarAction',
457 + 'KeywordsEvarAction',
458 +]
459 +
460 +class EvarAction ( roverlay.packagerules.abstract.actions.PackageRuleAction ):
461 + """An EvarAction adds an ebuild variable to a PackageInfo."""
462 +
463 + def __init__ ( self, evar, priority=1000 ):
464 + """Constructor for EvarAction.
465 +
466 + arguments:
467 + * evar -- ebuild variable that will be added whenever calling
468 + apply_action()
469 + * priority -- priority of this action (used for sorting)
470 + """
471 + super ( EvarAction, self ).__init__ ( priority=priority )
472 + self._evar = evar
473 + # --- end of __init__ (...) ---
474 +
475 + def apply_action ( self, p_info ):
476 + """Adds the stored ebuild variable to p_info.
477 +
478 + Note:
479 + Since the ebuild variable will be added as reference, ebuild creation
480 + has to copy it before editing, else *all* ebuilds using this variable
481 + will be affected!
482 + (At least those that get processed after the evar modification.)
483 +
484 + arguments:
485 + * p_info --
486 + """
487 + # add ref to self._evar
488 + p_info.update_unsafe ( EVAR=self._evar )
489 + # --- end of apply_action (...) ---
490 +
491 +# --- end of EvarAction (...) ---
492 +
493 +
494 +class KeywordsEvarAction ( EvarAction ):
495 + """A KeywordsEvarAction adds a KEYWORDS=... variable to a PackageInfo."""
496 +
497 + def __init__ ( self, keywords, priority=1000 ):
498 + super ( KeywordsEvarAction, self ).__init__ (
499 + evar = roverlay.ebuild.evars.KEYWORDS ( keywords ),
500 + priority = priority,
501 + )
502 + # --- end of __init__ (...) ---
503 +
504 +# --- end of KeywordsEvarAction ---
505
506 diff --git a/roverlay/packagerules/loader.py b/roverlay/packagerules/loader.py
507 new file mode 100644
508 index 0000000..601264f
509 --- /dev/null
510 +++ b/roverlay/packagerules/loader.py
511 @@ -0,0 +1,33 @@
512 +# R overlay -- package rules, package rule loader (from files)
513 +# -*- coding: utf-8 -*-
514 +# Copyright (C) 2013 André Erdmann <dywi@×××××××.de>
515 +# Distributed under the terms of the GNU General Public License;
516 +# either version 2 of the License, or (at your option) any later version.
517 +
518 +__all__ = [ 'PackageRulesLoader', ]
519 +
520 +class PackageRulesLoader ( object ):
521 + """Loads PackageRules from a file."""
522 +
523 + # TODO
524 +
525 + def __init__ ( self, rules ):
526 + """Constructor for PackageRulesLoader.
527 +
528 + arguments:
529 + * rules -- object where new rules will be added to
530 + has to implement an "add_rule()" function
531 + """
532 + self.rules = rules
533 + # --- end of __init__ (...) ---
534 +
535 + def load ( self, rule_file ):
536 + """Loads a rule file.
537 +
538 + arguments:
539 + * rule_file --
540 + """
541 + raise NotImplementedError ( "TODO" )
542 + # --- end of load (...) ---
543 +
544 +# --- end of PackageRulesLoader ---
545
546 diff --git a/roverlay/packagerules/rules.py b/roverlay/packagerules/rules.py
547 new file mode 100644
548 index 0000000..1c1adbc
549 --- /dev/null
550 +++ b/roverlay/packagerules/rules.py
551 @@ -0,0 +1,54 @@
552 +# R overlay -- package rules
553 +# -*- coding: utf-8 -*-
554 +# Copyright (C) 2013 André Erdmann <dywi@×××××××.de>
555 +# Distributed under the terms of the GNU General Public License;
556 +# either version 2 of the License, or (at your option) any later version.
557 +
558 +__all__ = [ 'PackageRules', ]
559 +
560 +import roverlay.packagerules.abstract.rules
561 +import roverlay.packagerules.loader
562 +
563 +#import roverlay.packagerules.actions.evar
564 +
565 +class PackageRules ( roverlay.packagerules.abstract.rules.NestedPackageRule ):
566 + """The top level rule.
567 + Matches all PackageInfo instances and applies any rule that matches.
568 + """
569 +
570 + @classmethod
571 + def get_configured ( cls ):
572 + """Returns a PackageRules instance that uses the configured rules
573 + (roverlay.config).
574 +
575 + arguments:
576 + * cls --
577 +
578 + This is a stub since package rule loading is not implemented.
579 + """
580 + rules = PackageRules()
581 + f = rules.get_loader()
582 + # "example usage" (just a reminder for PackageRulesLoader)
583 +# rules.add_action (
584 +# roverlay.packagerules.actions.evar.KeywordsEvarAction ( "amd64" )
585 +# )
586 + return rules
587 + # --- end of get_configured (...) ---
588 +
589 + def __init__ ( self ):
590 + super ( PackageRules, self ).__init__ ( priority=-1 )
591 + # --- end of __init__ (...) ---
592 +
593 + def get_loader ( self ):
594 + """Returns a PackageRulesLoader that reads files and adds rules to
595 + this PackageRules instance.
596 + """
597 + return roverlay.packagerules.loader.PackageRulesLoader ( self )
598 + # --- end of get_loader (...) ---
599 +
600 + def accepts ( self, p_info ):
601 + """Returns True (and therefore doesn't need to be called)."""
602 + return True
603 + # --- end of accepts (...) ---
604 +
605 +# --- end of PackageRules ---