Skip to content

Commit

Permalink
Fix --format=html output for graphs with conflict errors (#14190)
Browse files Browse the repository at this point in the history
* Fix --format=html output for graphs with conflict errors

* Simplify tests
  • Loading branch information
AbrilRBS authored Jul 6, 2023
1 parent ebee60f commit 13c2acd
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 38 deletions.
20 changes: 7 additions & 13 deletions conan/cli/formatters/graph/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,20 +105,14 @@ def format_graph_html(result):
template_folder = os.path.join(conan_api.cache_folder, "templates")
user_template = os.path.join(template_folder, "graph.html")
template = load(user_template) if os.path.isfile(user_template) else graph_info_html
error = {
"type": "unknown",
"context": graph.error,
"should_highlight_node": lambda node: False
}
if isinstance(graph.error, GraphConflictError):
error = {
"label": graph.error.require.ref,
"type": "conflict",
"from": graph.error.node.id or graph.error.base_previous.id,
"prev_node": graph.error.prev_node,
"should_highlight_node": lambda node: node.id == graph.error.node.id
}
else:
error = {
"type": "unknown",
"error:": graph.error,
"should_highlight_node": lambda node: False
}
error["type"] = "conflict"
error["should_highlight_node"] = lambda node: node.id == graph.error.node.id
cli_out_write(_render_graph(graph, error, template, template_folder))
if graph.error:
raise graph.error
Expand Down
53 changes: 37 additions & 16 deletions conan/cli/formatters/graph/info_graph_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,32 +79,53 @@
]
{% if error is not none and error["type"] == "conflict" %}
// Add some helpful visualizations for conflicts
// Add error conflict node
nodes.push({
id: "{{ error["type"] }}",
label: "{{ error["label"] }}",
label: "{{ error["context"].require.ref }}",
shape: "circle",
font: { color: "white" },
color: "red",
fulllabel: '<h3>{{ error["label"] }}</h3><p>This node creates a conflict in the dependency graph with {{ error["prev_node"]["ref"] }}</p>',
fulllabel: '<h3>{{ error["context"].require.ref }}</h3><p>This node creates a conflict in the dependency graph</p>',
shapeProperties: { borderDashes: [5, 5] }
})
{% if error["from"] is not none %}
edges.push({from: {{ error["from"] }}, to: "{{ error["type"] }}", color: "red", dashes: true, title: "Conflict"})
{% if error["context"].node.id is not none %}
// Add edge from node that introduces the conflict to the new error node
edges.push({from: {{ error["context"].node.id }},
to: "{{ error["type"] }}",
color: "red",
dashes: true,
title: "Conflict",
physics: false,
color: "red",
arrows: "to;from"})
{% endif %}
{% if error["context"].prev_node is none and error["context"].base_previous.id is not none %}
// Add edge from base node to the new error node
edges.push({from: {{ error["context"].base_previous.id }},
to: "{{ error["type"] }}",
color: "red",
dashes: true,
title: "Conflict",
physics: false,
color: "red",
arrows: "to;from"})
{% endif %}
{% if error["context"].prev_node is not none and error["context"].prev_node.id is not none %}
// Add edge from previous node that already had conflicting dependency
edges.push({from: {{ error["context"].prev_node.id }},
to: "{{ error["type"] }}",
color: "red",
dashes: true,
title: "Conflict",
physics: false,
color: "red",
arrows: "to;from"})
{% endif %}
edges.push({from: {{ error["from"] }},
to: "{{ error["type"] }}",
dashes: true,
title: "Conflict",
physics: false,
color: "red",
arrows: "to;from"})
edges.push({from: {{ error["prev_node"].id }},
to: "{{ error["type"] }}",
color: "red",
title: "Problematic require"})
{% endif %}
var nodes = new vis.DataSet(nodes);
Expand Down
36 changes: 27 additions & 9 deletions conans/test/integration/command/info/test_graph_info_graphical.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,31 @@ def test_user_templates():

def test_graph_info_html_output():
tc = TestClient()
tc.run("new cmake_lib -d name=lib -d version=1.0")
tc.run("export .")
tc.run("new cmake_lib -d name=lib -d version=2.0 -f")
tc.run("export .")
tc.run("new cmake_lib -d name=ui -d version=1.0 -d requires=lib/1.0 -f")
tc.run("export .")
tc.run("new cmake_lib -d name=math -d version=1.0 -d requires=lib/2.0 -f")
tc.run("export .")
tc.save({"lib/conanfile.py": GenConanfile("lib"),
"ui/conanfile.py": GenConanfile("ui", "1.0").with_requirement("lib/1.0"),
"math/conanfile.py": GenConanfile("math", "1.0").with_requirement("lib/2.0"),
"libiconv/conanfile.py": GenConanfile("libiconv", "1.0").with_requirement("lib/2.0"),
"boost/conanfile.py": GenConanfile("boost", "1.0").with_requirement("libiconv/1.0"),
"openimageio/conanfile.py": GenConanfile("openimageio", "1.0").with_requirement("boost/1.0").with_requirement("lib/1.0")})
tc.run("export lib/ --version=1.0")
tc.run("export lib/ --version=2.0")
tc.run("export ui")
tc.run("export math")
tc.run("export libiconv")
tc.run("export boost")
tc.run("export openimageio")

tc.run("graph info --requires=math/1.0 --requires=ui/1.0 --format=html", assert_error=True)
assert "// Add some helpful visualizations for conflicts" in tc.out
assert "// Add error conflict node" in tc.out
assert "// Add edge from node that introduces the conflict to the new error node" in tc.out
assert "// Add edge from base node to the new error node" not in tc.out
assert "// Add edge from previous node that already had conflicting dependency" in tc.out

tc.run("graph info openimageio/ --format=html", assert_error=True)
assert "// Add error conflict node" in tc.out
assert "// Add edge from node that introduces the conflict to the new error node" in tc.out
assert "// Add edge from base node to the new error node" in tc.out
assert "// Add edge from previous node that already had conflicting dependency" not in tc.out
# There used to be a few bugs with weird graphs, check for regressions
assert "jinja2.exceptions.UndefinedError" not in tc.out
assert "from: ," not in tc.out

0 comments on commit 13c2acd

Please sign in to comment.