Files
dvc/dvc/command/pipeline.py
T

262 lines
7.9 KiB
Python

from __future__ import unicode_literals
from dvc.utils import relpath
from dvc.utils.compat import str
import argparse
import logging
from dvc.exceptions import DvcException
from dvc.command.base import CmdBase, fix_subparsers, append_doc_link
logger = logging.getLogger(__name__)
class CmdPipelineShow(CmdBase):
def _show(self, target, commands, outs, locked):
import networkx
from dvc.stage import Stage
stage = Stage.load(self.repo, target)
G = self.repo.graph()[0]
stages = networkx.get_node_attributes(G, "stage")
node = relpath(stage.path, self.repo.root_dir)
nodes = networkx.dfs_postorder_nodes(G, node)
if locked:
nodes = [n for n in nodes if stages[n].locked]
for n in nodes:
if commands:
if stages[n].cmd is None:
continue
logger.info(stages[n].cmd)
elif outs:
for out in stages[n].outs:
logger.info(str(out))
else:
logger.info(n)
def __build_graph(self, target, commands, outs):
import networkx
from dvc.stage import Stage
stage = Stage.load(self.repo, target)
node = relpath(stage.path, self.repo.root_dir)
pipelines = list(
filter(lambda g: node in g.nodes(), self.repo.pipelines())
)
assert len(pipelines) == 1
G = pipelines[0]
stages = networkx.get_node_attributes(G, "stage")
nodes = []
for n in G.nodes():
stage = stages[n]
if commands:
if stage.cmd is None:
continue
nodes.append(stage.cmd)
elif outs:
for out in stage.outs:
nodes.append(str(out))
else:
nodes.append(stage.relpath)
edges = []
for e in G.edges():
from_stage = stages[e[0]]
to_stage = stages[e[1]]
if commands:
if to_stage.cmd is None:
continue
edges.append((from_stage.cmd, to_stage.cmd))
elif outs:
for from_out in from_stage.outs:
for to_out in to_stage.outs:
edges.append((str(from_out), str(to_out)))
else:
edges.append((from_stage.relpath, to_stage.relpath))
return nodes, edges, networkx.is_tree(G)
def _show_ascii(self, target, commands, outs):
from dvc.dagascii import draw
nodes, edges, _ = self.__build_graph(target, commands, outs)
if not nodes:
return
draw(nodes, edges)
def _show_dependencies_tree(self, target, commands, outs):
from treelib import Tree
nodes, edges, is_tree = self.__build_graph(target, commands, outs)
if not nodes:
return
if not is_tree:
raise DvcException(
"DAG is not a tree, can not print it in tree-structure way, "
"please use --ascii instead"
)
tree = Tree()
tree.create_node(target, target) # Root node
observe_list = [target]
while len(observe_list) > 0:
current_root = observe_list[0]
for edge in edges:
if edge[0] == current_root:
tree.create_node(edge[1], edge[1], parent=current_root)
observe_list.append(edge[1])
observe_list.pop(0)
tree.show()
def __write_dot(self, target, commands, outs):
from dvc.utils.compat import StringIO
import networkx
from networkx.drawing.nx_pydot import write_dot
_, edges, _ = self.__build_graph(target, commands, outs)
edges = [edge[::-1] for edge in edges]
simple_g = networkx.DiGraph()
simple_g.add_edges_from(edges)
dot_file = StringIO()
write_dot(simple_g, dot_file)
logger.info(dot_file.getvalue())
def run(self):
if not self.args.targets:
self.args.targets = self.default_targets
for target in self.args.targets:
try:
if self.args.ascii:
self._show_ascii(
target, self.args.commands, self.args.outs
)
elif self.args.dot:
self.__write_dot(
target, self.args.commands, self.args.outs
)
elif self.args.tree:
self._show_dependencies_tree(
target, self.args.commands, self.args.outs
)
else:
self._show(
target,
self.args.commands,
self.args.outs,
self.args.locked,
)
except DvcException:
msg = "failed to show pipeline for '{}'".format(target)
logger.exception(msg)
return 1
return 0
class CmdPipelineList(CmdBase):
def run(self):
import networkx
pipelines = self.repo.pipelines()
for p in pipelines:
stages = networkx.get_node_attributes(p, "stage")
for stage in stages:
logger.info(stage)
if len(stages) != 0:
logger.info("=" * 80)
logger.info("{} pipeline(s) total".format(len(pipelines)))
return 0
def add_parser(subparsers, parent_parser):
PIPELINE_HELP = "Manage pipelines."
pipeline_parser = subparsers.add_parser(
"pipeline",
parents=[parent_parser],
description=append_doc_link(PIPELINE_HELP, "pipeline"),
help=PIPELINE_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
pipeline_subparsers = pipeline_parser.add_subparsers(
dest="cmd",
help="Use dvc pipeline CMD --help for command-specific help.",
)
fix_subparsers(pipeline_subparsers)
PIPELINE_SHOW_HELP = "Show pipeline."
pipeline_show_parser = pipeline_subparsers.add_parser(
"show",
parents=[parent_parser],
description=append_doc_link(PIPELINE_SHOW_HELP, "pipeline-show"),
help=PIPELINE_SHOW_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
pipeline_show_group = pipeline_show_parser.add_mutually_exclusive_group()
pipeline_show_group.add_argument(
"-c",
"--commands",
action="store_true",
default=False,
help="Print commands instead of paths to DVC-files.",
)
pipeline_show_group.add_argument(
"-o",
"--outs",
action="store_true",
default=False,
help="Print output files instead of paths to DVC-files.",
)
pipeline_show_parser.add_argument(
"-l",
"--locked",
action="store_true",
default=False,
help="Print locked DVC stages",
)
pipeline_show_parser.add_argument(
"--ascii",
action="store_true",
default=False,
help="Output DAG as ASCII.",
)
pipeline_show_parser.add_argument(
"--dot",
action="store_true",
default=False,
help="Print DAG with .dot format.",
)
pipeline_show_parser.add_argument(
"--tree",
action="store_true",
default=False,
help="Output DAG as Dependencies Tree.",
)
pipeline_show_parser.add_argument(
"targets", nargs="*", help="DVC-files. 'Dvcfile' by default."
)
pipeline_show_parser.set_defaults(func=CmdPipelineShow)
PIPELINE_LIST_HELP = "List pipelines."
pipeline_list_parser = pipeline_subparsers.add_parser(
"list",
parents=[parent_parser],
description=append_doc_link(PIPELINE_LIST_HELP, "pipeline-list"),
help=PIPELINE_LIST_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
pipeline_list_parser.set_defaults(func=CmdPipelineList)