Skip to content

Commit

Permalink
[graph][json] Same json output for conan graph info, create, install …
Browse files Browse the repository at this point in the history
…and export-pkg (#13967)

* Ensuring the output for the rest of commands

* export-pkg too

* graph as first output level

* wip

* typo

* typos
  • Loading branch information
franramirez688 authored May 26, 2023
1 parent 28d737c commit 1b8f238
Show file tree
Hide file tree
Showing 14 changed files with 56 additions and 67 deletions.
17 changes: 6 additions & 11 deletions conan/cli/commands/create.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
import json
import os
import shutil

from conan.api.output import ConanOutput, cli_out_write
from conan.api.output import ConanOutput
from conan.cli.args import add_lockfile_args, add_common_install_arguments
from conan.cli.command import conan_command, OnceArgument
from conan.cli.commands.export import common_args_export
from conan.cli.args import add_lockfile_args, add_common_install_arguments
from conan.cli.formatters.graph import format_graph_json
from conan.cli.printers import print_profiles
from conan.cli.printers.graph import print_graph_packages, print_graph_basic
from conan.errors import ConanException
from conans.util.files import mkdir


def json_create(deps_graph):
if deps_graph is None:
return
cli_out_write(json.dumps({"graph": deps_graph.serialize()}, indent=4))


@conan_command(group="Creator", formatters={"json": json_create})
@conan_command(group="Creator", formatters={"json": format_graph_json})
def create(conan_api, parser, *args):
"""
Create a package.
Expand Down Expand Up @@ -102,7 +96,8 @@ def create(conan_api, parser, *args):
clean=args.lockfile_clean)

conan_api.lockfile.save_lockfile(lockfile, args.lockfile_out, cwd)
return deps_graph
return {"graph": deps_graph,
"conan_api": conan_api}


def _check_tested_reference_matches(deps_graph, tested_ref, out):
Expand Down
18 changes: 7 additions & 11 deletions conan/cli/commands/export_pkg.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import json
import os

from conan.api.output import cli_out_write, ConanOutput
from conan.cli.command import conan_command, OnceArgument
from conan.cli.args import add_lockfile_args, add_profiles_args, add_reference_args
from conan.api.output import ConanOutput
from conan.cli import make_abs_path
from conan.cli.args import add_lockfile_args, add_profiles_args, add_reference_args
from conan.cli.command import conan_command, OnceArgument
from conan.cli.commands.create import _get_test_conanfile_path
from conan.cli.formatters.graph import format_graph_json
from conan.cli.printers.graph import print_graph_basic


def json_export_pkg(info):
deps_graph = info
cli_out_write(json.dumps({"graph": deps_graph.serialize()}, indent=4))


@conan_command(group="Creator", formatters={"json": json_export_pkg})
@conan_command(group="Creator", formatters={"json": format_graph_json})
def export_pkg(conan_api, parser, *args):
"""
Create a package directly from pre-compiled binaries.
Expand Down Expand Up @@ -100,4 +95,5 @@ def export_pkg(conan_api, parser, *args):
# TODO: Do something with lockfile, same as create()

conan_api.lockfile.save_lockfile(lockfile, args.lockfile_out, cwd)
return deps_graph
return {"graph": deps_graph,
"conan_api": conan_api}
17 changes: 7 additions & 10 deletions conan/cli/commands/install.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import json
import os

from conan.api.output import ConanOutput, cli_out_write
from conan.api.output import ConanOutput
from conan.cli import make_abs_path
from conan.cli.args import common_graph_args, validate_common_graph_args
from conan.cli.command import conan_command
from conan.cli import make_abs_path
from conan.cli.formatters.graph import format_graph_json
from conan.cli.printers import print_profiles
from conan.cli.printers.graph import print_graph_packages, print_graph_basic


def json_install(info):
deps_graph = info
cli_out_write(json.dumps({"graph": deps_graph.serialize()}, indent=4))


@conan_command(group="Consumer", formatters={"json": json_install})
@conan_command(group="Consumer", formatters={"json": format_graph_json})
def install(conan_api, parser, *args):
"""
Install the requirements specified in a recipe (conanfile.py or conanfile.txt).
Expand Down Expand Up @@ -95,4 +90,6 @@ def install(conan_api, parser, *args):
lockfile = conan_api.lockfile.update_lockfile(lockfile, deps_graph, args.lockfile_packages,
clean=args.lockfile_clean)
conan_api.lockfile.save_lockfile(lockfile, args.lockfile_out, cwd)
return deps_graph
return {"graph": deps_graph,
"conan_api": conan_api}

8 changes: 4 additions & 4 deletions conan/cli/formatters/graph/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,11 @@ def format_graph_dot(result):

def format_graph_json(result):
graph = result["graph"]
field_filter = result["field_filter"]
package_filter = result["package_filter"]
field_filter = result.get("field_filter")
package_filter = result.get("package_filter")
serial = graph.serialize()
serial = filter_graph(serial, package_filter, field_filter)
json_result = json.dumps(serial, indent=4)
serial = filter_graph(serial, package_filter=package_filter, field_filter=field_filter)
json_result = json.dumps({"graph": serial}, indent=4)
cli_out_write(json_result)
if graph.error:
raise graph.error
2 changes: 1 addition & 1 deletion conan/cli/formatters/graph/graph_info_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from conan.api.output import ConanOutput


def filter_graph(graph, package_filter, field_filter=None):
def filter_graph(graph, package_filter=None, field_filter=None):
if package_filter is not None:
graph["nodes"] = {id_: n for id_, n in graph["nodes"].items()
if any(fnmatch.fnmatch(n["ref"] or "", p) for p in package_filter)}
Expand Down
5 changes: 3 additions & 2 deletions conans/test/functional/tools/system/package_manager_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ def system_requirements(self):
redirect_stdout="graph2.json")
graph2 = json.loads(client.load("graph2.json"))
# TODO: Unify format of ``graph info`` and ``install``
assert {"apt-get": {"install": ["pkg1", "pkg2"]}} == graph2["nodes"]["0"]["system_requires"]
assert {"apt-get": {"install": ["pkg1", "pkg2"]}} == \
graph2["graph"]["nodes"]["0"]["system_requires"]

# Check report-installed
with patch.object(_SystemPackageManagerTool, '_conanfile_run', MagicMock(return_value=True)):
Expand All @@ -195,7 +196,7 @@ def system_requirements(self):
redirect_stdout="graph2.json")
graph2 = json.loads(client.load("graph2.json"))
assert {"apt-get": {"install": ["pkg1", "pkg2"],
'missing': ['pkg1', 'pkg2']}} == graph2["nodes"]["0"]["system_requires"]
'missing': ['pkg1', 'pkg2']}} == graph2["graph"]["nodes"]["0"]["system_requires"]

# Default "check" will fail, as dpkg-query not installed
client.run("graph info . -c tools.system.package_manager:tool=apt-get "
Expand Down
4 changes: 2 additions & 2 deletions conans/test/integration/command/create_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ class MyTest(ConanFile):
pkg_pkg_ref = 'pkg/0.2#db78b8d06a78af5c3ac56706f133098d'
consumer_info = hello_pkg_info = pkg_pkg_info = None

for _, n in nodes.items():
for n in nodes.values():
ref = n["ref"]
if ref == consumer_ref:
consumer_info = n
Expand Down Expand Up @@ -586,7 +586,7 @@ def package_info(self):
hello_pkg_ref = 'hello/0.1#18d5440ae45afc4c36139a160ac071c7'
pkg_pkg_ref = 'pkg/0.2#926714b5fb0a994f47ec37e071eba1da'
hello_cpp_info = pkg_cpp_info = None
for _, n in nodes.items():
for n in nodes.values():
ref = n["ref"]
if ref == hello_pkg_ref:
assert n['binary'] == "Build"
Expand Down
2 changes: 1 addition & 1 deletion conans/test/integration/command/export_pkg_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ def package_info(self):
hello_pkg_ref = 'hello/0.1#18d5440ae45afc4c36139a160ac071c7'
pkg_pkg_ref = 'pkg/0.2#926714b5fb0a994f47ec37e071eba1da'
hello_cpp_info = pkg_cpp_info = None
for _, n in nodes.items():
for n in nodes.values():
ref = n["ref"]
if ref == hello_pkg_ref:
assert n['binary'] is None # The exported package has no binary status
Expand Down
10 changes: 5 additions & 5 deletions conans/test/integration/command/info/info_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class MyTest(ConanFile):
""")
client.save({"conanfile.py": conanfile})
client.run("graph info . --format=json")
recipe = json.loads(client.stdout)["nodes"]["0"]
recipe = json.loads(client.stdout)["graph"]["nodes"]["0"]
assert type(recipe["topics"]) == list
assert recipe["topics"] == ["foo"]
assert type(recipe["provides"]) == list
Expand All @@ -90,7 +90,7 @@ class MyTest(ConanFile):
""")
client2.save({"conanfile.py": conanfile2})
client2.run("graph info . --format=json")
recipe = json.loads(client2.stdout)["nodes"]["0"]
recipe = json.loads(client2.stdout)["graph"]["nodes"]["0"]
assert type(recipe["topics"]) == list
assert recipe["topics"] == ["foo"]
assert type(recipe["provides"]) == list
Expand Down Expand Up @@ -166,15 +166,15 @@ def test_json_package_filter(self):
assert '"nodes": {}' in client.out
client.run("graph info . --package-filter=pkg* --format=json")
graph = json.loads(client.stdout)
assert graph["nodes"]["0"]["ref"] == "pkg/0.1"
assert graph["graph"]["nodes"]["0"]["ref"] == "pkg/0.1"

def test_json_info_outputs(self):
client = TestClient()
conanfile = GenConanfile("pkg", "0.1").with_setting("build_type")
client.save({"conanfile.py": conanfile})
client.run("graph info . -s build_type=Debug --format=json")
graph = json.loads(client.stdout)
assert graph["nodes"]["0"]["settings"]["build_type"] == "Debug"
assert graph["graph"]["nodes"]["0"]["settings"]["build_type"] == "Debug"


class TestAdvancedCliOutput:
Expand All @@ -199,7 +199,7 @@ class pkg(ConanFile):

client.run("graph info . --format=json")
info = json.loads(client.stdout)
assert info["nodes"]["0"]["python_requires"] == ['tool/0.1#4d670581ccb765839f2239cc8dff8fbd']
assert info["graph"]["nodes"]["0"]["python_requires"] == ['tool/0.1#4d670581ccb765839f2239cc8dff8fbd']

def test_build_id_info(self):
client = TestClient()
Expand Down
12 changes: 6 additions & 6 deletions conans/test/integration/command/info/test_info_folders.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def test_basic():
client.save({CONANFILE: conanfile_py})
client.run(f"export . --user=user --channel=testing")
client.run(f"graph info --requires=package/0.1.0@user/testing --format=json")
nodes = json.loads(client.stdout)["nodes"]
nodes = json.loads(client.stdout)["graph"]["nodes"]
assert client.cache_folder in nodes["1"]["recipe_folder"]
assert os.path.basename(nodes["1"]["recipe_folder"]).strip() == EXPORT_FOLDER
assert nodes["1"]["source_folder"] is None
Expand Down Expand Up @@ -84,9 +84,9 @@ def test_deps_basic(client_deps):
client_deps.run(f"graph info {ref} --format=json")
nodes = json.loads(client_deps.stdout)
found_ref = False
assert len(nodes["nodes"]) == 3
assert len(nodes["graph"]["nodes"]) == 3

for _, node in nodes["nodes"].items():
for _, node in nodes["graph"]["nodes"].items():
if node["ref"] == "conanfile":
assert node["source_folder"] is None
else:
Expand All @@ -101,15 +101,15 @@ def test_deps_basic(client_deps):

def test_deps_specific_information(client_deps):
client_deps.run("graph info . --package-filter package/* --format=json")
nodes = json.loads(client_deps.stdout)["nodes"]
nodes = json.loads(client_deps.stdout)["graph"]["nodes"]
assert len(nodes) == 1
assert "package/0.1.0@user/testing" in nodes["2"]["ref"]
assert nodes["2"]["source_folder"] is None
assert nodes["2"]["build_folder"] is None
assert nodes["2"]["package_folder"] is None

client_deps.run("graph info . --package-filter package* --format=json")
nodes = json.loads(client_deps.stdout)["nodes"]
nodes = json.loads(client_deps.stdout)["graph"]["nodes"]
assert len(nodes) == 2
assert "package2/0.2.0@user/testing" in nodes["1"]["ref"]
assert nodes["1"]["source_folder"] is None
Expand All @@ -126,7 +126,7 @@ def test_single_field():
client.save({CONANFILE: conanfile_py})
client.run(f"export . --user=user --channel=testing")
client.run(f"graph info --requires package/0.1.0@user/testing --format=json")
nodes = json.loads(client.stdout)["nodes"]
nodes = json.loads(client.stdout)["graph"]["nodes"]
assert len(nodes) == 2
assert "package/0.1.0@user/testing" in nodes["1"]["ref"]
assert nodes["1"]["source_folder"] is None
Expand Down
4 changes: 2 additions & 2 deletions conans/test/integration/graph/test_validate_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ def package_id(self):

# What happens with a conan info?
t.run(f"graph info --requires=foo/1.0 {settings_gcc} --format=json", redirect_stdout="myjson")
myjson = json.loads(t.load("myjson"))["nodes"]
myjson = json.loads(t.load("myjson"))["graph"]["nodes"]
assert myjson["1"]["invalid_build"] == "This doesn't build in GCC"

t.run(f"graph info --requires=foo/1.0 {settings_clang} --format=json", redirect_stdout="myjson")
myjson = json.loads(t.load("myjson"))["nodes"]
myjson = json.loads(t.load("myjson"))["graph"]["nodes"]
assert myjson["1"]["invalid_build"] is False


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,9 @@ def test_override(self):
"ros_core/pr-53@3rdparty/snapshot#4d670581ccb765839f2239cc8dff8fbd"
]
}
self.assertEqual(info["overrides"], expected_overrides)
self.assertEqual(info['graph']["overrides"], expected_overrides)
expected_resolved_ranges = {
"pkgb/[~0]@common/unstable": "pkgb/0.1@common/unstable",
"ros_perception/[~1.1]@3rdparty/unstable": "ros_perception/1.1.4@3rdparty/unstable"
}
self.assertEqual(info["resolved_ranges"], expected_resolved_ranges)
self.assertEqual(info['graph']["resolved_ranges"], expected_resolved_ranges)
16 changes: 8 additions & 8 deletions conans/test/integration/lockfile/test_graph_overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ def test_overrides_half_diamond(override, force):
assert "pkga/0.2" in requires
assert "pkga/0.1" not in requires
c.run("graph info pkgc --lockfile=pkgc/conan.lock --format=json")
dependencies = json.loads(c.stdout)["nodes"]["0"]["dependencies"]
dependencies = json.loads(c.stdout)["graph"]["nodes"]["0"]["dependencies"]
assert "pkga/0.2" in str(dependencies)
assert "pkga/0.1" not in str(dependencies)
# apply the lockfile to pkgb, should it lock to pkga/0.2
c.run("graph info pkgb --lockfile=pkgc/conan.lock --format=json")
dependencies = json.loads(c.stdout)["nodes"]["0"]["dependencies"]
dependencies = json.loads(c.stdout)["graph"]["nodes"]["0"]["dependencies"]
assert "pkga/0.2" in str(dependencies)
assert "pkga/0.1" not in str(dependencies)

Expand Down Expand Up @@ -126,25 +126,25 @@ def test_overrides_diamond(override, force):
assert "pkga/0.1" not in requires
c.run("graph info pkgd --lockfile=pkgd/conan.lock --format=json")
json_graph = json.loads(c.stdout)
deps = json_graph["nodes"]["0"]["dependencies"]
deps = json_graph["graph"]["nodes"]["0"]["dependencies"]
assert "pkga/0.3" in str(deps)
assert "pkga/0.2" not in str(deps)
assert "pkga/0.1" not in str(deps)
# Redundant assert, but checking "overrides" summary
overrides = json_graph["overrides"]
overrides = json_graph['graph']["overrides"]
assert len(overrides) == 2
assert overrides['pkga/0.1'] == ['pkga/0.3']
assert overrides['pkga/0.2'] == ['pkga/0.3#e3afb975277bc245212d6e8de88f4f8f']

# apply the lockfile to pkgb, should it lock to pkga/0.3
c.run("graph info pkgb --lockfile=pkgd/conan.lock --format=json")
json_graph = json.loads(c.stdout)
deps = json_graph["nodes"]["0"]["dependencies"]
deps = json_graph["graph"]["nodes"]["0"]["dependencies"]
assert "pkga/0.3" in str(deps)
assert "pkga/0.2" not in str(deps)
assert "pkga/0.1" not in str(deps)
# Redundant assert, but checking "overrides" summary
overrides = json_graph["overrides"]
overrides = json_graph['graph']["overrides"]
assert len(overrides) == 1
assert overrides["pkga/0.1"] == ["pkga/0.3"]

Expand Down Expand Up @@ -178,13 +178,13 @@ def test_overrides_diamond_ranges(override, force):
assert "pkga/0.2" not in requires
assert "pkga/0.1" not in requires
c.run("graph info pkgd --lockfile=pkgd/conan.lock --format=json")
dependencies = json.loads(c.stdout)["nodes"]["0"]["dependencies"]
dependencies = json.loads(c.stdout)["graph"]["nodes"]["0"]["dependencies"]
assert "pkga/0.3" in str(dependencies)
assert "pkga/0.2" not in str(dependencies)
assert "pkga/0.1" not in str(dependencies)
# apply the lockfile to pkgb, should it lock to pkga/0.3
c.run("graph info pkgb --lockfile=pkgd/conan.lock --format=json")
dependencies = json.loads(c.stdout)["nodes"]["0"]["dependencies"]
dependencies = json.loads(c.stdout)["graph"]["nodes"]["0"]["dependencies"]
assert "pkga/0.3" in str(dependencies)
assert "pkga/0.2" not in str(dependencies)
assert "pkga/0.1" not in str(dependencies)
Expand Down
4 changes: 2 additions & 2 deletions conans/test/integration/package_id/test_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ def validate(self):

client.run("graph info --require pkg/0.1 -s os=Windows --format json")
myjson = json.loads(client.stdout)
self.assertEqual(myjson["nodes"]["1"]["binary"], BINARY_INVALID)
assert myjson["nodes"]["1"]["info_invalid"] == "Windows not supported" in client.out
self.assertEqual(myjson["graph"]["nodes"]["1"]["binary"], BINARY_INVALID)
assert myjson["graph"]["nodes"]["1"]["info_invalid"] == "Windows not supported" in client.out

def test_validate_header_only(self):
client = TestClient()
Expand Down

0 comments on commit 1b8f238

Please sign in to comment.