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 |
+ |