Skip to content

Commit acc30c3

Browse files
authored
feat: Improve Sharded Repodata Progress Message (#4225)
Signed-off-by: Julien Jerphanion <git@jjerphan.xyz>
1 parent fef1285 commit acc30c3

File tree

5 files changed

+102
-11
lines changed

5 files changed

+102
-11
lines changed

libmamba/include/mamba/core/output.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,10 @@ namespace mamba
128128
/**
129129
* Check if status messages can be reported to stdout.
130130
*
131-
* Returns true if Console is available and JSON output is not enabled.
132-
* Use this before printing status messages to ensure they don't
133-
* interfere with JSON output.
131+
* Returns true when Console is available, `libmamba` is running from an
132+
* end-user executable (`mamba`/`micromamba`), and JSON output is disabled.
133+
* Use this before printing status messages to avoid leaking CLI-only
134+
* status lines to third-party `libmamba` integrations.
134135
*/
135136
[[nodiscard]] static bool can_report_status();
136137
static ConsoleStream stream();
@@ -146,6 +147,7 @@ namespace mamba
146147
static std::string hide_secrets(std::string_view str);
147148

148149
void print(std::string_view str, bool force_print = false);
150+
void print_in_place(std::string_view str, bool finalize = false, bool force_print = false);
149151
void json_write(const nlohmann::json& j);
150152
void json_append(const std::string& value);
151153
void json_append(const nlohmann::json& j);

libmamba/src/api/utils.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// The full license is in the file LICENSE, distributed with this software.
66

77
#include <cctype>
8+
#include <chrono>
89
#include <fstream>
910
#include <unordered_set>
1011

@@ -46,6 +47,12 @@ namespace mamba
4647
{
4748
namespace
4849
{
50+
auto done_with_duration(std::chrono::steady_clock::duration elapsed) -> std::string
51+
{
52+
const double seconds = std::chrono::duration<double>(elapsed).count();
53+
return fmt::format("✔ Done ({:.1f} sec)", seconds);
54+
}
55+
4956
tl::expected<command_args, std::runtime_error> get_pkg_mgr_install_command(
5057
const std::string& name,
5158
const std::string& target_prefix,
@@ -458,10 +465,11 @@ namespace mamba
458465
{
459466
if (Console::can_report_status())
460467
{
461-
Console::instance().print(
468+
Console::instance().print_in_place(
462469
fmt::format("{:<85} {:>20}", "Resolving Environment", "⧖ Starting")
463470
);
464471
}
472+
const auto started_at = std::chrono::steady_clock::now();
465473
auto outcome = solver::libsolv::Solver()
466474
.solve(
467475
db,
@@ -473,7 +481,14 @@ namespace mamba
473481
.value();
474482
if (Console::can_report_status())
475483
{
476-
Console::instance().print(fmt::format("{:<85} {:>20}", "Resolving Environment", "✔ Done"));
484+
Console::instance().print_in_place(
485+
fmt::format(
486+
"{:<85} {:>20}",
487+
"Resolving Environment",
488+
done_with_duration(std::chrono::steady_clock::now() - started_at)
489+
),
490+
true
491+
);
477492
}
478493
return outcome;
479494
}

libmamba/src/core/output.cpp

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <cstdlib>
99
#include <iostream>
1010
#include <map>
11+
#include <optional>
1112
#include <string>
1213

1314
#include <fmt/color.h>
@@ -23,6 +24,7 @@
2324
#include "mamba/core/tasksync.hpp"
2425
#include "mamba/core/thread_utils.hpp"
2526
#include "mamba/core/util.hpp"
27+
#include "mamba/core/util_os.hpp"
2628
#include "mamba/specs/conda_url.hpp"
2729
#include "mamba/util/string.hpp"
2830
#include "mamba/util/synchronized_value.hpp"
@@ -301,6 +303,7 @@ namespace mamba
301303
{
302304
std::unique_ptr<ProgressBarManager> progress_bar_manager;
303305
ConsoleBuffer buffer;
306+
std::optional<std::size_t> active_in_place_width;
304307
};
305308

306309
util::synchronized_value<Data> m_synched_data;
@@ -346,7 +349,8 @@ namespace mamba
346349

347350
bool Console::can_report_status()
348351
{
349-
return is_available() && !instance().context().output_params.json;
352+
const auto& ctx = instance().context();
353+
return is_available() && ctx.command_params.is_mamba_exe && !ctx.output_params.json;
350354
}
351355

352356
void Console::cancel_json_print()
@@ -371,11 +375,54 @@ namespace mamba
371375
}
372376
else
373377
{
378+
if (synched_data->active_in_place_width.has_value())
379+
{
380+
std::cout << '\n';
381+
synched_data->active_in_place_width.reset();
382+
}
374383
std::cout << hide_secrets(str) << std::endl;
375384
}
376385
}
377386
}
378387

388+
void Console::print_in_place(std::string_view str, bool finalize, bool force_print)
389+
{
390+
if (force_print || !(context().output_params.quiet || context().output_params.json))
391+
{
392+
auto synched_data = p_data->m_synched_data.synchronize();
393+
394+
if (synched_data->progress_bar_manager && synched_data->progress_bar_manager->started())
395+
{
396+
synched_data->buffer.push_back(hide_secrets(str));
397+
return;
398+
}
399+
400+
const std::string sanitized = hide_secrets(str);
401+
const bool can_update_in_place = is_atty(std::cout);
402+
if (!can_update_in_place)
403+
{
404+
std::cout << sanitized << std::endl;
405+
return;
406+
}
407+
408+
const std::size_t previous_width = synched_data->active_in_place_width.value_or(0);
409+
const std::size_t next_width = sanitized.size();
410+
const std::size_t pad = previous_width > next_width ? previous_width - next_width : 0;
411+
std::cout << '\r' << sanitized << std::string(pad, ' ');
412+
413+
if (finalize)
414+
{
415+
std::cout << '\n';
416+
synched_data->active_in_place_width.reset();
417+
}
418+
else
419+
{
420+
std::cout << std::flush;
421+
synched_data->active_in_place_width = next_width;
422+
}
423+
}
424+
}
425+
379426
void Console::print_buffer(std::ostream& ostream)
380427
{
381428
auto& data = instance().p_data;

libmamba/src/core/shard_index_loader.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@
3434

3535
namespace
3636
{
37+
auto done_with_duration(std::chrono::steady_clock::duration elapsed) -> std::string
38+
{
39+
const double seconds = std::chrono::duration<double>(elapsed).count();
40+
return fmt::format("✔ Done ({:.1f} sec)", seconds);
41+
}
42+
3743
/**
3844
* Parse the "shards" map from msgpack (package name -> hash bytes).
3945
*/
@@ -611,8 +617,9 @@ namespace mamba
611617
label = label.substr(0, 82) + "...";
612618
}
613619

614-
Console::instance().print(fmt::format("{:<85} {:>20}", label, "⧖ Starting"));
620+
Console::instance().print_in_place(fmt::format("{:<85} {:>20}", label, "⧖ Starting"));
615621
}
622+
const auto started_at = std::chrono::steady_clock::now();
616623

617624
// Build download request
618625
auto request_opt = build_shard_index_request(
@@ -686,7 +693,14 @@ namespace mamba
686693
{
687694
done_label = done_label.substr(0, 82) + "...";
688695
}
689-
Console::instance().print(fmt::format("{:<85} {:>20}", done_label, "✔ Done"));
696+
Console::instance().print_in_place(
697+
fmt::format(
698+
"{:<85} {:>20}",
699+
done_label,
700+
done_with_duration(std::chrono::steady_clock::now() - started_at)
701+
),
702+
true
703+
);
690704
}
691705
return std::optional<ShardsIndexDict>(std::move(index_result.value()));
692706
}

libmamba/src/core/shard_traversal.cpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// The full license is in the file LICENSE, distributed with this software.
66

77
#include <algorithm>
8+
#include <chrono>
89
#include <set>
910
#include <vector>
1011

@@ -43,6 +44,12 @@ namespace mamba
4344

4445
namespace
4546
{
47+
auto done_with_duration(std::chrono::steady_clock::duration elapsed) -> std::string
48+
{
49+
const double seconds = std::chrono::duration<double>(elapsed).count();
50+
return fmt::format("✔ Done ({:.1f} sec)", seconds);
51+
}
52+
4653
void add_names_from_specs(const std::vector<std::string>& specs, std::set<std::string>& names)
4754
{
4855
for (const auto& spec : specs)
@@ -111,10 +118,11 @@ namespace mamba
111118
}
112119
if (Console::can_report_status())
113120
{
114-
Console::instance().print(
121+
Console::instance().print_in_place(
115122
fmt::format("{:<85} {:>20}", "Fetching and Parsing Packages' Shards", "⧖ Starting")
116123
);
117124
}
125+
const auto started_at = std::chrono::steady_clock::now();
118126
if (strategy == "bfs")
119127
{
120128
reachable_bfs(root_packages, root_shards);
@@ -125,8 +133,13 @@ namespace mamba
125133
}
126134
if (Console::can_report_status())
127135
{
128-
Console::instance().print(
129-
fmt::format("{:<85} {:>20}", "Fetching and Parsing Packages' Shards", "✔ Done")
136+
Console::instance().print_in_place(
137+
fmt::format(
138+
"{:<85} {:>20}",
139+
"Fetching and Parsing Packages' Shards",
140+
done_with_duration(std::chrono::steady_clock::now() - started_at)
141+
),
142+
true
130143
);
131144
}
132145
}

0 commit comments

Comments
 (0)