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]