Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dev2][conan list] Enhancements #12781

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion conan/api/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,9 @@ def prefs(self):
return prefs

def add_prefs(self, prefs, configurations=None):
confs = configurations or {}
for pref in prefs:
binary_info = {} if not configurations else configurations.get(pref)
binary_info = confs.get(pref, {})
self.recipes.setdefault(pref.ref, []).append((pref, binary_info))

def serialize(self):
Expand Down
14 changes: 7 additions & 7 deletions conan/api/subapi/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,17 @@ def filter_packages_configurations(self, pkg_configurations, query):
return filter_packages(query, pkg_configurations)

# TODO: could it be possible to merge this with subapi.search.select?
def select(self, pattern, package_query=None, remote=None):
def select(self, pattern, package_query=None, remote=None, search_mode=None):
if package_query and pattern.package_id and "*" not in pattern.package_id:
raise ConanException("Cannot specify '-p' package queries, "
"if 'package_id' is not a pattern")
list_mode = pattern.mode
search_mode = search_mode or pattern.mode
select_bundle = SelectBundle()
refs = self.conan_api.search.recipes(pattern.ref, remote=remote)
pattern.check_refs(refs)

# Show only the recipe references
if list_mode == ListPatternMode.SHOW_REFS:
if search_mode == ListPatternMode.SHOW_REFS:
select_bundle.add_refs(refs)
return select_bundle

Expand All @@ -111,7 +111,7 @@ def select(self, pattern, package_query=None, remote=None):
rrevs = pattern.filter_rrevs(rrevs)
select_bundle.add_refs(rrevs)
# Show only the latest recipe revision or all of them
if list_mode in (ListPatternMode.SHOW_ALL_RREVS, ListPatternMode.SHOW_LATEST_RREV):
if search_mode in (ListPatternMode.SHOW_ALL_RREVS, ListPatternMode.SHOW_LATEST_RREV):
continue

for rrev in rrevs:
Expand All @@ -124,14 +124,14 @@ def select(self, pattern, package_query=None, remote=None):
prefs = pattern.filter_prefs(prefs)

# Show all the package IDs and their configurations
if list_mode == ListPatternMode.SHOW_PACKAGE_IDS:
if search_mode == ListPatternMode.SHOW_PACKAGE_IDS:
# add pref and its package configuration
select_bundle.add_prefs(prefs, configurations=packages)
continue

for pref in prefs:
if list_mode in (ListPatternMode.SHOW_LATEST_PREV,
ListPatternMode.SHOW_ALL_PREVS):
if search_mode in (ListPatternMode.SHOW_LATEST_PREV,
ListPatternMode.SHOW_ALL_PREVS):
if pattern.is_latest_prev:
prevs = [self.conan_api.list.latest_package_revision(pref, remote)]
else:
Expand Down
34 changes: 22 additions & 12 deletions conan/cli/commands/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from conan.cli.command import conan_command, OnceArgument
from conan.cli.commands import ConanJSONEncoder
from conan.cli.formatters.list import list_packages_html
from conan.internal.api.select_pattern import ListPattern
from conan.internal.api.select_pattern import ListPattern, ListPatternMode
from conans.util.dates import timestamp_to_str

remote_color = Color.BRIGHT_BLUE
Expand All @@ -18,9 +18,9 @@


def print_list_text(results):
results = results["results"]

for remote, refs in results.items():
info = results["results"]
search_mode = results["search_mode"]
for remote, refs in info.items():
cli_out_write(f"{remote}:", fg=remote_color)

if refs.get("error"):
Expand All @@ -43,13 +43,17 @@ def print_list_text(results):
if prefs:
for pref, binary_info in prefs:
pref_date = f" ({timestamp_to_str(pref.timestamp)})" if pref.timestamp else ""
cli_out_write(f"{indentation * 3}PID: {pref.package_id}{pref_date}",
fg=reference_color)
if not binary_info and pref.revision:
cli_out_write(f"{indentation * 4}PREV: {pref.revision}", fg=field_color)
continue
elif not binary_info:
cli_out_write(f"{indentation * 4}No package info/revision was found.",
if search_mode == ListPatternMode.SHOW_PACKAGE_IDS:
cli_out_write(f"{indentation * 3}PID: {pref.package_id}{pref_date}",
fg=reference_color)
if not binary_info:
cli_out_write(f"{indentation * 4}Empty package information",
fg=field_color)
continue
elif search_mode in (ListPatternMode.SHOW_ALL_PREVS,
ListPatternMode.SHOW_LATEST_PREV):
cli_out_write(f"{indentation * 3}PID: {pref.package_id}", fg=reference_color)
cli_out_write(f"{indentation * 4}PREV: {pref.revision}{pref_date}",
fg=field_color)
continue
for item, contents in binary_info.items():
Expand All @@ -62,6 +66,11 @@ def print_list_text(results):
else:
for c in contents:
cli_out_write(f"{indentation * 5}{c}", fg=value_color)
elif search_mode in (ListPatternMode.SHOW_PACKAGE_IDS,
ListPatternMode.SHOW_ALL_PREVS,
ListPatternMode.SHOW_LATEST_PREV):
cli_out_write(f"{indentation * 3}There are no packages for this revision.",
fg=field_color)


def print_list_json(data):
Expand Down Expand Up @@ -89,6 +98,7 @@ def list(conan_api: ConanAPI, parser, *args):
parser.add_argument("-c", "--cache", action='store_true', help="Search in the local cache")

args = parser.parse_args(*args)
ref_pattern = ListPattern(args.reference)
# If neither remote nor cache are defined, show results only from cache
remotes = []
if args.cache or not args.remote:
Expand All @@ -98,7 +108,6 @@ def list(conan_api: ConanAPI, parser, *args):
results = {}
for remote in remotes:
name = getattr(remote, "name", "Local Cache")
ref_pattern = ListPattern(args.reference)
try:
list_bundle = conan_api.list.select(ref_pattern, args.package_query, remote)
except Exception as e:
Expand All @@ -108,5 +117,6 @@ def list(conan_api: ConanAPI, parser, *args):
else list_bundle.recipes
return {
"results": results,
"search_mode": ref_pattern.mode,
"conan_api": conan_api
}
14 changes: 9 additions & 5 deletions conan/cli/commands/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
from conan.api.conan_api import ConanAPI
from conan.cli.command import conan_command
from conan.cli.commands.list import print_list_text, print_list_json
from conan.internal.api.select_pattern import ListPattern
from conan.internal.api.select_pattern import ListPattern, ListPatternMode
from conans.errors import ConanException


# FIXME: "conan search" == "conan list recipes -r="*" -c" --> implement @conan_alias_command??
# FIXME: "conan search" == "conan list (*) -r="*"" --> implement @conan_alias_command??
@conan_command(group="Consumer", formatters={"text": print_list_text,
"json": print_list_json})
def search(conan_api: ConanAPI, parser, *args):
Expand All @@ -20,21 +20,25 @@ def search(conan_api: ConanAPI, parser, *args):
help="Remote names. Accepts wildcards. If not specified it searches "
"in all the remotes")
args = parser.parse_args(*args)
ref_pattern = ListPattern(args.reference)
search_mode = ListPatternMode.SHOW_REFS

remotes = conan_api.remotes.list(args.remote)
if not remotes:
raise ConanException("There are no remotes to search from")

results = OrderedDict()
for remote in remotes:
ref_pattern = ListPattern(args.reference)
try:
list_bundle = conan_api.list.select(ref_pattern, package_query=None, remote=remote)
list_bundle = conan_api.list.select(ref_pattern, package_query=None,
remote=remote, search_mode=search_mode)
except Exception as e:
results[remote.name] = {"error": str(e)}
else:
results[remote.name] = list_bundle.serialize() if args.format == "json" else list_bundle.recipes
results[remote.name] = list_bundle.serialize() if args.format == "json" \
else list_bundle.recipes
return {
"results": results,
"search_mode": search_mode,
"conan_api": conan_api
}
12 changes: 9 additions & 3 deletions conan/internal/api/select_pattern.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def mode(self):
# zlib:PID -> it will fail on server side! Nothing to do here
else:
if self.package_id is None:
# zlib/1.2.11
# zlib/1.2.11 | zlib/1.2.11#latest
if self.is_latest_rrev:
return ListPatternMode.SHOW_LATEST_RREV
# zlib/1.2.11#RREV
Expand All @@ -100,15 +100,21 @@ def mode(self):
else:
if self.package_id is None:
# zlib/* | zlib*
if self.is_latest_rrev:
if self.is_latest_rrev and "#latest" not in self.raw:
return ListPatternMode.SHOW_REFS
# zlib/*#latest
elif "#latest" in self.raw:
return ListPatternMode.SHOW_LATEST_RREV
# zlib/1.2.11#*
elif "*" in self.rrev:
return ListPatternMode.SHOW_ALL_RREVS
else:
# zlib/1.2.11#latest:* | zlib/1.2.11#RREV:*
if self.is_latest_prev:
if self.is_latest_prev and "#latest" not in self.raw:
return ListPatternMode.SHOW_PACKAGE_IDS
# zlib/1.2.11:*#latest
elif "#latest" in self.raw:
return ListPatternMode.SHOW_LATEST_PREV
# zlib/1.2.11#latest:*#* | zlib/1.2.11#RREV:PID#*
elif "*" in self.prev:
return ListPatternMode.SHOW_ALL_PREVS
Expand Down
56 changes: 50 additions & 6 deletions conans/test/integration/command_v2/list_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def test_list_recipes(self):
""")
assert expected_output == self.client.out

def test_list_latest_recipe_revision(self):
def test_list_latest_recipe_revision_by_default(self):
self.client.save({
"conanfile.py": GenConanfile("test_recipe", "1.0.0").with_package_file("file.h", "0.1")
})
Expand All @@ -192,7 +192,28 @@ def test_list_latest_recipe_revision(self):
""" % rrev.repr_notime())
assert bool(re.match(expected_output, str(self.client.out), re.MULTILINE))

def test_list_latest_package_revisions(self):
def test_list_all_the_latest_recipe_revision(self):
self.client.save({
"hello1.py": GenConanfile("hello", "1.0.0").with_generator("CMakeToolchain"), # rrev
"hello.py": GenConanfile("hello", "1.0.0"), # latest rrev
"bye.py": GenConanfile("bye", "1.0.0")
})
self.client.run("export hello1.py --user=user --channel=channel")
self.client.run("export hello.py --user=user --channel=channel")
hello_latest_rrev = self._get_lastest_recipe_ref("hello/1.0.0@user/channel")
self.client.run("export bye.py --user=user --channel=channel")
self.client.run(f"list *#latest")
expected_output = textwrap.dedent(f"""\
Local Cache:
bye
bye/1.0.0@user/channel#c720a82a9c904a0450ec1aa177281ea2 .*
hello
hello/1.0.0@user/channel#7a34833afbd87d791b2201882b1afb2b .*
""")
assert bool(re.match(expected_output, str(self.client.out), re.MULTILINE))
assert hello_latest_rrev.repr_notime() in expected_output

def test_list_latest_package_revisions_by_default(self):
self.client.save({
"conanfile.py": GenConanfile("test_recipe", "1.0.0").with_package_file("file.h", "0.1")
})
Expand All @@ -205,12 +226,35 @@ def test_list_latest_package_revisions(self):
Local Cache:
test_recipe
test_recipe/1.0.0@user/channel#ddfadce26d00a560850eb8767fe76ae4 .*
PID: da39a3ee5e6b4b0d3255bfef95601890afd80709 .*
PREV: 9c929aed65f04337a4143311d72fc897
PID: da39a3ee5e6b4b0d3255bfef95601890afd80709
PREV: 9c929aed65f04337a4143311d72fc897 .*
""")
assert bool(re.match(expected_output, str(self.client.out), re.MULTILINE))

def test_list_all_the_latest_package_revisions(self):
self.client.save({
"hello.py": GenConanfile("hello", "1.0.0").with_package_file("file.h", "0.1"),
"bye.py": GenConanfile("bye", "1.0.0").with_package_file("file.h", "0.1")
})
self.client.run("create hello.py --user=user --channel=channel")
self.client.run("create hello.py --user=user --channel=channel") # latest prev
self.client.run("create bye.py --user=user --channel=channel")
self.client.run("create bye.py --user=user --channel=channel") # latest prev
Copy link
Contributor Author

@franramirez688 franramirez688 Dec 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@memsharded Actually, it's passing, but it's not creating several PREVs. I know that using the command conan new xxxx and several conan create . will work as expected but it's too slow, so how could I improve it?

self.client.run("list *:*#latest")
expected_output = textwrap.dedent(f"""\
Local Cache:
bye
bye/1.0.0@user/channel#51edd97e27e407a01be830282558c32a .*
PID: da39a3ee5e6b4b0d3255bfef95601890afd80709
PREV: 9c929aed65f04337a4143311d72fc897 .*
hello
hello/1.0.0@user/channel#6fccfa5dd0bbb1223578c1771839eb6d .*
PID: da39a3ee5e6b4b0d3255bfef95601890afd80709
PREV: 9c929aed65f04337a4143311d72fc897 .*
""")
assert bool(re.match(expected_output, str(self.client.out), re.MULTILINE))

def test_search_with_full_reference_but_package_has_no_properties(self):
def test_search_package_ids_but_empty_conan_info(self):
remote_name = "remote1"
recipe_name = "test_recipe/1.0.0@user/channel"
self._add_remote(remote_name)
Expand All @@ -222,7 +266,7 @@ def test_search_with_full_reference_but_package_has_no_properties(self):
test_recipe
test_recipe/1.0.0@user/channel#4d670581ccb765839f2239cc8dff8fbd .*
PID: da39a3ee5e6b4b0d3255bfef95601890afd80709
No package info/revision was found.
Empty package information
""")
assert bool(re.match(expected_output, str(self.client.out), re.MULTILINE))

Expand Down
1 change: 1 addition & 0 deletions conans/test/utils/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,7 @@ def package_revision(self, pref):
latest_prev = self.cache.get_latest_package_reference(tmp)
return latest_prev.revision

# FIXME: 2.0: adapt this function to using the new "conan list xxxx" and recover the xfail tests
def search(self, pattern, remote=None, assert_error=False, args=None):
remote = " -r={}".format(remote) if remote else ""
self.run("search {} --json {} {} {}".format(pattern, ".tmp.json", remote,
Expand Down