diff --git a/conan/api/output.py b/conan/api/output.py index d42f0347d50..f161ee48464 100644 --- a/conan/api/output.py +++ b/conan/api/output.py @@ -1,5 +1,6 @@ import fnmatch import sys +import time from colorama import Fore, Style @@ -252,3 +253,19 @@ def cli_out_write(data, fg=None, bg=None, endline="\n", indentation=0): data = f"{' ' * indentation}{data}{endline}" sys.stdout.write(data) + + +class TimedOutput: + def __init__(self, interval, out=None, msg_format=None): + self._interval = interval + self._msg_format = msg_format + self._t = time.time() + self._out = out or ConanOutput() + + def info(self, msg, *args, **kwargs): + t = time.time() + if t - self._t > self._interval: + self._t = t + if self._msg_format: + msg = self._msg_format(msg, *args, **kwargs) + self._out.info(msg) diff --git a/conan/api/subapi/list.py b/conan/api/subapi/list.py index 3ff3bf6f39d..6a1674150c2 100644 --- a/conan/api/subapi/list.py +++ b/conan/api/subapi/list.py @@ -1,7 +1,7 @@ from typing import Dict from conan.api.model import PackagesList -from conan.api.output import ConanOutput +from conan.api.output import ConanOutput, TimedOutput from conan.internal.conan_app import ConanApp from conans.errors import ConanException, NotFoundException from conans.model.info import load_binary_info @@ -99,11 +99,14 @@ def select(self, pattern, package_query=None, remote=None, lru=None): search_ref = pattern.search_ref app = ConanApp(self.conan_api.cache_folder, self.conan_api.config.global_conf) limit_time = timelimit(lru) if lru else None + out = ConanOutput() + remote_name = "local cache" if not remote else remote.name if search_ref: refs = self.conan_api.search.recipes(search_ref, remote=remote) refs = pattern.filter_versions(refs) refs = sorted(refs) # Order alphabetical and older versions first pattern.check_refs(refs) + out.info(f"Found {len(refs)} pkg/version recipes matching {search_ref} in {remote_name}") else: refs = [RecipeReference(pattern.name, pattern.version, pattern.user, pattern.channel)] @@ -112,7 +115,12 @@ def select(self, pattern, package_query=None, remote=None, lru=None): select_bundle.add_refs(refs) return select_bundle + def msg_format(msg, item, total): + return msg + f" ({total.index(item)}/{len(total)})" + + trefs = TimedOutput(5, msg_format=msg_format) for r in refs: # Older versions first + trefs.info(f"Listing revisions of {r} in {remote_name}", r, refs) if pattern.is_latest_rrev or pattern.rrev is None: rrev = self.latest_recipe_revision(r, remote) if rrev is None: @@ -131,7 +139,9 @@ def select(self, pattern, package_query=None, remote=None, lru=None): if pattern.package_id is None: # Stop if not displaying binaries continue + trrevs = TimedOutput(5, msg_format=msg_format) for rrev in rrevs: + trrevs.info(f"Listing binaries of {rrev.repr_notime()} in {remote_name}", rrev, rrevs) prefs = [] if "*" not in pattern.package_id and pattern.prev is not None: prefs.append(PkgReference(rrev, package_id=pattern.package_id)) diff --git a/conans/client/downloaders/file_downloader.py b/conans/client/downloaders/file_downloader.py index 2ff95a98768..925dcbb2cc2 100644 --- a/conans/client/downloaders/file_downloader.py +++ b/conans/client/downloaders/file_downloader.py @@ -3,7 +3,7 @@ import time -from conan.api.output import ConanOutput +from conan.api.output import ConanOutput, TimedOutput from conans.client.rest import response_to_str from conans.errors import ConanException, NotFoundException, AuthenticationException, \ ForbiddenException, ConanConnectionError, RequestErrorException @@ -104,8 +104,13 @@ def get_total_length(): try: total_length = get_total_length() is_large_file = total_length > 10000000 # 10 MB - t_start = time.time() base_name = os.path.basename(file_path) + + def msg_format(msg, downloaded): + perc = int(total_downloaded_size * 100 / total_length) + return msg + f" {human_size(downloaded)} {perc}% {base_name}" + timed_output = TimedOutput(10, out=self._output, msg_format=msg_format) + if is_large_file: hs = human_size(total_length) action = "Downloading" if range_start == 0 else "Continuing download of" @@ -119,12 +124,7 @@ def get_total_length(): file_handler.write(chunk) total_downloaded_size += len(chunk) if is_large_file: - t = time.time() - if t - t_start > 10: # Every 10 seconds - hs = human_size(total_downloaded_size) - perc = int(total_downloaded_size*100/total_length) - self._output.info(f"Downloaded {hs} {perc}% {base_name}") - t_start = t + timed_output.info("Downloaded", total_downloaded_size) gzip = (response.headers.get("content-encoding") == "gzip") response.close() diff --git a/conans/test/integration/command_v2/list_test.py b/conans/test/integration/command_v2/list_test.py index 2368045035e..5d72a9ce9d1 100644 --- a/conans/test/integration/command_v2/list_test.py +++ b/conans/test/integration/command_v2/list_test.py @@ -100,7 +100,7 @@ def check(client, pattern, remote, expected): expected_output = f"{r_msg}\n" + expected expected_output = re.sub(r"\(.*\)", "", expected_output) output = re.sub(r"\(.*\)", "", str(client.out)) - assert expected_output == output + assert expected_output in output @staticmethod def check_json(client, pattern, remote, expected): @@ -293,7 +293,7 @@ def check(client, pattern, remote, expected): expected_output = f"{r_msg}\n" + expected expected_output = re.sub(r"\(.*\)", "", expected_output) output = re.sub(r"\(.*\)", "", str(client.out)) - assert expected_output == output + assert expected_output in output @staticmethod def check_json(client, pattern, remote, expected): diff --git a/conans/test/integration/command_v2/search_test.py b/conans/test/integration/command_v2/search_test.py index 4580c122224..be4ff12aa3d 100644 --- a/conans/test/integration/command_v2/search_test.py +++ b/conans/test/integration/command_v2/search_test.py @@ -113,7 +113,7 @@ def test_search_by_name(self): " {}\n".format(recipe_name) ) - assert expected_output == self.client.out + assert expected_output in self.client.out def test_search_in_all_remotes(self): remote1 = "remote1" @@ -144,7 +144,7 @@ def test_search_in_all_remotes(self): self._add_recipe(remote2, remote2_recipe2) self.client.run("search test_recipe") - assert expected_output == self.client.out + assert expected_output in self.client.out def test_search_in_one_remote(self): remote1 = "remote1" @@ -201,7 +201,7 @@ def test_search_package_found_in_one_remote(self): self._add_recipe(remote2, remote2_recipe2) self.client.run("search test_recipe") - assert expected_output == self.client.out + assert expected_output in self.client.out def test_search_in_missing_remote(self): remote1 = "remote1"