Merge pull request #29 from harlowja/optional-reqs
Allow for optional task requirements.
This commit is contained in:
@@ -68,6 +68,10 @@ def task(*args, **kwargs):
|
|||||||
requires_what = kwargs.pop('requires', [])
|
requires_what = kwargs.pop('requires', [])
|
||||||
f = requires(*requires_what, **kwargs)(f)
|
f = requires(*requires_what, **kwargs)(f)
|
||||||
|
|
||||||
|
# Attach any optional requirements this function needs for running.
|
||||||
|
optional_what = kwargs.pop('optional', [])
|
||||||
|
f = optional(*optional_what, **kwargs)(f)
|
||||||
|
|
||||||
# Attach any items this function provides as output
|
# Attach any items this function provides as output
|
||||||
provides_what = kwargs.pop('provides', [])
|
provides_what = kwargs.pop('provides', [])
|
||||||
f = provides(*provides_what, **kwargs)(f)
|
f = provides(*provides_what, **kwargs)(f)
|
||||||
@@ -111,6 +115,33 @@ def versionize(major, minor=None):
|
|||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def optional(*args, **kwargs):
|
||||||
|
"""Attaches a set of items that the decorated function would like as input
|
||||||
|
to the functions underlying dictionary."""
|
||||||
|
|
||||||
|
def decorator(f):
|
||||||
|
if not hasattr(f, 'optional'):
|
||||||
|
f.optional = set()
|
||||||
|
|
||||||
|
f.optional.update([a for a in args if _take_arg(a)])
|
||||||
|
|
||||||
|
@wraps(f)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
# This is needed to handle when the decorator has args or the decorator
|
||||||
|
# doesn't have args, python is rather weird here...
|
||||||
|
if kwargs or not args:
|
||||||
|
return decorator
|
||||||
|
else:
|
||||||
|
if isinstance(args[0], collections.Callable):
|
||||||
|
return decorator(args[0])
|
||||||
|
else:
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def requires(*args, **kwargs):
|
def requires(*args, **kwargs):
|
||||||
"""Attaches a set of items that the decorated function requires as input
|
"""Attaches a set of items that the decorated function requires as input
|
||||||
to the functions underlying dictionary."""
|
to the functions underlying dictionary."""
|
||||||
|
|||||||
@@ -50,17 +50,31 @@ class Flow(ordered_flow.Flow):
|
|||||||
self._connected = False
|
self._connected = False
|
||||||
|
|
||||||
def _fetch_task_inputs(self, task):
|
def _fetch_task_inputs(self, task):
|
||||||
inputs = collections.defaultdict(list)
|
|
||||||
|
|
||||||
for n in getattr(task, 'requires', []):
|
def extract_inputs(place_where, would_like, is_optional=False):
|
||||||
|
for n in would_like:
|
||||||
for (them, there_result) in self.results:
|
for (them, there_result) in self.results:
|
||||||
if (not self._graph.has_edge(them, task) or
|
if not n in set(getattr(them, 'provides', [])):
|
||||||
not n in getattr(them, 'provides', [])):
|
continue
|
||||||
|
if (not is_optional and
|
||||||
|
not self._graph.has_edge(them, task)):
|
||||||
continue
|
continue
|
||||||
if there_result and n in there_result:
|
if there_result and n in there_result:
|
||||||
inputs[n].append(there_result[n])
|
place_where[n].append(there_result[n])
|
||||||
else:
|
if is_optional:
|
||||||
inputs[n].append(None)
|
# Take the first task that provides this optional
|
||||||
|
# item.
|
||||||
|
break
|
||||||
|
elif not is_optional:
|
||||||
|
place_where[n].append(None)
|
||||||
|
|
||||||
|
required_inputs = set(getattr(task, 'requires', []))
|
||||||
|
optional_inputs = set(getattr(task, 'optional', []))
|
||||||
|
optional_inputs = optional_inputs - required_inputs
|
||||||
|
|
||||||
|
task_inputs = collections.defaultdict(list)
|
||||||
|
extract_inputs(task_inputs, required_inputs)
|
||||||
|
extract_inputs(task_inputs, optional_inputs, is_optional=True)
|
||||||
|
|
||||||
def collapse_functor(k_v):
|
def collapse_functor(k_v):
|
||||||
(k, v) = k_v
|
(k, v) = k_v
|
||||||
@@ -68,7 +82,7 @@ class Flow(ordered_flow.Flow):
|
|||||||
v = v[0]
|
v = v[0]
|
||||||
return (k, v)
|
return (k, v)
|
||||||
|
|
||||||
return dict(map(collapse_functor, inputs.iteritems()))
|
return dict(map(collapse_functor, task_inputs.iteritems()))
|
||||||
|
|
||||||
def order(self):
|
def order(self):
|
||||||
self.connect()
|
self.connect()
|
||||||
|
|||||||
@@ -30,16 +30,19 @@ class Flow(ordered_flow.Flow):
|
|||||||
self._tasks = []
|
self._tasks = []
|
||||||
|
|
||||||
def _fetch_task_inputs(self, task):
|
def _fetch_task_inputs(self, task):
|
||||||
|
would_like = set(getattr(task, 'requires', []))
|
||||||
|
would_like.update(getattr(task, 'optional', []))
|
||||||
|
|
||||||
inputs = {}
|
inputs = {}
|
||||||
for r in getattr(task, 'requires', []):
|
for n in would_like:
|
||||||
# Find the last task that provided this.
|
# Find the last task that provided this.
|
||||||
for (last_task, last_results) in reversed(self.results):
|
for (last_task, last_results) in reversed(self.results):
|
||||||
if r not in getattr(last_task, 'provides', []):
|
if n not in getattr(last_task, 'provides', []):
|
||||||
continue
|
continue
|
||||||
if last_results and r in last_results:
|
if last_results and n in last_results:
|
||||||
inputs[r] = last_results[r]
|
inputs[n] = last_results[n]
|
||||||
else:
|
else:
|
||||||
inputs[r] = None
|
inputs[n] = None
|
||||||
# Some task said they had it, get the next requirement.
|
# Some task said they had it, get the next requirement.
|
||||||
break
|
break
|
||||||
return inputs
|
return inputs
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ class Task(object):
|
|||||||
# An *immutable* input 'resource' name set this task depends
|
# An *immutable* input 'resource' name set this task depends
|
||||||
# on existing before this task can be applied.
|
# on existing before this task can be applied.
|
||||||
self.requires = set()
|
self.requires = set()
|
||||||
|
# An *immutable* input 'resource' name set this task would like to
|
||||||
|
# depends on existing before this task can be applied (but does not
|
||||||
|
# strongly depend on existing).
|
||||||
|
self.optional = set()
|
||||||
# An *immutable* output 'resource' name set this task
|
# An *immutable* output 'resource' name set this task
|
||||||
# produces that other tasks may depend on this task providing.
|
# produces that other tasks may depend on this task providing.
|
||||||
self.provides = set()
|
self.provides = set()
|
||||||
|
|||||||
Reference in New Issue
Block a user