You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ariatosca.apache.org by mx...@apache.org on 2016/10/31 09:19:42 UTC
incubator-ariatosca git commit: CFY-4 reworked task_graph API and
added some test [Forced Update!]
Repository: incubator-ariatosca
Updated Branches:
refs/heads/ARIA-3-api-for-creating-workflows 94bbcf6f8 -> d31aa9c6e (forced update)
CFY-4 reworked task_graph API and added some test
Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/d31aa9c6
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/d31aa9c6
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/d31aa9c6
Branch: refs/heads/ARIA-3-api-for-creating-workflows
Commit: d31aa9c6ec287ed620f814832e884b13148d0c56
Parents: 936e2f9
Author: mxmrlv <mx...@gmail.com>
Authored: Sun Oct 30 17:50:37 2016 +0200
Committer: mxmrlv <mx...@gmail.com>
Committed: Mon Oct 31 11:19:27 2016 +0200
----------------------------------------------------------------------
aria/workflows/api/task_graph.py | 184 ++++++++-------
aria/workflows/builtin/execute_operation.py | 2 +-
aria/workflows/builtin/heal.py | 8 +-
aria/workflows/builtin/install.py | 7 +-
aria/workflows/builtin/uninstall.py | 2 +-
aria/workflows/builtin/workflows.py | 4 +-
tests/workflows/api/test_task_graph.py | 279 ++++++++++++++---------
7 files changed, 278 insertions(+), 208 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/d31aa9c6/aria/workflows/api/task_graph.py
----------------------------------------------------------------------
diff --git a/aria/workflows/api/task_graph.py b/aria/workflows/api/task_graph.py
index 2d8ee54..2cb92cb 100644
--- a/aria/workflows/api/task_graph.py
+++ b/aria/workflows/api/task_graph.py
@@ -18,6 +18,7 @@ Task graph. Used by users to build workflows
"""
from uuid import uuid4
+from collections import Iterable
from networkx import DiGraph, topological_sort
@@ -29,6 +30,16 @@ class TaskNotInGraphError(Exception):
pass
+def _filter_out_empty_tasks(func=None):
+ if func is None:
+ return lambda f: _filter_out_empty_tasks(func=f)
+
+ def _wrapper(task, *tasks, **kwargs):
+ tasks = filter(lambda t: bool(t), [task] + list(tasks))
+ return func(*tasks, **kwargs)
+ return _wrapper
+
+
class TaskGraph(object):
"""
A tasks graph builder.
@@ -79,7 +90,7 @@ class TaskGraph(object):
:yields: Iterator over all tasks which dependency_task depends on
:raise: TaskNotInGraphError if dependent_task is not in the graph
"""
- if not self.has_task(dependent_task):
+ if not self.has_tasks(dependent_task):
raise TaskNotInGraphError('Task id: {0}'.format(dependent_task.id))
for _, dependency_id in self._graph.out_edges_iter(dependent_task.id):
yield self.get_task(dependency_id)
@@ -91,7 +102,7 @@ class TaskGraph(object):
:yields: Iterator over all tasks which depend on dependency_task
:raise: TaskNotInGraphError if dependency_task is not in the graph
"""
- if not self.has_task(dependency_task):
+ if not self.has_tasks(dependency_task):
raise TaskNotInGraphError('Task id: {0}'.format(dependency_task.id))
for dependent_id, _ in self._graph.in_edges_iter(dependency_task.id):
yield self.get_task(dependent_id)
@@ -111,38 +122,61 @@ class TaskGraph(object):
data = self._graph.node[task_id]
return data['task']
- def add_task(self, task):
+ @_filter_out_empty_tasks
+ def add_tasks(self, *tasks):
"""
Add a task to the graph
:param BaseTask task: The task
- :return: False if task was already in the graph, otherwise True
- :rtype: bool
+ :return: A list of added tasks
+ :rtype: list
"""
- if self.has_task(task):
- return False
- self._graph.add_node(task.id, task=task)
- return True
+ return_tasks = []
+
+ for task in tasks:
+ if isinstance(task, Iterable):
+ return_tasks += self.add_tasks(*task)
+ elif not self.has_tasks(task):
+ self._graph.add_node(task.id, task=task)
+ return_tasks.append(task)
+
+ return return_tasks
- def remove_task(self, task):
+ @_filter_out_empty_tasks
+ def remove_tasks(self, *tasks):
"""
Remove the provided task from the graph
:param BaseTask task: The task
- :return: False if task wasn't in the graph, otherwise True
- :rtype: bool
+ :return: A list of removed tasks
+ :rtype: list
"""
- if not self.has_task(task):
- return False
- self._graph.remove_node(task.id)
- return True
+ return_tasks = []
+
+ if len(tasks) > 1:
+ for sub_task in tasks:
+ return_tasks += self.remove_tasks(*sub_task)
+ elif self.has_tasks(tasks[0]):
+ self._graph.remove_node(tasks[0].id)
+ return_tasks.append(tasks[0])
+
+ return return_tasks
- def has_task(self, task):
+ @_filter_out_empty_tasks
+ def has_tasks(self, *tasks):
"""
Check whether a task is in the graph or not
:param BaseTask task: The task
- :return: True if task is in the graph, otherwise True
- :rtype: bool
+ :return: True if all tasks are in the graph, otherwise True
+ :rtype: list
"""
- return self._graph.has_node(task.id)
+ return_value = True
+
+ for task in tasks:
+ if isinstance(task, Iterable):
+ return_value &= self.has_tasks(*task)
+ else:
+ return_value &= self._graph.has_node(task.id)
+
+ return return_value
def add_dependency(self, dependent, dependency):
"""
@@ -157,14 +191,22 @@ class TaskGraph(object):
:raise TaskNotInGraphError if either the dependent or dependency are tasks which
are not in the graph
"""
+ if not (self.has_tasks(dependent) and self.has_tasks(dependency)):
+ raise TaskNotInGraphError()
+
if self.has_dependency(dependent, dependency):
return False
- dependent_tasks, dependency_tasks = \
- self._extract_dependent_and_dependency_tasks(dependent, dependency)
- for dependent_task in dependent_tasks:
- for dependency_task in dependency_tasks:
- self._graph.add_edge(dependent_task.id, dependency_task.id)
+ if isinstance(dependent, Iterable):
+ for dependent_task in dependent:
+ self.add_dependency(dependent_task, dependency)
+ else:
+ if isinstance(dependency, Iterable):
+ for dependency_task in dependency:
+ self.add_dependency(dependent, dependency_task)
+ else:
+ self._graph.add_edge(dependent.id, dependency.id)
+
return True
def has_dependency(self, dependent, dependency):
@@ -182,13 +224,22 @@ class TaskGraph(object):
:raise TaskNotInGraphError if either the dependent or dependency are tasks
which are not in the graph
"""
- dependent_tasks, dependency_tasks = \
- self._extract_dependent_and_dependency_tasks(dependent, dependency)
- for dependent_task in dependent_tasks:
- for dependency_task in dependency_tasks:
- if not self._graph.has_edge(dependent_task.id, dependency_task.id):
- return False
- return True
+ if not (self.has_tasks(dependent) and self.has_tasks(dependency)):
+ raise TaskNotInGraphError()
+
+ return_value = True
+
+ if isinstance(dependent, Iterable):
+ for dependent_task in dependent:
+ return_value &= self.has_dependency(dependent_task, dependency)
+ else:
+ if isinstance(dependency, Iterable):
+ for dependency_task in dependency:
+ return_value &= self.has_dependency(dependent, dependency_task)
+ else:
+ return_value &= self._graph.has_edge(dependent.id, dependency.id)
+
+ return return_value
def remove_dependency(self, dependent, dependency):
"""
@@ -205,61 +256,30 @@ class TaskGraph(object):
:raise TaskNotInGraphError if either the dependent or dependency are tasks
which are not in the graph
"""
+ if not (self.has_tasks(dependent) and self.has_tasks(dependency)):
+ raise TaskNotInGraphError()
+
if not self.has_dependency(dependent, dependency):
return False
- dependent_tasks, dependency_tasks = \
- self._extract_dependent_and_dependency_tasks(dependent, dependency)
- for dependent_task in dependent_tasks:
- for dependency_task in dependency_tasks:
- self._graph.remove_edge(dependent_task.id, dependency_task.id)
- return True
-
- def sequence(self, *items):
- group = self.parallel(*items)
-
- for i in xrange(1, len(items)):
- self.add_dependency(items[i], items[i-1])
-
- return group
-
- def parallel(self, *items):
- for item in items:
- if not isinstance(item, _TasksGroup):
- self.add_task(item)
-
- return _TasksGroup(items)
-
- def _extract_dependent_and_dependency_tasks(self, dependent, dependency):
- if isinstance(dependent, _TasksGroup):
- dependent_tasks = dependent.tasks
+ if isinstance(dependent, Iterable):
+ for dependent_task in dependent:
+ self.remove_dependency(dependent_task, dependency)
else:
- if not self._graph.has_node(dependent.id):
- raise TaskNotInGraphError(
- 'dependent task {0!r} is not in graph (task id: {0.id})'.format(dependent))
- dependent_tasks = [dependent]
-
- if isinstance(dependency, _TasksGroup):
- dependency_tasks = dependency.tasks
- else:
- if not self._graph.has_node(dependency.id):
- raise TaskNotInGraphError(
- 'dependency task {0!r} is not in graph (task id: {0.id})'.format(dependency))
- dependency_tasks = [dependency]
-
- return dependent_tasks, dependency_tasks
+ if isinstance(dependency, Iterable):
+ for dependency_task in dependency:
+ self.remove_dependency(dependent, dependency_task)
+ else:
+ self._graph.remove_edge(dependent.id, dependency.id)
+ return True
-class _TasksGroup(object):
+ @_filter_out_empty_tasks
+ def sequence(self, *tasks):
+ if tasks:
+ self.add_tasks(*tasks)
- def __init__(self, tasks):
- self._tasks = map(lambda t: t.tasks if isinstance(t, _TasksGroup) else t, tasks)
+ for i in xrange(1, len(tasks)):
+ self.add_dependency(tasks[i], tasks[i-1])
- @property
- def tasks(self):
- """
- The tasks in this group
- :rtype list
- :return: tasks list
- """
- return self._tasks
+ return tasks
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/d31aa9c6/aria/workflows/builtin/execute_operation.py
----------------------------------------------------------------------
diff --git a/aria/workflows/builtin/execute_operation.py b/aria/workflows/builtin/execute_operation.py
index a664904..2843375 100644
--- a/aria/workflows/builtin/execute_operation.py
+++ b/aria/workflows/builtin/execute_operation.py
@@ -76,7 +76,7 @@ def execute_operation(
allow_kwargs_override=allow_kwargs_override)
for _, node_instance_sub_workflow in subgraphs.items():
- graph.add_task(node_instance_sub_workflow)
+ graph.add_tasks(node_instance_sub_workflow)
# adding tasks dependencies if required
if run_by_dependency_order:
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/d31aa9c6/aria/workflows/builtin/heal.py
----------------------------------------------------------------------
diff --git a/aria/workflows/builtin/heal.py b/aria/workflows/builtin/heal.py
index 78a8312..efdd7b1 100644
--- a/aria/workflows/builtin/heal.py
+++ b/aria/workflows/builtin/heal.py
@@ -52,8 +52,8 @@ def heal(context, graph, node_instance_id):
failing_node_instances=failed_node_instance_subgraph,
targeted_node_instances=targeted_node_instances)
- graph.add_task(uninstall_subgraph)
- graph.add_task(install_subgraph)
+ graph.add_tasks(uninstall_subgraph)
+ graph.add_tasks(install_subgraph)
graph.add_dependency(install_subgraph, uninstall_subgraph)
@@ -75,7 +75,7 @@ def heal_uninstall(context, graph, failing_node_instances, targeted_node_instanc
node_instance_sub_workflow = context.task_graph(
name='uninstall_stub_{0}'.format(node_instance.id))
node_instance_sub_workflows[node_instance.id] = node_instance_sub_workflow
- graph.add_task(node_instance_sub_workflow)
+ graph.add_tasks(node_instance_sub_workflow)
# Create install sub workflow for each failing node
uninstall(
@@ -122,7 +122,7 @@ def heal_install(context, graph, failing_node_instances, targeted_node_instances
node_instance_sub_workflow = context.task_graph(
name='install_stub_{0}'.format(node_instance.id))
node_instance_sub_workflows[node_instance.id] = node_instance_sub_workflow
- graph.add_task(node_instance_sub_workflow)
+ graph.add_tasks(node_instance_sub_workflow)
# create install sub workflow for every node instance
install(
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/d31aa9c6/aria/workflows/builtin/install.py
----------------------------------------------------------------------
diff --git a/aria/workflows/builtin/install.py b/aria/workflows/builtin/install.py
index 552af90..c6b392f 100644
--- a/aria/workflows/builtin/install.py
+++ b/aria/workflows/builtin/install.py
@@ -42,13 +42,12 @@ def install(context, graph, node_instances=(), node_instance_sub_workflows=None)
context=context,
node_instance=node_instance)
node_instance_sub_workflows[node_instance.id] = node_instance_sub_workflow
- graph.add_task(node_instance_sub_workflow)
+ graph.add_tasks(node_instance_sub_workflow)
# create dependencies between the node instance sub workflow
for node_instance in node_instances:
node_instance_sub_workflow = node_instance_sub_workflows[node_instance.id]
if node_instance.relationship_instances:
- dependencies = \
- graph.parallel(*(node_instance_sub_workflows[relationship.target_id]
- for relationship in node_instance.relationship_instances))
+ dependencies = [node_instance_sub_workflows[relationship_instance.target_id]
+ for relationship_instance in node_instance.relationship_instances]
graph.add_dependency(node_instance_sub_workflow, dependencies)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/d31aa9c6/aria/workflows/builtin/uninstall.py
----------------------------------------------------------------------
diff --git a/aria/workflows/builtin/uninstall.py b/aria/workflows/builtin/uninstall.py
index 9659cb9..9557dd8 100644
--- a/aria/workflows/builtin/uninstall.py
+++ b/aria/workflows/builtin/uninstall.py
@@ -42,7 +42,7 @@ def uninstall(context, graph, node_instances=(), node_instance_sub_workflows=Non
context=context,
node_instance=node_instance)
node_instance_sub_workflows[node_instance.id] = node_instance_sub_workflow
- graph.add_task(node_instance_sub_workflow)
+ graph.add_tasks(node_instance_sub_workflow)
# create dependencies between the node instance sub workflow
for node_instance in node_instances:
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/d31aa9c6/aria/workflows/builtin/workflows.py
----------------------------------------------------------------------
diff --git a/aria/workflows/builtin/workflows.py b/aria/workflows/builtin/workflows.py
index 3763f5c..0119e26 100644
--- a/aria/workflows/builtin/workflows.py
+++ b/aria/workflows/builtin/workflows.py
@@ -185,7 +185,7 @@ def execute_operation_on_instance(
node_instance=node_instance,
operation_name=operation)
- graph.add_task(
+ graph.add_tasks(
context.operation(
name=task_name,
operation_details=node_instance.node.operations[operation],
@@ -256,4 +256,4 @@ def relationship_tasks(graph,
node_instance=context.model.node_instance.get(relationship_instance.target_id),
operation_details=relationship_instance.relationship.target_operations[
operation_name])
- return graph.parallel(source_operation, target_operation)
+ return graph.add_tasks(source_operation, target_operation)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/d31aa9c6/tests/workflows/api/test_task_graph.py
----------------------------------------------------------------------
diff --git a/tests/workflows/api/test_task_graph.py b/tests/workflows/api/test_task_graph.py
index efa1e9a..a3752ad 100644
--- a/tests/workflows/api/test_task_graph.py
+++ b/tests/workflows/api/test_task_graph.py
@@ -34,18 +34,18 @@ class TestTaskGraphTasks(object):
def test_add_task(self, graph):
task = MockTask()
- add_result = graph.add_task(task)
- assert add_result is True
+ add_result = graph.add_tasks(task)
+ assert add_result == [task]
tasks = [t for t in graph.tasks]
assert len(tasks) == 1
assert tasks[0] == task
def test_add_existing_task(self, graph):
task = MockTask()
- graph.add_task(task)
+ graph.add_tasks(task)
# adding a task already in graph - should have no effect, and return False
- add_result = graph.add_task(task)
- assert add_result is False
+ add_result = graph.add_tasks(task)
+ assert add_result == []
tasks = [t for t in graph.tasks]
assert len(tasks) == 1
assert tasks[0] == task
@@ -53,21 +53,21 @@ class TestTaskGraphTasks(object):
def test_remove_task(self, graph):
task = MockTask()
other_task = MockTask()
- graph.add_task(task)
- graph.add_task(other_task)
- graph.remove_task(other_task)
+ graph.add_tasks(task)
+ graph.add_tasks(other_task)
+ graph.remove_tasks(other_task)
tasks = [t for t in graph.tasks]
assert len(tasks) == 1
assert tasks[0] == task
- def test_remove_task_with_dependency(self, graph):
+ def test_remove_tasks_with_dependency(self, graph):
task = MockTask()
dependent_task = MockTask()
- graph.add_task(task)
- graph.add_task(dependent_task)
+ graph.add_tasks(task)
+ graph.add_tasks(dependent_task)
graph.add_dependency(dependent_task, task)
- remove_result = graph.remove_task(dependent_task)
- assert remove_result is True
+ remove_result = graph.remove_tasks(dependent_task)
+ assert remove_result == [dependent_task]
tasks = [t for t in graph.tasks]
assert len(tasks) == 1
assert tasks[0] == task
@@ -77,34 +77,34 @@ class TestTaskGraphTasks(object):
def test_remove_nonexistent_task(self, graph):
task = MockTask()
task_not_in_graph = MockTask()
- graph.add_task(task)
+ graph.add_tasks(task)
# removing a task not in graph - should have no effect, and return False
- remove_result = graph.remove_task(task_not_in_graph)
- assert remove_result is False
+ remove_result = graph.remove_tasks(task_not_in_graph)
+ assert remove_result == []
tasks = [t for t in graph.tasks]
assert len(tasks) == 1
assert tasks[0] == task
def test_has_task(self, graph):
task = MockTask()
- graph.add_task(task)
- assert graph.has_task(task) is True
+ graph.add_tasks(task)
+ assert graph.has_tasks(task) is True
def test_has_nonexistent_task(self, graph):
task = MockTask()
task_not_in_graph = MockTask()
- graph.add_task(task)
- assert graph.has_task(task_not_in_graph) is False
+ graph.add_tasks(task)
+ assert graph.has_tasks(task_not_in_graph) is False
def test_get_task(self, graph):
task = MockTask()
- graph.add_task(task)
+ graph.add_tasks(task)
assert graph.get_task(task.id) == task
def test_get_nonexistent_task(self, graph):
task = MockTask()
task_not_in_graph = MockTask()
- graph.add_task(task)
+ graph.add_tasks(task)
with pytest.raises(TaskNotInGraphError):
graph.get_task(task_not_in_graph.id)
@@ -114,8 +114,8 @@ class TestTaskGraphGraphTraversal(object):
def test_tasks_iteration(self, graph):
task = MockTask()
other_task = MockTask()
- graph.add_task(task)
- graph.add_task(other_task)
+ graph.add_tasks(task)
+ graph.add_tasks(other_task)
tasks = [t for t in graph.tasks]
assert set(tasks) == set([task, other_task])
@@ -125,10 +125,10 @@ class TestTaskGraphGraphTraversal(object):
dependent_task_2 = MockTask()
transitively_dependent_task = MockTask()
- graph.add_task(task)
- graph.add_task(dependent_task_1)
- graph.add_task(dependent_task_2)
- graph.add_task(transitively_dependent_task)
+ graph.add_tasks(task)
+ graph.add_tasks(dependent_task_1)
+ graph.add_tasks(dependent_task_2)
+ graph.add_tasks(transitively_dependent_task)
graph.add_dependency(dependent_task_1, task)
graph.add_dependency(dependent_task_2, task)
@@ -141,15 +141,15 @@ class TestTaskGraphGraphTraversal(object):
def test_get_task_empty_dependents(self, graph):
task = MockTask()
other_task = MockTask()
- graph.add_task(task)
- graph.add_task(other_task)
+ graph.add_tasks(task)
+ graph.add_tasks(other_task)
dependent_tasks = list(graph.get_task_dependents(task))
assert len(dependent_tasks) == 0
def test_get_nonexistent_task_dependents(self, graph):
task = MockTask()
task_not_in_graph = MockTask()
- graph.add_task(task)
+ graph.add_tasks(task)
with pytest.raises(TaskNotInGraphError):
list(graph.get_task_dependents(task_not_in_graph))
@@ -159,10 +159,10 @@ class TestTaskGraphGraphTraversal(object):
dependency_task_2 = MockTask()
transitively_dependency_task = MockTask()
- graph.add_task(task)
- graph.add_task(dependency_task_1)
- graph.add_task(dependency_task_2)
- graph.add_task(transitively_dependency_task)
+ graph.add_tasks(task)
+ graph.add_tasks(dependency_task_1)
+ graph.add_tasks(dependency_task_2)
+ graph.add_tasks(transitively_dependency_task)
graph.add_dependency(task, dependency_task_1)
graph.add_dependency(task, dependency_task_2)
@@ -175,15 +175,15 @@ class TestTaskGraphGraphTraversal(object):
def test_get_task_empty_dependencies(self, graph):
task = MockTask()
other_task = MockTask()
- graph.add_task(task)
- graph.add_task(other_task)
+ graph.add_tasks(task)
+ graph.add_tasks(other_task)
dependency_tasks = list(graph.get_task_dependencies(task))
assert len(dependency_tasks) == 0
def test_get_nonexistent_task_dependencies(self, graph):
task = MockTask()
task_not_in_graph = MockTask()
- graph.add_task(task)
+ graph.add_tasks(task)
with pytest.raises(TaskNotInGraphError):
list(graph.get_task_dependencies(task_not_in_graph))
@@ -194,9 +194,9 @@ class TestTaskGraphDependencies(object):
task = MockTask()
dependency_task = MockTask()
unrelated_task = MockTask()
- graph.add_task(task)
- graph.add_task(dependency_task)
- graph.add_task(unrelated_task)
+ graph.add_tasks(task)
+ graph.add_tasks(dependency_task)
+ graph.add_tasks(unrelated_task)
add_result = graph.add_dependency(task, dependency_task)
assert add_result is True
dependency_tasks = list(graph.get_task_dependencies(task))
@@ -206,8 +206,8 @@ class TestTaskGraphDependencies(object):
def test_add_existing_dependency(self, graph):
task = MockTask()
dependency_task = MockTask()
- graph.add_task(task)
- graph.add_task(dependency_task)
+ graph.add_tasks(task)
+ graph.add_tasks(dependency_task)
graph.add_dependency(task, dependency_task)
# adding a dependency already in graph - should have no effect, and return False
add_result = graph.add_dependency(task, dependency_task)
@@ -219,43 +219,43 @@ class TestTaskGraphDependencies(object):
def test_add_dependency_nonexistent_dependent(self, graph):
task = MockTask()
task_not_in_graph = MockTask()
- graph.add_task(task)
+ graph.add_tasks(task)
with pytest.raises(TaskNotInGraphError):
graph.add_dependency(task_not_in_graph, task)
def test_add_dependency_nonexistent_dependency(self, graph):
task = MockTask()
task_not_in_graph = MockTask()
- graph.add_task(task)
+ graph.add_tasks(task)
with pytest.raises(TaskNotInGraphError):
graph.add_dependency(task, task_not_in_graph)
def test_has_dependency(self, graph):
task = MockTask()
dependency_task = MockTask()
- graph.add_task(task)
- graph.add_task(dependency_task)
+ graph.add_tasks(task)
+ graph.add_tasks(dependency_task)
graph.add_dependency(task, dependency_task)
assert graph.has_dependency(task, dependency_task) is True
def test_has_nonexistent_dependency(self, graph):
task = MockTask()
other_task = MockTask()
- graph.add_task(task)
- graph.add_task(other_task)
+ graph.add_tasks(task)
+ graph.add_tasks(other_task)
assert graph.has_dependency(task, other_task) is False
def test_has_dependency_nonexistent_dependent(self, graph):
task = MockTask()
task_not_in_graph = MockTask()
- graph.add_task(task)
+ graph.add_tasks(task)
with pytest.raises(TaskNotInGraphError):
graph.has_dependency(task_not_in_graph, task)
def test_has_dependency_nonexistent_dependency(self, graph):
task = MockTask()
task_not_in_graph = MockTask()
- graph.add_task(task)
+ graph.add_tasks(task)
with pytest.raises(TaskNotInGraphError):
graph.has_dependency(task, task_not_in_graph)
@@ -263,9 +263,9 @@ class TestTaskGraphDependencies(object):
task = MockTask()
dependency_task = MockTask()
another_dependent_task = MockTask()
- graph.add_task(task)
- graph.add_task(dependency_task)
- graph.add_task(another_dependent_task)
+ graph.add_tasks(task)
+ graph.add_tasks(dependency_task)
+ graph.add_tasks(another_dependent_task)
graph.add_dependency(task, dependency_task)
graph.add_dependency(another_dependent_task, dependency_task)
@@ -277,8 +277,8 @@ class TestTaskGraphDependencies(object):
def test_remove_nonexistent_dependency(self, graph):
task = MockTask()
dependency_task = MockTask()
- graph.add_task(task)
- graph.add_task(dependency_task)
+ graph.add_tasks(task)
+ graph.add_tasks(dependency_task)
# removing a dependency not in graph - should have no effect, and return False
remove_result = graph.remove_dependency(task, dependency_task)
assert remove_result is False
@@ -288,7 +288,7 @@ class TestTaskGraphDependencies(object):
def test_remove_dependency_nonexistent_dependent(self, graph):
task = MockTask()
task_not_in_graph = MockTask()
- graph.add_task(task)
+ graph.add_tasks(task)
with pytest.raises(TaskNotInGraphError):
graph.remove_dependency(task_not_in_graph, task)
@@ -296,7 +296,7 @@ class TestTaskGraphDependencies(object):
# in this test the dependency *task* is not in the graph, not just the dependency itself
task = MockTask()
task_not_in_graph = MockTask()
- graph.add_task(task)
+ graph.add_tasks(task)
with pytest.raises(TaskNotInGraphError):
graph.remove_dependency(task, task_not_in_graph)
@@ -315,8 +315,8 @@ class TestTaskGraphArrangements(object):
def test_sequence_with_some_tasks_and_dependencies_already_in_graph(self, graph):
tasks = [MockTask(), MockTask(), MockTask()]
# insert some tasks and dependencies to the graph
- graph.add_task(tasks[1])
- graph.add_task(tasks[2])
+ graph.add_tasks(tasks[1])
+ graph.add_tasks(tasks[2])
graph.add_dependency(tasks[2], tasks[1])
graph.sequence(*tasks)
@@ -338,10 +338,9 @@ class TestTaskGraphArrangements(object):
assert len(list(graph.get_task_dependencies(tasks[3]))) == 2
assert len(list(graph.get_task_dependencies(tasks[4]))) == 3
-
def test_sequence_with_nested_parallel(self, graph):
tasks = [MockTask() for _ in xrange(5)]
- graph.sequence(tasks[0], graph.parallel(tasks[1], tasks[2], tasks[3]), tasks[4])
+ graph.sequence(tasks[0], (tasks[1], tasks[2], tasks[3]), tasks[4])
graph_tasks = [t for t in graph.tasks]
assert set(graph_tasks) == set(tasks)
# first task should have no dependencies
@@ -354,7 +353,7 @@ class TestTaskGraphArrangements(object):
def test_parallel(self, graph):
tasks = [MockTask(), MockTask(), MockTask()]
- graph.parallel(*tasks)
+ graph.add_tasks(tasks)
graph_tasks = [t for t in graph.tasks]
assert set(graph_tasks) == set(tasks)
for i in xrange(len(tasks)):
@@ -364,40 +363,47 @@ class TestTaskGraphArrangements(object):
tasks = [MockTask(), MockTask(), MockTask()]
# insert some tasks and dependencies to the graph
- graph.add_task(tasks[1])
- graph.add_task(tasks[2])
+ graph.add_tasks(tasks[1])
+ graph.add_tasks(tasks[2])
+ graph.add_tasks(*tasks)
- graph.parallel(*tasks)
- graph_tasks = [t for t in graph.tasks]
- assert set(graph_tasks) == set(tasks)
+ assert set(graph.tasks) == set(tasks)
for i in xrange(len(tasks)):
assert len(list(graph.get_task_dependencies(tasks[i]))) == 0
def test_parallel_with_nested_sequence(self, graph):
- tasks = [MockTask() for _ in xrange(5)]
- graph.parallel(tasks[0], graph.sequence(tasks[1], tasks[2], tasks[3]), tasks[4])
- graph_tasks = [t for t in graph.tasks]
- assert set(graph_tasks) == set(tasks)
+ all_tasks = [MockTask() for _ in xrange(5)]
+ graph.add_tasks(all_tasks[0],
+ graph.sequence(all_tasks[1], all_tasks[2], all_tasks[3]),
+ all_tasks[4])
+ assert set(graph.tasks) == set(all_tasks)
# tasks[2] and tasks[3] should each have a single dependency; the rest should have none
- assert len(list(graph.get_task_dependencies(tasks[0]))) == 0
- assert len(list(graph.get_task_dependencies(tasks[1]))) == 0
- assert set(graph.get_task_dependencies(tasks[2])) == set([tasks[1]])
- assert set(graph.get_task_dependencies(tasks[3])) == set([tasks[2]])
- assert len(list(graph.get_task_dependencies(tasks[4]))) == 0
+ assert len(list(graph.get_task_dependencies(all_tasks[0]))) == 0
+ assert len(list(graph.get_task_dependencies(all_tasks[1]))) == 0
+ assert set(graph.get_task_dependencies(all_tasks[2])) == set([all_tasks[1]])
+ assert set(graph.get_task_dependencies(all_tasks[3])) == set([all_tasks[2]])
+ assert len(list(graph.get_task_dependencies(all_tasks[4]))) == 0
def test_parallel_with_nested_parallel(self, graph):
tasks = [MockTask() for _ in xrange(5)]
- graph.parallel(tasks[0], graph.parallel(tasks[1], tasks[2], tasks[3]), tasks[4])
+ graph.add_tasks(tasks[0], (tasks[1], tasks[2], tasks[3]), tasks[4])
graph_tasks = [t for t in graph.tasks]
assert set(graph_tasks) == set(tasks)
# none of the tasks should have any dependencies
for i in xrange(len(tasks)):
assert len(list(graph.get_task_dependencies(tasks[i]))) == 0
- def test_sequence_and_parallel_deep_nesting(self, graph):
- # including connecting seq to parallel
- pass
+ def test_group_with_twice_nested_group(self, graph):
+ twice_nested_tasks = [MockTask(), MockTask(), MockTask()]
+ once_nested_tasks = [MockTask(), MockTask(), MockTask(), twice_nested_tasks]
+ tasks = [MockTask(), MockTask(), MockTask(), once_nested_tasks]
+ graph.add_tasks(*tasks)
+
+ assert set(graph.tasks) == set(tasks[:3] + once_nested_tasks[:3] + twice_nested_tasks)
+ for tasks_list in [tasks, once_nested_tasks, twice_nested_tasks]:
+ for i in xrange(len(tasks_list[:3])):
+ assert len(list(graph.get_task_dependencies(tasks_list[i]))) == 0
class TestTaskGraphDependenciesWithArrangements(object):
@@ -405,7 +411,7 @@ class TestTaskGraphDependenciesWithArrangements(object):
def test_add_dependency_dependent_sequence(self, graph):
task = MockTask()
sequence_tasks = [MockTask() for _ in xrange(3)]
- graph.add_task(task)
+ graph.add_tasks(task)
sequence = graph.sequence(*sequence_tasks)
graph.add_dependency(sequence, task)
assert graph.has_dependency(sequence_tasks[0], task) is True
@@ -415,7 +421,7 @@ class TestTaskGraphDependenciesWithArrangements(object):
def test_add_dependency_dependency_sequence(self, graph):
task = MockTask()
sequence_tasks = [MockTask() for _ in xrange(3)]
- graph.add_task(task)
+ graph.add_tasks(task)
sequence = graph.sequence(*sequence_tasks)
graph.add_dependency(task, sequence)
assert graph.has_dependency(task, sequence_tasks[0]) is True
@@ -425,8 +431,8 @@ class TestTaskGraphDependenciesWithArrangements(object):
def test_add_dependency_dependent_parallel(self, graph):
task = MockTask()
parallel_tasks = [MockTask() for _ in xrange(3)]
- graph.add_task(task)
- parallel = graph.parallel(*parallel_tasks)
+ graph.add_tasks(task)
+ parallel = graph.add_tasks(*parallel_tasks)
graph.add_dependency(parallel, task)
assert graph.has_dependency(parallel_tasks[0], task) is True
assert graph.has_dependency(parallel_tasks[1], task) is True
@@ -435,8 +441,8 @@ class TestTaskGraphDependenciesWithArrangements(object):
def test_add_dependency_dependency_parallel(self, graph):
task = MockTask()
parallel_tasks = [MockTask() for _ in xrange(3)]
- graph.add_task(task)
- parallel = graph.parallel(*parallel_tasks)
+ graph.add_tasks(task)
+ parallel = graph.add_tasks(*parallel_tasks)
graph.add_dependency(task, parallel)
assert graph.has_dependency(task, parallel_tasks[0]) is True
assert graph.has_dependency(task, parallel_tasks[1]) is True
@@ -446,7 +452,7 @@ class TestTaskGraphDependenciesWithArrangements(object):
sequence_tasks = [MockTask() for _ in xrange(3)]
parallel_tasks = [MockTask() for _ in xrange(3)]
sequence = graph.sequence(*sequence_tasks)
- parallel = graph.parallel(*parallel_tasks)
+ parallel = graph.add_tasks(*parallel_tasks)
graph.add_dependency(sequence, parallel)
for parallel_task in parallel_tasks:
assert graph.has_dependency(sequence_tasks[0], parallel_task) is True
@@ -457,7 +463,7 @@ class TestTaskGraphDependenciesWithArrangements(object):
sequence_tasks = [MockTask() for _ in xrange(3)]
parallel_tasks = [MockTask() for _ in xrange(3)]
sequence = graph.sequence(*sequence_tasks)
- parallel = graph.parallel(*parallel_tasks)
+ parallel = graph.add_tasks(*parallel_tasks)
graph.add_dependency(parallel, sequence)
for parallel_task in parallel_tasks:
assert graph.has_dependency(parallel_task, sequence_tasks[0]) is True
@@ -467,8 +473,8 @@ class TestTaskGraphDependenciesWithArrangements(object):
def test_add_dependency_dependency_parallel_with_some_existing_dependencies(self, graph):
task = MockTask()
parallel_tasks = [MockTask() for _ in xrange(3)]
- graph.add_task(task)
- parallel = graph.parallel(*parallel_tasks)
+ graph.add_tasks(task)
+ parallel = graph.add_tasks(*parallel_tasks)
# adding a dependency on a specific task manually,
# before adding a dependency on the whole parallel
graph.add_dependency(task, parallel_tasks[1])
@@ -481,7 +487,7 @@ class TestTaskGraphDependenciesWithArrangements(object):
sequence_tasks = [MockTask() for _ in xrange(3)]
parallel_tasks = [MockTask() for _ in xrange(3)]
sequence = graph.sequence(*sequence_tasks)
- parallel = graph.parallel(*parallel_tasks)
+ parallel = graph.add_tasks(*parallel_tasks)
add_result = graph.add_dependency(sequence, parallel)
assert add_result is True
# adding a dependency already in graph - should have no effect, and return False
@@ -495,7 +501,7 @@ class TestTaskGraphDependenciesWithArrangements(object):
def test_has_dependency_dependent_sequence(self, graph):
task = MockTask()
sequence_tasks = [MockTask() for _ in xrange(3)]
- graph.add_task(task)
+ graph.add_tasks(task)
sequence = graph.sequence(*sequence_tasks)
assert graph.has_dependency(sequence, task) is False
graph.add_dependency(sequence, task)
@@ -504,7 +510,7 @@ class TestTaskGraphDependenciesWithArrangements(object):
def test_has_dependency_dependency_sequence(self, graph):
task = MockTask()
sequence_tasks = [MockTask() for _ in xrange(3)]
- graph.add_task(task)
+ graph.add_tasks(task)
sequence = graph.sequence(*sequence_tasks)
assert graph.has_dependency(task, sequence) is False
graph.add_dependency(task, sequence)
@@ -513,8 +519,8 @@ class TestTaskGraphDependenciesWithArrangements(object):
def test_has_dependency_dependent_parallel(self, graph):
task = MockTask()
parallel_tasks = [MockTask() for _ in xrange(3)]
- graph.add_task(task)
- parallel = graph.parallel(*parallel_tasks)
+ graph.add_tasks(task)
+ parallel = graph.add_tasks(*parallel_tasks)
assert graph.has_dependency(parallel, task) is False
graph.add_dependency(parallel, task)
assert graph.has_dependency(parallel, task) is True
@@ -522,8 +528,8 @@ class TestTaskGraphDependenciesWithArrangements(object):
def test_has_dependency_dependency_parallel(self, graph):
task = MockTask()
parallel_tasks = [MockTask() for _ in xrange(3)]
- graph.add_task(task)
- parallel = graph.parallel(*parallel_tasks)
+ graph.add_tasks(task)
+ parallel = graph.add_tasks(*parallel_tasks)
assert graph.has_dependency(task, parallel) is False
graph.add_dependency(task, parallel)
assert graph.has_dependency(task, parallel) is True
@@ -532,7 +538,7 @@ class TestTaskGraphDependenciesWithArrangements(object):
sequence_tasks = [MockTask() for _ in xrange(3)]
parallel_tasks = [MockTask() for _ in xrange(3)]
sequence = graph.sequence(*sequence_tasks)
- parallel = graph.parallel(*parallel_tasks)
+ parallel = graph.add_tasks(*parallel_tasks)
assert graph.has_dependency(sequence, parallel) is False
graph.add_dependency(sequence, parallel)
assert graph.has_dependency(sequence, parallel) is True
@@ -541,7 +547,7 @@ class TestTaskGraphDependenciesWithArrangements(object):
sequence_tasks = [MockTask() for _ in xrange(3)]
parallel_tasks = [MockTask() for _ in xrange(3)]
sequence = graph.sequence(*sequence_tasks)
- parallel = graph.parallel(*parallel_tasks)
+ parallel = graph.add_tasks(*parallel_tasks)
assert graph.has_dependency(parallel, sequence) is False
graph.add_dependency(parallel, sequence)
assert graph.has_dependency(parallel, sequence) is True
@@ -549,8 +555,8 @@ class TestTaskGraphDependenciesWithArrangements(object):
def test_has_dependency_dependency_parallel_with_some_existing_dependencies(self, graph):
task = MockTask()
parallel_tasks = [MockTask() for _ in xrange(3)]
- graph.add_task(task)
- parallel = graph.parallel(*parallel_tasks)
+ graph.add_tasks(task)
+ parallel = graph.add_tasks(*parallel_tasks)
graph.add_dependency(task, parallel_tasks[1])
# only a partial dependency exists - has_dependency is expected to return False
assert graph.has_dependency(task, parallel) is False
@@ -559,13 +565,13 @@ class TestTaskGraphDependenciesWithArrangements(object):
sequence_tasks = [MockTask() for _ in xrange(3)]
parallel_tasks = [MockTask() for _ in xrange(3)]
sequence = graph.sequence(*sequence_tasks)
- parallel = graph.parallel(*parallel_tasks)
+ parallel = graph.add_tasks(*parallel_tasks)
assert graph.has_dependency(sequence, parallel) is False
def test_remove_dependency_dependent_sequence(self, graph):
task = MockTask()
sequence_tasks = [MockTask() for _ in xrange(3)]
- graph.add_task(task)
+ graph.add_tasks(task)
sequence = graph.sequence(*sequence_tasks)
graph.add_dependency(sequence, task)
remove_result = graph.remove_dependency(sequence, task)
@@ -577,7 +583,7 @@ class TestTaskGraphDependenciesWithArrangements(object):
def test_remove_dependency_dependency_sequence(self, graph):
task = MockTask()
sequence_tasks = [MockTask() for _ in xrange(3)]
- graph.add_task(task)
+ graph.add_tasks(task)
sequence = graph.sequence(*sequence_tasks)
graph.add_dependency(task, sequence)
remove_result = graph.remove_dependency(task, sequence)
@@ -589,8 +595,8 @@ class TestTaskGraphDependenciesWithArrangements(object):
def test_remove_dependency_dependent_parallel(self, graph):
task = MockTask()
parallel_tasks = [MockTask() for _ in xrange(3)]
- graph.add_task(task)
- parallel = graph.parallel(*parallel_tasks)
+ graph.add_tasks(task)
+ parallel = graph.add_tasks(*parallel_tasks)
graph.add_dependency(parallel, task)
remove_result = graph.remove_dependency(parallel, task)
assert remove_result is True
@@ -601,8 +607,8 @@ class TestTaskGraphDependenciesWithArrangements(object):
def test_remove_dependency_dependency_parallel(self, graph):
task = MockTask()
parallel_tasks = [MockTask() for _ in xrange(3)]
- graph.add_task(task)
- parallel = graph.parallel(*parallel_tasks)
+ graph.add_tasks(task)
+ parallel = graph.add_tasks(*parallel_tasks)
graph.add_dependency(task, parallel)
remove_result = graph.remove_dependency(task, parallel)
assert remove_result is True
@@ -614,7 +620,7 @@ class TestTaskGraphDependenciesWithArrangements(object):
sequence_tasks = [MockTask() for _ in xrange(3)]
parallel_tasks = [MockTask() for _ in xrange(3)]
sequence = graph.sequence(*sequence_tasks)
- parallel = graph.parallel(*parallel_tasks)
+ parallel = graph.add_tasks(*parallel_tasks)
graph.add_dependency(sequence, parallel)
remove_result = graph.remove_dependency(sequence, parallel)
assert remove_result is True
@@ -627,7 +633,7 @@ class TestTaskGraphDependenciesWithArrangements(object):
sequence_tasks = [MockTask() for _ in xrange(3)]
parallel_tasks = [MockTask() for _ in xrange(3)]
sequence = graph.sequence(*sequence_tasks)
- parallel = graph.parallel(*parallel_tasks)
+ parallel = graph.add_tasks(*parallel_tasks)
graph.add_dependency(parallel, sequence)
remove_result = graph.remove_dependency(parallel, sequence)
assert remove_result is True
@@ -639,8 +645,8 @@ class TestTaskGraphDependenciesWithArrangements(object):
def test_remove_dependency_dependency_parallel_with_some_existing_dependencies(self, graph):
task = MockTask()
parallel_tasks = [MockTask() for _ in xrange(3)]
- graph.add_task(task)
- parallel = graph.parallel(*parallel_tasks)
+ graph.add_tasks(task)
+ parallel = graph.add_tasks(*parallel_tasks)
graph.add_dependency(task, parallel_tasks[1])
remove_result = graph.remove_dependency(task, parallel)
# only a partial dependency exists - remove_dependency is expected to return False
@@ -654,7 +660,52 @@ class TestTaskGraphDependenciesWithArrangements(object):
sequence_tasks = [MockTask() for _ in xrange(3)]
parallel_tasks = [MockTask() for _ in xrange(3)]
sequence = graph.sequence(*sequence_tasks)
- parallel = graph.parallel(*parallel_tasks)
+ parallel = graph.add_tasks(*parallel_tasks)
# removing a dependency not in graph - should have no effect, and return False
remove_result = graph.remove_dependency(sequence, parallel)
assert remove_result is False
+
+ def test_sequence_with_twice_nested_sequence_and_interdependencies(self, graph):
+ twice_nested_tasks = list(graph.sequence(MockTask(), MockTask(), MockTask()))
+ once_nested_tasks = list(graph.sequence(MockTask(), MockTask(), MockTask(), twice_nested_tasks))
+ tasks = [MockTask(), MockTask(), MockTask(), once_nested_tasks]
+ graph.sequence(*tasks)
+
+ assert set(graph.tasks) == set(tasks[:3] + once_nested_tasks[:3] + twice_nested_tasks)
+ assert set(graph.get_task_dependencies(tasks[0])) == set()
+ for i in xrange(1, len(tasks[:-1])):
+ assert set(graph.get_task_dependencies(tasks[i])) == set([tasks[i-1]])
+
+ assert set(graph.get_task_dependencies(once_nested_tasks[0])) == set([tasks[2]])
+ for i in xrange(1, len(once_nested_tasks[:-1])):
+ assert set(graph.get_task_dependencies(once_nested_tasks[i])) == \
+ set([tasks[2], once_nested_tasks[i-1]])
+
+ assert set(graph.get_task_dependencies(twice_nested_tasks[0])) == \
+ set([tasks[2], once_nested_tasks[2]])
+ for i in xrange(1, len(twice_nested_tasks[:-1])):
+ assert set(graph.get_task_dependencies(twice_nested_tasks[i])) == \
+ set([tasks[2], once_nested_tasks[2], twice_nested_tasks[i-1]])
+
+ def test_group_with_twice_nested_group_and_interdependencies(self, graph):
+ twice_nested_tasks = [MockTask(), MockTask(), MockTask()]
+ once_nested_tasks = [MockTask(), MockTask(), MockTask(), twice_nested_tasks]
+ tasks = [MockTask(), MockTask(), MockTask(), once_nested_tasks]
+ graph.add_tasks(*tasks)
+
+ graph.add_dependency(tasks[2], once_nested_tasks[2])
+ graph.add_dependency(once_nested_tasks[1], twice_nested_tasks[0])
+ graph.add_dependency(twice_nested_tasks[1], tasks[0])
+
+ assert set(graph.tasks) == set(tasks[:3] + once_nested_tasks[:3] + twice_nested_tasks)
+ assert set(graph.get_task_dependencies(tasks[0])) == set()
+ assert set(graph.get_task_dependencies(tasks[1])) == set()
+ assert set(graph.get_task_dependencies(tasks[2])) == set([once_nested_tasks[2]])
+
+ assert set((graph.get_task_dependencies(once_nested_tasks[0]))) == set()
+ assert set(graph.get_task_dependencies(once_nested_tasks[1])) == set([twice_nested_tasks[0]])
+ assert set(graph.get_task_dependencies(once_nested_tasks[2])) == set()
+
+ assert set((graph.get_task_dependencies(twice_nested_tasks[0]))) == set()
+ assert set(graph.get_task_dependencies(twice_nested_tasks[1])) == set([tasks[0]])
+ assert set(graph.get_task_dependencies(twice_nested_tasks[2])) == set()