Module jumpscale.tools.depsresolver.depsresolver
Expand source code
from dataclasses import dataclass, field
from typing import List, Dict, Tuple, Callable
from enum import Enum
class NodeColor(Enum):
White = 0
Gray = 1
Black = 2
def has_cycle_dfs(
graph: Dict[str, List[str]],
node: str,
colors: Dict[str, NodeColor],
has_cycle: List[bool],
parent_map: Dict[str, str],
):
if has_cycle[0]:
return
colors[node] = NodeColor.Gray
for dep in graph[node]:
parent_map[dep] = node
if colors[dep] == NodeColor.Gray:
has_cycle[0] = True
parent_map["__CYCLESTART__"] = dep
return
if colors[dep] == NodeColor.White:
has_cycle_dfs(graph, dep, colors, has_cycle, parent_map)
colors[node] = NodeColor.Black
def graph_has_cycle(graph: Dict[str, List[str]]) -> Tuple[bool, Dict[str, str]]:
colors: Dict[str, NodeColor] = {}
for node in graph:
colors[node] = NodeColor.White
parent_map: Dict[str, str] = {}
has_cycle = [False]
for node in graph:
parent_map[node] = "null"
if colors[node] == NodeColor.White:
has_cycle_dfs(graph, node, colors, has_cycle, parent_map)
if has_cycle[0]:
return (True, parent_map)
return (False, parent_map)
@dataclass
class Task:
name: str
requires: List[str]
action: Callable
@dataclass
class DepsResolver:
tasksgraph: Dict[str, List[str]] = field(default_factory=dict)
tasks: Dict[str, Task] = field(default_factory=dict)
def add_task(self, task_name: str, deps: List[str], action: str):
thetask = Task(task_name, deps, action)
self.tasksgraph[task_name] = deps
self.tasks[task_name] = thetask
def run_task(self, task_name: str):
has_cycle, parent_map = graph_has_cycle(self.tasksgraph)
if has_cycle:
print(f"cycle found: {parent_map}")
return
deps = []
seen = []
self._run_task_helper(task_name, deps, seen)
for tsk in deps:
t = self.tasks[tsk]
if callable(t.action):
t.action()
elif isinstance(t.action, str):
print("executing ", t.action)
def _run_task_helper(self, task_name: str, deps: List[str], seen: List[str]):
if task_name in seen:
print(f"[+]resolved {task_name} before. no need to repeat action.")
tsk = self.tasks[task_name]
seen.append(task_name)
if tsk.requires:
for subtask in self.tasksgraph[tsk.name]:
self._run_task_helper(subtask, deps, seen)
deps.append(task_name)
if __name__ == "__main__":
def with_cycle():
deps_resolver = DepsResolver()
deps_resolver.add_task("publish", ["build-release"], "print publish")
deps_resolver.add_task("build-release", ["nim-installed"], "print exec command to build release mode")
deps_resolver.add_task("nim-installed", ["curl-installed"], "print curl LINK | bash")
deps_resolver.add_task("curl-installed", ["publish", "apt-installed"], "apt-get install curl")
deps_resolver.add_task("apt-installed", [], "code to install apt...")
deps_resolver.run_task("publish")
def without_cycle():
deps_resolver = DepsResolver()
deps_resolver.add_task("publish", ["build-release"], "print publish")
deps_resolver.add_task("build-release", ["nim-installed"], "print exec command to build release mode")
deps_resolver.add_task("nim-installed", ["curl-installed"], "print curl LINK | bash")
deps_resolver.add_task("curl-installed", ["apt-installed"], "apt-get install curl")
deps_resolver.add_task("apt-installed", [], "code to install apt...")
deps_resolver.run_task("publish")
print("without cycle: ")
without_cycle()
print("with cycle")
with_cycle()
Functions
def graph_has_cycle(graph: Dict[str, List[str]]) ‑> Tuple[bool, Dict[str, str]]
-
Expand source code
def graph_has_cycle(graph: Dict[str, List[str]]) -> Tuple[bool, Dict[str, str]]: colors: Dict[str, NodeColor] = {} for node in graph: colors[node] = NodeColor.White parent_map: Dict[str, str] = {} has_cycle = [False] for node in graph: parent_map[node] = "null" if colors[node] == NodeColor.White: has_cycle_dfs(graph, node, colors, has_cycle, parent_map) if has_cycle[0]: return (True, parent_map) return (False, parent_map)
def has_cycle_dfs(graph: Dict[str, List[str]], node: str, colors: Dict[str, NodeColor], has_cycle: List[bool], parent_map: Dict[str, str])
-
Expand source code
def has_cycle_dfs( graph: Dict[str, List[str]], node: str, colors: Dict[str, NodeColor], has_cycle: List[bool], parent_map: Dict[str, str], ): if has_cycle[0]: return colors[node] = NodeColor.Gray for dep in graph[node]: parent_map[dep] = node if colors[dep] == NodeColor.Gray: has_cycle[0] = True parent_map["__CYCLESTART__"] = dep return if colors[dep] == NodeColor.White: has_cycle_dfs(graph, dep, colors, has_cycle, parent_map) colors[node] = NodeColor.Black
Classes
class DepsResolver (tasksgraph: Dict[str, List[str]] = <factory>, tasks: Dict[str, Task] = <factory>)
-
DepsResolver(tasksgraph: Dict[str, List[str]] =
, tasks: Dict[str, jumpscale.tools.depsresolver.depsresolver.Task] = ) Expand source code
@dataclass class DepsResolver: tasksgraph: Dict[str, List[str]] = field(default_factory=dict) tasks: Dict[str, Task] = field(default_factory=dict) def add_task(self, task_name: str, deps: List[str], action: str): thetask = Task(task_name, deps, action) self.tasksgraph[task_name] = deps self.tasks[task_name] = thetask def run_task(self, task_name: str): has_cycle, parent_map = graph_has_cycle(self.tasksgraph) if has_cycle: print(f"cycle found: {parent_map}") return deps = [] seen = [] self._run_task_helper(task_name, deps, seen) for tsk in deps: t = self.tasks[tsk] if callable(t.action): t.action() elif isinstance(t.action, str): print("executing ", t.action) def _run_task_helper(self, task_name: str, deps: List[str], seen: List[str]): if task_name in seen: print(f"[+]resolved {task_name} before. no need to repeat action.") tsk = self.tasks[task_name] seen.append(task_name) if tsk.requires: for subtask in self.tasksgraph[tsk.name]: self._run_task_helper(subtask, deps, seen) deps.append(task_name)
Class variables
var tasks : Dict[str, Task]
var tasksgraph : Dict[str, List[str]]
Methods
def add_task(self, task_name: str, deps: List[str], action: str)
-
Expand source code
def add_task(self, task_name: str, deps: List[str], action: str): thetask = Task(task_name, deps, action) self.tasksgraph[task_name] = deps self.tasks[task_name] = thetask
def run_task(self, task_name: str)
-
Expand source code
def run_task(self, task_name: str): has_cycle, parent_map = graph_has_cycle(self.tasksgraph) if has_cycle: print(f"cycle found: {parent_map}") return deps = [] seen = [] self._run_task_helper(task_name, deps, seen) for tsk in deps: t = self.tasks[tsk] if callable(t.action): t.action() elif isinstance(t.action, str): print("executing ", t.action)
class NodeColor (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
An enumeration.
Expand source code
class NodeColor(Enum): White = 0 Gray = 1 Black = 2
Ancestors
- enum.Enum
Class variables
var Black
var Gray
var White
class Task (name: str, requires: List[str], action: Callable)
-
Task(name: str, requires: List[str], action: Callable)
Expand source code
@dataclass class Task: name: str requires: List[str] action: Callable
Class variables
var action : Callable
var name : str
var requires : List[str]