1 |
commit: 0e181458fcfaf0bd1eaf993a47d09befa6d19fa6 |
2 |
Author: André Erdmann <dywi <AT> mailerd <DOT> de> |
3 |
AuthorDate: Tue Jul 2 21:03:31 2013 +0000 |
4 |
Commit: André Erdmann <dywi <AT> mailerd <DOT> de> |
5 |
CommitDate: Tue Jul 2 21:03:31 2013 +0000 |
6 |
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=0e181458 |
7 |
|
8 |
roverlay/interface: access subsystems more easily |
9 |
|
10 |
roverlay/interface provides abstraction layers for certain roverlay modules. |
11 |
Currently, it supports dependency resolution only. |
12 |
|
13 |
--- |
14 |
roverlay/interface/__init__.py | 0 |
15 |
roverlay/interface/depres.py | 249 +++++++++++++++++++++++++++++++++++++++++ |
16 |
roverlay/interface/generic.py | 75 +++++++++++++ |
17 |
roverlay/interface/root.py | 62 ++++++++++ |
18 |
4 files changed, 386 insertions(+) |
19 |
|
20 |
diff --git a/roverlay/interface/__init__.py b/roverlay/interface/__init__.py |
21 |
new file mode 100644 |
22 |
index 0000000..e69de29 |
23 |
|
24 |
diff --git a/roverlay/interface/depres.py b/roverlay/interface/depres.py |
25 |
new file mode 100644 |
26 |
index 0000000..d416312 |
27 |
--- /dev/null |
28 |
+++ b/roverlay/interface/depres.py |
29 |
@@ -0,0 +1,249 @@ |
30 |
+# R overlay -- |
31 |
+# -*- coding: utf-8 -*- |
32 |
+# Copyright (C) 2013 André Erdmann <dywi@×××××××.de> |
33 |
+# Distributed under the terms of the GNU General Public License; |
34 |
+# either version 2 of the License, or (at your option) any later version. |
35 |
+ |
36 |
+#import weakref |
37 |
+ |
38 |
+import roverlay.interface.generic |
39 |
+ |
40 |
+ |
41 |
+import roverlay.depres.channels |
42 |
+import roverlay.depres.depresolver |
43 |
+import roverlay.depres.deptype |
44 |
+import roverlay.depres.simpledeprule.pool |
45 |
+import roverlay.depres.simpledeprule.rules |
46 |
+import roverlay.depres.simpledeprule.rulemaker |
47 |
+ |
48 |
+DEFAULT_DEPTYPE = roverlay.depres.deptype.ALL |
49 |
+ |
50 |
+class RuleSyntaxException ( Exception ): |
51 |
+ pass |
52 |
+ |
53 |
+class DepresInterface ( roverlay.interface.generic.RoverlaySubInterface ): |
54 |
+ |
55 |
+ def __init__ ( self, parent_interface ): |
56 |
+ super ( DepresInterface, self ).__init__ ( |
57 |
+ parent_interface=parent_interface |
58 |
+ ) |
59 |
+ |
60 |
+ # set up the resolver |
61 |
+ self._resolver = roverlay.depres.depresolver.DependencyResolver ( |
62 |
+ err_queue=self.err_queue |
63 |
+ ) |
64 |
+ ## log everything |
65 |
+ self._resolver.set_logmask ( -1 ) |
66 |
+ ## disable passing events to listeners |
67 |
+ self._resolver.set_listenermask ( 0 ) |
68 |
+ |
69 |
+ # dependency rule pools (a set of rules) are organized in a FIFO |
70 |
+ # structure that allows to create and delete new pools at runtime |
71 |
+ self._poolstack = self._resolver.static_rule_pools |
72 |
+ self._pool_id = -1 |
73 |
+ |
74 |
+ self._parser = roverlay.depres.simpledeprule.rulemaker.SimpleRuleMaker() |
75 |
+ # --- end of __init__ (...) --- |
76 |
+ |
77 |
+ @property |
78 |
+ def resolver ( self ): |
79 |
+ return self._resolver |
80 |
+ |
81 |
+ @property |
82 |
+ def parser ( self ): |
83 |
+ return self._parser |
84 |
+ |
85 |
+ @property |
86 |
+ def poolstack ( self ): |
87 |
+ return self._poolstack |
88 |
+ |
89 |
+ @property |
90 |
+ def pool_id ( self ): |
91 |
+ return self._pool_id |
92 |
+ |
93 |
+ def _update_resolver ( self ): |
94 |
+ # sort() should be called on a per-pool basis |
95 |
+ self._resolver._reset_unresolvable() |
96 |
+ # --- end of _update_resolver (...) --- |
97 |
+ |
98 |
+ def close ( self ): |
99 |
+ super ( DepresInterface, self ).close() |
100 |
+ self._resolver.close() |
101 |
+ # --- end of close (...) --- |
102 |
+ |
103 |
+ def update ( self ): |
104 |
+ super ( DepresInterface, self ).update() |
105 |
+ if self._poolstack: |
106 |
+ self._poolstack[-1].sort() |
107 |
+ self._update_resolver() |
108 |
+ # --- end of update (...) --- |
109 |
+ |
110 |
+ def has_pool ( self ): |
111 |
+ return self._poolstack |
112 |
+ # --- end of has_pool (...) --- |
113 |
+ |
114 |
+ def has_nonempty_pool ( self ): |
115 |
+ return self._poolstack and not self._poolstack[-1].empty() |
116 |
+ # --- end of has_nonempty_pool (...) --- |
117 |
+ |
118 |
+ def get_pool ( self ): |
119 |
+ if self._poolstack: |
120 |
+ return self._poolstack[-1] |
121 |
+ else: |
122 |
+ return self.get_new_pool ( force=True ) |
123 |
+ # --- end of get_pool (...) --- |
124 |
+ |
125 |
+ def get_new_pool ( self, force=False, with_deptype=DEFAULT_DEPTYPE ): |
126 |
+ if force or not self._poolstack or not self._poolstack[-1].empty(): |
127 |
+ self._pool_id += 1 |
128 |
+ try: |
129 |
+ pool = roverlay.depres.simpledeprule.pool.SimpleDependencyRulePool ( |
130 |
+ "pool" + str ( self._pool_id ), |
131 |
+ deptype_mask=DEFAULT_DEPTYPE |
132 |
+ ) |
133 |
+ self.poolstack.append ( pool ) |
134 |
+ except: |
135 |
+ self._pool_id -= 1 |
136 |
+ raise |
137 |
+ |
138 |
+ self._update_resolver() |
139 |
+ # -- end if force or ... |
140 |
+ return self._poolstack[-1] |
141 |
+ # --- end of get_new_pool (...) --- |
142 |
+ |
143 |
+ def discard_pool ( self ): |
144 |
+ try: |
145 |
+ self._poolstack.pop() |
146 |
+ self._pool_id -= 1 |
147 |
+ assert self._pool_id >= -1 |
148 |
+ return True |
149 |
+ except AssertionError: |
150 |
+ raise |
151 |
+ except: |
152 |
+ return False |
153 |
+ # --- end of discard_pool (...) --- |
154 |
+ |
155 |
+ def discard_pools ( self, count ): |
156 |
+ for i in range ( count ): |
157 |
+ if not self.discard_pool(): |
158 |
+ return i |
159 |
+ else: |
160 |
+ return count |
161 |
+ # --- end of discard_pools (...) --- |
162 |
+ |
163 |
+ def discard_empty_pools ( self ): |
164 |
+ removed = 0 |
165 |
+ while self._poolstack and self._poolstack[-1].empty(): |
166 |
+ if self.discard_pool(): |
167 |
+ removed += 1 |
168 |
+ else: |
169 |
+ raise AssertionError ( |
170 |
+ "discard_pool() should succeed if topmost pool exists." |
171 |
+ ) |
172 |
+ return removed |
173 |
+ # --- end of discard_empty_pools (...) --- |
174 |
+ |
175 |
+ def load_rules_from_config ( self ): |
176 |
+ return self.load_rule_files ( |
177 |
+ self.config.get_or_fail ( "DEPRES.simple_rules.files" ) |
178 |
+ ) |
179 |
+ # --- end of load_rules_from_config (...) --- |
180 |
+ |
181 |
+ def load_rule_files ( self, files_or_dirs ): |
182 |
+ return self._resolver.get_reader().read ( files_or_dirs ) |
183 |
+ # --- end of load_rule_files (...) --- |
184 |
+ |
185 |
+ def add_rule ( self, rule_str ): |
186 |
+ if not self._parser.add ( rule_str ): |
187 |
+ raise RuleSyntaxException ( rule_str ) |
188 |
+ return True |
189 |
+ # --- end of add_rule (...) --- |
190 |
+ |
191 |
+ def add_rules ( self, *rule_str_list ): |
192 |
+ for rule_str in rule_str_list: |
193 |
+ self.add_rule ( rule_str ) |
194 |
+ return True |
195 |
+ # --- end of add_rules (...) --- |
196 |
+ |
197 |
+ def add_rule_list ( self, rule_str_list ): |
198 |
+ for rule_str in rule_str_list: |
199 |
+ self.add_rule ( rule_str ) |
200 |
+ return True |
201 |
+ # --- end of add_rule_list (...) --- |
202 |
+ |
203 |
+ def compile_rules ( self, new_pool=False ): |
204 |
+ rules = self._parser.done() |
205 |
+ destpool = self.get_new_pool() if new_pool else self.get_pool() |
206 |
+ |
207 |
+ try: |
208 |
+ # FIXME/COULDFIX: deptypes not supported here |
209 |
+ for deptype, rule in rules: |
210 |
+ destpool.rules.append ( rule ) |
211 |
+ |
212 |
+ if destpool.empty(): |
213 |
+ self.discard_empty_pools() |
214 |
+ else: |
215 |
+ destpool.sort() |
216 |
+ self._update_resolver() |
217 |
+ return True |
218 |
+ except: |
219 |
+ if new_pool: |
220 |
+ # this could discard (previosly) empty pools, too |
221 |
+ # (side-effect of "optimizations" in get_new_pool()) |
222 |
+ # |
223 |
+ self.discard_pool() |
224 |
+ raise |
225 |
+ # --- end of compile_rules (...) --- |
226 |
+ |
227 |
+ def add_immediate_rule ( self, rule_str ): |
228 |
+ return self.add_rule ( rule_str ) and self.compile_rules() |
229 |
+ # --- end of add_immediate_rule (...) --- |
230 |
+ |
231 |
+ def visualize_pool ( self ): |
232 |
+ if self._poolstack: |
233 |
+ return '\n'.join ( |
234 |
+ '\n'.join ( rule.export_rule() ) |
235 |
+ for rule in self._poolstack[-1].rules |
236 |
+ ) |
237 |
+ else: |
238 |
+ return "" |
239 |
+ # --- end of visualize_pool (...) --- |
240 |
+ |
241 |
+ def get_channel ( self, channel_name="channel" ): |
242 |
+ channel = roverlay.depres.channels.EbuildJobChannel ( |
243 |
+ err_queue=self.err_queue, name=channel_name |
244 |
+ ) |
245 |
+ self._resolver.register_channel ( channel ) |
246 |
+ return channel |
247 |
+ # --- end of get_channel (...) --- |
248 |
+ |
249 |
+ def do_resolve ( self, deps, with_deptype=DEFAULT_DEPTYPE ): |
250 |
+ channel = self.get_channel() |
251 |
+ # FIXME/COULDFIX: once again, hardcoded deptype |
252 |
+ try: |
253 |
+ channel.add_dependencies ( deps, with_deptype ) |
254 |
+ |
255 |
+ channel_result = channel.satisfy_request ( |
256 |
+ close_if_unresolvable=False, |
257 |
+ preserve_order=True |
258 |
+ ) |
259 |
+ finally: |
260 |
+ channel.close() |
261 |
+ |
262 |
+ return channel_result |
263 |
+ # --- end of do_resolve (...) --- |
264 |
+ |
265 |
+ def resolve ( self, *deps, **kw ): |
266 |
+ result = self.do_resolve ( deps, **kw ) |
267 |
+ return None if result is None else result [0] |
268 |
+ # --- end of resolve (...) --- |
269 |
+ |
270 |
+ def can_resolve ( self, *deps, **kw ): |
271 |
+ return self.do_resolve ( deps, **kw ) is not None |
272 |
+ # --- end of can_resolve (...) --- |
273 |
+ |
274 |
+ def cannot_resolve ( self, *deps, **kw ): |
275 |
+ return self.do_resolve ( deps, **kw ) is None |
276 |
+ # --- end of cannot_resolve (...) --- |
277 |
+ |
278 |
+# --- end of DepresInterface --- |
279 |
|
280 |
diff --git a/roverlay/interface/generic.py b/roverlay/interface/generic.py |
281 |
new file mode 100644 |
282 |
index 0000000..08806ea |
283 |
--- /dev/null |
284 |
+++ b/roverlay/interface/generic.py |
285 |
@@ -0,0 +1,75 @@ |
286 |
+# R overlay -- |
287 |
+# -*- coding: utf-8 -*- |
288 |
+# Copyright (C) 2013 André Erdmann <dywi@×××××××.de> |
289 |
+# Distributed under the terms of the GNU General Public License; |
290 |
+# either version 2 of the License, or (at your option) any later version. |
291 |
+ |
292 |
+#import weakref |
293 |
+ |
294 |
+class RoverlayInterface ( object ): |
295 |
+ |
296 |
+ def __init__ ( self ): |
297 |
+ super ( RoverlayInterface, self ).__init__() |
298 |
+ self._interfaces = dict() |
299 |
+ # --- end of __init__ (...) --- |
300 |
+ |
301 |
+ def close_interfaces ( self ): |
302 |
+ if hasattr ( self, '_interfaces' ): |
303 |
+ for iface in self._interfaces.values(): |
304 |
+ iface.close() |
305 |
+ # --- end of close_interfaces (...) --- |
306 |
+ |
307 |
+ def close ( self ): |
308 |
+ self.close_interfaces() |
309 |
+ # --- end of close (...) --- |
310 |
+ |
311 |
+ def update ( self ): |
312 |
+ pass |
313 |
+ # --- end of update (...) --- |
314 |
+ |
315 |
+ def attach_interface ( self, name, interface, close_detached=True ): |
316 |
+ if name in self._interfaces: |
317 |
+ self.detach ( name, close=close_detached ) |
318 |
+ |
319 |
+ self._interfaces [name] = interface |
320 |
+ return interface |
321 |
+ # --- end of attach_interface (...) --- |
322 |
+ |
323 |
+ def detach_interface ( self, name, close=False ): |
324 |
+ detached = self._interfaces [name] |
325 |
+ if close: |
326 |
+ detached.close() |
327 |
+ return True |
328 |
+ else: |
329 |
+ return detached |
330 |
+ # --- end of detach_interface (...) --- |
331 |
+ |
332 |
+ def get_interface ( self, name ): |
333 |
+ return self._interfaces [name] |
334 |
+ # --- end of get_interface (...) --- |
335 |
+ |
336 |
+ def has_interface ( self, name ): |
337 |
+ return name and name in self._interfaces |
338 |
+ # --- end of has_interface (...) --- |
339 |
+ |
340 |
+ def __getattr__ ( self, name ): |
341 |
+ if name [-10:] == '_interface': |
342 |
+ iface_name = name [:-10] |
343 |
+ if iface_name and iface_name in self._interfaces: |
344 |
+ return self._interfaces [iface_name] |
345 |
+ |
346 |
+ raise AttributeError ( name ) |
347 |
+ # --- end of __getattr__ (...) --- |
348 |
+ |
349 |
+# --- end of RoverlayInterface --- |
350 |
+ |
351 |
+class RoverlaySubInterface ( RoverlayInterface ): |
352 |
+ |
353 |
+ def __init__ ( self, parent_interface ): |
354 |
+ super ( RoverlaySubInterface, self ).__init__() |
355 |
+ # weakref? (would require to explicitly keep "parent" instances around) |
356 |
+ self.parent = parent_interface |
357 |
+ self.err_queue = parent_interface.err_queue |
358 |
+ self.config = parent_interface.config |
359 |
+ self.logger = parent_interface.logger |
360 |
+ # --- end of __init__ (...) --- |
361 |
|
362 |
diff --git a/roverlay/interface/root.py b/roverlay/interface/root.py |
363 |
new file mode 100644 |
364 |
index 0000000..d500582 |
365 |
--- /dev/null |
366 |
+++ b/roverlay/interface/root.py |
367 |
@@ -0,0 +1,62 @@ |
368 |
+# R overlay -- |
369 |
+# -*- coding: utf-8 -*- |
370 |
+# Copyright (C) 2013 André Erdmann <dywi@×××××××.de> |
371 |
+# Distributed under the terms of the GNU General Public License; |
372 |
+# either version 2 of the License, or (at your option) any later version. |
373 |
+ |
374 |
+import logging |
375 |
+ |
376 |
+import roverlay |
377 |
+import roverlay.errorqueue |
378 |
+ |
379 |
+import roverlay.interface.generic |
380 |
+ |
381 |
+# does nothing if already initialized |
382 |
+roverlay.setup_initial_logger() |
383 |
+ |
384 |
+class RootInterface ( roverlay.interface.generic.RoverlayInterface ): |
385 |
+ |
386 |
+ SPAWN_MAP = dict() |
387 |
+ |
388 |
+ @classmethod |
389 |
+ def register_interface ( my_cls, name, cls, force=False ): |
390 |
+ if name and ( force or name not in my_cls.SPAWN_MAP ): |
391 |
+ my_cls.SPAWN_MAP [name] = cls |
392 |
+ return True |
393 |
+ else: |
394 |
+ return False |
395 |
+ # --- end of register_interface (...) --- |
396 |
+ |
397 |
+ def __init__ ( self, |
398 |
+ config_file=None, config=None, additional_config=None |
399 |
+ ): |
400 |
+ super ( RootInterface, self ).__init__() |
401 |
+ self.parent = None |
402 |
+ self.err_queue = roverlay.errorqueue.ErrorQueue() |
403 |
+ |
404 |
+ if config is not None: |
405 |
+ self.config = config |
406 |
+ elif config_file is not None: |
407 |
+ self.config = roverlay.load_config_file ( |
408 |
+ config_file, extraconf=additional_config |
409 |
+ ) |
410 |
+ else: |
411 |
+ raise Exception ( "config, config_file?" ) |
412 |
+ |
413 |
+ self.logger = logging.getLogger ( self.__class__.__name__ ) |
414 |
+ # --- end of __init__ (...) --- |
415 |
+ |
416 |
+ def spawn_interface ( self, name ): |
417 |
+ if self.has_interface ( name ): |
418 |
+ return self.get_interface ( name ) |
419 |
+ else: |
420 |
+ iface_cls = self.SPAWN_MAP.get ( name, None ) |
421 |
+ if iface_cls is None: |
422 |
+ raise Exception ( |
423 |
+ "unknown interface identifier {!r}".format ( name ) |
424 |
+ ) |
425 |
+ else: |
426 |
+ return self.attach_interface ( |
427 |
+ name, iface_cls ( parent_interface=self ) |
428 |
+ ) |
429 |
+ # --- end of spawn_interface (...) --- |