* [gentoo-commits] proj/R_overlay:master commit in: roverlay/ebuild/, roverlay/depres/simpledeprule/, roverlay/depres/, roverlay/, ...
@ 2012-07-16 16:15 André Erdmann
0 siblings, 0 replies; only message in thread
From: André Erdmann @ 2012-07-16 16:15 UTC (permalink / raw
To: gentoo-commits
commit: 7ebbaf11c479641eb8cc67267266c1d1862ca21d
Author: André Erdmann <dywi <AT> mailerd <DOT> de>
AuthorDate: Thu Jul 12 17:48:59 2012 +0000
Commit: André Erdmann <dywi <AT> mailerd <DOT> de>
CommitDate: Thu Jul 12 18:19:55 2012 +0000
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=7ebbaf11
introducing dependency types
The dependency resolver can now differentiate between dependency types
The actual deptype is a bit field that sets flags like 'is mandatory',
'is an external dep', 'try other dep types, too'.
Functionality for this has been added to most modules/classes, particularly
to EbuildJobChannel and ebuild/depres, but not to the rule reader,
which means that there's currently no way to _use_ the dep types!
Also added an ErrorQueue implementation that saves some "if <has err_queue>"
checks. It's also able to unblock waiting queues, which helps to end
threads when an exception has been raised.
---
roverlay/depres/channels.py | 116 +++++++++++++---------------
roverlay/depres/depenv.py | 89 ++++++++++++----------
roverlay/depres/depresolver.py | 89 +++++++++++----------
roverlay/depres/deprule.py | 17 +++--
roverlay/depres/deptype.py | 19 +++++
roverlay/depres/simpledeprule/console.py | 15 +++-
roverlay/depres/simpledeprule/pool.py | 24 ++----
roverlay/depres/simpledeprule/rulemaker.py | 4 +
roverlay/ebuild/depres.py | 54 +++++++++----
roverlay/errorqueue.py | 115 +++++++++++++++++++++++++++
roverlay/overlay/creator.py | 42 +++-------
roverlay/overlay/worker.py | 21 ++----
roverlay/recipe/easyresolver.py | 8 +-
13 files changed, 379 insertions(+), 234 deletions(-)
diff --git a/roverlay/depres/channels.py b/roverlay/depres/channels.py
index 72f2b87..f15a6d0 100644
--- a/roverlay/depres/channels.py
+++ b/roverlay/depres/channels.py
@@ -4,12 +4,13 @@
import logging
+from roverlay.depres import deptype
from roverlay.depres.depenv import DepEnv
from roverlay.depres.communication import DependencyResolverChannel
COMLINK = logging.getLogger ( "depres.com" )
-class EbuildJobChannel ( DependencyResolverChannel ):
+class _EbuildJobChannelBase ( DependencyResolverChannel ):
"""The EbuildJobChannel is an interface to the dependency resolver used
in EbuildJobs.
It can be used to insert dependency strings, trigger dep resolution,
@@ -26,10 +27,10 @@ class EbuildJobChannel ( DependencyResolverChannel ):
* name -- name of this channel, optional
* logger --
"""
- super ( EbuildJobChannel, self ) . __init__ ( main_resolver=None )
+ super ( _EbuildJobChannelBase, self ) . __init__ ( main_resolver=None )
- # this is the number of resolved deps so far, should only be modified
- # in the join()-method
+ # this is the number of resolved deps so far,
+ # should only be modified in the join()-method
self._depdone = 0
self.err_queue = err_queue
@@ -41,11 +42,10 @@ class EbuildJobChannel ( DependencyResolverChannel ):
# used to communicate with the resolver
self._depres_queue = None
- # the list of dependency lookups assigned to this channel
- ## todo: could remove this list
- self.dep_env_list = list ()
+ # the count of dep strings ever assigned to this channel
+ self._depcount = 0
- _logger = logger if hasattr ( logger, 'log' ) else COMLINK
+ _logger = logger if logger is not None else COMLINK
if name:
self.name = name
self.logger = _logger.getChild ( 'channel.' + name )
@@ -58,8 +58,9 @@ class EbuildJobChannel ( DependencyResolverChannel ):
if self._depdone >= 0:
# else already closed
- super ( EbuildJobChannel, self ) . close()
- del self.dep_env_list, self._collected_deps, self._depres_queue
+ super ( _EbuildJobChannelBase, self ).close()
+ self.err_queue.remove_queue ( self._depres_queue )
+ del self._collected_deps, self._depres_queue
del self.logger
self._depdone = -1
@@ -77,11 +78,12 @@ class EbuildJobChannel ( DependencyResolverChannel ):
if self._depres_master is None:
self._depres_master = resolver
self._depres_queue = channel_queue
+ self.err_queue.attach_queue ( self._depres_queue, None )
else:
raise Exception ( "channel already bound to a resolver." )
# --- end of set_resolver (...) ---
- def add_dependency ( self, dep_str ):
+ def add_dependency ( self, dep_str, deptype_mask ):
"""Adds a dependency string that should be looked up.
This channel will create a "dependency environment" for it and enqueues
that in the dep resolver.
@@ -98,13 +100,13 @@ class EbuildJobChannel ( DependencyResolverChannel ):
"This channel is 'done', it doesn't accept new dependencies."
)
else:
- dep_env = DepEnv ( dep_str )
- self.dep_env_list.append ( dep_env )
+ dep_env = DepEnv ( dep_str=dep_str, deptype_mask=deptype_mask )
+ self._depcount += 1
self._depres_master.enqueue ( dep_env, self.ident )
# --- end of add_dependency (...) ---
- def add_dependencies ( self, dep_list ):
+ def add_dependencies ( self, dep_list, deptype_mask ):
"""Adds dependency strings to this channel. See add_dependency (...)
for details.
@@ -115,30 +117,10 @@ class EbuildJobChannel ( DependencyResolverChannel ):
returns: None (implicit)
"""
- for dep_str in dep_list: self.add_dependency ( dep_str )
+ for dep_str in dep_list:
+ self.add_dependency ( dep_str=dep_str, deptype_mask=deptype_mask )
# --- end of add_dependencies (...) ---
- def lookup ( self, dep_str ):
- """Looks up a specific dep str. Use the (faster) collect_dependencies()
- method for getting all dependencies if order doesn't matter.
-
- ! It assumes that dep_str is unique in this channel.
-
- arguments:
- * dep_str -- to be looked up
-
- raises: Exception if dep_str not in the dep env list.
- """
- # it's no requirement that this channel is done when calling this method
- for de in self.dep_env_list:
- if de.dep_str == dep_str:
- return de.get_result() [1]
-
- raise Exception (
- "bad usage: %s not in channel's dep env list!" % dep_str
- )
- # --- end of lookup (...) ---
-
def collect_dependencies ( self ):
"""Returns a list that contains all resolved deps,
including ignored deps that resolve to None.
@@ -155,12 +137,19 @@ class EbuildJobChannel ( DependencyResolverChannel ):
raise Exception ( "cannot do that" )
# --- end of collect_dependencies (...) ---
+ def satisfy_request ( self, *args, **kw ):
+ raise Exception ( "method stub" )
+
+
+class EbuildJobChannel ( _EbuildJobChannelBase ):
+
+
def satisfy_request ( self,
close_if_unresolvable=True, preserve_order=False
):
"""Tells to the dependency resolver to run.
It blocks until this channel is done, which means that either all
- deps are resolved or one is unresolvable.
+ deps are resolved or a mandatory one is unresolvable.
arguments:
* close_if_unresolvable -- close the channel if one dep is unresolvable
@@ -173,54 +162,57 @@ class EbuildJobChannel ( DependencyResolverChannel ):
Returns the list of resolved dependencies if all could be resolved,
else None.
"""
-
- # using a set allows easy difference() operations between
- # DEPEND/RDEPEND/.. later, seewave requires sci-libs/fftw
- # in both DEPEND and RDEPEND for example
dep_collected = list()
- satisfiable = True \
- if self.err_queue is None \
- else self.err_queue.empty()
-
+ resolved = dep_collected.append
def handle_queue_item ( dep_env ):
self._depdone += 1
+ ret = False
if dep_env is None:
- # could used to unblock the queue
- if self.err_queue is None:
- return False
- else:
- return self.err_queue.empty()
+ # queue unblocked -> on_error mode, return False
+ #ret = False
+ pass
elif dep_env.is_resolved():
- ### and dep_env in self.dep_env_list
# successfully resolved
- dep_collected.append ( dep_env.get_result() [1] )
- self._depres_queue.task_done()
- return True
- else:
- # failed
- self._depres_queue.task_done()
- return False
+ resolved ( dep_env.get_resolved() )
+ ret = True
+ elif deptype.mandatory & ~dep_env.deptype_mask:
+ # not resolved, but deptype has no mandatory bit set
+ # => dep is not required, resolve as None
+ resolved ( None )
+ ret = True
+ # else failed
+
+ self._depres_queue.task_done()
+ return ret
# --- end of handle_queue_item (...) ---
+ satisfiable = True
# loop until
- # (a) at least one dependency could not be resolved or
+ # (a) at least one required dependency could not be resolved or
# (b) all deps processed or
# (c) error queue not empty
- while self._depdone < len ( self.dep_env_list ) and satisfiable:
+ while self._depdone < self._depcount and \
+ satisfiable and self.err_queue.empty \
+ :
# tell the resolver to start
self._depres_master.start()
+ # ^, race condition, running resolver vs waiting queue (FIXME)
+
# wait for one result at least
satisfiable = handle_queue_item ( self._depres_queue.get() )
# and process all available results
- while not self._depres_queue.empty() and satisfiable:
+ while satisfiable and not self._depres_queue.empty():
satisfiable = handle_queue_item ( self._depres_queue.get_nowait() )
# --- end while
- if satisfiable:
+ if satisfiable and self.err_queue.empty:
+ # using a set allows easy difference() operations between
+ # DEPEND/RDEPEND/.. later, seewave requires sci-libs/fftw
+ # in both DEPEND and RDEPEND for example
self._collected_deps = frozenset ( dep_collected )
if preserve_order:
return tuple ( dep_collected )
diff --git a/roverlay/depres/depenv.py b/roverlay/depres/depenv.py
index eba03a8..7cef1c9 100644
--- a/roverlay/depres/depenv.py
+++ b/roverlay/depres/depenv.py
@@ -3,58 +3,61 @@
# Distributed under the terms of the GNU General Public License v2
import re
-
-# excluding A-Z since dep_str_low will be used to find a match
-_NAME = '(?P<name>[a-z0-9_\-/]+)'
-_VER = '(?P<ver>[0-9._\-]+)'
-# { <, >, <=, >=, =, != } (TODO !=)
-_VERMOD = '(?P<vmod>[<>]|[<>!]?[=])'
-
-# this lists ... <fixme>
-V_REGEX_STR = frozenset ( (
- # 'R >= 2.15', 'R >=2.15' etc. (but not 'R>=2.15'!)
- '^%s\s+%s?\s*%s\s*$' % ( _NAME, _VERMOD, _VER ),
-
- # TODO: merge these regexes: ([{ )]}]
-
- # 'R (>= 2.15)', 'R(>=2.15)' etc.
- '^%s\s*\(%s?\s*%s\s*\)$' % ( _NAME, _VERMOD, _VER ),
-
- # 'R [>= 2.15]', 'R[>=2.15]' etc.
- '^%s\s*\[%s?\s*%s\s*\]$' % ( _NAME, _VERMOD, _VER ),
-
-
- # 'R {>= 2.15}', 'R{>=2.15}' etc.
- '^%s\s*\{%s?\s*%s\s*\}$' % ( _NAME, _VERMOD, _VER ),
-) )
-
-VERSION_REGEX = frozenset ( re.compile ( regex ) for regex in V_REGEX_STR )
-
-FIXVERSION_REGEX = re.compile ( '[_\-]' )
-
-TRY_ALL_REGEXES = False
-
-
class DepEnv ( object ):
+ # excluding A-Z since dep_str_low will be used to find a match
+ _NAME = '(?P<name>[a-z0-9_\-/]+)'
+ _VER = '(?P<ver>[0-9._\-]+)'
+ # { <, >, <=, >=, =, != } (TODO !=)
+ _VERMOD = '(?P<vmod>[<>]|[<>!]?[=])'
+
+ V_REGEX_STR = frozenset ( (
+ # 'R >= 2.15', 'R >=2.15' etc. (but not 'R>=2.15'!)
+ '^{name}\s+{vermod}?\s*{ver}\s*$'.format (
+ name=_NAME, vermod=_VERMOD, ver=_VER
+ ),
+ # TODO: merge these regexes: () [] {} (but not (],{), ...)
+ # 'R (>= 2.15)', 'R(>=2.15)' etc.
+ '^{name}\s*\(\s*{vermod}?\s*{ver}\s*\)$'.format (
+ name=_NAME, vermod=_VERMOD, ver=_VER
+ ),
+ # 'R [>= 2.15]', 'R[>=2.15]' etc.
+ '^{name}\s*\[\s*{vermod}?\s*{ver}\s*\]$'.format (
+ name=_NAME, vermod=_VERMOD, ver=_VER
+ ),
+
+ # 'R {>= 2.15}', 'R{>=2.15}' etc.
+ '^{name}\s*\{{\s*{vermod}?\s*{ver}\s*\}}$'.format (
+ name=_NAME, vermod=_VERMOD, ver=_VER
+ ),
+ ) )
+
+ VERSION_REGEX = frozenset (
+ re.compile ( regex ) for regex in V_REGEX_STR
+ )
+ FIXVERSION_REGEX = re.compile ( '[_\-]' )
+ TRY_ALL_REGEXES = False
+
STATUS_UNDONE = 1
STATUS_RESOLVED = 2
STATUS_UNRESOLVABLE = 4
- def __init__ ( self, dep_str ):
+ def __init__ ( self, dep_str, deptype_mask ):
"""Initializes a dependency environment that represents the dependency
resolution of one entry in the description data of an R package.
+ Precalculating most (if not all) data since this object will be passed
+ through many dep rules.
arguments:
* dep_str -- dependency string at it appears in the description data.
"""
- self.ident = id ( self )
- self.dep_str = dep_str
- self.dep_str_low = dep_str.lower()
- self.status = DepEnv.STATUS_UNDONE
- self.resolved_by = None
+ self.deptype_mask = deptype_mask
+ self.dep_str = dep_str
+ self.dep_str_low = dep_str.lower()
+ self.status = DepEnv.STATUS_UNDONE
+ self.resolved_by = None
- self.try_all_regexes = TRY_ALL_REGEXES
+ self.try_all_regexes = self.__class__.TRY_ALL_REGEXES
self._depsplit()
@@ -66,14 +69,14 @@ class DepEnv ( object ):
def _depsplit ( self ):
result = list()
- for r in VERSION_REGEX:
+ for r in self.__class__.VERSION_REGEX:
m = r.match ( self.dep_str_low )
if m is not None:
result.append ( dict (
name = m.group ( 'name' ),
version_modifier = m.group ( 'vmod' ),
- version = FIXVERSION_REGEX.sub (
+ version = self.__class__.FIXVERSION_REGEX.sub (
'.', m.group ( 'ver' )
)
) )
@@ -143,3 +146,7 @@ class DepEnv ( object ):
return ( self.dep_str, self.resolved_by )
# --- end of get_result (...) ---
+
+ def get_resolved ( self ):
+ return self.resolved_by
+ # --- end of get_resolved (...) ---
diff --git a/roverlay/depres/depresolver.py b/roverlay/depres/depresolver.py
index d0383dd..bbe58b4 100644
--- a/roverlay/depres/depresolver.py
+++ b/roverlay/depres/depresolver.py
@@ -13,7 +13,8 @@ except ImportError:
from roverlay import config
-from roverlay.depres import communication, events
+from roverlay.depres import communication, deptype, events
+#from roverlay.depres import simpledeprule
#from roverlay.depres.depenv import DepEnv (implicit)
@@ -31,7 +32,7 @@ class DependencyResolver ( object ):
NUMTHREADS = config.get ( "DEPRES.jobcount", 0 )
- def __init__ ( self ):
+ def __init__ ( self, err_queue ):
"""Initializes a DependencyResolver."""
self.logger = logging.getLogger ( self.__class__.__name__ )
@@ -51,7 +52,7 @@ class DependencyResolver ( object ):
# the dep res worker threads
self._threads = None
- self.err_queue = None
+ self.err_queue = err_queue
# the list of registered listener modules
@@ -80,7 +81,6 @@ class DependencyResolver ( object ):
# list of rule pools that have been created from reading files
self.static_rule_pools = list ()
-
if SAFE_CHANNEL_IDS:
# this lock is used in register_channel
self._chanlock = threading.Lock()
@@ -88,10 +88,6 @@ class DependencyResolver ( object ):
self.all_channel_ids = set()
# --- end of __init__ (...) ---
- def set_exception_queue ( self, equeue ):
- self.err_queue = equeue
- # --- end of set_exception_queue (...) ---
-
def _sort ( self ):
"""Sorts the rule pools of this resolver."""
for pool in self.static_rule_pools: pool.sort()
@@ -104,6 +100,18 @@ class DependencyResolver ( object ):
self._dep_unresolvable.clear()
# --- end of _reset_unresolvable (...) ---
+ def _new_rulepools_added ( self ):
+ """Called after adding new rool pools."""
+ self._reset_unresolvable()
+ self._sort()
+ # --- end of _new_rulepools_added (...) ---
+
+ #def get_reader ( self ):
+ # return simpledeprule.reader.SimpleDependencyRuleReader (
+ # pool_add=self.static_rule_pools.append,
+ # when_done=self._new_rulepools_added
+ # )
+
def add_rulepool ( self, rulepool, pool_type=None ):
"""Adds a (static) rule pool to this resolver.
Calls self.sort() afterwards.
@@ -113,14 +121,12 @@ class DependencyResolver ( object ):
* pool_type -- ignored.
"""
self.static_rule_pools.append ( rulepool )
- self._reset_unresolvable()
- self._sort()
+ self._new_rulepools_added()
# --- end of add_rulepool (...) ---
def _report_event ( self, event, dep_env=None, pkg_env=None, msg=None ):
"""Reports an event to the log and listeners.
-
arguments:
* event -- name of the event (RESOLVED etc., use capslock!)
* dep_env -- dependency env
@@ -308,23 +314,6 @@ class DependencyResolver ( object ):
# self.runlock is released when _thread_run_main is done
# --- end of start (...) ---
- def unblock_channels ( self ):
- # unblock all channels by processing all remaining deps as
- # unresolved
- ## other option: select channel, but this may interfere with
- ## channel_closed()
- channel_gone = set()
- while not self._depqueue.empty():
- chan, dep_env = self._depqueue.get_nowait()
- dep_env.set_unresolvable()
-
- if chan not in channel_gone:
- try:
- self._depqueue_done [chan].put_nowait ( dep_env )
- except KeyError:
- channel_gone.add ( chan )
- # --- end of unblock_channels (...) ---
-
def _thread_run_main ( self ):
"""Tells the resolver to run."""
jobcount = self.__class__.NUMTHREADS
@@ -362,7 +351,7 @@ class DependencyResolver ( object ):
# iterate over _depqueue_failed and report unresolved
## todo can thread this
- while not self._depqueue_failed.empty():
+ while not self._depqueue_failed.empty() and self.err_queue.empty:
try:
channel_id, dep_env = self._depqueue_failed.get_nowait()
@@ -383,14 +372,12 @@ class DependencyResolver ( object ):
# channel has been closed before calling put, ignore this
pass
- if self.err_queue and not self.err_queue.empty():
- self.unblock_channels()
+ if not self.err_queue.really_empty():
+ self.err_queue.unblock_queues()
except ( Exception, KeyboardInterrupt ) as e:
- self.unblock_channels()
-
- if jobcount > 0 and self.err_queue:
- self.err_queue.put_nowait ( id ( self ), e )
+ if jobcount > 0:
+ self.err_queue.push ( id ( self ), e )
return
else:
raise e
@@ -407,9 +394,7 @@ class DependencyResolver ( object ):
returns: None (implicit)
"""
try:
- while ( not self.err_queue or self.err_queue.empty() ) \
- and not self._depqueue.empty() \
- :
+ while self.err_queue.empty and not self._depqueue.empty():
try:
to_resolve = self._depqueue.get_nowait()
except queue.Empty:
@@ -446,14 +431,34 @@ class DependencyResolver ( object ):
is_resolved = 1
if is_resolved == 0:
- # search for a match in the rule pools
- for rulepool in self.static_rule_pools:
+ # search for a match in the rule pools that accept
+ # the dep type
+ for rulepool in ( p for p in self.static_rule_pools \
+ if p.deptype_mask & dep_env.deptype_mask
+ ):
result = rulepool.matches ( dep_env )
if not result is None and result [0] > 0:
resolved = result [1]
is_resolved = 2
break
+ if is_resolved == 0 and \
+ dep_env.deptype_mask & deptype.try_other \
+ :
+ # search for a match in the rule pools that (normally)
+ # don't accept the dep type
+ for rulepool in ( p for p in self.static_rule_pools \
+ if p.deptype_mask & ~dep_env.deptype_mask
+ ):
+ result = rulepool.matches ( dep_env )
+ if not result is None and result [0] > 0:
+ resolved = result [1]
+ is_resolved = 2
+ break
+
+
+
+
if is_resolved == 2:
dep_env.set_resolved ( resolved, append=False )
@@ -485,8 +490,8 @@ class DependencyResolver ( object ):
# --- end while
except ( Exception, KeyboardInterrupt ) as e:
- if self.__class__.NUMTHREADS > 0 and self.err_queue:
- self.err_queue.put_nowait ( id ( self ), e )
+ if self.__class__.NUMTHREADS > 0:
+ self.err_queue.push ( id ( self ), e )
return
else:
raise e
diff --git a/roverlay/depres/deprule.py b/roverlay/depres/deprule.py
index 27e0f47..32c73e1 100644
--- a/roverlay/depres/deprule.py
+++ b/roverlay/depres/deprule.py
@@ -2,6 +2,8 @@
# Copyright 2006-2012 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
+from roverlay.depres import deptype
+
class DependencyRule ( object ):
"""Prototype of a dependency rule. Not meant for instantiation."""
@@ -26,7 +28,7 @@ class DependencyRule ( object ):
class DependencyRulePool ( object ):
- def __init__ ( self, name, priority ):
+ def __init__ ( self, name, priority, deptype_mask ):
"""Initializes an DependencyRulePool, which basically is a set of
dependency rules with methods like "search for x in all rules."
@@ -34,13 +36,16 @@ class DependencyRulePool ( object ):
* name -- name of this rule pool
* priority -- priority of this pool (lower is better)
"""
- self.rules = list()
- self.name = name
- self.priority = priority
+ self.rules = list()
+ self._rule_add = self.rules.append
+ self.name = name
+ self.priority = priority
+ # filter out deptype flags like "mandatory"
+ self.deptype_mask = deptype_mask & deptype.RESOLVE_ALL
# the "rule weight" is the sum of the rules' priorities
# it's used to compare/sort dependency pools with
# the same priority (lesser weight is better)
- self.rule_weight = 0
+ self.rule_weight = 0
# --- end of __init__ (...) ---
def sort ( self ):
@@ -64,7 +69,7 @@ class DependencyRulePool ( object ):
* rule --
"""
if issubclass ( rule, DependencyRule ):
- self.rules.append ( rule )
+ self._rule_add ( rule )
else:
raise Exception ( "bad usage (dependency rule expected)." )
diff --git a/roverlay/depres/deptype.py b/roverlay/depres/deptype.py
new file mode 100644
index 0000000..73d6265
--- /dev/null
+++ b/roverlay/depres/deptype.py
@@ -0,0 +1,19 @@
+# not using a single bool for these two types
+# * some dependencies could be resolved as sys and internal ("just resolve it")
+# * allows to add other types
+
+# <deptype> ::= 2**k | k in {0,1,2,...}
+# try_call indicates that the dep can be checked world-wide (in non-accepting
+# rule pools) after unsuccessful resolution
+try_other = 1
+mandatory = 2
+external = 4
+internal = 8
+
+ALL = external | internal | mandatory
+RESOLVE_ALL = external | internal
+
+SYS = internal | mandatory
+PKG = external | mandatory
+
+MANDATORY_TRY = try_other | mandatory
diff --git a/roverlay/depres/simpledeprule/console.py b/roverlay/depres/simpledeprule/console.py
index e3db541..5d84010 100644
--- a/roverlay/depres/simpledeprule/console.py
+++ b/roverlay/depres/simpledeprule/console.py
@@ -5,6 +5,8 @@ import shlex
import logging
from roverlay import config
+from roverlay.errorqueue import NopErrorQueue
+from roverlay.depres import deptype
from roverlay.depres.depresolver import DependencyResolver
from roverlay.depres.channels import EbuildJobChannel
from roverlay.depres.simpledeprule.pool import SimpleDependencyRulePool
@@ -63,7 +65,9 @@ class DepResConsole ( object ):
whitespace = re.compile ( "\s+" )
def __init__ ( self ):
- self.resolver = DependencyResolver()
+ self.err_queue = NopErrorQueue()
+
+ self.resolver = DependencyResolver ( err_queue=self.err_queue )
# log everything
self.resolver.set_logmask ( -1 )
# disable passing events to listeners
@@ -122,7 +126,10 @@ class DepResConsole ( object ):
def _getpool ( self, new=False ):
if new or not self.poolstack:
- pool = SimpleDependencyRulePool ( "pool" + str ( self.pool_id ) )
+ pool = SimpleDependencyRulePool (
+ "pool" + str ( self.pool_id ),
+ deptype_mask=deptype.RESOLVE_ALL
+ )
self.pool_id += 1
self.poolstack.append ( pool )
self.resolver._reset_unresolvable()
@@ -360,7 +367,9 @@ class DepResConsole ( object ):
if not dep_list:
self.stdout ( "Resolve what?\n" )
else:
- channel = EbuildJobChannel ( err_queue=None, name="channel" )
+ channel = EbuildJobChannel (
+ err_queue=self.err_queue, name="channel"
+ )
self.resolver.register_channel ( channel )
self.stdout ( "Trying to resolve {!r}.\n".format ( dep_list ) )
diff --git a/roverlay/depres/simpledeprule/pool.py b/roverlay/depres/simpledeprule/pool.py
index 8a2b7b2..4d7905b 100644
--- a/roverlay/depres/simpledeprule/pool.py
+++ b/roverlay/depres/simpledeprule/pool.py
@@ -2,20 +2,15 @@
# Copyright 2006-2012 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
-#import re
import logging
-#from roverlay import config
from roverlay.depres import deprule
from roverlay.depres.simpledeprule.reader import SimpleDependencyRuleReader
-#from roverlay.depres.simpledeprule.rules import *
from roverlay.depres.simpledeprule.abstractrules import SimpleRule
-TMP_LOGGER = logging.getLogger ('simpledeps')
-
class SimpleDependencyRulePool ( deprule.DependencyRulePool ):
- def __init__ ( self, name, priority=70, filepath=None ):
+ def __init__ ( self, name, priority=70, **kw ):
"""Initializes a SimpleDependencyRulePool, which is a DependencyRulePool
specialized in simple dependency rules;
it offers loading rules from files.
@@ -23,13 +18,10 @@ class SimpleDependencyRulePool ( deprule.DependencyRulePool ):
arguments:
* name -- string identifier for this pool
* priority -- of this pool
- * filepath -- if set and not None: load a rule file directly
"""
- super ( SimpleDependencyRulePool, self ) . __init__ ( name, priority )
-
- if not filepath is None:
- self.load_rule_file ( filepath )
-
+ super ( SimpleDependencyRulePool, self ) . __init__ (
+ name, priority, **kw
+ )
# --- end of __init__ (...) ---
def add ( self, rule ):
@@ -39,15 +31,17 @@ class SimpleDependencyRulePool ( deprule.DependencyRulePool ):
arguments:
* rule --
"""
+ # trust in proper usage
if isinstance ( rule, SimpleRule ):
- self.rules.append ( rule )
+ self._rule_add ( rule )
else:
raise Exception ( "bad usage (simple dependency rule expected)." )
-
# --- end of add (...) ---
def get_reader ( self ):
- return SimpleDependencyRuleReader ( rule_add=self.add )
+ # trusting in SimpleDependencyRuleReader's correctness,
+ # let it add rules directly without is-instance checks
+ return SimpleDependencyRuleReader ( rule_add=self._rule_add )
# --- end of get_reader (...) ---
def load_rule_file ( self, filepath ):
diff --git a/roverlay/depres/simpledeprule/rulemaker.py b/roverlay/depres/simpledeprule/rulemaker.py
index 2435787..e1ebd6d 100644
--- a/roverlay/depres/simpledeprule/rulemaker.py
+++ b/roverlay/depres/simpledeprule/rulemaker.py
@@ -7,11 +7,14 @@ import logging
from roverlay import config
+#from roverlay.depres import deptype
from roverlay.depres.simpledeprule import rules
from roverlay.depres.simpledeprule.abstractrules import *
class SimpleRuleMaker ( object ):
+# class RuleTypes ( object ): pass
+
class RuleKeywords ( object ):
def __init__ ( self ):
self._default_rule, self._rule_map = rules.get_rule_map()
@@ -49,6 +52,7 @@ class SimpleRuleMaker ( object ):
self._kw = self.__class__.RuleKeywords()
self._next = None
self._rules = list()
+ #self._rules = list() :: ( deptype, rule )
# --- end of __init__ (...) ---
def zap ( self ):
diff --git a/roverlay/ebuild/depres.py b/roverlay/ebuild/depres.py
index 1c19a00..5a85bb4 100644
--- a/roverlay/ebuild/depres.py
+++ b/roverlay/ebuild/depres.py
@@ -2,15 +2,38 @@
# Copyright 2006-2012 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
+from roverlay.depres import deptype
from roverlay.ebuild import evars
# TODO/FIXME/IGNORE move this to const / config
-FIELDS = {
- 'R_SUGGESTS' : [ 'Suggests' ],
- 'DEPENDS' : [ 'Depends', 'Imports' ],
- 'RDEPENDS' : [ 'LinkingTo', 'SystemRequirements' ]
+FIELDS_TO_EVAR = {
+ 'R_SUGGESTS' : ( 'Suggests', ),
+ 'DEPENDS' : ( 'Depends', 'Imports' ),
+ 'RDEPENDS' : ( 'LinkingTo', 'SystemRequirements' ),
+ # ? : ( 'Enhances', )
}
+# setting per-field dep types here, in accordance with
+# http://cran.r-project.org/doc/manuals/R-exts.html#The-DESCRIPTION-file
+FIELDS = {
+ # "The Depends field gives a comma-separated
+ # list of >>package names<< which this package depends on."
+ 'Depends' : deptype.SYS,
+ # "Other dependencies (>>external to the R system<<)
+ # should be listed in the SystemRequirements field"
+ 'SystemRequirements' : deptype.PKG,
+ # "The Imports field lists >>packages<< whose namespaces
+ # are imported from (as specified in the NAMESPACE file)
+ # but which do not need to be attached."
+ 'Imports' : deptype.PKG,
+ # "The Suggests field uses the same syntax as Depends
+ # and lists >>packages<< that are >>not necessarily needed<<."
+ 'Suggests' : deptype.external,
+ # "A package that wishes to make use of header files
+ # in other >>packages<< needs to declare them as
+ # a comma-separated list in the field LinkingTo in the DESCRIPTION file."
+ 'LinkingTo' : deptype.PKG,
+}
EBUILDVARS = {
'R_SUGGESTS' : evars.R_SUGGESTS,
'DEPENDS' : evars.DEPEND,
@@ -54,10 +77,10 @@ class EbuildDepRes ( object ):
# --- end of __init__ (...) ---
- def done ( self ) : return self.status < 1
- def busy ( self ) : return self.status > 0
+ #def done ( self ) : return self.status < 1
+ #def busy ( self ) : return self.status > 0
def success ( self ) : return self.status == 0
- def fail ( self ) : return self.status < 0
+ #def fail ( self ) : return self.status < 0
def get_result ( self ):
"""Returns the result of dependency resolution,
@@ -93,7 +116,7 @@ class EbuildDepRes ( object ):
self._channels [dependency_type] = self.request_resolver (
name=dependency_type,
logger=self.logger,
- err_queue=self.err_queue
+ err_queue=self.err_queue,
)
return self._channels [dependency_type]
# --- end of get_channel (...) ---
@@ -115,21 +138,18 @@ class EbuildDepRes ( object ):
dep_type = desc_field = None
- for dep_type in FIELDS:
+ for dep_type in FIELDS_TO_EVAR:
resolver = None
- for desc_field in FIELDS [dep_type]:
+ for desc_field in FIELDS_TO_EVAR [dep_type]:
if desc_field in desc:
if not resolver:
resolver = self._get_channel ( dep_type )
- if isinstance ( desc [desc_field], str ):
- resolver.add_dependency ( desc [desc_field] )
- elif hasattr ( desc [desc_field], '__iter__' ):
- resolver.add_dependencies ( desc [desc_field] )
- else:
- logger.warning (
- "Cannot add dependency '%s'." % desc [desc_field]
+ # make sure that DescriptionReader reads all dep fields as list
+ resolver.add_dependencies (
+ dep_list = desc [desc_field],
+ deptype_mask = FIELDS [desc_field]
)
# -- for dep_type
diff --git a/roverlay/errorqueue.py b/roverlay/errorqueue.py
new file mode 100644
index 0000000..d0aa042
--- /dev/null
+++ b/roverlay/errorqueue.py
@@ -0,0 +1,115 @@
+import sys
+import threading
+
+class _EQueue ( object ) : pass
+
+class NopErrorQueue ( _EQueue ):
+ """This can be used as error queue in single-threaded execution."""
+ def __init__ ( self ):
+ self.empty = True
+
+ def push ( self, context, error ):
+ self.empty = False
+ sys.stderr.write ( "Exception from {!r}:\n".format ( context ) )
+ raise error
+
+ def really_empty ( self ): return self.empty
+ def attach_queue ( self, q, unblock_item ): pass
+ def remove_queue ( self, q ): pass
+ def unblock_queues ( self ): pass
+ def peek ( self ): return ( None, None )
+ def get_all ( self ): return ( ( None, None ), )
+ def get_exceptions ( self ): return ()
+
+
+#class ErrorQueue ( NopErrorQueue ):
+class ErrorQueue ( _EQueue ):
+ """This is the error queue for threaded execution."""
+ # (it's not a queue)
+
+ def __init__ ( self ):
+ self.empty = True
+ self._exceptions = list()
+ # this error queue is able to unblock waiting queues (in future; TODO)
+ # id -> queue [, unblocking_item:=None]
+ self._queues_to_unblock = dict()
+
+ self._lock = threading.Lock()
+
+ def really_empty ( self ):
+ """Returns true if no exception stored. Uses a lock to ensure
+ correctness of the result.
+ """
+ with self._lock:
+ self.empty = len ( self._exceptions ) == 0
+ return self.empty
+
+ def _unblock_queues ( self ):
+ """Sends an unblock item to all attached queues."""
+ for q, v in self._queues_to_unblock.values():
+ try:
+ q.put_nowait ( v )
+ except:
+ pass
+
+ def push ( self, context, error ):
+ """Pushes an exception. This also triggers on-error mode, which
+ unblock all attached queues.
+
+ arguments:
+ * context -- the origin of the exception
+ * error -- the exception
+ """
+ with self._lock:
+ self._exceptions.append ( ( context, error ) )
+ self.empty = False
+ self._unblock_queues()
+
+ def unblock_queues ( self ):
+ """Unblocks all attached queues."""
+ with self._lock:
+ self._unblock_queues()
+
+ def attach_queue ( self, q, unblock_item ):
+ """Attaches a queue. Nothing will be done with it, unless an exception
+ is pushed to this ErrorQueue, in which case all attached queues will
+ be unblocked which allows queue-waiting threads to end.
+
+ arguments:
+ * q -- queue
+ * unblock_item -- item that is used for unblocking, e.g. 'None'
+ """
+ with self._lock:
+ self._queues_to_unblock [id (q)] = ( q, unblock_item )
+
+ def remove_queue ( self, q ):
+ """Removes a queue. It will no longer receive an unblock item if
+ on error mode.
+
+ arguments:
+ * q -- queue to remove
+ """
+ self._lock.acquire()
+ try:
+ del self._queues_to_unblock [id (q)]
+ except KeyError:
+ pass
+ finally:
+ self._lock.release()
+
+ def peek ( self ):
+ """Returns the latest pushed exception."""
+ return self._exceptions [-1]
+
+ def get_all ( self ):
+ """Returns all pushed exceptions."""
+ # not copying, caller shouldn't modify the exception list
+ return self._exceptions
+
+ def get_exceptions ( self ):
+ """Similar to get_all, but a generator that filters out no-op exception
+ pushes that are used to trigger on-error mode without a valid exception.
+ """
+ for e in self._exceptions:
+ if isinstance ( e [1], ( Exception, KeyboardInterrupt ) ):
+ yield e
diff --git a/roverlay/overlay/creator.py b/roverlay/overlay/creator.py
index bb2391b..78be3c3 100644
--- a/roverlay/overlay/creator.py
+++ b/roverlay/overlay/creator.py
@@ -18,7 +18,7 @@ except ImportError:
import Queue as queue
-from roverlay import config
+from roverlay import config, errorqueue
from roverlay.overlay import Overlay
from roverlay.overlay.worker import OverlayWorker
from roverlay.packageinfo import PackageInfo
@@ -83,35 +83,29 @@ class OverlayCreator ( object ):
else:
self.logger = logger.getChild ( 'OverlayCreator' )
+ # this queue is used to propagate exceptions from threads
+ self._err_queue = errorqueue.ErrorQueue()
+
# init overlay using config values
self.overlay = Overlay ( logger=self.logger )
- self.depresolver = easyresolver.setup()
+ self.depresolver = easyresolver.setup ( self._err_queue )
self.NUMTHREADS = config.get ( 'EBUILD.jobcount', 0 )
- # --
self._pkg_queue = queue.Queue()
-
- # this queue is used to propagate exceptions from threads
- # it's
- self._err_queue = queue.Queue()
+ self._err_queue.attach_queue ( self._pkg_queue, None )
#self._time_start_run = list()
#self._time_stop_run = list()
-
self._workers = None
self._runlock = threading.RLock()
self.can_write_overlay = OVERLAY_WRITE_ALLOWED
-
- self.depresolver.set_exception_queue ( self._err_queue )
-
self.closed = False
-
# queued packages counter,
# package_added != (create_success + create_fail) if a thread hangs
# or did not call _pkg_done
@@ -137,9 +131,6 @@ class OverlayCreator ( object ):
processed = pkg_created + pkg_failed
failed = pkg_failed + ov_failed
-
- # namedtuple? TODO
-
return (
pkg_added, pkg_created, pkg_failed,
ov_added, ov_failed,
@@ -272,7 +263,7 @@ class OverlayCreator ( object ):
self.join()
self._make_workers()
except:
- self._err_queue.put_nowait ( ( -1, None ) )
+ self._err_queue.push ( context=-1, error=None )
raise
finally:
self._runlock.release()
@@ -324,7 +315,7 @@ class OverlayCreator ( object ):
if self.NUMTHREADS > 0: start = time.time()
if do_close:
- self._err_queue.put_nowait ( ( -1, None ) )
+ self._err_queue.push ( context=-1, error=None )
# fixme: remove enabled?
for w in self._workers: w.enabled = False
else:
@@ -336,14 +327,9 @@ class OverlayCreator ( object ):
del self._workers
self._workers = None
- while not self._err_queue.empty():
- e = self._err_queue.get_nowait()
- self._err_queue.put_nowait ( ( -2, None ) )
- if isinstance ( e [1], ( Exception, KeyboardInterrupt ) ):
- self._err_queue.put ( e )
- self.logger.warning ( "Reraising thread exception." )
- raise e [1]
-
+ for e in self._err_queue.get_exceptions():
+ self.logger.warning ( "Reraising thread exception." )
+ raise e [1]
except ( Exception, KeyboardInterrupt ) as err:
@@ -355,11 +341,9 @@ class OverlayCreator ( object ):
# )
try:
- self._err_queue.put_nowait ( ( -1, None ) )
- self.depresolver.unblock_channels()
- while hasattr ( self, '_workers' ) and self._workers is not None:
-
+ self._err_queue.push ( context=-1, error=None )
+ while hasattr ( self, '_workers' ) and self._workers is not None:
if True in ( w.active() for w in self._workers ):
self._pkg_queue.put_nowait ( None )
else:
diff --git a/roverlay/overlay/worker.py b/roverlay/overlay/worker.py
index 541de7f..880835d 100644
--- a/roverlay/overlay/worker.py
+++ b/roverlay/overlay/worker.py
@@ -106,27 +106,18 @@ class OverlayWorker ( object ):
try:
self.running = True
self.halting = False
- while self.enabled or (
- self.halting and not self.pkg_queue.empty()
+ while ( self.enabled or (
+ self.halting and not self.pkg_queue.empty()
+ ) and \
+ self.err_queue.empty
):
- if not self.err_queue.empty():
- # other workers died (or exit request sent)
- debug ( "STOPPING #1" )
- break
-
debug ( "WAITING" )
p = self.pkg_queue.get()
debug ( "RECEIVED A TASK, " + str ( p ) )
- if not self.err_queue.empty():
- debug ( "STOPPING #2" )
- break
-
# drop empty requests that are used to unblock get()
- if p is not None:
+ if p is not None and self.err_queue.empty:
debug ( "ENTER PROC" )
- if self.err_queue.empty():
- debug ( "__ empty exception/error queue!" )
self._process ( p )
elif self.halting:
# receiving an empty request while halting means 'stop now',
@@ -138,7 +129,7 @@ class OverlayWorker ( object ):
debug ( "STOPPING - DONE" )
except ( Exception, KeyboardInterrupt ) as e:
self.logger.exception ( e )
- self.err_queue.put_nowait ( ( id ( self ), e ) )
+ self.err_queue.push ( id ( self ), e )
self.running = False
diff --git a/roverlay/recipe/easyresolver.py b/roverlay/recipe/easyresolver.py
index 749396b..1ed75fa 100644
--- a/roverlay/recipe/easyresolver.py
+++ b/roverlay/recipe/easyresolver.py
@@ -1,19 +1,19 @@
from roverlay import config
-from roverlay.depres import listeners
+from roverlay.depres import listeners, deptype
from roverlay.depres.depresolver import DependencyResolver
from roverlay.depres.simpledeprule import SimpleDependencyRulePool
-def setup():
- res = DependencyResolver()
+def setup ( err_queue ):
+ res = DependencyResolver ( err_queue=err_queue )
# log everything
res.set_logmask ( -1 )
srule_files = config.get ( 'DEPRES.simple_rules.files', None )
if srule_files:
- srule_pool = SimpleDependencyRulePool ( 'default pool', priority=45 )
+ srule_pool = SimpleDependencyRulePool ( 'default pool', priority=45, deptype_mask=deptype.RESOLVE_ALL, )
srule_pool.get_reader().read ( srule_files )
res.add_rulepool ( srule_pool )
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2012-07-16 16:17 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-07-16 16:15 [gentoo-commits] proj/R_overlay:master commit in: roverlay/ebuild/, roverlay/depres/simpledeprule/, roverlay/depres/, roverlay/, André Erdmann
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox