Refactored error handling

This commit is contained in:
Johan Mabille 2022-03-15 14:36:16 +01:00
parent f45446473e
commit 3eed546f4a
15 changed files with 276 additions and 118 deletions

View File

@ -77,6 +77,7 @@ set(LIBMAMBA_SOURCES
${LIBMAMBA_SOURCE_DIR}/core/channel.cpp
${LIBMAMBA_SOURCE_DIR}/core/context.cpp
${LIBMAMBA_SOURCE_DIR}/core/environments_manager.cpp
${LIBMAMBA_SOURCE_DIR}/core/error_handling.cpp
${LIBMAMBA_SOURCE_DIR}/core/fetch.cpp
${LIBMAMBA_SOURCE_DIR}/core/transaction_context.cpp
${LIBMAMBA_SOURCE_DIR}/core/link.cpp
@ -129,6 +130,7 @@ set(LIBMAMBA_HEADERS
${LIBMAMBA_INCLUDE_DIR}/mamba/core/context.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/environment.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/environments_manager.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/error_handling.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/fetch.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/fsutil.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/graph_util.hpp

View File

@ -6,5 +6,6 @@
namespace mamba
{
void load_channels(MPool& pool, MultiPackageCache& package_caches, int is_retry);
expected_t<void, mamba_aggregated_error>
load_channels(MPool& pool, MultiPackageCache& package_caches, int is_retry);
}

View File

@ -0,0 +1,79 @@
#ifndef MAMBA_ERROR_HANDLING_HPP
#define MAMBA_ERROR_HANDLING_HPP
#include <exception>
#include <vector>
#include "tl/expected.hpp"
namespace mamba
{
/*********************
* Mamba esxceptions *
*********************/
enum class mamba_error_code
{
unknown,
prefix_data_not_loaded,
subdirdata_not_loaded,
cache_not_loaded,
repodata_not_loaded,
aggregated
};
class mamba_error : public std::runtime_error
{
public:
using base_type = std::runtime_error;
mamba_error(const std::string& msg, mamba_error_code ec);
mamba_error(const char* msg, mamba_error_code ec);
mamba_error_code error_code() const noexcept;
private:
mamba_error_code m_error_code;
};
class mamba_aggregated_error : public mamba_error
{
public:
using base_type = mamba_error;
using error_list_t = std::vector<mamba_error>;
explicit mamba_aggregated_error(error_list_t&& error_list);
const char* what() const noexcept override;
private:
error_list_t m_error_list;
mutable std::string m_aggregated_message;
static std::string m_base_message;
};
/********************************
* wrappers around tl::expected *
********************************/
template <class T, class E = mamba_error>
using expected_t = tl::expected<T, E>;
tl::unexpected<mamba_error>
make_unexpected(const char* msg, mamba_error_code ec);
tl::unexpected<mamba_error>
make_unexpected(const std::string& msg, mamba_error_code sc);
tl::unexpected<mamba_aggregated_error>
make_unexpected(std::vector<mamba_error>&& error_list);
template <class T>
auto forward_error(const expected_t<T>& exp)
{
return tl::make_unexpected(exp.error());
}
}
#endif

View File

@ -11,25 +11,20 @@
#include <unordered_map>
#include "tl/expected.hpp"
#include "error_handling.hpp"
#include "history.hpp"
#include "package_info.hpp"
#include "util.hpp"
namespace mamba
{
enum class prefixdata_error
{
unknown,
load
};
class PrefixData
{
public:
using package_map = std::unordered_map<std::string, PackageInfo>;
template <class T>
using expected = tl::expected<T, mamba_error<prefixdata_error>>;
using expected = tl::expected<T, mamba_error>;
static expected<PrefixData> create(const fs::path& prefix_path);

View File

@ -11,7 +11,6 @@
#include <regex>
#include <string>
#include "tl/expected.hpp"
#include "nlohmann/json.hpp"
#include "mamba/core/channel.hpp"
@ -33,12 +32,6 @@ namespace decompress
namespace mamba
{
enum class subdirdata_error
{
unknown,
load
};
/**
* Represents a channel subdirectory (i.e. a platform)
* packages index. Handles downloading of the index
@ -47,14 +40,12 @@ namespace mamba
class MSubdirData
{
public:
template <class T>
using expected = tl::expected<T, mamba_error<subdirdata_error>>;
static expected<MSubdirData> create(const Channel& channel,
const std::string& platform,
const std::string& url,
MultiPackageCache& caches,
const std::string& repodata_fn = "repodata.json");
static expected_t<MSubdirData> create(const Channel& channel,
const std::string& platform,
const std::string& url,
MultiPackageCache& caches,
const std::string& repodata_fn = "repodata.json");
~MSubdirData() = default;
@ -72,13 +63,13 @@ namespace mamba
bool forbid_cache();
void clear_cache();
std::string cache_path() const;
expected_t<std::string> cache_path() const;
const std::string& name() const;
DownloadTarget* target();
bool finalize_transfer();
MRepo& create_repo(MPool& pool);
expected_t<MRepo*> create_repo(MPool& pool);
private:
MSubdirData(const Channel& channel,

View File

@ -111,26 +111,6 @@ namespace mamba
return result;
}
enum class unspecified_error
{
unkown
};
template <class error_type>
class mamba_error : public std::runtime_error
{
public:
using base_type = std::runtime_error;
mamba_error(const std::string& msg, error_type ec);
mamba_error(const char* msg, error_type ec);
error_type error_code() const noexcept;
private:
error_type m_error_code;
};
class TemporaryDirectory
{
public:
@ -481,29 +461,7 @@ namespace mamba
std::tuple<std::vector<std::string>, std::unique_ptr<TemporaryFile>> prepare_wrapped_call(
const fs::path& prefix, const std::vector<std::string>& cmd);
/******************************
* mamba_error implementation *
******************************/
template <class ET>
mamba_error<ET>::mamba_error(const std::string& msg, ET ec)
: base_type(msg)
, m_error_code(ec)
{
}
template <class ET>
mamba_error<ET>::mamba_error(const char* msg, ET ec)
: base_type(msg)
, m_error_code(ec)
{
}
template <class ET>
ET mamba_error<ET>::error_code() const noexcept
{
return m_error_code;
}
} // namespace mamba

View File

@ -36,7 +36,8 @@ namespace mamba
}
}
void load_channels(MPool& pool, MultiPackageCache& package_caches, int is_retry)
expected_t<void, mamba_aggregated_error>
load_channels(MPool& pool, MultiPackageCache& package_caches, int is_retry)
{
int RETRY_SUBDIR_FETCH = 1 << 0;
@ -55,6 +56,8 @@ namespace mamba
load_tokens();
std::vector<mamba_error> error_list;
for (auto channel : get_channels(channel_urls))
{
for (auto& [platform, url] : channel->platform_urls(true))
@ -62,11 +65,9 @@ namespace mamba
auto sdires = MSubdirData::create(*channel, platform, url, package_caches);
if (!sdires.has_value())
{
// TODO: error handling
error_list.push_back(std::move(sdires).error());
continue;
}
// auto sdir = std::make_shared<MSubdirData>(*channel, platform, url,
// package_caches);
auto sdir = std::move(sdires).value();
multi_dl.add(sdir.target());
@ -106,18 +107,49 @@ namespace mamba
auto& subdir = subdirs[i];
if (!subdir.loaded())
{
if (ctx.offline || !mamba::ends_with(subdir.name(), "/noarch"))
if (!ctx.offline && mamba::ends_with(subdir.name(), "/noarch"))
{
error_list.push_back(mamba_error(
"Subdir " + subdir.name() + " not loaded!",
mamba_error_code::subdirdata_not_loaded
));
}
continue;
/*if (ctx.offline || !mamba::ends_with(subdir.name(), "/noarch"))
{
continue;
}
else
{
throw std::runtime_error("Subdir " + subdir.name() + " not loaded!");
}
}*/
}
auto& prio = priorities[i];
try
auto repo = subdir.create_repo(pool);
if (repo)
{
auto& prio = priorities[i];
repo.value()->set_priority(prio.first, prio.second);
}
else
{
if (is_retry & RETRY_SUBDIR_FETCH)
{
std::stringstream ss;
ss << "Could not load repodata.json for " << subdir.name() << " after retry."
<< "Please check repodata source. Exiting." << std::endl;
error_list.push_back(mamba_error(ss.str(),
mamba_error_code::repodata_not_loaded));
}
else
{
LOG_WARNING << "Could not load repodata.json for " << subdir.name()
<< ". Deleting cache, and retrying.";
subdir.clear_cache();
loading_failed = true;
}
}
/*try
{
MRepo& repo = subdir.create_repo(pool);
repo.set_priority(prio.first, prio.second);
@ -136,7 +168,7 @@ namespace mamba
<< ". Deleting cache, and retrying.";
subdir.clear_cache();
loading_failed = true;
}
}*/
}
if (loading_failed)
@ -146,8 +178,14 @@ namespace mamba
LOG_WARNING << "Encountered malformed repodata.json cache. Redownloading.";
return load_channels(pool, package_caches, is_retry | RETRY_SUBDIR_FETCH);
}
throw std::runtime_error("Could not load repodata. Cache corrupted?");
error_list.push_back(mamba_error(
"Could not load repodata. Cache corrupted?",
mamba_error_code::repodata_not_loaded));
//throw std::runtime_error("Could not load repodata. Cache corrupted?");
}
using return_type = expected_t<void, mamba_aggregated_error>;
return error_list.empty() ? return_type()
: return_type(make_unexpected(std::move(error_list)));
}
}

View File

@ -357,14 +357,22 @@ namespace mamba
}
MPool pool;
load_channels(pool, package_caches, is_retry);
auto sprefix_data = PrefixData::create(ctx.target_prefix);
if (!sprefix_data)
// functions implied in 'and_then' coding-styles must return the same type
// which limits this syntax
/*auto exp_prefix_data = load_channels(pool, package_caches, is_retry)
.and_then([&ctx](const auto&) { return PrefixData::create(ctx.target_prefix); } )
.map_error([](const mamba_error& err) {
throw std::runtime_error(err.what());
});*/
auto exp_load = load_channels(pool, package_caches, is_retry);
auto exp_prefix_data = PrefixData::create(ctx.target_prefix);
if (!exp_load || !exp_prefix_data)
{
const char* msg = !exp_load ? exp_load.error().what() : exp_prefix_data.error().what();
// TODO: propagate tl::expected mechanism
throw std::runtime_error("could not load prefix data");
throw std::runtime_error(msg);
}
PrefixData& prefix_data = sprefix_data.value();
PrefixData& prefix_data = exp_prefix_data.value();
std::vector<std::string> prefix_pkgs;
for (auto& it : prefix_data.records())
@ -468,13 +476,13 @@ namespace mamba
{
MPool pool;
auto& ctx = Context::instance();
auto sprefix_data = PrefixData::create(ctx.target_prefix);
if (!sprefix_data)
auto exp_prefix_data = PrefixData::create(ctx.target_prefix);
if (!exp_prefix_data)
{
// TODO: propagate tl::expected mechanism
throw std::runtime_error("could not load prefix data");
throw std::runtime_error(exp_prefix_data.error().what());
}
PrefixData& prefix_data = sprefix_data.value();
PrefixData& prefix_data = exp_prefix_data.value();
fs::path pkgs_dirs(Context::instance().root_prefix / "pkgs");
MultiPackageCache pkg_caches({ pkgs_dirs });

View File

@ -72,13 +72,13 @@ namespace mamba
throw std::runtime_error("Aborted.");
}
auto sprefix_data = PrefixData::create(ctx.target_prefix);
if (!sprefix_data)
auto exp_prefix_data = PrefixData::create(ctx.target_prefix);
if (!exp_prefix_data)
{
// TODO: propagate tl::expected mechanism
throw std::runtime_error("could not load prefix data");
throw std::runtime_error(exp_prefix_data.error().what());
}
PrefixData& prefix_data = sprefix_data.value();
PrefixData& prefix_data = exp_prefix_data.value();
MPool pool;
MRepo::create(pool, prefix_data);

View File

@ -29,19 +29,23 @@ namespace mamba
MultiPackageCache package_caches(ctx.pkgs_dirs);
if (use_local)
{
auto sprefix_data = PrefixData::create(ctx.target_prefix);
if (!sprefix_data)
auto exp_prefix_data = PrefixData::create(ctx.target_prefix);
if (!exp_prefix_data)
{
// TODO: propagate tl::expected mechanism
throw std::runtime_error("could not load prefix data");
throw std::runtime_error(exp_prefix_data.error().what());
}
PrefixData& prefix_data = sprefix_data.value();
PrefixData& prefix_data = exp_prefix_data.value();
MRepo::create(pool, prefix_data);
Console::stream() << "Loaded current active prefix: " << ctx.target_prefix << std::endl;
}
else
{
load_channels(pool, package_caches, 0);
auto exp_load = load_channels(pool, package_caches, 0);
if (!exp_load)
{
throw std::runtime_error(exp_load.error().what());
}
}
Query q(pool);

View File

@ -35,15 +35,19 @@ namespace mamba
MPool pool;
MultiPackageCache package_caches(ctx.pkgs_dirs);
load_channels(pool, package_caches, 0);
auto exp_loaded = load_channels(pool, package_caches, 0);
if (!exp_loaded)
{
throw std::runtime_error(exp_loaded.error().what());
}
auto sprefix_data = PrefixData::create(ctx.target_prefix);
if (!sprefix_data)
auto exp_prefix_data = PrefixData::create(ctx.target_prefix);
if (!exp_prefix_data)
{
// TODO: propagate tl::expected mechanism
throw std::runtime_error("could not load prefix data");
throw std::runtime_error(exp_prefix_data.error().what());
}
PrefixData& prefix_data = sprefix_data.value();
PrefixData& prefix_data = exp_prefix_data.value();
std::vector<std::string> prefix_pkgs;
for (auto& it : prefix_data.records())

View File

@ -0,0 +1,65 @@
#include "mamba/core/error_handling.hpp"
namespace mamba
{
mamba_error::mamba_error(const std::string& msg, mamba_error_code ec)
: base_type(msg)
, m_error_code(ec)
{
}
mamba_error::mamba_error(const char* msg, mamba_error_code ec)
: base_type(msg)
, m_error_code(ec)
{
}
mamba_error_code mamba_error::error_code() const noexcept
{
return m_error_code;
}
std::string mamba_aggregated_error::m_base_message = "Many errors occured:\n";
mamba_aggregated_error::mamba_aggregated_error(error_list_t&& error_list)
: base_type(mamba_aggregated_error::m_base_message, mamba_error_code::aggregated)
, m_error_list(std::move(error_list))
, m_aggregated_message()
{
}
const char* mamba_aggregated_error::what() const noexcept
{
if (m_aggregated_message.empty())
{
m_aggregated_message = m_base_message;
for (const mamba_error& er : m_error_list)
{
m_aggregated_message += er.what();
m_aggregated_message += "\n";
}
}
return m_aggregated_message.c_str();
}
tl::unexpected<mamba_error>
make_unexpected(const char* msg, mamba_error_code ec)
{
return tl::make_unexpected(mamba_error(msg, ec));
}
tl::unexpected<mamba_error>
make_unexpected(const std::string& msg, mamba_error_code ec)
{
return tl::make_unexpected(mamba_error(msg, ec));
}
tl::unexpected<mamba_aggregated_error>
make_unexpected(std::vector<mamba_error>&& error_list)
{
return tl::make_unexpected(mamba_aggregated_error(std::move(error_list)));
}
}

View File

@ -22,13 +22,13 @@ namespace mamba
}
catch (std::exception& e)
{
return tl::make_unexpected(mamba_error(e.what(), prefixdata_error::load));
return tl::make_unexpected(mamba_error(e.what(), mamba_error_code::prefix_data_not_loaded));
}
catch (...)
{
return tl::make_unexpected(mamba_error("Unkown error when trying to load prefix data "
+ std::string(prefix_path),
prefixdata_error::unknown));
mamba_error_code::unknown));
}
}

View File

@ -160,11 +160,11 @@ namespace mamba
}
auto MSubdirData::create(const Channel& channel,
const std::string& platform,
const std::string& url,
MultiPackageCache& caches,
const std::string& repodata_fn) -> expected<MSubdirData>
expected_t<MSubdirData> MSubdirData::create(const Channel& channel,
const std::string& platform,
const std::string& url,
MultiPackageCache& caches,
const std::string& repodata_fn)
{
try
{
@ -172,12 +172,12 @@ namespace mamba
}
catch (std::exception& e)
{
return tl::make_unexpected(mamba_error(e.what(), subdirdata_error::load));
return make_unexpected(e.what(), mamba_error_code::subdirdata_not_loaded);
}
catch (...)
{
return tl::make_unexpected(mamba_error(
"Unkown error when trying to load subdir data " + url, subdirdata_error::unknown));
return make_unexpected(
"Unkown error when trying to load subdir data " + url, mamba_error_code::unknown);
}
}
@ -187,12 +187,12 @@ namespace mamba
MultiPackageCache& caches,
const std::string& repodata_fn)
: m_target(nullptr)
, m_writable_pkgs_dir(caches.first_writable_path())
, m_progress_bar()
, m_loaded(false)
, m_download_complete(false)
, m_repodata_url(concat(url, "/", repodata_fn))
, m_name(concat(channel.canonical_name(), "/", platform))
, m_writable_pkgs_dir(caches.first_writable_path())
, m_is_noarch(platform == "noarch")
, p_channel(&channel)
{
@ -389,7 +389,7 @@ namespace mamba
return true;
}
std::string MSubdirData::cache_path() const
expected_t<std::string> MSubdirData::cache_path() const
{
// TODO invalidate solv cache on version updates!!
if (m_json_cache_valid && m_solv_cache_valid)
@ -400,7 +400,7 @@ namespace mamba
{
return m_valid_cache_path / "cache" / m_json_fn;
}
throw std::runtime_error("Cache not loaded!");
return make_unexpected("Cache not loaded", mamba_error_code::cache_not_loaded);
}
DownloadTarget* MSubdirData::target()
@ -665,14 +665,17 @@ namespace mamba
return cache_dir;
}
MRepo& MSubdirData::create_repo(MPool& pool)
expected_t<MRepo*> MSubdirData::create_repo(MPool& pool)
{
using return_type = expected_t<MRepo*>;
RepoMetadata meta{ m_repodata_url,
Context::instance().add_pip_as_python_dependency,
m_mod_etag.value("_etag", ""),
m_mod_etag.value("_mod", "") };
return MRepo::create(pool, m_name, cache_path(), meta, *p_channel);
auto cache = cache_path();
return cache ? return_type(&MRepo::create(pool, m_name, *cache, meta, *p_channel))
: return_type(forward_error(cache));
}
void MSubdirData::clear_cache()

View File

@ -57,7 +57,7 @@ PYBIND11_MODULE(bindings, m)
py::class_<mamba::LockFile>(m, "LockFile").def(py::init<fs::path>());
py::register_exception<mamba_error<unspecified_error>>(m, "MambaNativeException");
py::register_exception<mamba_error>(m, "MambaNativeException");
py::add_ostream_redirect(m, "ostream_redirect");
@ -236,8 +236,6 @@ PYBIND11_MODULE(bindings, m)
return res_stream.str();
});
py::register_exception<mamba_error<subdirdata_error>>(m, "SubdirDataError");
py::class_<MSubdirData>(m, "SubdirData")
.def(py::init(
[](const Channel& channel,
@ -256,7 +254,19 @@ PYBIND11_MODULE(bindings, m)
throw sres.error();
}
}))
.def("create_repo", &MSubdirData::create_repo, py::return_value_policy::reference)
.def("create_repo",
[](MSubdirData& subdir, MPool& pool) -> MRepo&{
auto exp_res = subdir.create_repo(pool);
if (exp_res)
{
return *(exp_res.value());
}
else
{
throw exp_res.error();
}
},
py::return_value_policy::reference)
.def("loaded", &MSubdirData::loaded)
.def("cache_path", &MSubdirData::cache_path);