Skip to content

Commit

Permalink
conan list --format=compact (#15011)
Browse files Browse the repository at this point in the history
* conan list --forma=compact

* new test for no options no settings
  • Loading branch information
memsharded authored Oct 25, 2023
1 parent f36b629 commit 0d02774
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 4 deletions.
62 changes: 61 additions & 1 deletion conan/cli/commands/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,65 @@ def format_timestamps(item):
print_serial(info)


def print_list_compact(results):
info = results["results"]
# Extract command single package name
new_info = {}

for remote, remote_info in info.items():
if not remote_info or "error" in remote_info:
new_info[remote] = {"warning": "There are no matching recipe references"}
continue
new_remote_info = {}
for ref, ref_info in remote_info.items():
new_ref_info = {}
for rrev, rrev_info in ref_info.get("revisions", {}).items():
new_rrev_info = {}
new_rrev = f"{ref}#{rrev}"
timestamp = rrev_info.get("timestamp")
if timestamp:
new_rrev += f" ({timestamp_to_str(timestamp)})"
# collect all options
common_options = {}
for pid, pid_info in rrev_info.get("packages", {}).items():
options = pid_info.get("info", {}).get("options", {})
common_options.update(options)
for pid, pid_info in rrev_info.get("packages", {}).items():
options = pid_info.get("info", {}).get("options")
if options: # If a package has no options, like header-only, skip
common_options = {k: v for k, v in common_options.items()
if k in options and v == options[k]}
for pid, pid_info in rrev_info.get("packages", {}).items():
options = pid_info.get("info", {}).get("options")
if options:
for k, v in options.items():
if v != common_options.get(k):
common_options.pop(k, None)
# format options
for pid, pid_info in rrev_info.get("packages", {}).items():
new_pid = f"{ref}#{rrev}:{pid}"
new_pid_info = {}
info = pid_info.get("info")
settings = info.get("settings")
if settings: # A bit of pretty order, first OS-ARCH
values = [settings.pop(s, None)
for s in ("os", "arch", "build_type", "compiler")]
values = [v for v in values if v is not None]
values.extend(settings.values())
new_pid_info["settings"] = ", ".join(values)
options = info.get("options")
if options:
diff_options = {k: v for k, v in options.items() if k not in common_options}
options = ", ".join(f"{k}={v}" for k, v in diff_options.items())
new_pid_info["options(diff)"] = options
new_rrev_info[new_pid] = new_pid_info
new_ref_info[new_rrev] = new_rrev_info
new_remote_info[ref] = new_ref_info
new_info[remote] = new_remote_info

print_serial(new_info)


def print_list_json(data):
results = data["results"]
myjson = json.dumps(results, indent=4)
Expand All @@ -90,7 +149,8 @@ def print_list_json(data):

@conan_command(group="Consumer", formatters={"text": print_list_text,
"json": print_list_json,
"html": list_packages_html})
"html": list_packages_html,
"compact": print_list_compact})
def list(conan_api: ConanAPI, parser, *args):
"""
List existing recipes, revisions, or packages in the cache (by default) or the remotes.
Expand Down
50 changes: 47 additions & 3 deletions conans/test/integration/command_v2/list_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -731,9 +731,8 @@ def test_list_html(self):
c.run("create dep")
c.run("create pkg -s os=Windows -s arch=x86")
# Revision is needed explicitly!
c.run("list pkg/2.3.4#latest --format=html", redirect_stdout="table.html")
table = c.load("table.html")
assert "<!DOCTYPE html>" in table
c.run("list pkg/2.3.4#latest --format=html")
assert "<!DOCTYPE html>" in c.stdout
# TODO: The actual good html is missing

def test_list_html_custom(self):
Expand All @@ -747,3 +746,48 @@ def test_list_html_custom(self):
c.save({"list_packages.html": '{{ base_template_path }}'}, path=template_folder)
c.run("list lib/0.1#latest --format=html")
assert template_folder in c.stdout


class TestListCompact:
def test_list_compact(self):
c = TestClient()
c.save({"conanfile.py": GenConanfile("pkg", "1.0").with_settings("os", "arch")
.with_shared_option(False)})
c.run("create . -s os=Windows -s arch=x86")
c.run("create . -s os=Linux -s arch=armv8")
c.run("create . -s os=Macos -s arch=armv8 -o shared=True")
c.run("list pkg:* --format=compact")

expected = textwrap.dedent("""\
pkg/1.0#03591c8b22497dd74214e08b3bf2a56f:2a67a51fbf36a4ee345b2125dd2642be60ffd3ec
settings: Macos, armv8
options(diff): shared=True
pkg/1.0#03591c8b22497dd74214e08b3bf2a56f:2d46abc802bbffdf2af11591e3e452bc6149ea2b
settings: Linux, armv8
options(diff): shared=False
pkg/1.0#03591c8b22497dd74214e08b3bf2a56f:d2e97769569ac0a583d72c10a37d5ca26de7c9fa
settings: Windows, x86
options(diff): shared=False
""")
assert textwrap.indent(expected, " ") in c.stdout

def test_list_compact_no_settings_no_options(self):
c = TestClient()
c.save({"pkg/conanfile.py": GenConanfile("pkg", "1.0").with_settings("os", "arch"),
"other/conanfile.py": GenConanfile("other", "1.0")})
c.run("create pkg -s os=Windows -s arch=x86")
c.run("create other")
c.run("list *:* --format=compact")
expected_output = re.sub(r"\(.*\)", "(timestamp)", c.stdout)
expected = textwrap.dedent("""\
Local Cache
other/1.0
other/1.0#d3c8cc5e6d23ca8c6f0eaa6285c04cbd (timestamp)
other/1.0#d3c8cc5e6d23ca8c6f0eaa6285c04cbd:da39a3ee5e6b4b0d3255bfef95601890afd80709
pkg/1.0
pkg/1.0#d24b74828b7681f08d8f5ba0e7fd791e (timestamp)
pkg/1.0#d24b74828b7681f08d8f5ba0e7fd791e:c11e463c49652ba9c5adc62573ee49f966bd8417
settings: Windows, x86
""")

assert expected == expected_output

0 comments on commit 0d02774

Please sign in to comment.