NoArchType as standalone enum (#3108)

* NoArch as standalone enum

* Bind NoArchType
This commit is contained in:
Antoine Prouvost 2024-01-08 14:26:42 +01:00 committed by GitHub
parent 53a5171360
commit dae5e0e378
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 213 additions and 59 deletions

View File

@ -81,6 +81,69 @@ namespace mamba::specs
*/
void from_json(const nlohmann::json& j, Platform& p);
/**
* Noarch packages are packages that are not architecture specific.
*
* Noarch packages only have to be built once.
*/
enum struct NoArchType
{
/** Not a noarch type. */
No,
/** Noarch generic packages allow users to distribute docs, datasets, and source code. */
Generic,
/**
* A noarch python package is a python package without any precompiled python files.
*
* Normally, precompiled files (`.pyc` or `__pycache__`) are bundled with the package.
* However, these files are tied to a specific version of Python and must therefore be
* generated for every target platform and architecture.
* This complicates the build process.
* For noarch Python packages these files are generated when installing the package by
* invoking the compilation process through the python binary that is installed in the
* same environment.
*
* @see https://www.anaconda.com/blog/condas-new-noarch-packages
* @see
* https://docs.conda.io/projects/conda/en/latest/user-guide/concepts/packages.html#noarch-python
*/
Python,
// For reflexion purposes only
count_,
};
constexpr auto known_noarch_count() -> std::size_t
{
return static_cast<std::size_t>(NoArchType::count_);
}
constexpr auto known_noarch() -> std::array<NoArchType, known_noarch_count()>;
constexpr auto known_noarch_names() -> std::array<std::string_view, known_noarch_count()>;
/**
* Convert the enumeration to its conda string.
*/
constexpr auto noarch_name(NoArchType noarch) -> std::string_view;
/**
* Return the enum matching the noarch name.
*/
auto noarch_parse(std::string_view str) -> std::optional<NoArchType>;
/**
* Serialize to JSON string.
*/
void to_json(nlohmann::json& j, const NoArchType& noarch);
/**
* Deserialize from JSON string.
*/
void from_json(const nlohmann::json& j, NoArchType& noarch);
/********************
* Implementation *
********************/
@ -149,5 +212,40 @@ namespace mamba::specs
return out;
}
constexpr auto noarch_name(NoArchType noarch) -> std::string_view
{
switch (noarch)
{
case NoArchType::No:
return "no";
case NoArchType::Generic:
return "generic";
case NoArchType::Python:
return "python";
default:
return "";
}
}
constexpr auto known_noarch() -> std::array<NoArchType, known_noarch_count()>
{
auto out = std::array<NoArchType, known_noarch_count()>{};
for (std::size_t idx = 0; idx < out.size(); ++idx)
{
out[idx] = static_cast<NoArchType>(idx);
}
return out;
}
constexpr auto known_noarch_names() -> std::array<std::string_view, known_noarch_count()>
{
auto out = std::array<std::string_view, known_noarch_count()>{};
auto iter = out.begin();
for (auto p : known_noarch())
{
*(iter++) = noarch_name(p);
}
return out;
}
}
#endif

View File

@ -16,33 +16,6 @@
namespace mamba::specs
{
/**
* Noarch packages are packages that are not architecture specific.
*
* Noarch packages only have to be built once.
*/
enum struct NoArchType
{
/** Noarch generic packages allow users to distribute docs, datasets, and source code. */
Generic,
/**
* A noarch python package is a python package without any precompiled python files.
*
* Normally, precompiled files (`.pyc` or `__pycache__`) are bundled with the package.
* However, these files are tied to a specific version of Python and must therefore be
* generated for every target platform and architecture.
* This complicates the build process.
* For noarch Python packages these files are generated when installing the package by
* invoking the compilation process through the python binary that is installed in the
* same environment.
*
* @see https://www.anaconda.com/blog/condas-new-noarch-packages
* @see
* https://docs.conda.io/projects/conda/en/latest/user-guide/concepts/packages.html#noarch-python
*/
Python,
};
/**
* A single record in the Conda ``repodata.json``.

View File

@ -4,8 +4,6 @@
//
// The full license is in the file LICENSE, distributed with this software.
#include <algorithm>
#include <cassert>
#include <string>
#include <fmt/format.h>
@ -18,7 +16,7 @@ namespace mamba::specs
{
auto platform_parse(std::string_view str) -> std::optional<Platform>
{
std::string const str_clean = util::to_lower(util::strip(str));
const std::string str_clean = util::to_lower(util::strip(str));
for (const auto p : known_platforms())
{
if (str_clean == platform_name(p))
@ -116,4 +114,45 @@ namespace mamba::specs
throw std::invalid_argument(fmt::format("Invalid platform: {}", j_str));
}
}
auto noarch_parse(std::string_view str) -> std::optional<NoArchType>
{
const std::string str_clean = util::to_lower(util::strip(str));
for (const auto p : known_noarch())
{
if (str_clean == noarch_name(p))
{
return { p };
}
}
return {};
}
void to_json(nlohmann::json& j, const NoArchType& noarch)
{
if (noarch != NoArchType::No)
{
j = noarch_name(noarch);
}
}
void from_json(const nlohmann::json& j, NoArchType& noarch)
{
// Legacy deserilization
if (j.is_boolean())
{
noarch = j.get<bool>() ? NoArchType::Generic : NoArchType::No;
return;
}
const auto str = j.get<std::string_view>();
if (const auto maybe = noarch_parse(str))
{
noarch = *maybe;
}
else
{
throw std::invalid_argument(fmt::format("Invalid noarch: {}", str));
}
}
}

View File

@ -13,14 +13,6 @@
namespace mamba::specs
{
NLOHMANN_JSON_SERIALIZE_ENUM(
NoArchType,
{
{ NoArchType::Generic, "generic" },
{ NoArchType::Python, "python" },
}
)
void to_json(nlohmann::json& j, const RepoDataPackage& p)
{
j["name"] = p.name;

View File

@ -12,30 +12,57 @@ using namespace mamba::specs;
TEST_SUITE("specs::platform")
{
TEST_CASE("name")
TEST_CASE("Platform")
{
CHECK_EQ(platform_name(Platform::linux_riscv32), "linux-riscv32");
CHECK_EQ(platform_name(Platform::osx_arm64), "osx-arm64");
CHECK_EQ(platform_name(Platform::win_64), "win-64");
SUBCASE("name")
{
CHECK_EQ(platform_name(Platform::linux_riscv32), "linux-riscv32");
CHECK_EQ(platform_name(Platform::osx_arm64), "osx-arm64");
CHECK_EQ(platform_name(Platform::win_64), "win-64");
}
SUBCASE("parse")
{
CHECK_EQ(platform_parse("linux-armv6l"), Platform::linux_armv6l);
CHECK_EQ(platform_parse(" win-32 "), Platform::win_32);
CHECK_EQ(platform_parse(" OSX-64"), Platform::osx_64);
CHECK_EQ(platform_parse("linus-46"), std::nullopt);
}
SUBCASE("known_platform")
{
static constexpr decltype(known_platform_names()) expected{
"noarch", "linux-32", "linux-64", "linux-armv6l", "linux-armv7l",
"linux-aarch64", "linux-ppc64le", "linux-ppc64", "linux-s390x", "linux-riscv32",
"linux-riscv64", "osx-64", "osx-arm64", "win-32", "win-64",
"win-arm64", "zos-z",
};
CHECK_EQ(expected, known_platform_names());
}
}
TEST_CASE("parse")
TEST_CASE("NoArch")
{
CHECK_EQ(platform_parse("linux-armv6l"), Platform::linux_armv6l);
CHECK_EQ(platform_parse(" win-32 "), Platform::win_32);
CHECK_EQ(platform_parse(" OSX-64"), Platform::osx_64);
CHECK_EQ(platform_parse("linus-46"), std::nullopt);
}
SUBCASE("name")
{
CHECK_EQ(noarch_name(NoArchType::No), "no");
CHECK_EQ(noarch_name(NoArchType::Generic), "generic");
CHECK_EQ(noarch_name(NoArchType::Python), "python");
}
TEST_CASE("known_platform")
{
static constexpr decltype(known_platform_names()) expected{
"noarch", "linux-32", "linux-64", "linux-armv6l", "linux-armv7l",
"linux-aarch64", "linux-ppc64le", "linux-ppc64", "linux-s390x", "linux-riscv32",
"linux-riscv64", "osx-64", "osx-arm64", "win-32", "win-64",
"win-arm64", "zos-z",
SUBCASE("parse")
{
CHECK_EQ(noarch_parse(""), std::nullopt);
CHECK_EQ(noarch_parse(" Python "), NoArchType::Python);
CHECK_EQ(noarch_parse(" geNeric"), NoArchType::Generic);
CHECK_EQ(noarch_parse("Nothing we know"), std::nullopt);
}
};
CHECK_EQ(expected, known_platform_names());
SUBCASE("known_noarch")
{
static constexpr decltype(known_noarch_names()) expected{ "no", "generic", "python" };
CHECK_EQ(expected, known_noarch_names());
}
}
}

View File

@ -15,7 +15,7 @@
using namespace mamba::specs;
namespace nl = nlohmann;
TEST_SUITE("repo_data")
TEST_SUITE("specs::repo_data")
{
TEST_CASE("RepoDataPackage_to_json")
{

View File

@ -77,6 +77,15 @@ namespace mambapy
.def_static("build_platform", &build_platform);
py::implicitly_convertible<py::str, Platform>();
py::enum_<NoArchType>(m, "NoArchType")
.value("No", NoArchType::No)
.value("Generic", NoArchType::Generic)
.value("Python", NoArchType::Python)
.def(py::init(&enum_from_str<NoArchType>))
.def_static("parse", &noarch_parse)
.def_static("count", &known_noarch_count);
py::implicitly_convertible<py::str, NoArchType>();
auto py_conda_url = py::class_<CondaURL>(m, "CondaURL");
py::enum_<CondaURL::Credentials>(py_conda_url, "Credentials")

View File

@ -57,7 +57,23 @@ def test_Platform():
with pytest.raises(KeyError):
# No parsing, explicit name
Platform("linux-64") == Platform.linux_64
Platform("linux-64")
def test_NoArchType():
NoArchType = libmambapy.specs.NoArchType
assert NoArchType.No.name == "No"
assert NoArchType.Generic.name == "Generic"
assert NoArchType.Python.name == "Python"
assert len(NoArchType.__members__) == NoArchType.count()
assert NoArchType.parse(" Python") == NoArchType.Python
assert NoArchType("Generic") == NoArchType.Generic
with pytest.raises(KeyError):
# No parsing, explicit name, needs "Generic"
NoArchType("generic")
def test_CondaURL_Credentials():