mirror of https://github.com/mamba-org/mamba.git
Improve progress bars (#1350)
* refactor progress bars * use fmt to better format progress bars fields * add a Chrono class to handle time and status/state * add a ProgressBarRepr class to handle representation of the bars * add capability to remove progress bars from a manager * add capability to set width when computing bars repr and printing * handle no_progress_bars context value in all situations * also ensure quiet, not a tty, and json modes are handled * add and update tests * make it optional to display sub-bars in aggregated mode * display a carrousel of downloading or extracting packages is the aggregated bar * make sure extracting packages from cache are printed by the progress bars manager * remove postfix when printing the completed download status * protect chrono state against modifications when sorting bars * fix to_human_readable_filesize free function * only wait for watch_print thread to exit if started * store speed, also compute average speed from member or Chrono elapsed time * use the average speed on download aggregated bar if cURL info is missing * allows to get average speed on total elapsed time * improved color scheme of progress bars * collect and show the in-progress status using cyan * init the progress randomly when activating spinner * control sorting of multi downloads using an option * use C-style options also for fastfail * make sure pbar manager is init on multi bars when dl channels * refactor on api changes
This commit is contained in:
parent
8b3629130f
commit
129920a304
|
@ -56,7 +56,7 @@ Here is an example usage of the mamba_api:
|
|||
index.append((sd, channel))
|
||||
dlist.add(sd)
|
||||
|
||||
is_downloaded = dlist.download(True)
|
||||
is_downloaded = dlist.download(mamba_api.MAMBA_DOWNLOAD_FAILFAST)
|
||||
|
||||
if not is_downloaded:
|
||||
raise RuntimeError("Error downloading repodata.")
|
||||
|
|
|
@ -17,7 +17,9 @@ extern "C"
|
|||
#include <vector>
|
||||
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
#include "output.hpp"
|
||||
#include "progress_bar.hpp"
|
||||
#include "validate.hpp"
|
||||
|
||||
namespace mamba
|
||||
|
@ -43,6 +45,7 @@ namespace mamba
|
|||
void set_expected_size(std::size_t size);
|
||||
|
||||
const std::string& name() const;
|
||||
std::size_t expected_size() const;
|
||||
|
||||
void init_curl_target(const std::string& url);
|
||||
|
||||
|
@ -74,6 +77,9 @@ namespace mamba
|
|||
bool can_retry();
|
||||
CURL* retry();
|
||||
|
||||
std::chrono::steady_clock::time_point progress_throttle_time() const;
|
||||
void set_progress_throttle_time(const std::chrono::steady_clock::time_point& time);
|
||||
|
||||
CURLcode result;
|
||||
bool failed = false;
|
||||
int http_status = 10000;
|
||||
|
@ -108,6 +114,9 @@ namespace mamba
|
|||
std::ofstream m_file;
|
||||
|
||||
static void init_curl_handle(CURL* handle, const std::string& url);
|
||||
std::function<void(ProgressBarRepr&)> download_repr();
|
||||
|
||||
std::chrono::steady_clock::time_point m_progress_throttle_time;
|
||||
};
|
||||
|
||||
class MultiDownloadTarget
|
||||
|
@ -118,7 +127,7 @@ namespace mamba
|
|||
|
||||
void add(DownloadTarget* target);
|
||||
bool check_msgs(bool failfast);
|
||||
bool download(bool failfast);
|
||||
bool download(int options);
|
||||
|
||||
private:
|
||||
std::vector<DownloadTarget*> m_targets;
|
||||
|
@ -126,6 +135,8 @@ namespace mamba
|
|||
CURLM* m_handle;
|
||||
};
|
||||
|
||||
const int MAMBA_DOWNLOAD_FAILFAST = 1 << 0;
|
||||
const int MAMBA_DOWNLOAD_SORT = 1 << 1;
|
||||
} // namespace mamba
|
||||
|
||||
#endif // MAMBA_FETCH_HPP
|
||||
|
|
|
@ -46,97 +46,6 @@
|
|||
|
||||
#define PREFIX_LENGTH 25
|
||||
|
||||
namespace cursor
|
||||
{
|
||||
class CursorMovementTriple
|
||||
{
|
||||
public:
|
||||
CursorMovementTriple(const char* esc, int n, const char* mod)
|
||||
: m_esc(esc)
|
||||
, m_mod(mod)
|
||||
, m_n(n)
|
||||
{
|
||||
}
|
||||
|
||||
const char* m_esc;
|
||||
const char* m_mod;
|
||||
int m_n;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& o, const CursorMovementTriple& m)
|
||||
{
|
||||
o << m.m_esc << m.m_n << m.m_mod;
|
||||
return o;
|
||||
}
|
||||
|
||||
class CursorMod
|
||||
{
|
||||
public:
|
||||
CursorMod(const char* mod)
|
||||
: m_mod(mod)
|
||||
{
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& o)
|
||||
{
|
||||
o << m_mod;
|
||||
return o;
|
||||
}
|
||||
|
||||
const char* m_mod;
|
||||
};
|
||||
|
||||
inline auto up(int n)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "A");
|
||||
}
|
||||
|
||||
inline auto down(int n)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "B");
|
||||
}
|
||||
|
||||
inline auto forward(int n)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "C");
|
||||
}
|
||||
|
||||
inline auto back(int n)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "D");
|
||||
}
|
||||
|
||||
inline auto next_line(int n)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "E");
|
||||
}
|
||||
|
||||
inline auto prev_line(int n)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "F");
|
||||
}
|
||||
|
||||
inline auto horizontal_abs(int n)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "G");
|
||||
}
|
||||
|
||||
inline auto erase_line(int n = 0)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "K");
|
||||
}
|
||||
|
||||
inline auto show(int n)
|
||||
{
|
||||
return CursorMod("\x1b[?25h");
|
||||
}
|
||||
|
||||
inline auto hide(int n)
|
||||
{
|
||||
return CursorMod("\x1b[?25l");
|
||||
}
|
||||
} // namespace cursor
|
||||
|
||||
namespace mamba
|
||||
{
|
||||
std::string cut_repo_name(const std::string& reponame);
|
||||
|
@ -204,11 +113,6 @@ namespace mamba
|
|||
std::ostringstream table_like(const std::vector<std::string>& data, std::size_t max_width);
|
||||
} // namespace printers
|
||||
|
||||
// The next two functions / classes were ported from the awesome indicators
|
||||
// library by p-ranav (MIT License) https://github.com/p-ranav/indicators
|
||||
std::ostream& write_duration(std::ostream& os, std::chrono::nanoseconds ns);
|
||||
int get_console_width();
|
||||
|
||||
// Todo: replace public inheritance with
|
||||
// private one + using directives
|
||||
class ConsoleStream : public std::stringstream
|
||||
|
@ -236,7 +140,10 @@ namespace mamba
|
|||
std::istream& input_stream = std::cin);
|
||||
|
||||
ProgressProxy add_progress_bar(const std::string& name, size_t expected_total = 0);
|
||||
void init_multi_progress(ProgressBarMode mode = ProgressBarMode::multi);
|
||||
void clear_progress_bars();
|
||||
ProgressBarManager& init_progress_bar_manager(ProgressBarMode mode
|
||||
= ProgressBarMode::multi);
|
||||
ProgressBarManager& progress_bar_manager();
|
||||
|
||||
static std::string hide_secrets(const std::string_view& str);
|
||||
|
||||
|
@ -247,30 +154,26 @@ namespace mamba
|
|||
void json_down(const std::string& key);
|
||||
void json_up();
|
||||
|
||||
static void print_buffer(std::ostream& ostream);
|
||||
|
||||
private:
|
||||
Console();
|
||||
~Console() = default;
|
||||
|
||||
void deactivate_progress_bar(std::size_t idx, const std::string_view& msg = "");
|
||||
void print_progress(std::size_t idx);
|
||||
bool skip_progress_bars() const;
|
||||
|
||||
std::mutex m_mutex;
|
||||
std::unique_ptr<ProgressBarManager> p_progress_manager;
|
||||
std::unique_ptr<ProgressBarManager> p_progress_bar_manager;
|
||||
|
||||
std::string json_hier;
|
||||
unsigned int json_index;
|
||||
nlohmann::json json_log;
|
||||
|
||||
static std::vector<std::string> m_buffer;
|
||||
|
||||
friend class ProgressProxy;
|
||||
};
|
||||
|
||||
inline void ProgressProxy::set_postfix(const std::string& s)
|
||||
{
|
||||
p_bar->set_postfix(s);
|
||||
Console::instance().print_progress(m_idx);
|
||||
}
|
||||
|
||||
class MessageLogger
|
||||
{
|
||||
public:
|
||||
|
@ -279,11 +182,21 @@ namespace mamba
|
|||
|
||||
std::stringstream& stream();
|
||||
|
||||
static void activate_buffer();
|
||||
static void deactivate_buffer();
|
||||
static void print_buffer(std::ostream& ostream);
|
||||
|
||||
private:
|
||||
std::string m_file;
|
||||
int m_line;
|
||||
spdlog::level::level_enum m_level;
|
||||
std::stringstream m_stream;
|
||||
|
||||
static std::mutex m_mutex;
|
||||
static bool use_buffer;
|
||||
static std::vector<std::pair<std::string, spdlog::level::level_enum>> m_buffer;
|
||||
|
||||
static void emit(const std::string& msg, const spdlog::level::level_enum& level);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -7,12 +7,291 @@
|
|||
#ifndef MAMBA_CORE_PROGRESS_BAR_HPP
|
||||
#define MAMBA_CORE_PROGRESS_BAR_HPP
|
||||
|
||||
#include <string_view>
|
||||
#include "spdlog/fmt/fmt.h"
|
||||
#include "spdlog/fmt/bundled/color.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
|
||||
|
||||
namespace cursor
|
||||
{
|
||||
class CursorMovementTriple
|
||||
{
|
||||
public:
|
||||
CursorMovementTriple(const char* esc, int n, const char* mod)
|
||||
: m_esc(esc)
|
||||
, m_mod(mod)
|
||||
, m_n(n)
|
||||
{
|
||||
}
|
||||
|
||||
const char* m_esc;
|
||||
const char* m_mod;
|
||||
int m_n;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& o, const CursorMovementTriple& m)
|
||||
{
|
||||
o << m.m_esc << m.m_n << m.m_mod;
|
||||
return o;
|
||||
}
|
||||
|
||||
class CursorMod
|
||||
{
|
||||
public:
|
||||
CursorMod(const char* mod)
|
||||
: m_mod(mod)
|
||||
{
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& o)
|
||||
{
|
||||
o << m_mod;
|
||||
return o;
|
||||
}
|
||||
|
||||
const char* m_mod;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& o, const CursorMod& m)
|
||||
{
|
||||
o << m.m_mod;
|
||||
return o;
|
||||
}
|
||||
|
||||
inline auto up(int n)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "A");
|
||||
}
|
||||
|
||||
inline auto down(int n)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "B");
|
||||
}
|
||||
|
||||
inline auto forward(int n)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "C");
|
||||
}
|
||||
|
||||
inline auto back(int n)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "D");
|
||||
}
|
||||
|
||||
inline auto next_line(int n)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "E");
|
||||
}
|
||||
|
||||
inline auto prev_line(int n)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "F");
|
||||
}
|
||||
|
||||
inline auto horizontal_abs(int n)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "G");
|
||||
}
|
||||
|
||||
inline auto home()
|
||||
{
|
||||
return CursorMod("\x1b[H");
|
||||
}
|
||||
|
||||
inline auto erase_display(int n = 0)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "J");
|
||||
}
|
||||
|
||||
inline auto erase_line(int n = 0)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "K");
|
||||
}
|
||||
|
||||
inline auto scroll_up(int n = 1)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "S");
|
||||
}
|
||||
|
||||
inline auto scroll_down(int n = 1)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "T");
|
||||
}
|
||||
|
||||
inline auto show()
|
||||
{
|
||||
return CursorMod("\x1b[?25h");
|
||||
}
|
||||
|
||||
inline auto hide()
|
||||
{
|
||||
return CursorMod("\x1b[?25l");
|
||||
}
|
||||
|
||||
inline auto pos()
|
||||
{
|
||||
return CursorMod("\x1b[R");
|
||||
}
|
||||
|
||||
inline auto delete_line(int n = 1)
|
||||
{
|
||||
return CursorMovementTriple("\x1b[", n, "M");
|
||||
}
|
||||
|
||||
inline auto alternate_screen()
|
||||
{
|
||||
return CursorMod("\x1b[?1049h");
|
||||
}
|
||||
|
||||
inline auto main_screen()
|
||||
{
|
||||
return CursorMod("\x1b[?1049l");
|
||||
}
|
||||
} // namespace cursor
|
||||
|
||||
namespace mamba
|
||||
{
|
||||
void to_human_readable_filesize(std::ostream& o, double bytes, std::size_t precision = 0);
|
||||
std::string to_human_readable_filesize(double bytes, std::size_t precision);
|
||||
|
||||
std::ostream& write_duration(std::ostream& os, std::chrono::nanoseconds ns);
|
||||
std::stringstream duration_stream(std::chrono::nanoseconds ns);
|
||||
std::string duration_str(std::chrono::nanoseconds ns);
|
||||
|
||||
int get_console_width();
|
||||
int get_console_height();
|
||||
|
||||
enum ChronoState
|
||||
{
|
||||
unset = 0,
|
||||
started = 1,
|
||||
paused = 2,
|
||||
stopped = 3,
|
||||
terminated = 4
|
||||
};
|
||||
|
||||
class Chrono
|
||||
{
|
||||
public:
|
||||
using duration_t = std::chrono::milliseconds;
|
||||
using time_point_t
|
||||
= std::chrono::time_point<std::chrono::high_resolution_clock, duration_t>;
|
||||
|
||||
Chrono() = default;
|
||||
|
||||
ChronoState status() const;
|
||||
bool started() const;
|
||||
bool paused() const;
|
||||
bool stopped() const;
|
||||
bool terminated() const;
|
||||
bool unset() const;
|
||||
|
||||
void start();
|
||||
void start(const time_point_t& time_point);
|
||||
void pause();
|
||||
void resume();
|
||||
void stop();
|
||||
void terminate();
|
||||
|
||||
void set_start_time(const time_point_t& time_point);
|
||||
time_point_t start_time() const;
|
||||
time_point_t last_active_time();
|
||||
|
||||
void set_elapsed_time(const duration_t& time);
|
||||
std::string elapsed_time_to_str();
|
||||
duration_t elapsed();
|
||||
|
||||
std::unique_lock<std::mutex> chrono_lock();
|
||||
|
||||
private:
|
||||
time_point_t m_start;
|
||||
duration_t m_elapsed_ns = duration_t::zero();
|
||||
ChronoState m_state = ChronoState::unset;
|
||||
std::mutex m_mutex;
|
||||
|
||||
void compute_elapsed();
|
||||
|
||||
protected:
|
||||
static time_point_t now();
|
||||
};
|
||||
|
||||
class FieldRepr
|
||||
{
|
||||
public:
|
||||
bool active() const;
|
||||
bool defined() const;
|
||||
operator bool() const;
|
||||
bool overflow() const;
|
||||
|
||||
std::string formatted_value(bool allow_overflow = false) const;
|
||||
std::size_t width(bool allow_overflow = true) const;
|
||||
std::size_t stored_width() const;
|
||||
std::string value() const;
|
||||
|
||||
FieldRepr& activate();
|
||||
FieldRepr& deactivate();
|
||||
FieldRepr& set_format(const std::string& str);
|
||||
FieldRepr& set_format(const std::string& str, std::size_t size);
|
||||
FieldRepr& set_value(const std::string& str);
|
||||
FieldRepr& set_width(std::size_t size);
|
||||
FieldRepr& reset_width();
|
||||
FieldRepr& resize(std::size_t size);
|
||||
|
||||
private:
|
||||
std::string m_value = "";
|
||||
std::size_t m_width = 0;
|
||||
std::string m_format = "";
|
||||
bool m_active = true;
|
||||
|
||||
static std::string resize(const std::string& str, std::size_t size);
|
||||
};
|
||||
|
||||
class ProgressBar;
|
||||
class ProgressBarManager;
|
||||
|
||||
class ProgressBarRepr
|
||||
{
|
||||
public:
|
||||
ProgressBarRepr() = default;
|
||||
ProgressBarRepr(ProgressBar* pbar);
|
||||
|
||||
FieldRepr prefix, progress, current, separator, total, speed, postfix, elapsed;
|
||||
fmt::text_style style;
|
||||
|
||||
void print(std::ostream& ostream, std::size_t width = 0, bool with_endl = true);
|
||||
|
||||
void compute_progress();
|
||||
void compute_progress_width();
|
||||
void compute_progress_value();
|
||||
|
||||
ProgressBarRepr& set_width(std::size_t width);
|
||||
std::size_t width() const;
|
||||
|
||||
ProgressBarRepr& reset_fields();
|
||||
|
||||
const ProgressBar& progress_bar() const;
|
||||
|
||||
private:
|
||||
ProgressBar* p_progress_bar;
|
||||
std::size_t m_width = 0;
|
||||
|
||||
void set_same_widths(const ProgressBarRepr& r);
|
||||
void deactivate_empty_fields();
|
||||
std::vector<FieldRepr*> fields();
|
||||
|
||||
friend class ProgressBar;
|
||||
friend class ProgressBarManager;
|
||||
};
|
||||
|
||||
class ProgressBarManager;
|
||||
|
||||
/*******************************
|
||||
* Public API of progress bars *
|
||||
|
@ -22,7 +301,7 @@ namespace mamba
|
|||
{
|
||||
public:
|
||||
ProgressProxy() = default;
|
||||
ProgressProxy(ProgressBar* ptr, std::size_t idx);
|
||||
ProgressProxy(ProgressBar* ptr);
|
||||
~ProgressProxy() = default;
|
||||
|
||||
ProgressProxy(const ProgressProxy&) = default;
|
||||
|
@ -30,22 +309,65 @@ namespace mamba
|
|||
ProgressProxy(ProgressProxy&&) = default;
|
||||
ProgressProxy& operator=(ProgressProxy&&) = default;
|
||||
|
||||
void set_full();
|
||||
void set_progress(size_t current, size_t total);
|
||||
void elapsed_time_to_stream(std::stringstream& s);
|
||||
void set_postfix(const std::string& s);
|
||||
void mark_as_completed(const std::string_view& final_message = "");
|
||||
void mark_as_extracted();
|
||||
bool defined() const;
|
||||
operator bool() const;
|
||||
ProgressProxy& set_bar(ProgressBar* ptr);
|
||||
|
||||
ProgressProxy& set_progress(std::size_t current, std::size_t total);
|
||||
ProgressProxy& update_progress(std::size_t current, std::size_t total);
|
||||
ProgressProxy& set_progress(double progress);
|
||||
ProgressProxy& set_current(std::size_t current);
|
||||
ProgressProxy& set_in_progress(std::size_t in_progress);
|
||||
ProgressProxy& update_current(std::size_t current);
|
||||
ProgressProxy& set_total(std::size_t total);
|
||||
ProgressProxy& set_speed(std::size_t speed);
|
||||
ProgressProxy& set_full();
|
||||
ProgressProxy& activate_spinner();
|
||||
ProgressProxy& deactivate_spinner();
|
||||
|
||||
std::size_t current() const;
|
||||
std::size_t in_progress() const;
|
||||
std::size_t total() const;
|
||||
std::size_t speed() const;
|
||||
std::size_t avg_speed(const std::chrono::milliseconds& ref_duration
|
||||
= std::chrono::milliseconds::max());
|
||||
double progress() const;
|
||||
bool completed() const;
|
||||
|
||||
ProgressProxy& set_prefix(const std::string& text);
|
||||
ProgressProxy& set_postfix(const std::string& text);
|
||||
ProgressProxy& set_repr_hook(std::function<void(ProgressBarRepr&)> f);
|
||||
ProgressProxy& set_progress_hook(std::function<void(ProgressProxy&)> f);
|
||||
ProgressProxy& mark_as_completed(const std::chrono::milliseconds& delay
|
||||
= std::chrono::milliseconds::zero());
|
||||
|
||||
std::string elapsed_time_to_str() const;
|
||||
std::string prefix() const;
|
||||
|
||||
ProgressBarRepr& update_repr(bool compute_progress = true);
|
||||
const ProgressBarRepr& repr() const;
|
||||
ProgressBarRepr& repr();
|
||||
ProgressProxy& print(std::ostream& stream, std::size_t width = 0, bool with_endl = true);
|
||||
|
||||
ProgressProxy& start();
|
||||
ProgressProxy& pause();
|
||||
ProgressProxy& resume();
|
||||
ProgressProxy& stop();
|
||||
|
||||
bool started() const;
|
||||
|
||||
int width() const;
|
||||
|
||||
private:
|
||||
ProgressBar* p_bar;
|
||||
std::size_t m_idx;
|
||||
|
||||
friend class ProgressBarManager;
|
||||
};
|
||||
|
||||
class ProgressBarManager
|
||||
class ProgressBarManager : public Chrono
|
||||
{
|
||||
public:
|
||||
virtual ~ProgressBarManager() = default;
|
||||
virtual ~ProgressBarManager();
|
||||
|
||||
ProgressBarManager(const ProgressBarManager&) = delete;
|
||||
ProgressBarManager& operator=(const ProgressBarManager&) = delete;
|
||||
|
@ -54,13 +376,53 @@ namespace mamba
|
|||
|
||||
virtual ProgressProxy add_progress_bar(const std::string& name, size_t expected_total = 0)
|
||||
= 0;
|
||||
virtual void print_progress(std::size_t idx) = 0;
|
||||
virtual void deactivate_progress_bar(std::size_t idx, const std::string_view& msg = "") = 0;
|
||||
virtual void clear_progress_bars();
|
||||
virtual void add_label(const std::string& label, const ProgressProxy& progress_bar);
|
||||
|
||||
virtual void print(const std::string_view& str, bool skip_progress_bars) = 0;
|
||||
void watch_print(const duration_t& period = std::chrono::milliseconds(100));
|
||||
virtual std::size_t print(std::ostream& os,
|
||||
std::size_t width = 0,
|
||||
std::size_t max_lines = std::numeric_limits<std::size_t>::max(),
|
||||
bool with_endl = true)
|
||||
= 0;
|
||||
void start();
|
||||
void terminate();
|
||||
|
||||
void register_print_hook(std::function<void(std::ostream&)> f);
|
||||
void register_pre_start_hook(std::function<void()> f);
|
||||
void register_post_stop_hook(std::function<void()> f);
|
||||
|
||||
void compute_bars_progress(std::vector<ProgressBar*>& bars);
|
||||
|
||||
void activate_sorting();
|
||||
void deactivate_sorting();
|
||||
|
||||
protected:
|
||||
using progress_bar_ptr = std::unique_ptr<ProgressBar>;
|
||||
|
||||
ProgressBarManager() = default;
|
||||
ProgressBarManager(std::size_t width);
|
||||
|
||||
duration_t m_period = std::chrono::milliseconds(100);
|
||||
std::vector<progress_bar_ptr> m_progress_bars = {};
|
||||
std::map<std::string, std::vector<ProgressBar*>> m_labels;
|
||||
bool m_marked_to_terminate = false, m_watch_print_started = false;
|
||||
bool m_sort_bars = false;
|
||||
std::size_t m_width = 0;
|
||||
|
||||
std::mutex m_mutex;
|
||||
|
||||
ProgressBar* raw_bar(const ProgressProxy& progress_bar);
|
||||
|
||||
void run();
|
||||
|
||||
std::vector<std::function<void(std::ostream&)>> m_print_hooks;
|
||||
std::vector<std::function<void()>> m_pre_start_hooks;
|
||||
std::vector<std::function<void()>> m_post_stop_hooks;
|
||||
|
||||
void erase_lines(std::ostream& ostream, std::size_t count);
|
||||
void call_print_hooks(std::ostream& ostream);
|
||||
void sort_bars(bool max_height_exceeded);
|
||||
};
|
||||
|
||||
enum ProgressBarMode
|
||||
|
@ -71,110 +433,158 @@ namespace mamba
|
|||
|
||||
std::unique_ptr<ProgressBarManager> make_progress_bar_manager(ProgressBarMode);
|
||||
|
||||
/******************************************
|
||||
* Internal use of progress bars, *
|
||||
* should not be used directly by clients *
|
||||
******************************************/
|
||||
|
||||
/*********************************
|
||||
* Internal use of progress bars *
|
||||
*********************************/
|
||||
|
||||
class MultiBarManager : public ProgressBarManager
|
||||
{
|
||||
public:
|
||||
MultiBarManager();
|
||||
MultiBarManager(std::size_t width);
|
||||
virtual ~MultiBarManager() = default;
|
||||
|
||||
ProgressProxy add_progress_bar(const std::string& name, size_t expected_total) override;
|
||||
void print_progress(std::size_t idx) override;
|
||||
void deactivate_progress_bar(std::size_t idx, const std::string_view& msg = "") override;
|
||||
|
||||
void print(const std::string_view& str, bool skip_progress_bars) override;
|
||||
|
||||
private:
|
||||
void print_progress();
|
||||
|
||||
using progress_bar_ptr = std::unique_ptr<ProgressBar>;
|
||||
std::vector<progress_bar_ptr> m_progress_bars;
|
||||
std::vector<ProgressBar*> m_active_progress_bars;
|
||||
bool m_progress_started;
|
||||
std::size_t print(std::ostream& os,
|
||||
std::size_t width = 0,
|
||||
std::size_t max_lines = std::numeric_limits<std::size_t>::max(),
|
||||
bool with_endl = true) override;
|
||||
};
|
||||
|
||||
class AggregatedBarManager : public ProgressBarManager
|
||||
{
|
||||
public:
|
||||
AggregatedBarManager();
|
||||
AggregatedBarManager(std::size_t width);
|
||||
virtual ~AggregatedBarManager() = default;
|
||||
|
||||
ProgressProxy add_progress_bar(const std::string& name, size_t expected_total) override;
|
||||
void print_progress(std::size_t idx) override;
|
||||
void deactivate_progress_bar(std::size_t idx, const std::string_view& msg = "") override;
|
||||
|
||||
void print(const std::string_view& str, bool skip_progress_bars) override;
|
||||
ProgressProxy add_progress_bar(const std::string& name,
|
||||
std::size_t expected_total) override;
|
||||
|
||||
void update_download_bar(std::size_t current_diff);
|
||||
void update_extract_bar();
|
||||
|
||||
void add_label(const std::string& label, const ProgressProxy& progress_bar) override;
|
||||
void clear_progress_bars() override;
|
||||
|
||||
void activate_sub_bars();
|
||||
void deactivate_sub_bars();
|
||||
|
||||
ProgressBar* aggregated_bar(const std::string& label);
|
||||
|
||||
std::size_t print(std::ostream& os,
|
||||
std::size_t width = 0,
|
||||
std::size_t max_lines = std::numeric_limits<std::size_t>::max(),
|
||||
bool with_endl = true) override;
|
||||
|
||||
private:
|
||||
void print_progress();
|
||||
std::map<std::string, progress_bar_ptr> m_aggregated_bars;
|
||||
bool m_print_sub_bars = false;
|
||||
|
||||
bool is_complete() const;
|
||||
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
|
||||
|
||||
using progress_bar_ptr = std::unique_ptr<ProgressBar>;
|
||||
std::vector<progress_bar_ptr> m_progress_bars;
|
||||
progress_bar_ptr p_download_bar;
|
||||
progress_bar_ptr p_extract_bar;
|
||||
size_t m_completed;
|
||||
size_t m_extracted;
|
||||
std::mutex m_main_mutex;
|
||||
size_t m_current;
|
||||
size_t m_total;
|
||||
bool m_progress_started;
|
||||
void update_aggregates_progress();
|
||||
};
|
||||
|
||||
class ProgressBar
|
||||
class ProgressBar : public Chrono
|
||||
{
|
||||
public:
|
||||
virtual ~ProgressBar() = default;
|
||||
virtual ~ProgressBar();
|
||||
|
||||
ProgressBar(const ProgressBar&) = delete;
|
||||
ProgressBar& operator=(const ProgressBar&) = delete;
|
||||
ProgressBar(ProgressBar&&) = delete;
|
||||
ProgressBar& operator=(ProgressBar&&) = delete;
|
||||
virtual void print() = 0;
|
||||
virtual void set_full() = 0;
|
||||
virtual void set_progress(size_t current, size_t total) = 0;
|
||||
virtual void set_extracted() = 0;
|
||||
|
||||
void set_start();
|
||||
void set_postfix(const std::string& postfix_text);
|
||||
void elapsed_time_to_stream(std::stringstream& s);
|
||||
const std::string& prefix() const;
|
||||
virtual void print(std::ostream& stream, std::size_t width = 0, bool with_endl = true) = 0;
|
||||
|
||||
ProgressBarRepr& update_repr(bool compute_bar = true);
|
||||
const ProgressBarRepr& repr() const;
|
||||
ProgressBarRepr& repr();
|
||||
|
||||
ProgressBar& set_progress(std::size_t current, std::size_t total);
|
||||
ProgressBar& update_progress(std::size_t current, std::size_t total);
|
||||
ProgressBar& set_progress(double progress);
|
||||
ProgressBar& set_current(std::size_t current);
|
||||
ProgressBar& set_in_progress(std::size_t in_progress);
|
||||
ProgressBar& update_current(std::size_t current);
|
||||
ProgressBar& set_total(std::size_t total);
|
||||
ProgressBar& set_speed(std::size_t speed);
|
||||
ProgressBar& set_full();
|
||||
ProgressBar& activate_spinner();
|
||||
ProgressBar& deactivate_spinner();
|
||||
|
||||
ProgressBar& mark_as_completed(const std::chrono::milliseconds& delay
|
||||
= std::chrono::milliseconds::zero());
|
||||
|
||||
std::size_t current() const;
|
||||
std::size_t in_progress() const;
|
||||
std::size_t total() const;
|
||||
std::size_t speed() const;
|
||||
std::size_t avg_speed(const std::chrono::milliseconds& ref_duration
|
||||
= std::chrono::milliseconds::max());
|
||||
double progress() const;
|
||||
bool completed() const;
|
||||
bool is_spinner() const;
|
||||
|
||||
const std::set<std::string>& active_tasks() const;
|
||||
std::set<std::string>& active_tasks();
|
||||
const std::set<std::string>& all_tasks() const;
|
||||
std::set<std::string>& all_tasks();
|
||||
ProgressBar& add_active_task(const std::string& name);
|
||||
ProgressBar& add_task(const std::string& name);
|
||||
std::string last_active_task();
|
||||
|
||||
ProgressBar& set_repr_hook(std::function<void(ProgressBarRepr&)> f);
|
||||
ProgressBar& set_progress_hook(std::function<void(ProgressProxy&)> f);
|
||||
|
||||
ProgressBar& set_prefix(const std::string& str);
|
||||
ProgressBar& set_postfix(const std::string& str);
|
||||
|
||||
std::string prefix() const;
|
||||
|
||||
int width() const;
|
||||
|
||||
protected:
|
||||
ProgressBar(const std::string& prefix);
|
||||
ProgressBar(const std::string& prefix, std::size_t total, int width = 0);
|
||||
|
||||
std::chrono::nanoseconds m_elapsed_ns;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
|
||||
double m_progress = 0.;
|
||||
std::size_t m_current = 0;
|
||||
std::size_t m_in_progress = 0;
|
||||
std::size_t m_total = 0;
|
||||
std::size_t m_speed = 0, m_avg_speed = 0, m_current_avg = 0;
|
||||
int m_width = 0;
|
||||
|
||||
std::string m_prefix;
|
||||
std::string m_postfix;
|
||||
bool m_start_time_saved;
|
||||
bool m_activate_bob;
|
||||
std::set<std::string> m_active_tasks = {};
|
||||
std::set<std::string> m_all_tasks = {};
|
||||
std::string m_last_active_task = "";
|
||||
time_point_t m_task_time, m_avg_speed_time;
|
||||
|
||||
ProgressBarRepr m_repr;
|
||||
|
||||
bool m_is_spinner;
|
||||
bool m_completed = false;
|
||||
|
||||
std::mutex m_mutex;
|
||||
|
||||
void run();
|
||||
|
||||
std::function<void(ProgressBarRepr&)> p_repr_hook;
|
||||
std::function<void(ProgressProxy&)> p_progress_hook;
|
||||
|
||||
ProgressBar& call_progress_hook();
|
||||
ProgressBar& call_repr_hook();
|
||||
};
|
||||
|
||||
class DefaultProgressBar : public ProgressBar
|
||||
{
|
||||
public:
|
||||
DefaultProgressBar(const std::string& prefix, int width_cap = 20);
|
||||
DefaultProgressBar(const std::string& prefix, std::size_t total, int width = 0);
|
||||
virtual ~DefaultProgressBar() = default;
|
||||
|
||||
void print() override;
|
||||
void set_full() override;
|
||||
void set_progress(size_t current, size_t total) override;
|
||||
void set_extracted() override;
|
||||
|
||||
private:
|
||||
size_t m_progress;
|
||||
int m_width_cap;
|
||||
void print(std::ostream& stream, std::size_t width = 0, bool with_endl = true) override;
|
||||
};
|
||||
|
||||
class HiddenProgressBar : public ProgressBar
|
||||
|
@ -182,19 +592,12 @@ namespace mamba
|
|||
public:
|
||||
HiddenProgressBar(const std::string& prefix,
|
||||
AggregatedBarManager* manager,
|
||||
size_t expected_total);
|
||||
std::size_t total,
|
||||
int width = 0);
|
||||
virtual ~HiddenProgressBar() = default;
|
||||
|
||||
void print() override;
|
||||
void set_full() override;
|
||||
void set_progress(size_t current, size_t total) override;
|
||||
void set_extracted() override;
|
||||
|
||||
private:
|
||||
AggregatedBarManager* p_manager;
|
||||
std::size_t m_current;
|
||||
std::size_t m_total;
|
||||
void print(std::ostream& stream, std::size_t width = 0, bool with_endl = true) override;
|
||||
};
|
||||
}
|
||||
} // namespace mamba
|
||||
|
||||
#endif
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "package_cache.hpp"
|
||||
#include "package_handling.hpp"
|
||||
#include "prefix_data.hpp"
|
||||
#include "progress_bar.hpp"
|
||||
#include "repo.hpp"
|
||||
#include "thread_utils.hpp"
|
||||
#include "transaction_context.hpp"
|
||||
|
@ -56,6 +57,7 @@ namespace mamba
|
|||
bool extract_from_cache();
|
||||
bool validate_extract();
|
||||
const std::string& name() const;
|
||||
std::size_t expected_size() const;
|
||||
auto validation_result() const;
|
||||
void clear_cache() const;
|
||||
|
||||
|
@ -80,7 +82,8 @@ namespace mamba
|
|||
std::string m_sha256, m_md5;
|
||||
std::size_t m_expected_size;
|
||||
|
||||
ProgressProxy m_progress_proxy;
|
||||
bool m_has_progress_bars = false;
|
||||
ProgressProxy m_download_bar, m_extract_bar;
|
||||
std::unique_ptr<DownloadTarget> m_target;
|
||||
|
||||
std::string m_url, m_name, m_channel, m_filename;
|
||||
|
@ -89,6 +92,9 @@ namespace mamba
|
|||
std::future<bool> m_extract_future;
|
||||
|
||||
VALIDATION_RESULT m_validation_result = VALIDATION_RESULT::UNDEFINED;
|
||||
|
||||
std::function<void(ProgressBarRepr&)> extract_repr();
|
||||
std::function<void(ProgressProxy&)> extract_progress_callback();
|
||||
};
|
||||
|
||||
class DownloadExtractSemaphore
|
||||
|
@ -115,8 +121,9 @@ namespace mamba
|
|||
MTransaction(MPool& pool,
|
||||
const std::vector<MatchSpec>& specs_to_remove,
|
||||
const std::vector<MatchSpec>& specs_to_install,
|
||||
MultiPackageCache& caches);
|
||||
MTransaction(MSolver& solver, MultiPackageCache& caches);
|
||||
MultiPackageCache& caches,
|
||||
std::vector<MRepo*> repos);
|
||||
MTransaction(MSolver& solver, MultiPackageCache& caches, std::vector<MRepo*> repos);
|
||||
|
||||
~MTransaction();
|
||||
|
||||
|
@ -133,9 +140,9 @@ namespace mamba
|
|||
void init();
|
||||
to_conda_type to_conda();
|
||||
void log_json();
|
||||
bool fetch_extract_packages(std::vector<MRepo*>& repos);
|
||||
bool fetch_extract_packages();
|
||||
bool empty();
|
||||
bool prompt(std::vector<MRepo*>& repos);
|
||||
bool prompt();
|
||||
void print();
|
||||
bool execute(PrefixData& prefix);
|
||||
bool filter(Solvable* s);
|
||||
|
@ -150,6 +157,8 @@ namespace mamba
|
|||
MultiPackageCache m_multi_cache;
|
||||
const fs::path m_cache_path;
|
||||
std::vector<Solvable*> m_to_install, m_to_remove;
|
||||
std::vector<MRepo*> m_repos;
|
||||
|
||||
History::UserRequest m_history_entry;
|
||||
Transaction* m_transaction;
|
||||
|
||||
|
@ -160,7 +169,8 @@ namespace mamba
|
|||
|
||||
MTransaction create_explicit_transaction_from_urls(MPool& pool,
|
||||
const std::vector<std::string>& urls,
|
||||
MultiPackageCache& package_caches);
|
||||
MultiPackageCache& package_caches,
|
||||
std::vector<MRepo*>& repos);
|
||||
} // namespace mamba
|
||||
|
||||
#endif // MAMBA_TRANSACTION_HPP
|
||||
|
|
|
@ -59,7 +59,6 @@ namespace mamba
|
|||
|
||||
bool is_package_file(const std::string_view& fn);
|
||||
|
||||
void to_human_readable_filesize(std::ostream& o, double bytes, std::size_t precision = 0);
|
||||
bool lexists(const fs::path& p);
|
||||
bool lexists(const fs::path& p, std::error_code& ec);
|
||||
std::vector<fs::path> filter_dir(const fs::path& dir, const std::string& suffix);
|
||||
|
|
|
@ -46,6 +46,8 @@ namespace mamba
|
|||
int max_prio = static_cast<int>(channel_urls.size());
|
||||
std::string prev_channel_name;
|
||||
|
||||
Console::instance().init_progress_bar_manager(ProgressBarMode::multi);
|
||||
|
||||
if (ctx.experimental)
|
||||
{
|
||||
load_tokens();
|
||||
|
@ -86,7 +88,7 @@ namespace mamba
|
|||
// TODO load local channels even when offline
|
||||
if (!ctx.offline)
|
||||
{
|
||||
multi_dl.download(true);
|
||||
multi_dl.download(MAMBA_DOWNLOAD_FAILFAST);
|
||||
}
|
||||
|
||||
std::vector<MRepo> repos;
|
||||
|
|
|
@ -348,7 +348,6 @@ namespace mamba
|
|||
LOG_WARNING << "No 'channels' specified";
|
||||
}
|
||||
|
||||
|
||||
std::vector<MRepo> repos;
|
||||
MPool pool;
|
||||
if (ctx.offline)
|
||||
|
@ -431,23 +430,20 @@ namespace mamba
|
|||
throw std::runtime_error("UnsatisfiableError");
|
||||
}
|
||||
|
||||
MTransaction trans(solver, package_caches);
|
||||
std::vector<MRepo*> repo_ptrs;
|
||||
for (auto& r : repos)
|
||||
repo_ptrs.push_back(&r);
|
||||
|
||||
MTransaction trans(solver, package_caches, repo_ptrs);
|
||||
|
||||
if (ctx.json)
|
||||
{
|
||||
trans.log_json();
|
||||
}
|
||||
// TODO this is not so great
|
||||
std::vector<MRepo*> repo_ptrs;
|
||||
for (auto& r : repos)
|
||||
{
|
||||
repo_ptrs.push_back(&r);
|
||||
}
|
||||
|
||||
Console::stream();
|
||||
|
||||
bool yes = trans.prompt(repo_ptrs);
|
||||
if (yes)
|
||||
if (trans.prompt())
|
||||
{
|
||||
if (create_env && !Context::instance().dry_run)
|
||||
detail::create_target_directory(ctx.target_prefix);
|
||||
|
@ -470,19 +466,18 @@ namespace mamba
|
|||
PrefixData prefix_data(ctx.target_prefix);
|
||||
fs::path pkgs_dirs(Context::instance().root_prefix / "pkgs");
|
||||
MultiPackageCache pkg_caches({ pkgs_dirs });
|
||||
auto transaction = create_explicit_transaction_from_urls(pool, specs, pkg_caches);
|
||||
|
||||
std::vector<MRepo*> repos = {};
|
||||
auto transaction = create_explicit_transaction_from_urls(pool, specs, pkg_caches, repos);
|
||||
|
||||
prefix_data.load();
|
||||
prefix_data.add_virtual_packages(get_virtual_packages());
|
||||
MRepo(pool, prefix_data);
|
||||
|
||||
std::vector<MRepo*> repo_ptrs;
|
||||
|
||||
if (ctx.json)
|
||||
transaction.log_json();
|
||||
|
||||
bool yes = transaction.prompt(repo_ptrs);
|
||||
if (yes)
|
||||
if (transaction.prompt())
|
||||
{
|
||||
if (create_env && !Context::instance().dry_run)
|
||||
detail::create_target_directory(ctx.target_prefix);
|
||||
|
|
|
@ -75,29 +75,25 @@ namespace mamba
|
|||
auto repo = MRepo(pool, prefix_data);
|
||||
repos.push_back(repo);
|
||||
|
||||
const fs::path pkgs_dirs(ctx.root_prefix / "pkgs");
|
||||
MultiPackageCache package_caches({ pkgs_dirs });
|
||||
|
||||
// TODO this is not so great
|
||||
std::vector<MRepo*> repo_ptrs;
|
||||
for (auto& r : repos)
|
||||
{
|
||||
repo_ptrs.push_back(&r);
|
||||
}
|
||||
|
||||
const fs::path pkgs_dirs(ctx.root_prefix / "pkgs");
|
||||
MultiPackageCache package_caches({ pkgs_dirs });
|
||||
|
||||
auto execute_transaction = [&](MTransaction& transaction) {
|
||||
if (ctx.json)
|
||||
transaction.log_json();
|
||||
|
||||
bool yes = transaction.prompt(repo_ptrs);
|
||||
if (yes)
|
||||
if (transaction.prompt())
|
||||
transaction.execute(prefix_data);
|
||||
};
|
||||
|
||||
if (force)
|
||||
{
|
||||
std::vector<MatchSpec> mspecs(specs.begin(), specs.end());
|
||||
auto transaction = MTransaction(pool, mspecs, {}, package_caches);
|
||||
auto transaction = MTransaction(pool, mspecs, {}, package_caches, repo_ptrs);
|
||||
execute_transaction(transaction);
|
||||
}
|
||||
else
|
||||
|
@ -124,7 +120,7 @@ namespace mamba
|
|||
solver.add_jobs(specs, solver_flag);
|
||||
solver.solve();
|
||||
|
||||
MTransaction transaction(solver, package_caches);
|
||||
MTransaction transaction(solver, package_caches, repo_ptrs);
|
||||
execute_transaction(transaction);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,20 +106,19 @@ namespace mamba
|
|||
|
||||
solver.solve();
|
||||
|
||||
MTransaction transaction(solver, package_caches);
|
||||
|
||||
// TODO this is not so great
|
||||
std::vector<MRepo*> repo_ptrs;
|
||||
for (auto& r : repos)
|
||||
{
|
||||
repo_ptrs.push_back(&r);
|
||||
}
|
||||
MTransaction transaction(solver, package_caches, repo_ptrs);
|
||||
|
||||
auto execute_transaction = [&](MTransaction& transaction) {
|
||||
if (ctx.json)
|
||||
transaction.log_json();
|
||||
|
||||
bool yes = transaction.prompt(repo_ptrs);
|
||||
bool yes = transaction.prompt();
|
||||
if (yes)
|
||||
transaction.execute(prefix_data);
|
||||
};
|
||||
|
|
|
@ -347,41 +347,62 @@ namespace mamba
|
|||
return nitems * size;
|
||||
}
|
||||
|
||||
std::function<void(ProgressBarRepr&)> DownloadTarget::download_repr()
|
||||
{
|
||||
return [&](ProgressBarRepr& r) -> void {
|
||||
r.current.set_value(
|
||||
fmt::format("{:>7}", to_human_readable_filesize(m_progress_bar.current(), 1)));
|
||||
|
||||
std::string total_str;
|
||||
if (!m_progress_bar.total()
|
||||
|| (m_progress_bar.total() == std::numeric_limits<std::size_t>::max()))
|
||||
total_str = "??.?MB";
|
||||
else
|
||||
total_str = to_human_readable_filesize(m_progress_bar.total(), 1);
|
||||
r.total.set_value(fmt::format("{:>7}", total_str));
|
||||
|
||||
auto speed = m_progress_bar.speed();
|
||||
r.speed.set_value(
|
||||
fmt::format("@ {:>7}/s", speed ? to_human_readable_filesize(speed, 1) : "??.?MB"));
|
||||
|
||||
r.separator.set_value("/");
|
||||
};
|
||||
}
|
||||
|
||||
std::chrono::steady_clock::time_point DownloadTarget::progress_throttle_time() const
|
||||
{
|
||||
return m_progress_throttle_time;
|
||||
}
|
||||
|
||||
void DownloadTarget::set_progress_throttle_time(
|
||||
const std::chrono::steady_clock::time_point& time)
|
||||
{
|
||||
m_progress_throttle_time = time;
|
||||
}
|
||||
|
||||
int DownloadTarget::progress_callback(
|
||||
void* f, curl_off_t total_to_download, curl_off_t now_downloaded, curl_off_t, curl_off_t)
|
||||
{
|
||||
auto* target = static_cast<DownloadTarget*>(f);
|
||||
|
||||
if (Context::instance().quiet || Context::instance().json)
|
||||
{
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (now - target->progress_throttle_time() < std::chrono::milliseconds(50))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((total_to_download != 0 || target->m_expected_size != 0))
|
||||
{
|
||||
std::stringstream postfix;
|
||||
postfix << std::setw(6);
|
||||
to_human_readable_filesize(postfix, now_downloaded);
|
||||
postfix << " / ";
|
||||
postfix << std::setw(6);
|
||||
to_human_readable_filesize(postfix, total_to_download);
|
||||
postfix << " (";
|
||||
postfix << std::setw(6);
|
||||
to_human_readable_filesize(postfix, target->get_speed(), 2);
|
||||
postfix << "/s)";
|
||||
target->m_progress_bar.set_progress(now_downloaded, total_to_download);
|
||||
target->m_progress_bar.set_postfix(postfix.str());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream postfix;
|
||||
to_human_readable_filesize(postfix, now_downloaded);
|
||||
postfix << " / ?? (";
|
||||
to_human_readable_filesize(postfix, target->get_speed(), 2);
|
||||
postfix << "/s)";
|
||||
target->m_progress_bar.set_progress(SIZE_MAX, SIZE_MAX);
|
||||
target->m_progress_bar.set_postfix(postfix.str());
|
||||
}
|
||||
target->set_progress_throttle_time(now);
|
||||
|
||||
if (!total_to_download && !target->expected_size())
|
||||
target->m_progress_bar.activate_spinner();
|
||||
else
|
||||
target->m_progress_bar.deactivate_spinner();
|
||||
|
||||
if (!total_to_download && target->expected_size())
|
||||
target->m_progress_bar.update_current(now_downloaded);
|
||||
else
|
||||
target->m_progress_bar.update_progress(now_downloaded, total_to_download);
|
||||
|
||||
target->m_progress_bar.set_speed(target->get_speed());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -407,6 +428,8 @@ namespace mamba
|
|||
{
|
||||
m_has_progress_bar = true;
|
||||
m_progress_bar = progress_proxy;
|
||||
m_progress_bar.set_repr_hook(download_repr());
|
||||
|
||||
curl_easy_setopt(m_handle, CURLOPT_XFERINFOFUNCTION, &DownloadTarget::progress_callback);
|
||||
curl_easy_setopt(m_handle, CURLOPT_XFERINFODATA, this);
|
||||
curl_easy_setopt(m_handle, CURLOPT_NOPROGRESS, 0L);
|
||||
|
@ -422,12 +445,16 @@ namespace mamba
|
|||
return m_name;
|
||||
}
|
||||
|
||||
std::size_t DownloadTarget::expected_size() const
|
||||
{
|
||||
return m_expected_size;
|
||||
}
|
||||
|
||||
static size_t discard(char* ptr, size_t size, size_t nmemb, void*)
|
||||
{
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
|
||||
bool DownloadTarget::resource_exists()
|
||||
{
|
||||
auto handle = curl_easy_init();
|
||||
|
@ -474,7 +501,14 @@ namespace mamba
|
|||
{
|
||||
curl_off_t speed;
|
||||
CURLcode res = curl_easy_getinfo(m_handle, CURLINFO_SPEED_DOWNLOAD_T, &speed);
|
||||
return res == CURLE_OK ? speed : 0;
|
||||
if (res != CURLE_OK)
|
||||
{
|
||||
if (m_has_progress_bar)
|
||||
speed = m_progress_bar.avg_speed();
|
||||
else
|
||||
speed = 0;
|
||||
}
|
||||
return speed;
|
||||
}
|
||||
|
||||
void DownloadTarget::set_result(CURLcode r)
|
||||
|
@ -499,7 +533,8 @@ namespace mamba
|
|||
|
||||
if (m_has_progress_bar)
|
||||
{
|
||||
m_progress_bar.set_progress(0, 1);
|
||||
m_progress_bar.update_progress(0, 1);
|
||||
// m_progress_bar.set_elapsed_time();
|
||||
m_progress_bar.set_postfix(curl_easy_strerror(result));
|
||||
}
|
||||
if (!m_ignore_failure && !can_retry())
|
||||
|
@ -513,17 +548,12 @@ namespace mamba
|
|||
{
|
||||
char* effective_url = nullptr;
|
||||
|
||||
auto cres = curl_easy_getinfo(m_handle, CURLINFO_SPEED_DOWNLOAD_T, &avg_speed);
|
||||
if (cres != CURLE_OK)
|
||||
{
|
||||
avg_speed = 0;
|
||||
}
|
||||
|
||||
avg_speed = get_speed();
|
||||
curl_easy_getinfo(m_handle, CURLINFO_RESPONSE_CODE, &http_status);
|
||||
curl_easy_getinfo(m_handle, CURLINFO_EFFECTIVE_URL, &effective_url);
|
||||
curl_easy_getinfo(m_handle, CURLINFO_SIZE_DOWNLOAD_T, &downloaded_size);
|
||||
|
||||
LOG_INFO << "Transfer finalized, status: " << http_status << " [" << effective_url << "] "
|
||||
LOG_INFO << "Transfer finalized, status " << http_status << " [" << effective_url << "] "
|
||||
<< downloaded_size << " bytes";
|
||||
|
||||
if (http_status >= 500 && can_retry())
|
||||
|
@ -533,26 +563,53 @@ namespace mamba
|
|||
= std::chrono::steady_clock::now() + std::chrono::seconds(m_retry_wait_seconds);
|
||||
std::stringstream msg;
|
||||
msg << "Failed (" << http_status << "), retry in " << m_retry_wait_seconds << "s";
|
||||
m_progress_bar.set_progress(0, downloaded_size);
|
||||
m_progress_bar.set_postfix(msg.str());
|
||||
if (m_has_progress_bar)
|
||||
{
|
||||
m_progress_bar.update_progress(0, downloaded_size);
|
||||
m_progress_bar.set_postfix(msg.str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
m_file.close();
|
||||
|
||||
final_url = effective_url;
|
||||
if (m_finalize_callback)
|
||||
|
||||
if (m_has_progress_bar)
|
||||
{
|
||||
return m_finalize_callback();
|
||||
m_progress_bar.set_speed(avg_speed);
|
||||
m_progress_bar.set_total(downloaded_size);
|
||||
m_progress_bar.set_full();
|
||||
m_progress_bar.set_postfix("downloaded");
|
||||
}
|
||||
|
||||
bool ret = true;
|
||||
if (m_finalize_callback)
|
||||
ret = m_finalize_callback();
|
||||
else
|
||||
{
|
||||
if (m_has_progress_bar)
|
||||
{
|
||||
m_progress_bar.mark_as_completed("Downloaded " + m_name);
|
||||
}
|
||||
m_progress_bar.mark_as_completed();
|
||||
else
|
||||
Console::instance().print(name() + " completed");
|
||||
}
|
||||
return true;
|
||||
|
||||
if (m_has_progress_bar)
|
||||
{
|
||||
// make sure total value is up-to-date
|
||||
m_progress_bar.update_repr(false);
|
||||
// select field to display and make sure they are
|
||||
// properly set if not yet printed by the progress bar manager
|
||||
ProgressBarRepr r = m_progress_bar.repr();
|
||||
r.prefix.set_format("{:<35}").reset_width();
|
||||
r.progress.deactivate();
|
||||
r.current.deactivate();
|
||||
r.separator.deactivate();
|
||||
|
||||
auto console_stream = Console::stream();
|
||||
r.print(console_stream, 0, false);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**************************************
|
||||
|
@ -622,7 +679,7 @@ namespace mamba
|
|||
|
||||
if (msg->msg == CURLMSG_DONE)
|
||||
{
|
||||
LOG_INFO << "Transfer done ...";
|
||||
LOG_INFO << "Transfer done for '" << current_target->name() << "'";
|
||||
// We are only interested in messages about finished transfers
|
||||
curl_multi_remove_handle(m_handle, current_target->handle());
|
||||
|
||||
|
@ -632,7 +689,7 @@ namespace mamba
|
|||
// transfer did not work! can we retry?
|
||||
if (current_target->can_retry())
|
||||
{
|
||||
LOG_INFO << "Adding target to retry!";
|
||||
LOG_INFO << "Setting retry for '" << current_target->name() << "'";
|
||||
m_retry_targets.push_back(current_target);
|
||||
}
|
||||
else
|
||||
|
@ -648,10 +705,40 @@ namespace mamba
|
|||
return true;
|
||||
}
|
||||
|
||||
bool MultiDownloadTarget::download(bool failfast)
|
||||
bool MultiDownloadTarget::download(int options)
|
||||
{
|
||||
bool failfast = options & MAMBA_DOWNLOAD_FAILFAST;
|
||||
bool sort = options & MAMBA_DOWNLOAD_SORT;
|
||||
|
||||
auto& ctx = Context::instance();
|
||||
|
||||
if (m_targets.empty())
|
||||
{
|
||||
LOG_INFO << "All targets to download are cached";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sort)
|
||||
std::sort(m_targets.begin(),
|
||||
m_targets.end(),
|
||||
[](DownloadTarget* a, DownloadTarget* b) -> bool {
|
||||
return a->expected_size() > b->expected_size();
|
||||
});
|
||||
|
||||
LOG_INFO << "Starting to download targets";
|
||||
|
||||
auto& pbar_manager = Console::instance().progress_bar_manager();
|
||||
interruption_guard g([]() { Console::instance().progress_bar_manager().terminate(); });
|
||||
|
||||
// be sure the progress bar manager was not already started
|
||||
// it would mean this code is part of a larger process using progress bars
|
||||
bool pbar_manager_started = pbar_manager.started();
|
||||
if (!(ctx.no_progress_bars || ctx.json || ctx.quiet || pbar_manager_started))
|
||||
{
|
||||
pbar_manager.start();
|
||||
pbar_manager.watch_print();
|
||||
}
|
||||
|
||||
int still_running, repeats = 0;
|
||||
const long max_wait_msecs = 1000;
|
||||
do
|
||||
|
@ -723,6 +810,13 @@ namespace mamba
|
|||
curl_multi_cleanup(m_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(ctx.no_progress_bars || ctx.json || ctx.quiet || pbar_manager_started))
|
||||
{
|
||||
pbar_manager.terminate();
|
||||
pbar_manager.clear_progress_bars();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace mamba
|
||||
|
|
|
@ -26,53 +26,6 @@
|
|||
|
||||
namespace mamba
|
||||
{
|
||||
std::ostream& write_duration(std::ostream& os, std::chrono::nanoseconds ns)
|
||||
{
|
||||
using std::chrono::duration;
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::hours;
|
||||
using std::chrono::minutes;
|
||||
using std::chrono::seconds;
|
||||
|
||||
using days = duration<int, std::ratio<86400>>;
|
||||
char fill = os.fill();
|
||||
os.fill('0');
|
||||
auto d = duration_cast<days>(ns);
|
||||
ns -= d;
|
||||
auto h = duration_cast<hours>(ns);
|
||||
ns -= h;
|
||||
auto m = duration_cast<minutes>(ns);
|
||||
ns -= m;
|
||||
auto s = duration_cast<seconds>(ns);
|
||||
if (d.count() > 0)
|
||||
{
|
||||
os << std::setw(2) << d.count() << "d:";
|
||||
}
|
||||
if (h.count() > 0)
|
||||
{
|
||||
os << std::setw(2) << h.count() << "h:";
|
||||
}
|
||||
os << std::setw(2) << m.count() << "m:" << std::setw(2) << s.count() << 's';
|
||||
os.fill(fill);
|
||||
return os;
|
||||
}
|
||||
|
||||
int get_console_width()
|
||||
{
|
||||
#ifndef _WIN32
|
||||
struct winsize w;
|
||||
ioctl(0, TIOCGWINSZ, &w);
|
||||
return w.ws_col;
|
||||
#else
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO coninfo;
|
||||
auto res = GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
|
||||
return coninfo.dwSize.X;
|
||||
#endif
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string cut_repo_name(const std::string& full_url)
|
||||
{
|
||||
std::string remaining_url, scheme, auth, token;
|
||||
|
@ -297,8 +250,8 @@ namespace mamba
|
|||
|
||||
Console::Console()
|
||||
: m_mutex()
|
||||
, p_progress_manager(make_progress_bar_manager(ProgressBarMode::multi))
|
||||
{
|
||||
init_progress_bar_manager(ProgressBarMode::multi);
|
||||
#ifdef _WIN32
|
||||
// initialize ANSI codes on Win terminals
|
||||
auto hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
@ -339,10 +292,10 @@ namespace mamba
|
|||
if (!(Context::instance().quiet || Context::instance().json) || force_print)
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(instance().m_mutex);
|
||||
if (instance().p_progress_manager)
|
||||
|
||||
if (instance().p_progress_bar_manager && instance().p_progress_bar_manager->started())
|
||||
{
|
||||
instance().p_progress_manager->print(instance().hide_secrets(str),
|
||||
instance().skip_progress_bars());
|
||||
instance().m_buffer.push_back(instance().hide_secrets(str));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -351,6 +304,17 @@ namespace mamba
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> Console::m_buffer({});
|
||||
|
||||
void Console::print_buffer(std::ostream& ostream)
|
||||
{
|
||||
for (auto& message : m_buffer)
|
||||
ostream << message << "\n";
|
||||
|
||||
const std::lock_guard<std::mutex> lock(instance().m_mutex);
|
||||
m_buffer.clear();
|
||||
}
|
||||
|
||||
bool Console::prompt(const std::string_view& message, char fallback, std::istream& input_stream)
|
||||
{
|
||||
if (Context::instance().always_yes)
|
||||
|
@ -399,35 +363,31 @@ namespace mamba
|
|||
|
||||
ProgressProxy Console::add_progress_bar(const std::string& name, size_t expected_total)
|
||||
{
|
||||
return p_progress_manager->add_progress_bar(name, expected_total);
|
||||
if (Context::instance().no_progress_bars)
|
||||
return ProgressProxy();
|
||||
else
|
||||
return p_progress_bar_manager->add_progress_bar(name, expected_total);
|
||||
}
|
||||
|
||||
void Console::init_multi_progress(ProgressBarMode mode)
|
||||
void Console::clear_progress_bars()
|
||||
{
|
||||
p_progress_manager = make_progress_bar_manager(mode);
|
||||
return p_progress_bar_manager->clear_progress_bars();
|
||||
}
|
||||
|
||||
void Console::deactivate_progress_bar(std::size_t idx, const std::string_view& msg)
|
||||
ProgressBarManager& Console::init_progress_bar_manager(ProgressBarMode mode)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(instance().m_mutex);
|
||||
p_progress_manager->deactivate_progress_bar(idx, msg);
|
||||
p_progress_bar_manager = make_progress_bar_manager(mode);
|
||||
p_progress_bar_manager->register_print_hook(Console::print_buffer);
|
||||
p_progress_bar_manager->register_print_hook(MessageLogger::print_buffer);
|
||||
p_progress_bar_manager->register_pre_start_hook(MessageLogger::activate_buffer);
|
||||
p_progress_bar_manager->register_post_stop_hook(MessageLogger::deactivate_buffer);
|
||||
|
||||
return *p_progress_bar_manager;
|
||||
}
|
||||
|
||||
void Console::print_progress(std::size_t idx)
|
||||
ProgressBarManager& Console::progress_bar_manager()
|
||||
{
|
||||
if (skip_progress_bars())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(instance().m_mutex);
|
||||
p_progress_manager->print_progress(idx);
|
||||
}
|
||||
|
||||
bool Console::skip_progress_bars() const
|
||||
{
|
||||
return Context::instance().quiet || Context::instance().json
|
||||
|| Context::instance().no_progress_bars;
|
||||
return *p_progress_bar_manager;
|
||||
}
|
||||
|
||||
std::string strip_file_prefix(const std::string& file)
|
||||
|
@ -455,8 +415,25 @@ namespace mamba
|
|||
|
||||
MessageLogger::~MessageLogger()
|
||||
{
|
||||
auto str = Console::hide_secrets(m_stream.str());
|
||||
switch (m_level)
|
||||
if (!use_buffer)
|
||||
emit(m_stream.str(), m_level);
|
||||
else
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_buffer.push_back({ m_stream.str(), m_level });
|
||||
}
|
||||
}
|
||||
|
||||
std::mutex MessageLogger::m_mutex;
|
||||
|
||||
bool MessageLogger::use_buffer(false);
|
||||
|
||||
std::vector<std::pair<std::string, spdlog::level::level_enum>> MessageLogger::m_buffer({});
|
||||
|
||||
void MessageLogger::emit(const std::string& msg, const spdlog::level::level_enum& level)
|
||||
{
|
||||
auto str = Console::hide_secrets(msg);
|
||||
switch (level)
|
||||
{
|
||||
case spdlog::level::critical:
|
||||
SPDLOG_CRITICAL(prepend(str, "", std::string(4, ' ').c_str()));
|
||||
|
@ -488,11 +465,31 @@ namespace mamba
|
|||
return m_stream;
|
||||
}
|
||||
|
||||
void MessageLogger::activate_buffer()
|
||||
{
|
||||
use_buffer = true;
|
||||
}
|
||||
|
||||
void MessageLogger::deactivate_buffer()
|
||||
{
|
||||
use_buffer = false;
|
||||
}
|
||||
|
||||
void MessageLogger::print_buffer(std::ostream& /*ostream*/)
|
||||
{
|
||||
for (auto& [msg, level] : m_buffer)
|
||||
emit(msg, level);
|
||||
|
||||
spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->flush(); });
|
||||
|
||||
const std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_buffer.clear();
|
||||
}
|
||||
|
||||
Logger::Logger(const std::string& pattern)
|
||||
: spdlog::logger(std::string("mamba"),
|
||||
std::make_shared<spdlog::sinks::stderr_color_sink_mt>())
|
||||
{
|
||||
// set_pattern("%^[%L %Y-%m-%d %T:%e]%$ %v");
|
||||
set_pattern(pattern);
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -68,7 +68,8 @@ namespace mamba
|
|||
const std::string& repodata_fn,
|
||||
MultiPackageCache& caches,
|
||||
bool is_noarch)
|
||||
: m_loaded(false)
|
||||
: m_progress_bar(ProgressProxy())
|
||||
, m_loaded(false)
|
||||
, m_download_complete(false)
|
||||
, m_repodata_url(repodata_url)
|
||||
, m_name(name)
|
||||
|
@ -198,7 +199,7 @@ namespace mamba
|
|||
{
|
||||
std::string prefix = m_name;
|
||||
prefix.resize(PREFIX_LENGTH - 1, ' ');
|
||||
Console::stream() << prefix << " Using cache";
|
||||
Console::stream() << fmt::format("{:<35} Using cache", prefix);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -242,9 +243,13 @@ namespace mamba
|
|||
{
|
||||
LOG_INFO << "Unable to retrieve repodata (response: " << m_target->http_status
|
||||
<< ") for '" << m_repodata_url << "'";
|
||||
m_progress_bar.set_postfix(std::to_string(m_target->http_status) + " Failed");
|
||||
m_progress_bar.set_full();
|
||||
m_progress_bar.mark_as_completed();
|
||||
|
||||
if (m_progress_bar)
|
||||
{
|
||||
m_progress_bar.set_postfix(std::to_string(m_target->http_status) + " failed");
|
||||
m_progress_bar.set_full();
|
||||
m_progress_bar.mark_as_completed();
|
||||
}
|
||||
m_loaded = false;
|
||||
return false;
|
||||
}
|
||||
|
@ -314,7 +319,6 @@ namespace mamba
|
|||
m_valid_cache_path = writable_cache_path;
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
LOG_TRACE << "Refreshing '" << json_file.string() << "'";
|
||||
auto lock = LockFile(json_file);
|
||||
|
@ -328,9 +332,17 @@ namespace mamba
|
|||
m_solv_cache_valid = true;
|
||||
}
|
||||
|
||||
m_progress_bar.set_postfix("No change");
|
||||
m_progress_bar.set_full();
|
||||
m_progress_bar.mark_as_completed();
|
||||
if (m_progress_bar)
|
||||
{
|
||||
m_progress_bar.set_postfix("No change");
|
||||
m_progress_bar.mark_as_completed();
|
||||
|
||||
auto& r = m_progress_bar.repr();
|
||||
r.prefix.set_format("{:<35}");
|
||||
r.total.deactivate();
|
||||
r.speed.deactivate();
|
||||
r.elapsed.deactivate();
|
||||
}
|
||||
|
||||
m_json_cache_valid = true;
|
||||
m_loaded = true;
|
||||
|
@ -370,11 +382,13 @@ namespace mamba
|
|||
|
||||
if (ends_with(m_repodata_url, ".bz2"))
|
||||
{
|
||||
m_progress_bar.set_postfix("Decomp...");
|
||||
if (m_progress_bar)
|
||||
m_progress_bar.set_postfix("decompressing");
|
||||
decompress();
|
||||
}
|
||||
|
||||
m_progress_bar.set_postfix("Finalizing...");
|
||||
if (m_progress_bar)
|
||||
m_progress_bar.set_postfix("finalizing");
|
||||
|
||||
std::ifstream temp_file = open_ifstream(m_temp_file->path());
|
||||
std::stringstream temp_json;
|
||||
|
@ -397,9 +411,11 @@ namespace mamba
|
|||
exit(1);
|
||||
}
|
||||
|
||||
m_progress_bar.set_postfix("Done");
|
||||
m_progress_bar.set_full();
|
||||
m_progress_bar.mark_as_completed();
|
||||
if (m_progress_bar)
|
||||
{
|
||||
m_progress_bar.repr().postfix.set_value("downloaded").deactivate();
|
||||
m_progress_bar.mark_as_completed();
|
||||
}
|
||||
|
||||
m_valid_cache_path = writable_cache_path;
|
||||
m_json_cache_valid = true;
|
||||
|
@ -429,10 +445,14 @@ namespace mamba
|
|||
|
||||
void MSubdirData::create_target(nlohmann::json& mod_etag)
|
||||
{
|
||||
auto& ctx = Context::instance();
|
||||
m_temp_file = std::make_unique<TemporaryFile>();
|
||||
m_progress_bar = Console::instance().add_progress_bar(m_name);
|
||||
m_target = std::make_unique<DownloadTarget>(m_name, m_repodata_url, m_temp_file->path());
|
||||
m_target->set_progress_bar(m_progress_bar);
|
||||
if (!(ctx.no_progress_bars || ctx.quiet || ctx.json))
|
||||
{
|
||||
m_progress_bar = Console::instance().add_progress_bar(m_name);
|
||||
m_target->set_progress_bar(m_progress_bar);
|
||||
}
|
||||
// if we get something _other_ than the noarch, we DO NOT throw if the file
|
||||
// can't be retrieved
|
||||
if (!m_is_noarch)
|
||||
|
|
|
@ -75,6 +75,9 @@ namespace mamba
|
|||
m_expected_size = pkg_info.size;
|
||||
m_sha256 = pkg_info.sha256;
|
||||
m_md5 = pkg_info.md5;
|
||||
|
||||
auto& ctx = Context::instance();
|
||||
m_has_progress_bars = !(ctx.no_progress_bars || ctx.quiet || ctx.json);
|
||||
}
|
||||
|
||||
void PackageDownloadExtractTarget::write_repodata_record(const fs::path& base_path)
|
||||
|
@ -114,7 +117,12 @@ namespace mamba
|
|||
LOG_ERROR << "File not valid: file size doesn't match expectation " << m_tarball_path
|
||||
<< "\nExpected: " << m_expected_size
|
||||
<< "\nActual: " << size_t(m_target->downloaded_size) << "\n";
|
||||
m_progress_proxy.mark_as_completed("File size validation error.");
|
||||
if (m_has_progress_bars)
|
||||
{
|
||||
m_download_bar.set_postfix("validation failed");
|
||||
m_download_bar.mark_as_completed();
|
||||
}
|
||||
Console::instance().print(m_filename + " tarball has incorrect size");
|
||||
m_validation_result = SIZE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
@ -126,7 +134,12 @@ namespace mamba
|
|||
if (m_sha256 != sha256sum)
|
||||
{
|
||||
m_validation_result = SHA256_ERROR;
|
||||
m_progress_proxy.mark_as_completed("SHA256 sum validation error.");
|
||||
if (m_has_progress_bars)
|
||||
{
|
||||
m_download_bar.set_postfix("validation failed");
|
||||
m_download_bar.mark_as_completed();
|
||||
}
|
||||
Console::instance().print(m_filename + " tarball has incorrect checksum");
|
||||
LOG_ERROR << "File not valid: SHA256 sum doesn't match expectation "
|
||||
<< m_tarball_path << "\nExpected: " << m_sha256
|
||||
<< "\nActual: " << sha256sum << "\n";
|
||||
|
@ -139,23 +152,50 @@ namespace mamba
|
|||
if (m_md5 != md5sum)
|
||||
{
|
||||
m_validation_result = MD5SUM_ERROR;
|
||||
m_progress_proxy.mark_as_completed("MD5 sum validation error.");
|
||||
if (m_has_progress_bars)
|
||||
{
|
||||
m_download_bar.set_postfix("validation failed");
|
||||
m_download_bar.mark_as_completed();
|
||||
}
|
||||
Console::instance().print(m_filename + " tarball has incorrect checksum");
|
||||
LOG_ERROR << "File not valid: MD5 sum doesn't match expectation " << m_tarball_path
|
||||
<< "\nExpected: " << m_md5 << "\nActual: " << md5sum << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::function<void(ProgressBarRepr&)> PackageDownloadExtractTarget::extract_repr()
|
||||
{
|
||||
return [&](ProgressBarRepr& r) -> void {
|
||||
if (r.progress_bar().started())
|
||||
r.postfix.set_value("extracting");
|
||||
else
|
||||
r.postfix.set_value("extracted");
|
||||
};
|
||||
}
|
||||
|
||||
std::function<void(ProgressProxy&)> PackageDownloadExtractTarget::extract_progress_callback()
|
||||
{
|
||||
return [&](ProgressProxy& bar) -> void {
|
||||
if (bar.started())
|
||||
bar.set_progress(0, 1);
|
||||
};
|
||||
}
|
||||
|
||||
bool PackageDownloadExtractTarget::extract()
|
||||
{
|
||||
// Extracting is __not__ yet thread safe it seems...
|
||||
interruption_point();
|
||||
|
||||
if (m_has_progress_bars)
|
||||
m_extract_bar.start();
|
||||
|
||||
LOG_DEBUG << "Waiting for decompression " << m_tarball_path;
|
||||
m_progress_proxy.set_postfix("Waiting...");
|
||||
if (m_has_progress_bars)
|
||||
m_extract_bar.update_progress(0, 1);
|
||||
{
|
||||
std::lock_guard<counting_semaphore> lock(DownloadExtractSemaphore::semaphore);
|
||||
interruption_point();
|
||||
m_progress_proxy.set_postfix("Decompressing...");
|
||||
LOG_DEBUG << "Decompressing '" << m_tarball_path.string() << "'";
|
||||
fs::path extract_path;
|
||||
try
|
||||
|
@ -185,69 +225,77 @@ namespace mamba
|
|||
LOG_DEBUG << "Extracted to '" << extract_path.string() << "'";
|
||||
write_repodata_record(extract_path);
|
||||
add_url();
|
||||
|
||||
if (m_has_progress_bars)
|
||||
{
|
||||
m_extract_bar.set_full();
|
||||
m_extract_bar.mark_as_completed();
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
Console::instance().print(m_filename + " extraction failed");
|
||||
LOG_ERROR << "Error when extracting package: " << e.what();
|
||||
m_decompress_exception = e;
|
||||
m_validation_result = VALIDATION_RESULT::EXTRACT_ERROR;
|
||||
m_finished = true;
|
||||
m_progress_proxy.mark_as_completed("Extraction error");
|
||||
if (m_has_progress_bars)
|
||||
{
|
||||
m_extract_bar.set_postfix("extraction failed");
|
||||
m_extract_bar.mark_as_completed();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
m_finished = true;
|
||||
return m_finished;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PackageDownloadExtractTarget::extract_from_cache()
|
||||
{
|
||||
bool result = this->extract();
|
||||
if (result)
|
||||
{
|
||||
std::stringstream final_msg;
|
||||
final_msg << "Extracted " << std::left << std::setw(30) << m_name;
|
||||
m_progress_proxy.mark_as_completed(final_msg.str());
|
||||
return result;
|
||||
}
|
||||
// currently we always return true
|
||||
this->extract();
|
||||
m_finished = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PackageDownloadExtractTarget::validate_extract()
|
||||
{
|
||||
using std::chrono::nanoseconds;
|
||||
|
||||
if (m_has_progress_bars)
|
||||
{
|
||||
m_extract_bar.start();
|
||||
m_extract_bar.set_postfix("validating");
|
||||
}
|
||||
validate();
|
||||
|
||||
// Validation
|
||||
if (m_validation_result != VALIDATION_RESULT::VALID)
|
||||
{
|
||||
if (m_has_progress_bars)
|
||||
m_extract_bar.set_postfix("validation failed");
|
||||
LOG_WARNING << "'" << m_tarball_path.string() << "' validation failed";
|
||||
// abort here, but set finished to true
|
||||
m_finished = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::stringstream final_msg;
|
||||
final_msg << "Finished " << std::left << std::setw(30) << m_name << std::right
|
||||
<< std::setw(8);
|
||||
m_progress_proxy.elapsed_time_to_stream(final_msg);
|
||||
final_msg << " " << std::setw(12 + 2);
|
||||
to_human_readable_filesize(final_msg, m_expected_size);
|
||||
final_msg << " " << std::setw(6);
|
||||
to_human_readable_filesize(final_msg, m_target->avg_speed);
|
||||
final_msg << "/s";
|
||||
m_progress_proxy.mark_as_completed(final_msg.str());
|
||||
if (m_has_progress_bars)
|
||||
m_extract_bar.set_postfix("validated");
|
||||
LOG_DEBUG << "'" << m_tarball_path.string() << "' successfully validated";
|
||||
|
||||
bool result = this->extract();
|
||||
m_progress_proxy.mark_as_extracted();
|
||||
m_finished = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool PackageDownloadExtractTarget::finalize_callback()
|
||||
{
|
||||
m_progress_proxy.set_full();
|
||||
m_progress_proxy.set_postfix("Validating...");
|
||||
if (m_has_progress_bars)
|
||||
{
|
||||
m_download_bar.repr().postfix.set_value("downloaded").deactivate();
|
||||
m_download_bar.mark_as_completed();
|
||||
}
|
||||
|
||||
LOG_INFO << "Download finished, validating '" << m_tarball_path.string() << "'";
|
||||
|
||||
thread v(&PackageDownloadExtractTarget::validate_extract, this);
|
||||
v.detach();
|
||||
|
||||
|
@ -279,6 +327,11 @@ namespace mamba
|
|||
return m_name;
|
||||
}
|
||||
|
||||
std::size_t PackageDownloadExtractTarget::expected_size() const
|
||||
{
|
||||
return m_expected_size;
|
||||
}
|
||||
|
||||
// todo remove cache from this interface
|
||||
DownloadTarget* PackageDownloadExtractTarget::target(MultiPackageCache& caches)
|
||||
{
|
||||
|
@ -296,15 +349,25 @@ namespace mamba
|
|||
caches.first_writable_cache(true).clear_query_cache(m_package_info);
|
||||
m_cache_path = caches.first_writable_path();
|
||||
|
||||
if (m_has_progress_bars)
|
||||
{
|
||||
m_extract_bar = Console::instance().add_progress_bar(m_name, 1);
|
||||
m_extract_bar.activate_spinner();
|
||||
m_extract_bar.set_progress_hook(extract_progress_callback());
|
||||
m_extract_bar.set_repr_hook(extract_repr());
|
||||
Console::instance().progress_bar_manager().add_label("Extract", m_extract_bar);
|
||||
}
|
||||
|
||||
if (!tarball_cache.empty())
|
||||
{
|
||||
LOG_DEBUG << "Found valid tarball cache at '" << tarball_cache.string() << "'";
|
||||
|
||||
m_tarball_path = tarball_cache / m_filename;
|
||||
m_progress_proxy = Console::instance().add_progress_bar(m_name);
|
||||
m_validation_result = VALIDATION_RESULT::VALID;
|
||||
thread v(&PackageDownloadExtractTarget::extract_from_cache, this);
|
||||
v.detach();
|
||||
LOG_DEBUG << "Using cached tarball '" << m_filename << "'";
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -313,16 +376,21 @@ namespace mamba
|
|||
LOG_DEBUG << "Adding '" << m_name << "' to download targets from '" << m_url << "'";
|
||||
|
||||
m_tarball_path = m_cache_path / m_filename;
|
||||
m_progress_proxy = Console::instance().add_progress_bar(m_name, m_expected_size);
|
||||
m_target = std::make_unique<DownloadTarget>(m_name, m_url, m_tarball_path);
|
||||
m_target->set_finalize_callback(&PackageDownloadExtractTarget::finalize_callback,
|
||||
this);
|
||||
m_target->set_expected_size(m_expected_size);
|
||||
m_target->set_progress_bar(m_progress_proxy);
|
||||
if (m_has_progress_bars)
|
||||
{
|
||||
m_download_bar = Console::instance().add_progress_bar(m_name, m_expected_size);
|
||||
m_target->set_progress_bar(m_download_bar);
|
||||
Console::instance().progress_bar_manager().add_label("Download",
|
||||
m_download_bar);
|
||||
}
|
||||
return m_target.get();
|
||||
}
|
||||
}
|
||||
LOG_INFO << "Using cached '" << m_name << "'";
|
||||
LOG_DEBUG << "Using cached '" << m_name << "'";
|
||||
m_finished = true;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -350,8 +418,10 @@ namespace mamba
|
|||
MTransaction::MTransaction(MPool& pool,
|
||||
const std::vector<MatchSpec>& specs_to_remove,
|
||||
const std::vector<MatchSpec>& specs_to_install,
|
||||
MultiPackageCache& caches)
|
||||
MultiPackageCache& caches,
|
||||
std::vector<MRepo*> repos)
|
||||
: m_multi_cache(caches)
|
||||
, m_repos(repos)
|
||||
{
|
||||
// auto& ctx = Context::instance();
|
||||
std::vector<PackageInfo> pi_result;
|
||||
|
@ -453,8 +523,11 @@ namespace mamba
|
|||
}
|
||||
|
||||
|
||||
MTransaction::MTransaction(MSolver& solver, MultiPackageCache& caches)
|
||||
MTransaction::MTransaction(MSolver& solver,
|
||||
MultiPackageCache& caches,
|
||||
std::vector<MRepo*> repos)
|
||||
: m_multi_cache(caches)
|
||||
, m_repos(repos)
|
||||
{
|
||||
if (!solver.is_solved())
|
||||
{
|
||||
|
@ -696,10 +769,12 @@ namespace mamba
|
|||
}
|
||||
|
||||
LockFile lf(ctx.target_prefix / "conda-meta");
|
||||
|
||||
clean_trash_files(ctx.target_prefix, false);
|
||||
|
||||
Console::stream() << "\nTransaction starting";
|
||||
fetch_extract_packages();
|
||||
|
||||
// m_transaction_context = TransactionContext(prefix.path(), find_python_version());
|
||||
History::UserRequest ur = History::UserRequest::prefilled();
|
||||
|
||||
TransactionRollback rollback;
|
||||
|
@ -875,12 +950,14 @@ namespace mamba
|
|||
add_json(to_unlink, "UNLINK");
|
||||
}
|
||||
|
||||
bool MTransaction::fetch_extract_packages(std::vector<MRepo*>& repos)
|
||||
bool MTransaction::fetch_extract_packages()
|
||||
{
|
||||
std::vector<std::unique_ptr<PackageDownloadExtractTarget>> targets;
|
||||
MultiDownloadTarget multi_dl;
|
||||
|
||||
Console::instance().init_multi_progress(ProgressBarMode::aggregated);
|
||||
auto& pbar_manager
|
||||
= Console::instance().init_progress_bar_manager(ProgressBarMode::aggregated);
|
||||
auto& aggregated_pbar_manager = dynamic_cast<AggregatedBarManager&>(pbar_manager);
|
||||
|
||||
auto& ctx = Context::instance();
|
||||
DownloadExtractSemaphore::set_max(ctx.extract_threads);
|
||||
|
@ -892,7 +969,7 @@ namespace mamba
|
|||
{
|
||||
std::string url;
|
||||
MRepo* mamba_repo = nullptr;
|
||||
for (auto& r : repos)
|
||||
for (auto& r : m_repos)
|
||||
{
|
||||
if (r->repo() == s->repo)
|
||||
{
|
||||
|
@ -900,6 +977,7 @@ namespace mamba
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mamba_repo == nullptr || mamba_repo->url() == "")
|
||||
{
|
||||
// use fallback mediadir / mediafile
|
||||
|
@ -923,7 +1001,9 @@ namespace mamba
|
|||
}
|
||||
|
||||
targets.emplace_back(std::make_unique<PackageDownloadExtractTarget>(s));
|
||||
multi_dl.add(targets[targets.size() - 1]->target(m_multi_cache));
|
||||
DownloadTarget* download_target = targets.back()->target(m_multi_cache);
|
||||
if (download_target != nullptr)
|
||||
multi_dl.add(download_target);
|
||||
}
|
||||
|
||||
if (ctx.experimental && ctx.verify_artifacts)
|
||||
|
@ -932,9 +1012,75 @@ namespace mamba
|
|||
<< "package(s) are trusted " << termcolor::reset;
|
||||
LOG_INFO << "All package(s) are trusted";
|
||||
}
|
||||
interruption_guard g([]() { Console::instance().init_multi_progress(); });
|
||||
|
||||
bool downloaded = multi_dl.download(true);
|
||||
if (!(ctx.no_progress_bars || ctx.json || ctx.quiet))
|
||||
{
|
||||
interruption_guard g([]() { Console::instance().progress_bar_manager().terminate(); });
|
||||
|
||||
auto* dl_bar = aggregated_pbar_manager.aggregated_bar("Download");
|
||||
if (dl_bar)
|
||||
dl_bar->set_repr_hook([&](ProgressBarRepr& repr) -> void {
|
||||
auto active_tasks = dl_bar->active_tasks().size();
|
||||
if (active_tasks == 0)
|
||||
{
|
||||
repr.prefix.set_value(fmt::format("{:<16}", "Downloading"));
|
||||
repr.postfix.set_value(fmt::format("{:<25}", ""));
|
||||
}
|
||||
else
|
||||
{
|
||||
repr.prefix.set_value(fmt::format(
|
||||
"{:<11} {:>4}", "Downloading", fmt::format("({})", active_tasks)));
|
||||
repr.postfix.set_value(fmt::format("{:<25}", dl_bar->last_active_task()));
|
||||
}
|
||||
repr.current.set_value(
|
||||
fmt::format("{:>7}", to_human_readable_filesize(dl_bar->current(), 1)));
|
||||
repr.separator.set_value("/");
|
||||
|
||||
std::string total_str;
|
||||
if (dl_bar->total() == std::numeric_limits<std::size_t>::max())
|
||||
total_str = "??.?MB";
|
||||
else
|
||||
total_str = to_human_readable_filesize(dl_bar->total(), 1);
|
||||
repr.total.set_value(fmt::format("{:>7}", total_str));
|
||||
|
||||
auto speed = dl_bar->avg_speed(std::chrono::milliseconds(500));
|
||||
repr.speed.set_value(
|
||||
speed ? fmt::format("@ {:>7}/s", to_human_readable_filesize(speed, 1))
|
||||
: "");
|
||||
});
|
||||
|
||||
auto* extract_bar = aggregated_pbar_manager.aggregated_bar("Extract");
|
||||
if (extract_bar)
|
||||
extract_bar->set_repr_hook([&](ProgressBarRepr& repr) -> void {
|
||||
auto active_tasks = extract_bar->active_tasks().size();
|
||||
if (active_tasks == 0)
|
||||
{
|
||||
repr.prefix.set_value(fmt::format("{:<16}", "Extracting"));
|
||||
repr.postfix.set_value(fmt::format("{:<25}", ""));
|
||||
}
|
||||
else
|
||||
{
|
||||
repr.prefix.set_value(fmt::format(
|
||||
"{:<11} {:>4}", "Extracting", fmt::format("({})", active_tasks)));
|
||||
repr.postfix.set_value(
|
||||
fmt::format("{:<25}", extract_bar->last_active_task()));
|
||||
}
|
||||
repr.current.set_value(fmt::format("{:>3}", extract_bar->current()));
|
||||
repr.separator.set_value("/");
|
||||
|
||||
std::string total_str;
|
||||
if (extract_bar->total() == std::numeric_limits<std::size_t>::max())
|
||||
total_str = "?";
|
||||
else
|
||||
total_str = std::to_string(extract_bar->total());
|
||||
repr.total.set_value(fmt::format("{:>3}", total_str));
|
||||
});
|
||||
|
||||
pbar_manager.start();
|
||||
pbar_manager.watch_print();
|
||||
}
|
||||
|
||||
bool downloaded = multi_dl.download(MAMBA_DOWNLOAD_FAILFAST | MAMBA_DOWNLOAD_SORT);
|
||||
bool all_valid = true;
|
||||
|
||||
if (!downloaded)
|
||||
|
@ -961,6 +1107,12 @@ namespace mamba
|
|||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
if (!(ctx.no_progress_bars || ctx.json || ctx.quiet))
|
||||
{
|
||||
pbar_manager.terminate();
|
||||
pbar_manager.clear_progress_bars();
|
||||
}
|
||||
|
||||
for (const auto& t : targets)
|
||||
{
|
||||
if (t->validation_result() != PackageDownloadExtractTarget::VALIDATION_RESULT::VALID
|
||||
|
@ -982,20 +1134,13 @@ namespace mamba
|
|||
return m_to_install.size() == 0 && m_to_remove.size() == 0;
|
||||
}
|
||||
|
||||
bool MTransaction::prompt(std::vector<MRepo*>& repos)
|
||||
bool MTransaction::prompt()
|
||||
{
|
||||
print();
|
||||
if (Context::instance().dry_run || empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool res = Console::prompt("Confirm changes", 'y');
|
||||
if (res)
|
||||
{
|
||||
return fetch_extract_packages(repos);
|
||||
}
|
||||
return res;
|
||||
return Console::prompt("Confirm changes", 'y');
|
||||
}
|
||||
|
||||
void MTransaction::print()
|
||||
|
@ -1241,7 +1386,8 @@ namespace mamba
|
|||
|
||||
MTransaction create_explicit_transaction_from_urls(MPool& pool,
|
||||
const std::vector<std::string>& urls,
|
||||
MultiPackageCache& package_caches)
|
||||
MultiPackageCache& package_caches,
|
||||
std::vector<MRepo*>& repos)
|
||||
{
|
||||
std::vector<MatchSpec> specs_to_install;
|
||||
for (auto& u : urls)
|
||||
|
@ -1267,6 +1413,6 @@ namespace mamba
|
|||
}
|
||||
specs_to_install.push_back(ms);
|
||||
}
|
||||
return MTransaction(pool, {}, specs_to_install, package_caches);
|
||||
return MTransaction(pool, {}, specs_to_install, package_caches, repos);
|
||||
}
|
||||
} // namespace mamba
|
||||
|
|
|
@ -74,18 +74,6 @@ namespace mamba
|
|||
return status.type() != fs::file_type::not_found || status.type() == fs::file_type::symlink;
|
||||
}
|
||||
|
||||
void to_human_readable_filesize(std::ostream& o, double bytes, std::size_t precision)
|
||||
{
|
||||
const char* sizes[] = { " B", " KB", " MB", " GB", " TB" };
|
||||
int order = 0;
|
||||
while (bytes >= 1024 && order < (5 - 1))
|
||||
{
|
||||
order++;
|
||||
bytes = bytes / 1024;
|
||||
}
|
||||
o << std::fixed << std::setprecision(precision) << bytes << sizes[order];
|
||||
}
|
||||
|
||||
std::vector<fs::path> filter_dir(const fs::path& dir, const std::string& suffix)
|
||||
{
|
||||
std::vector<fs::path> result;
|
||||
|
|
|
@ -6,23 +6,25 @@ find_package(Threads REQUIRED)
|
|||
include_directories(${GTEST_INCLUDE_DIRS} SYSTEM)
|
||||
|
||||
set(TEST_SRCS
|
||||
test_activation.cpp
|
||||
test_channel.cpp
|
||||
test_configuration.cpp
|
||||
test_cpp.cpp
|
||||
test_url.cpp
|
||||
history_test/test_history.cpp
|
||||
test_shell_init.cpp
|
||||
test_activation.cpp
|
||||
test_string_methods.cpp
|
||||
test_env_file_reading.cpp
|
||||
test_environments_manager.cpp
|
||||
test_transfer.cpp
|
||||
test_thread_utils.cpp
|
||||
test_graph.cpp
|
||||
history_test/test_history.cpp
|
||||
test_lockfile.cpp
|
||||
test_pinning.cpp
|
||||
test_output.cpp
|
||||
test_progress_bar.cpp
|
||||
test_shell_init.cpp
|
||||
test_string_methods.cpp
|
||||
test_thread_utils.cpp
|
||||
test_transfer.cpp
|
||||
test_url.cpp
|
||||
test_validate.cpp
|
||||
test_virtual_packages.cpp
|
||||
test_env_file_reading.cpp
|
||||
test_lockfile.cpp
|
||||
)
|
||||
|
||||
message(STATUS "Building libmamba C++ tests")
|
||||
|
|
|
@ -255,19 +255,6 @@ namespace mamba
|
|||
"http://root:*****@myweb.com/test.repo\nhttp://myweb.com/t/*****/test.repo http://myweb.com/t/*****/test.repo http://root:*****@myweb.com/test.repo");
|
||||
}
|
||||
|
||||
TEST(output, no_progress_bars)
|
||||
{
|
||||
Context::instance().no_progress_bars = true;
|
||||
testing::internal::CaptureStdout();
|
||||
|
||||
auto proxy = Console::instance().add_progress_bar("conda-forge");
|
||||
proxy.set_progress(50, 100);
|
||||
proxy.set_postfix("Downloading");
|
||||
proxy.mark_as_completed("conda-forge channel downloaded");
|
||||
std::string output = testing::internal::GetCapturedStdout();
|
||||
EXPECT_TRUE(ends_with(output, "conda-forge channel downloaded\n"));
|
||||
Context::instance().no_progress_bars = false;
|
||||
}
|
||||
|
||||
class OutputPromptTests : public testing::TestWithParam<std::tuple<std::string, char, bool>>
|
||||
{
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
|
||||
#include "mamba/core/context.hpp"
|
||||
#include "mamba/core/output.hpp"
|
||||
|
||||
namespace mamba
|
||||
{
|
||||
TEST(output, no_progress_bars)
|
||||
{
|
||||
Context::instance().no_progress_bars = true;
|
||||
auto proxy = Console::instance().add_progress_bar("conda-forge");
|
||||
EXPECT_FALSE(proxy.defined());
|
||||
EXPECT_FALSE(proxy);
|
||||
|
||||
Context::instance().no_progress_bars = false;
|
||||
proxy = Console::instance().add_progress_bar("conda-forge");
|
||||
EXPECT_TRUE(proxy.defined());
|
||||
EXPECT_TRUE(proxy);
|
||||
}
|
||||
} // namespace mamba
|
|
@ -0,0 +1,381 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
|
||||
#include "mamba/core/progress_bar.hpp"
|
||||
|
||||
|
||||
namespace mamba
|
||||
{
|
||||
class progress_bar : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
progress_bar()
|
||||
{
|
||||
p_progress_bar_manager = std::make_unique<MultiBarManager>();
|
||||
proxy = p_progress_bar_manager->add_progress_bar("conda-forge");
|
||||
|
||||
auto& r = proxy.repr();
|
||||
r.progress.set_value("??");
|
||||
r.current.set_value("foo");
|
||||
r.separator.set_value("-");
|
||||
r.total.set_value("bar");
|
||||
r.speed.set_value("@10");
|
||||
r.postfix.set_value("downloading");
|
||||
r.elapsed.set_value("0.1s");
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<ProgressBarManager> p_progress_bar_manager;
|
||||
ProgressProxy proxy;
|
||||
std::ostringstream ostream;
|
||||
};
|
||||
|
||||
TEST_F(progress_bar, print)
|
||||
{
|
||||
auto& r = proxy.repr();
|
||||
|
||||
EXPECT_TRUE(r.prefix.active());
|
||||
EXPECT_EQ(r.prefix.value(), "conda-forge");
|
||||
EXPECT_EQ(r.prefix.width(), 11);
|
||||
|
||||
EXPECT_TRUE(r.progress);
|
||||
EXPECT_EQ(r.progress.value(), "??");
|
||||
EXPECT_EQ(r.progress.width(), 2);
|
||||
|
||||
EXPECT_TRUE(r.separator);
|
||||
EXPECT_EQ(r.separator.value(), "-");
|
||||
EXPECT_EQ(r.separator.width(), 1);
|
||||
|
||||
EXPECT_TRUE(r.total);
|
||||
EXPECT_EQ(r.total.value(), "bar");
|
||||
EXPECT_EQ(r.total.width(), 3);
|
||||
|
||||
EXPECT_TRUE(r.speed);
|
||||
EXPECT_EQ(r.speed.value(), "@10");
|
||||
EXPECT_EQ(r.speed.width(), 3);
|
||||
|
||||
EXPECT_TRUE(r.postfix.active());
|
||||
EXPECT_EQ(r.postfix.value(), "downloading");
|
||||
EXPECT_EQ(r.postfix.width(), 11);
|
||||
|
||||
EXPECT_TRUE(r.elapsed.active());
|
||||
EXPECT_EQ(r.elapsed.value(), "0.1s");
|
||||
EXPECT_EQ(r.elapsed.width(), 4);
|
||||
|
||||
proxy.print(ostream, 0, false);
|
||||
EXPECT_EQ(ostream.str(), "conda-forge ?? foo - bar @10 downloading 0.1s");
|
||||
ostream.str("");
|
||||
|
||||
r.set_width(21); // no impact if 'update_repr' not called
|
||||
proxy.print(ostream, 0, false);
|
||||
EXPECT_EQ(ostream.str(), "conda-forge ?? foo - bar @10 downloading 0.1s");
|
||||
ostream.str("");
|
||||
}
|
||||
|
||||
TEST_F(progress_bar, print_no_resize)
|
||||
{
|
||||
auto& r = proxy.repr();
|
||||
|
||||
r.set_width(150);
|
||||
proxy.update_repr();
|
||||
EXPECT_TRUE(r.prefix);
|
||||
EXPECT_TRUE(r.progress);
|
||||
EXPECT_TRUE(r.current);
|
||||
EXPECT_TRUE(r.separator);
|
||||
EXPECT_TRUE(r.total);
|
||||
EXPECT_TRUE(r.speed);
|
||||
EXPECT_TRUE(r.postfix);
|
||||
EXPECT_TRUE(r.elapsed);
|
||||
EXPECT_EQ(r.prefix.width(), 11);
|
||||
EXPECT_EQ(r.progress.width(), 106);
|
||||
EXPECT_EQ(r.current.width(), 3);
|
||||
EXPECT_EQ(r.separator.width(), 1);
|
||||
EXPECT_EQ(r.total.width(), 3);
|
||||
EXPECT_EQ(r.speed.width(), 3);
|
||||
EXPECT_EQ(r.postfix.width(), 11);
|
||||
EXPECT_EQ(r.elapsed.width(), 5);
|
||||
}
|
||||
|
||||
TEST_F(progress_bar, print_reduce_bar)
|
||||
{
|
||||
auto& r = proxy.repr();
|
||||
|
||||
r.set_width(84);
|
||||
proxy.update_repr();
|
||||
EXPECT_TRUE(r.prefix);
|
||||
EXPECT_TRUE(r.progress);
|
||||
EXPECT_TRUE(r.current);
|
||||
EXPECT_TRUE(r.separator);
|
||||
EXPECT_TRUE(r.total);
|
||||
EXPECT_TRUE(r.speed);
|
||||
EXPECT_TRUE(r.postfix);
|
||||
EXPECT_TRUE(r.elapsed);
|
||||
EXPECT_EQ(r.prefix.width(), 11);
|
||||
EXPECT_EQ(r.progress.width(), 40);
|
||||
EXPECT_EQ(r.current.width(), 3);
|
||||
EXPECT_EQ(r.separator.width(), 1);
|
||||
EXPECT_EQ(r.total.width(), 3);
|
||||
EXPECT_EQ(r.speed.width(), 3);
|
||||
EXPECT_EQ(r.postfix.width(), 11);
|
||||
EXPECT_EQ(r.elapsed.width(), 5);
|
||||
|
||||
// 1: reduce bar width
|
||||
// available space redistributed to the bar
|
||||
r.set_width(83);
|
||||
proxy.update_repr();
|
||||
EXPECT_TRUE(r.prefix);
|
||||
EXPECT_TRUE(r.progress);
|
||||
EXPECT_TRUE(r.current);
|
||||
EXPECT_TRUE(r.separator);
|
||||
EXPECT_TRUE(r.total);
|
||||
EXPECT_TRUE(r.speed);
|
||||
EXPECT_TRUE(r.postfix);
|
||||
EXPECT_TRUE(r.elapsed);
|
||||
EXPECT_EQ(r.prefix.width(), 11);
|
||||
EXPECT_EQ(r.progress.width(), 39);
|
||||
EXPECT_EQ(r.current.width(), 3);
|
||||
EXPECT_EQ(r.separator.width(), 1);
|
||||
EXPECT_EQ(r.total.width(), 3);
|
||||
EXPECT_EQ(r.speed.width(), 3);
|
||||
EXPECT_EQ(r.postfix.width(), 11);
|
||||
EXPECT_EQ(r.elapsed.width(), 5);
|
||||
}
|
||||
|
||||
TEST_F(progress_bar, print_remove_total_sep)
|
||||
{
|
||||
auto& r = proxy.repr();
|
||||
|
||||
r.set_width(59);
|
||||
proxy.update_repr();
|
||||
EXPECT_TRUE(r.prefix);
|
||||
EXPECT_TRUE(r.progress);
|
||||
EXPECT_TRUE(r.current);
|
||||
EXPECT_TRUE(r.separator);
|
||||
EXPECT_TRUE(r.total);
|
||||
EXPECT_TRUE(r.speed);
|
||||
EXPECT_TRUE(r.postfix);
|
||||
EXPECT_TRUE(r.elapsed);
|
||||
EXPECT_EQ(r.prefix.width(), 11);
|
||||
EXPECT_EQ(r.progress.width(), 15);
|
||||
EXPECT_EQ(r.current.width(), 3);
|
||||
EXPECT_EQ(r.separator.width(), 1);
|
||||
EXPECT_EQ(r.total.width(), 3);
|
||||
EXPECT_EQ(r.speed.width(), 3);
|
||||
EXPECT_EQ(r.postfix.width(), 11);
|
||||
EXPECT_EQ(r.elapsed.width(), 5);
|
||||
|
||||
// 2: remove the total value and the separator
|
||||
// available space redistributed to the bar
|
||||
r.set_width(58);
|
||||
proxy.update_repr();
|
||||
EXPECT_TRUE(r.prefix);
|
||||
EXPECT_TRUE(r.progress);
|
||||
EXPECT_TRUE(r.current);
|
||||
EXPECT_FALSE(r.separator);
|
||||
EXPECT_FALSE(r.total);
|
||||
EXPECT_TRUE(r.speed);
|
||||
EXPECT_TRUE(r.postfix);
|
||||
EXPECT_TRUE(r.elapsed);
|
||||
EXPECT_EQ(r.prefix.width(), 11);
|
||||
EXPECT_EQ(r.progress.width(), 20);
|
||||
EXPECT_EQ(r.current.width(), 3);
|
||||
EXPECT_EQ(r.speed.width(), 3);
|
||||
EXPECT_EQ(r.postfix.width(), 11);
|
||||
EXPECT_EQ(r.elapsed.width(), 5);
|
||||
}
|
||||
|
||||
TEST_F(progress_bar, print_remove_speed)
|
||||
{
|
||||
auto& r = proxy.repr();
|
||||
|
||||
r.set_width(53);
|
||||
proxy.update_repr();
|
||||
EXPECT_TRUE(r.prefix);
|
||||
EXPECT_TRUE(r.progress);
|
||||
EXPECT_TRUE(r.current);
|
||||
EXPECT_FALSE(r.separator);
|
||||
EXPECT_FALSE(r.total);
|
||||
EXPECT_TRUE(r.speed);
|
||||
EXPECT_TRUE(r.postfix);
|
||||
EXPECT_TRUE(r.elapsed);
|
||||
EXPECT_EQ(r.prefix.width(), 11);
|
||||
EXPECT_EQ(r.progress.width(), 15);
|
||||
EXPECT_EQ(r.current.width(), 3);
|
||||
EXPECT_EQ(r.speed.width(), 3);
|
||||
EXPECT_EQ(r.postfix.width(), 11);
|
||||
EXPECT_EQ(r.elapsed.width(), 5);
|
||||
|
||||
// 3: remove the speed
|
||||
// available space redistributed to the bar
|
||||
r.set_width(52);
|
||||
proxy.update_repr();
|
||||
EXPECT_TRUE(r.prefix);
|
||||
EXPECT_TRUE(r.progress);
|
||||
EXPECT_TRUE(r.current);
|
||||
EXPECT_FALSE(r.separator);
|
||||
EXPECT_FALSE(r.total);
|
||||
EXPECT_FALSE(r.speed);
|
||||
EXPECT_TRUE(r.postfix);
|
||||
EXPECT_TRUE(r.elapsed);
|
||||
EXPECT_EQ(r.prefix.width(), 11);
|
||||
EXPECT_EQ(r.progress.width(), 18);
|
||||
EXPECT_EQ(r.current.width(), 3);
|
||||
EXPECT_EQ(r.postfix.width(), 11);
|
||||
EXPECT_EQ(r.elapsed.width(), 5);
|
||||
}
|
||||
|
||||
TEST_F(progress_bar, print_remove_postfix)
|
||||
{
|
||||
auto& r = proxy.repr();
|
||||
|
||||
r.set_width(49);
|
||||
proxy.update_repr();
|
||||
EXPECT_TRUE(r.prefix);
|
||||
EXPECT_TRUE(r.progress);
|
||||
EXPECT_TRUE(r.current);
|
||||
EXPECT_FALSE(r.separator);
|
||||
EXPECT_FALSE(r.total);
|
||||
EXPECT_FALSE(r.speed);
|
||||
EXPECT_TRUE(r.postfix);
|
||||
EXPECT_TRUE(r.elapsed);
|
||||
EXPECT_EQ(r.prefix.width(), 11);
|
||||
EXPECT_EQ(r.progress.width(), 15);
|
||||
EXPECT_EQ(r.current.width(), 3);
|
||||
EXPECT_EQ(r.postfix.width(), 11);
|
||||
EXPECT_EQ(r.elapsed.width(), 5);
|
||||
|
||||
// 4: remove the postfix
|
||||
// available space redistributed to the bar
|
||||
r.set_width(48);
|
||||
proxy.update_repr();
|
||||
EXPECT_TRUE(r.prefix);
|
||||
EXPECT_TRUE(r.progress);
|
||||
EXPECT_TRUE(r.current);
|
||||
EXPECT_FALSE(r.separator);
|
||||
EXPECT_FALSE(r.total);
|
||||
EXPECT_FALSE(r.speed);
|
||||
EXPECT_FALSE(r.postfix);
|
||||
EXPECT_TRUE(r.elapsed);
|
||||
EXPECT_EQ(r.prefix.width(), 11);
|
||||
EXPECT_EQ(r.progress.width(), 26);
|
||||
EXPECT_EQ(r.current.width(), 3);
|
||||
EXPECT_EQ(r.elapsed.width(), 5);
|
||||
}
|
||||
|
||||
TEST_F(progress_bar, print_truncate_prefix)
|
||||
{
|
||||
auto& r = proxy.repr();
|
||||
proxy.set_prefix("some_very_very_long_prefix");
|
||||
|
||||
r.set_width(52);
|
||||
proxy.update_repr();
|
||||
EXPECT_TRUE(r.prefix);
|
||||
EXPECT_TRUE(r.progress);
|
||||
EXPECT_TRUE(r.current);
|
||||
EXPECT_FALSE(r.separator);
|
||||
EXPECT_FALSE(r.total);
|
||||
EXPECT_FALSE(r.speed);
|
||||
EXPECT_FALSE(r.postfix);
|
||||
EXPECT_TRUE(r.elapsed);
|
||||
EXPECT_EQ(r.prefix.width(), 26);
|
||||
EXPECT_EQ(r.progress.width(), 15);
|
||||
EXPECT_EQ(r.current.width(), 3);
|
||||
EXPECT_EQ(r.elapsed.width(), 5);
|
||||
|
||||
// 5: truncate the prefix if too long
|
||||
// available space redistributed to the prefix
|
||||
r.set_width(51);
|
||||
proxy.update_repr();
|
||||
EXPECT_TRUE(r.prefix);
|
||||
EXPECT_TRUE(r.progress);
|
||||
EXPECT_TRUE(r.current);
|
||||
EXPECT_FALSE(r.separator);
|
||||
EXPECT_FALSE(r.total);
|
||||
EXPECT_FALSE(r.speed);
|
||||
EXPECT_FALSE(r.postfix);
|
||||
EXPECT_TRUE(r.elapsed);
|
||||
EXPECT_EQ(r.prefix.width(), 25);
|
||||
EXPECT_EQ(r.progress.width(), 15);
|
||||
EXPECT_EQ(r.current.width(), 3);
|
||||
EXPECT_EQ(r.elapsed.width(), 5);
|
||||
}
|
||||
|
||||
TEST_F(progress_bar, print_without_bar)
|
||||
{
|
||||
auto& r = proxy.repr();
|
||||
|
||||
r.set_width(34).reset_fields();
|
||||
proxy.update_repr();
|
||||
EXPECT_TRUE(r.prefix);
|
||||
EXPECT_TRUE(r.progress);
|
||||
EXPECT_TRUE(r.current);
|
||||
EXPECT_FALSE(r.separator);
|
||||
EXPECT_FALSE(r.total);
|
||||
EXPECT_FALSE(r.speed);
|
||||
EXPECT_FALSE(r.postfix);
|
||||
EXPECT_TRUE(r.elapsed);
|
||||
EXPECT_EQ(r.prefix.width(), 11);
|
||||
EXPECT_EQ(r.progress.width(), 12);
|
||||
EXPECT_EQ(r.current.width(), 3);
|
||||
EXPECT_TRUE(r.progress.overflow());
|
||||
EXPECT_EQ(r.elapsed.width(), 5);
|
||||
|
||||
// 6: display progress without a bar
|
||||
r.set_width(33);
|
||||
proxy.update_repr();
|
||||
proxy.print(ostream, 0, false);
|
||||
EXPECT_EQ(ostream.str(), "conda-forge 0% foo --");
|
||||
ostream.str("");
|
||||
}
|
||||
|
||||
TEST_F(progress_bar, print_remove_current)
|
||||
{
|
||||
auto& r = proxy.repr();
|
||||
|
||||
r.set_width(26).reset_fields();
|
||||
proxy.update_repr();
|
||||
proxy.print(ostream, 0, false);
|
||||
EXPECT_EQ(ostream.str(), "conda-forge 0% foo --");
|
||||
ostream.str("");
|
||||
|
||||
// 7: remove the current value
|
||||
r.set_width(25).reset_fields();
|
||||
proxy.update_repr();
|
||||
proxy.print(ostream, 0, false);
|
||||
EXPECT_EQ(ostream.str(), "conda-forge 0% --");
|
||||
ostream.str("");
|
||||
}
|
||||
|
||||
TEST_F(progress_bar, print_remove_elapsed)
|
||||
{
|
||||
auto& r = proxy.repr();
|
||||
|
||||
r.set_width(22).reset_fields();
|
||||
proxy.update_repr();
|
||||
EXPECT_TRUE(r.prefix);
|
||||
EXPECT_TRUE(r.progress);
|
||||
EXPECT_FALSE(r.current);
|
||||
EXPECT_FALSE(r.separator);
|
||||
EXPECT_FALSE(r.total);
|
||||
EXPECT_FALSE(r.speed);
|
||||
EXPECT_FALSE(r.postfix);
|
||||
EXPECT_TRUE(r.elapsed);
|
||||
proxy.print(ostream, 0, false);
|
||||
EXPECT_EQ(r.prefix.width(), 11);
|
||||
EXPECT_EQ(r.progress.width(), 4);
|
||||
EXPECT_EQ(r.elapsed.width(), 5);
|
||||
EXPECT_EQ(ostream.str(), "conda-forge 0% --");
|
||||
ostream.str("");
|
||||
|
||||
// 8: remove the elapsed time
|
||||
r.set_width(21);
|
||||
proxy.update_repr();
|
||||
proxy.print(ostream, 0, false);
|
||||
EXPECT_EQ(r.prefix.width(), 11);
|
||||
EXPECT_EQ(r.progress.width(), 9);
|
||||
EXPECT_EQ(ostream.str(), "conda-forge 0%");
|
||||
ostream.str("");
|
||||
}
|
||||
} // namespace mamba
|
|
@ -17,12 +17,12 @@ namespace mamba
|
|||
// Ensures the compiler doe snot optimize away Context::instance()
|
||||
std::string current_command = Context::instance().current_command;
|
||||
EXPECT_EQ(current_command, "mamba");
|
||||
Console::instance().init_multi_progress();
|
||||
Console::instance().init_progress_bar_manager(ProgressBarMode::multi);
|
||||
{
|
||||
interruption_guard g([&res]() {
|
||||
// Test for double free (segfault if that happens)
|
||||
std::cout << "Interruption guard is interrupting" << std::endl;
|
||||
Console::instance().init_multi_progress();
|
||||
Console::instance().init_progress_bar_manager(ProgressBarMode::multi);
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(res_mutex);
|
||||
res -= 100;
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace mamba
|
|||
// file:// url should not retry
|
||||
EXPECT_EQ(cf.target()->can_retry(), false);
|
||||
|
||||
multi_dl.download(true);
|
||||
multi_dl.download(MAMBA_DOWNLOAD_FAILFAST);
|
||||
|
||||
// File does not exist
|
||||
EXPECT_EQ(cf.target()->result, 37);
|
||||
|
@ -38,7 +38,7 @@ namespace mamba
|
|||
true);
|
||||
cf.load();
|
||||
multi_dl.add(cf.target());
|
||||
EXPECT_THROW(multi_dl.download(true), std::runtime_error);
|
||||
EXPECT_THROW(multi_dl.download(MAMBA_DOWNLOAD_FAILFAST), std::runtime_error);
|
||||
}
|
||||
Context::instance().quiet = false;
|
||||
#endif
|
||||
|
|
|
@ -82,16 +82,14 @@ PYBIND11_MODULE(bindings, m)
|
|||
.def("clear", &MRepo::clear);
|
||||
|
||||
py::class_<MTransaction>(m, "Transaction")
|
||||
.def(py::init<MSolver&, MultiPackageCache&>())
|
||||
.def(py::init<MSolver&, MultiPackageCache&, std::vector<MRepo*>&>())
|
||||
.def("to_conda", &MTransaction::to_conda)
|
||||
.def("log_json", &MTransaction::log_json)
|
||||
.def("print", &MTransaction::print)
|
||||
.def("fetch_extract_packages", &MTransaction::fetch_extract_packages)
|
||||
.def("prompt", &MTransaction::prompt)
|
||||
.def("find_python_version", &MTransaction::find_python_version)
|
||||
.def("execute", [](MTransaction& self, PrefixData& target_prefix) -> bool {
|
||||
return self.execute(target_prefix);
|
||||
});
|
||||
.def("execute", &MTransaction::execute);
|
||||
|
||||
py::class_<MSolver>(m, "Solver")
|
||||
.def(py::init<MPool&, std::vector<std::pair<int, int>>>())
|
||||
|
@ -505,6 +503,10 @@ PYBIND11_MODULE(bindings, m)
|
|||
m.attr("MAMBA_ONLY_DEPS") = MAMBA_ONLY_DEPS;
|
||||
m.attr("MAMBA_FORCE_REINSTALL") = MAMBA_FORCE_REINSTALL;
|
||||
|
||||
// DOWNLOAD FLAGS
|
||||
m.attr("MAMBA_DOWNLOAD_FAILFAST") = MAMBA_DOWNLOAD_FAILFAST;
|
||||
m.attr("MAMBA_DOWNLOAD_SORT") = MAMBA_DOWNLOAD_SORT;
|
||||
|
||||
// CLEAN FLAGS
|
||||
m.attr("MAMBA_CLEAN_ALL") = MAMBA_CLEAN_ALL;
|
||||
m.attr("MAMBA_CLEAN_INDEX") = MAMBA_CLEAN_INDEX;
|
||||
|
|
|
@ -8,6 +8,7 @@ import os
|
|||
import sys
|
||||
from logging import getLogger
|
||||
from os.path import isdir, isfile, join
|
||||
from pathlib import Path
|
||||
|
||||
# create support
|
||||
from conda.base.constants import ChannelPriority, DepsModifier, UpdateModifier
|
||||
|
@ -217,10 +218,12 @@ def remove(args, parser):
|
|||
return exit_code
|
||||
|
||||
package_cache = api.MultiPackageCache(context.pkgs_dirs)
|
||||
transaction = api.Transaction(solver, package_cache)
|
||||
downloaded = transaction.prompt(repos)
|
||||
if not downloaded:
|
||||
transaction = api.Transaction(solver, package_cache, repos)
|
||||
|
||||
if not transaction.prompt():
|
||||
exit(0)
|
||||
elif not context.dry_run:
|
||||
transaction.fetch_extract_packages()
|
||||
|
||||
mmb_specs, to_link, to_unlink = transaction.to_conda()
|
||||
transaction.log_json()
|
||||
|
@ -552,23 +555,30 @@ def install(args, parser, command="install"):
|
|||
return exit_code
|
||||
|
||||
package_cache = api.MultiPackageCache(context.pkgs_dirs)
|
||||
transaction = api.Transaction(solver, package_cache)
|
||||
transaction = api.Transaction(solver, package_cache, repos)
|
||||
mmb_specs, to_link, to_unlink = transaction.to_conda()
|
||||
|
||||
specs_to_add = [MatchSpec(m) for m in mmb_specs[0]]
|
||||
specs_to_remove = [MatchSpec(m) for m in mmb_specs[1]]
|
||||
|
||||
transaction.log_json()
|
||||
downloaded = transaction.prompt(repos)
|
||||
if not downloaded:
|
||||
exit(0)
|
||||
|
||||
# if use_mamba_experimental and not os.name == "nt":
|
||||
if use_mamba_experimental:
|
||||
if newenv and not isdir(context.target_prefix) and not context.dry_run:
|
||||
mkdir_p(prefix)
|
||||
transaction.execute(prefix_data)
|
||||
if transaction.prompt():
|
||||
if (
|
||||
newenv
|
||||
and not isdir(Path(prefix) / "conda-meta")
|
||||
and not context.dry_run
|
||||
):
|
||||
mkdir_p(Path(prefix) / "conda-meta")
|
||||
|
||||
transaction.execute(prefix_data)
|
||||
else:
|
||||
if not transaction.prompt():
|
||||
exit(0)
|
||||
elif not context.dry_run:
|
||||
transaction.fetch_extract_packages()
|
||||
|
||||
conda_transaction = to_txn(
|
||||
specs_to_add,
|
||||
specs_to_remove,
|
||||
|
|
|
@ -136,15 +136,17 @@ def mamba_install(prefix, specs, args, env, dry_run=False, *_, **kwargs):
|
|||
exit(1)
|
||||
|
||||
package_cache = api.MultiPackageCache(context.pkgs_dirs)
|
||||
transaction = api.Transaction(solver, package_cache)
|
||||
transaction = api.Transaction(solver, package_cache, repos)
|
||||
mmb_specs, to_link, to_unlink = transaction.to_conda()
|
||||
|
||||
specs_to_add = [MatchSpec(m) for m in mmb_specs[0]]
|
||||
|
||||
transaction.log_json()
|
||||
downloaded = transaction.prompt(repos)
|
||||
if not downloaded:
|
||||
if not transaction.prompt():
|
||||
exit(0)
|
||||
elif not context.dry_run:
|
||||
transaction.fetch_extract_packages()
|
||||
|
||||
if prune:
|
||||
history = api.History(prefix)
|
||||
history_map = history.get_requested_specs_map()
|
||||
|
@ -162,7 +164,9 @@ def mamba_install(prefix, specs, args, env, dry_run=False, *_, **kwargs):
|
|||
conda_transaction = to_txn(
|
||||
specs_to_add, [], prefix, to_link, to_unlink, installed_pkg_recs, index
|
||||
)
|
||||
|
||||
handle_txn(conda_transaction, prefix, args, True)
|
||||
|
||||
try:
|
||||
installed_json_f.close()
|
||||
os.unlink(installed_json_f.name)
|
||||
|
|
|
@ -100,7 +100,7 @@ def get_index(
|
|||
)
|
||||
dlist.add(sd)
|
||||
|
||||
is_downloaded = dlist.download(True)
|
||||
is_downloaded = dlist.download(api.MAMBA_DOWNLOAD_FAILFAST)
|
||||
|
||||
if not is_downloaded:
|
||||
raise RuntimeError("Error downloading repodata.")
|
||||
|
|
|
@ -115,8 +115,7 @@ def install(*args, default_channel=True, no_rc=True, no_dry_run=False):
|
|||
cmd += ["--dry-run"]
|
||||
cmd += ["--log-level=info"]
|
||||
|
||||
print(f"Running command {' '.join(cmd)}", file=sys.stderr)
|
||||
res = subprocess.check_output(cmd, stderr=sys.stderr)
|
||||
res = subprocess.check_output(cmd)
|
||||
|
||||
if "--json" in args:
|
||||
try:
|
||||
|
|
Loading…
Reference in New Issue