diff --git a/conan/cli/commands/create.py b/conan/cli/commands/create.py index f1d81814f51..361d60941d8 100644 --- a/conan/cli/commands/create.py +++ b/conan/cli/commands/create.py @@ -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. @@ -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): diff --git a/conan/cli/commands/export_pkg.py b/conan/cli/commands/export_pkg.py index b030ce165e2..673330836c6 100644 --- a/conan/cli/commands/export_pkg.py +++ b/conan/cli/commands/export_pkg.py @@ -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. @@ -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} diff --git a/conan/cli/commands/install.py b/conan/cli/commands/install.py index f6934fb0e48..74c514a5944 100644 --- a/conan/cli/commands/install.py +++ b/conan/cli/commands/install.py @@ -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). @@ -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} + diff --git a/conan/cli/formatters/graph/graph.py b/conan/cli/formatters/graph/graph.py index 878a90758a0..92bb7635864 100644 --- a/conan/cli/formatters/graph/graph.py +++ b/conan/cli/formatters/graph/graph.py @@ -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 diff --git a/conan/cli/formatters/graph/graph_info_text.py b/conan/cli/formatters/graph/graph_info_text.py index 2da47f2172e..d7ad8d9c904 100644 --- a/conan/cli/formatters/graph/graph_info_text.py +++ b/conan/cli/formatters/graph/graph_info_text.py @@ -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)} diff --git a/conans/test/functional/tools/system/package_manager_test.py b/conans/test/functional/tools/system/package_manager_test.py index d9b485020b5..8ec00fb5a50 100644 --- a/conans/test/functional/tools/system/package_manager_test.py +++ b/conans/test/functional/tools/system/package_manager_test.py @@ -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)): @@ -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 " diff --git a/conans/test/integration/command/create_test.py b/conans/test/integration/command/create_test.py index 72716cae5e4..1749d850230 100644 --- a/conans/test/integration/command/create_test.py +++ b/conans/test/integration/command/create_test.py @@ -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 @@ -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" diff --git a/conans/test/integration/command/export_pkg_test.py b/conans/test/integration/command/export_pkg_test.py index 52beb043c6c..7e8d1d0d717 100644 --- a/conans/test/integration/command/export_pkg_test.py +++ b/conans/test/integration/command/export_pkg_test.py @@ -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 diff --git a/conans/test/integration/command/info/info_test.py b/conans/test/integration/command/info/info_test.py index c33e404d0cc..baa0212c7f5 100644 --- a/conans/test/integration/command/info/info_test.py +++ b/conans/test/integration/command/info/info_test.py @@ -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 @@ -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 @@ -166,7 +166,7 @@ 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() @@ -174,7 +174,7 @@ def test_json_info_outputs(self): 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: @@ -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() diff --git a/conans/test/integration/command/info/test_info_folders.py b/conans/test/integration/command/info/test_info_folders.py index e8eda3928cd..8d27599afa0 100644 --- a/conans/test/integration/command/info/test_info_folders.py +++ b/conans/test/integration/command/info/test_info_folders.py @@ -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 @@ -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: @@ -101,7 +101,7 @@ 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 @@ -109,7 +109,7 @@ def test_deps_specific_information(client_deps): 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 @@ -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 diff --git a/conans/test/integration/graph/test_validate_build.py b/conans/test/integration/graph/test_validate_build.py index 5e263e1d482..926362eac1f 100644 --- a/conans/test/integration/graph/test_validate_build.py +++ b/conans/test/integration/graph/test_validate_build.py @@ -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 diff --git a/conans/test/integration/graph/version_ranges/version_range_override_test.py b/conans/test/integration/graph/version_ranges/version_range_override_test.py index e13cd51b158..134a086d1ba 100644 --- a/conans/test/integration/graph/version_ranges/version_range_override_test.py +++ b/conans/test/integration/graph/version_ranges/version_range_override_test.py @@ -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) diff --git a/conans/test/integration/lockfile/test_graph_overrides.py b/conans/test/integration/lockfile/test_graph_overrides.py index cf2b6a9dcae..bae9f6817a4 100644 --- a/conans/test/integration/lockfile/test_graph_overrides.py +++ b/conans/test/integration/lockfile/test_graph_overrides.py @@ -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) @@ -126,12 +126,12 @@ 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'] @@ -139,12 +139,12 @@ def test_overrides_diamond(override, force): # 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"] @@ -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) diff --git a/conans/test/integration/package_id/test_validate.py b/conans/test/integration/package_id/test_validate.py index 13d3ca3a6a2..e2d43a1c551 100644 --- a/conans/test/integration/package_id/test_validate.py +++ b/conans/test/integration/package_id/test_validate.py @@ -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()