quote node names

This commit is contained in:
Saugat Pachhai (सौगात)
2022-06-15 12:43:45 +03:00
committed by Ruslan Kuprieiev
parent 8e003e5022
commit 4bc6b5699a
5 changed files with 61 additions and 11 deletions
+18 -2
View File
@@ -1,11 +1,15 @@
import argparse
from typing import TYPE_CHECKING
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.ui import ui
if TYPE_CHECKING:
from networkx import DiGraph
def _show_ascii(G):
def _show_ascii(G: "DiGraph"):
from dvc.dagascii import draw
from dvc.repo.graph import get_pipelines
@@ -18,12 +22,24 @@ def _show_ascii(G):
return "\n".join(ret)
def _show_dot(G):
def _quote_label(node):
label = str(node)
# Node names should not contain ":" unless they are quoted with "".
# See: https://github.com/pydot/pydot/issues/258.
if label[0] != '"' and label[-1] != '"':
return f'"{label}"'
return label
def _show_dot(G: "DiGraph"):
import io
import networkx as nx
from networkx.drawing.nx_pydot import write_dot
dot_file = io.StringIO()
nx.relabel_nodes(G, _quote_label, copy=False)
write_dot(G.reverse(), dot_file)
return dot_file.getvalue()
+1 -1
View File
@@ -33,7 +33,7 @@ def get_pipeline(pipelines, node):
return found[0]
def get_pipelines(G):
def get_pipelines(G: "DiGraph"):
import networkx as nx
return [G.subgraph(c).copy() for c in nx.weakly_connected_components(G)]
+1 -1
View File
@@ -51,7 +51,7 @@ install_requires =
packaging>=19.0
zc.lockfile>=1.2.1
flufl.lock>=5
networkx>=2.5,<2.8.5
networkx>=2.5
psutil>=5.8.0
pydot>=1.2.4
importlib-resources>=5.2.2; python_version < '3.9'
+40 -6
View File
@@ -139,18 +139,18 @@ def test_show_dot(repo):
# output doesn't depend upon order of lines. Use sorted values
# https://github.com/iterative/dvc/pull/7725
expected = [
"\"stage: '1'\";",
"\"stage: '2'\";",
"\"stage: '3'\" -> \"stage: '4'\";",
"\"stage: '3'\";",
"\"stage: '4'\";",
"\"stage: 'a.dvc'\" -> \"stage: '1'\";",
"\"stage: 'a.dvc'\" -> \"stage: '3'\";",
"\"stage: 'a.dvc'\" -> \"stage: '4'\";",
"\"stage: 'a.dvc'\";",
"\"stage: 'b.dvc'\" -> \"stage: '2'\";",
"\"stage: 'b.dvc'\" -> \"stage: '3'\";",
"stage;",
"stage;",
"stage;",
"stage;",
"stage;",
"stage;",
"\"stage: 'b.dvc'\";",
"strict digraph {",
"}",
]
@@ -160,6 +160,40 @@ def test_show_dot(repo):
assert actual == expected
def test_show_dot_properly_escapes():
graph = nx.DiGraph(
[
("evaluate", "train🚄"), # emoji
("evaluate", "featurize"),
("featurize", "prepare:1"), # colon
("prepare:1", "data/raw/1.dvc"), # posix path
("prepare:1", "data\\raw\\2.dvc"), # windows path
("prepare", "4"), # just a number
]
)
expected = {
"strict digraph {",
'"data\\raw\\2.dvc";',
'"prepare";',
'"4";',
'"data/raw/1.dvc";',
'"train🚄";',
'"evaluate";',
'"prepare:1";',
'"featurize";',
'"data\\raw\\2.dvc" -> "prepare:1";',
'"4" -> "prepare";',
'"data/raw/1.dvc" -> "prepare:1";',
'"train🚄" -> "evaluate";',
'"prepare:1" -> "featurize";',
'"featurize" -> "evaluate";',
"}",
}
actual = {line.rstrip() for line in _show_dot(graph).splitlines()}
assert actual == expected
def test_show_mermaid(repo):
assert [
line.rstrip() for line in _show_mermaid(repo.index.graph).splitlines()
+1 -1
View File
@@ -117,7 +117,7 @@ def test_import_url_to_remote(mocker):
)
def test_import_url_to_remote_invalid_combination(mocker, caplog):
def test_import_url_to_remote_invalid_combination(dvc, mocker, caplog):
cli_args = parse_args(
[
"import-url",