From adaf8d4dac2c56fe9acdedda7b64c3c452bbcfec Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Tue, 17 Sep 2019 01:46:28 -0400 Subject: [PATCH] oh yeah --- bake/bakefile.py | 85 +++++++++++++++++++++++++++++++------- bake/cli.py | 2 +- bake/tests/test_filters.py | 4 +- setup.py | 2 +- 4 files changed, 74 insertions(+), 19 deletions(-) diff --git a/bake/bakefile.py b/bake/bakefile.py index bbd37e1..9369af3 100644 --- a/bake/bakefile.py +++ b/bake/bakefile.py @@ -9,6 +9,7 @@ from tempfile import mkstemp import delegator import click +import networkx from .bash import Bash @@ -35,12 +36,20 @@ class BaseAction: class TaskFilter(BaseAction): - def __init__(self, s): + def __init__(self, s, bashfile): self.source = s + self.bashfile = bashfile def __str__(self): return f"{self.source}" + def __hash__(self): + return hash((self.bashfile, self.source)) + + def __eq__(self, other): + if hasattr(other, "source"): + return other.source == self.source + @property def name(self): return self.source.split(":", 1)[0][len("@") :] @@ -131,6 +140,15 @@ class TaskFilter(BaseAction): return self.execute_skip_if(yes=yes, **self.arguments) +class FakeTaskScript(BaseAction): + def __init__(self, s, bashfile): + self.source = s + self.bashfile = bashfile + + def __str__(self): + return str(click.style(self.source, fg="red")) + + class TaskScript(BaseAction): def __init__(self, bashfile, chunk_index=None): self.bashfile = bashfile @@ -140,11 +158,18 @@ class TaskScript(BaseAction): raise TaskNotInBashfile() def __repr__(self): - return f"" + return f"" def __str__(self): return f"{self.name}" + def __hash__(self): + return hash((self.bashfile, self._chunk_index)) + + def __eq__(self, other): + if hasattr(other, "_chunk_index"): + return other._chunk_index == self._chunk_index + @property def declaration_line(self): return self.chunk[0] @@ -159,20 +184,24 @@ class TaskScript(BaseAction): for i, task_string in task_name_index_tuples: - if i is None: + if task_string.startswith("@"): + yield TaskFilter(task_string, bashfile=self.bashfile) + elif i is None: # Create the filter. - yield TaskFilter(task_string) + yield FakeTaskScript(task_string, bashfile=self.bashfile) else: # Otherwise, create the task. - yield TaskScript(bashfile=self.bashfile, chunk_index=i) + yield TaskScript(chunk_index=i, bashfile=self.bashfile) actions = [t for t in gen_actions()] if recursive: - for i, task in enumerate(actions[:]): - for t in task.depends_on(): - if t.name not in [task.name for task in actions]: - actions.insert(i - 1, t) + actions = [] + _last_action = None + for _, action in list(networkx.dfs_edges(self.bashfile.graph, self))[:]: + if _last_action != action: + actions.append(action) + _last_action = action return actions @@ -301,6 +330,28 @@ class Bakefile: os.environ["BAKE_SKIP_DONE"] = "1" self.chunks + self._tasks = None + self._graph = None + + @property + def graph(self): + if self._graph: + return self._graph + + g = networkx.OrderedDiGraph() + + for task in self.tasks.values(): + if task not in g: + g.add_node(task) + + for dep in task.depends_on(): + if dep not in g: + g.add_node(dep) + + g.add_edge(task, dep) + + self._graph = g + return self.graph def __repr__(self): return f"" @@ -386,11 +437,11 @@ class Bakefile: def source_lines(self): return self.source.split("\n") - @staticmethod - def _is_declaration_line(line): - if ":" in line: - line = line.replace("\t", " " * 4) - return bool(len(line[:4].strip())) + def _is_declaration_line(self, line): + if not self._is_comment_line(line): + if ":" in line: + line = line.replace("\t", " " * 4) + return bool(len(line[:4].strip())) @staticmethod def _is_shebang_line(line): @@ -406,12 +457,16 @@ class Bakefile: @property def tasks(self): + if self._tasks: + return self._tasks + tasks = {} for i, chunk in enumerate(self.chunks): script = TaskScript._from_chunk_index(bashfile=self, i=i) tasks[script.name] = script - return tasks + self._tasks = tasks + return self.tasks @property def root_source_lines(self): diff --git a/bake/cli.py b/bake/cli.py index be8ac13..113d71b 100644 --- a/bake/cli.py +++ b/bake/cli.py @@ -265,7 +265,7 @@ def entrypoint( if dep.is_filter: dep = click.style(str(dep), fg="yellow") deps.append(str(dep)) - deps = f"\n {click.style('+', fg='yellow', bold=True)} {eng_join(deps)}." + deps = f"\n {click.style('+', fg='yellow', bold=True)} {eng_join(deps, conj='and finally')}." else: deps = "" colon = "" if not deps else "…" diff --git a/bake/tests/test_filters.py b/bake/tests/test_filters.py index 90c37a1..4e35565 100644 --- a/bake/tests/test_filters.py +++ b/bake/tests/test_filters.py @@ -11,7 +11,7 @@ def test_confirm_secure(bake): c.expect(":", timeout=0.5) c.send("4222\n") c.block() - assert "Aborted!" in c.out + assert "Wrong answer!" in c.out def test_confirm_dep(bake): @@ -27,4 +27,4 @@ def test_confirm_secure(bake): c.expect(":", timeout=0.5) c.send("4222\n") c.block() - assert "Aborted!" in c.out + assert "Wrong answer!" in c.out diff --git a/setup.py b/setup.py index ff4c00c..181d8a0 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ REQUIRES_PYTHON = ">=3.6.0" VERSION = "0.2.1" # What packages are required for this module to be executed? -REQUIRED = ["click", "delegator.py", "pygments"] +REQUIRED = ["click", "delegator.py", "pygments", "networkx"] # What packages are optional? EXTRAS = {