More specs bindings (#3080)

* Bind archive

* Minor specs::Version improvements

* Bind specs::Version

* Bind VersionSpec

* Use alias
This commit is contained in:
Antoine Prouvost 2023-12-22 08:54:02 +01:00 committed by GitHub
parent 32961e93c2
commit 2d85759c13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 255 additions and 25 deletions

View File

@ -32,13 +32,13 @@ namespace mamba::specs
VersionPartAtom(std::size_t numeral, std::string_view literal);
// The use of a template is only meant to prevent ambiguous conversions
template <typename Char>
VersionPartAtom(std::size_t numeral, std::basic_string<Char>&& literal);
VersionPartAtom(std::size_t numeral, std::basic_string<Char> literal);
auto numeral() const noexcept -> std::size_t;
auto literal() const& noexcept -> const std::string&;
[[nodiscard]] auto numeral() const noexcept -> std::size_t;
[[nodiscard]] auto literal() const& noexcept -> const std::string&;
auto literal() && noexcept -> std::string;
auto str() const -> std::string;
[[nodiscard]] auto str() const -> std::string;
auto operator==(const VersionPartAtom& other) const -> bool;
auto operator!=(const VersionPartAtom& other) const -> bool;
@ -54,7 +54,7 @@ namespace mamba::specs
std::size_t m_numeral = 0;
};
extern template VersionPartAtom::VersionPartAtom(std::size_t, std::string&&);
extern template VersionPartAtom::VersionPartAtom(std::size_t, std::string);
/**
* A sequence of VersionPartAtom meant to represent a part of a version (e.g. major, minor).
@ -104,7 +104,7 @@ namespace mamba::specs
/** Construct version ``0.0``. */
Version() noexcept = default;
Version(std::size_t epoch, CommonVersion&& version, CommonVersion&& local = {}) noexcept;
Version(std::size_t epoch, CommonVersion version, CommonVersion local = {}) noexcept;
[[nodiscard]] auto epoch() const noexcept -> std::size_t;
[[nodiscard]] auto version() const noexcept -> const CommonVersion&;

View File

@ -11,7 +11,6 @@
#include <optional>
#include <tuple>
#include "mamba/core/error_handling.hpp"
#include "mamba/specs/version.hpp"
#include "mamba/util/cast.hpp"
#include "mamba/util/string.hpp"
@ -65,13 +64,13 @@ namespace mamba::specs
}
template <typename Char>
VersionPartAtom::VersionPartAtom(std::size_t numeral, std::basic_string<Char>&& literal)
VersionPartAtom::VersionPartAtom(std::size_t numeral, std::basic_string<Char> literal)
: m_literal{ util::to_lower(std::move(literal)) }
, m_numeral{ numeral }
{
}
template VersionPartAtom::VersionPartAtom(std::size_t, std::string&&);
template VersionPartAtom::VersionPartAtom(std::size_t, std::string);
auto VersionPartAtom::numeral() const noexcept -> std::size_t
{
@ -180,7 +179,7 @@ namespace mamba::specs
* Implementation of Version *
*******************************/
Version::Version(std::size_t epoch, CommonVersion&& version, CommonVersion&& local) noexcept
Version::Version(std::size_t epoch, CommonVersion version, CommonVersion local) noexcept
: m_version{ std::move(version) }
, m_local{ std::move(local) }
, m_epoch{ epoch }

View File

@ -263,11 +263,6 @@ bind_submodule_impl(pybind11::module_ m)
{
using namespace mamba;
py::class_<specs::Version>(m, "Version")
.def_static("parse", &specs::Version::parse)
.def("__str__", &specs::Version::str);
// declare earlier to avoid C++ types in docstrings
auto pyPackageInfo = py::class_<PackageInfo>(m, "PackageInfo");
auto pyPrefixData = py::class_<PrefixData>(m, "PrefixData");

View File

@ -8,17 +8,23 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl_bind.h>
#include "mamba/specs/archive.hpp"
#include "mamba/specs/authentication_info.hpp"
#include "mamba/specs/channel.hpp"
#include "mamba/specs/channel_spec.hpp"
#include "mamba/specs/conda_url.hpp"
#include "mamba/specs/platform.hpp"
#include "mamba/specs/version.hpp"
#include "mamba/specs/version_spec.hpp"
#include "bindings.hpp"
#include "flat_set_caster.hpp"
#include "utils.hpp"
#include "weakening_map_bind.hpp"
PYBIND11_MAKE_OPAQUE(mamba::specs::VersionPart);
PYBIND11_MAKE_OPAQUE(mamba::specs::CommonVersion);
namespace mambapy
{
void bind_submodule_specs(pybind11::module_ m)
@ -26,6 +32,26 @@ namespace mambapy
namespace py = pybind11;
using namespace mamba::specs;
m.def("archive_extensions", []() { return ARCHIVE_EXTENSIONS; });
m.def(
"has_archive_extension",
[](std::string_view str) { return has_archive_extension(str); }
);
m.def(
"has_archive_extension",
[](const mamba::fs::u8path& p) { return has_archive_extension(p); }
);
m.def(
"strip_archive_extension",
[](std::string_view str) { return strip_archive_extension(str); }
);
m.def(
"strip_archive_extension",
[](const mamba::fs::u8path& p) { return strip_archive_extension(p); }
);
py::enum_<Platform>(m, "Platform")
.value("noarch", Platform::noarch)
.value("linux_32", Platform::linux_32)
@ -285,7 +311,7 @@ namespace mambapy
py::arg("type") = ChannelSpec::Type::Unknown
)
.def("__copy__", &copy<ChannelSpec>)
.def("__deepcopy__", &deepcopy<ChannelSpec>, pybind11::arg("memo"))
.def("__deepcopy__", &deepcopy<ChannelSpec>, py::arg("memo"))
.def_property_readonly("type", &ChannelSpec::type)
.def_property_readonly("location", &ChannelSpec::location)
.def_property_readonly("platform_filters", &ChannelSpec::platform_filters);
@ -309,7 +335,7 @@ namespace mambapy
.def(py::self == py::self)
.def(py::self != py::self)
.def("__copy__", &copy<BasicHTTPAuthentication>)
.def("__deepcopy__", &deepcopy<BasicHTTPAuthentication>, pybind11::arg("memo"))
.def("__deepcopy__", &deepcopy<BasicHTTPAuthentication>, py::arg("memo"))
.def("__hash__", &hash<BasicHTTPAuthentication>);
py::class_<BearerToken>(m, "BearerToken")
@ -321,7 +347,7 @@ namespace mambapy
.def(py::self == py::self)
.def(py::self != py::self)
.def("__copy__", &copy<BearerToken>)
.def("__deepcopy__", &deepcopy<BearerToken>, pybind11::arg("memo"))
.def("__deepcopy__", &deepcopy<BearerToken>, py::arg("memo"))
.def("__hash__", &hash<BearerToken>);
py::class_<CondaToken>(m, "CondaToken")
@ -333,7 +359,7 @@ namespace mambapy
.def(py::self == py::self)
.def(py::self != py::self)
.def("__copy__", &copy<CondaToken>)
.def("__deepcopy__", &deepcopy<CondaToken>, pybind11::arg("memo"))
.def("__deepcopy__", &deepcopy<CondaToken>, py::arg("memo"))
.def("__hash__", &hash<CondaToken>);
bind_weakening_map<AuthenticationDataBase>(m, "AuthenticationDataBase");
@ -384,7 +410,7 @@ namespace mambapy
.def_readwrite("home_dir", &ChannelResolveParams::home_dir)
.def_readwrite("current_working_dir", &ChannelResolveParams::current_working_dir)
.def("__copy__", &copy<BasicHTTPAuthentication>)
.def("__deepcopy__", &deepcopy<BasicHTTPAuthentication>, pybind11::arg("memo"));
.def("__deepcopy__", &deepcopy<BasicHTTPAuthentication>, py::arg("memo"));
py_channel //
.def_property_readonly_static(
@ -453,9 +479,83 @@ namespace mambapy
.def("contains_equivalent", &Channel::contains_equivalent)
.def(py::self == py::self)
.def(py::self != py::self)
.def(py::self != py::self)
.def("__hash__", &hash<Channel>)
.def("__copy__", &copy<Channel>)
.def("__deepcopy__", &deepcopy<Channel>, pybind11::arg("memo"));
.def("__deepcopy__", &deepcopy<Channel>, py::arg("memo"));
py::class_<VersionPartAtom>(m, "VersionPartAtom")
.def(py::init<>())
.def(py::init<std::size_t, std::string_view>(), py::arg("numeral"), py::arg("literal") = "")
.def_property_readonly("numeral", &VersionPartAtom::numeral)
.def_property_readonly(
"literal",
[](const VersionPartAtom& atom) { return atom.literal(); }
)
.def("__str__", &VersionPartAtom::str)
.def(py::self == py::self)
.def(py::self != py::self)
.def(py::self < py::self)
.def(py::self <= py::self)
.def(py::self > py::self)
.def(py::self >= py::self)
.def("__copy__", &copy<VersionPartAtom>)
.def("__deepcopy__", &deepcopy<VersionPartAtom>, py::arg("memo"));
// Type made opaque at the top of this file
py::bind_vector<VersionPart>(m, "VersionPart");
// Type made opaque at the top of this file
py::bind_vector<CommonVersion>(m, "CommonVersion");
py::class_<Version>(m, "Version")
.def_readonly_static("epoch_delim", &Version::epoch_delim)
.def_readonly_static("local_delim", &Version::local_delim)
.def_readonly_static("part_delim", &Version::part_delim)
.def_readonly_static("part_delim_alt", &Version::part_delim_alt)
.def_readonly_static("part_delim_special", &Version::part_delim_special)
.def_static("parse", &Version::parse, py::arg("str"))
.def(
py::init<std::size_t, CommonVersion, CommonVersion>(),
py::arg("epoch") = 0,
py::arg("version") = CommonVersion(),
py::arg("local") = CommonVersion()
)
.def_property_readonly("epoch", &Version::epoch)
.def_property_readonly("version", &Version::version)
.def_property_readonly("local", &Version::local)
.def("starts_with", &Version::starts_with, py::arg("prefix"))
.def("compatible_with", &Version::compatible_with, py::arg("older"), py::arg("level"))
.def("__str__", &Version::str)
.def(py::self == py::self)
.def(py::self != py::self)
.def(py::self < py::self)
.def(py::self <= py::self)
.def(py::self > py::self)
.def(py::self >= py::self)
.def("__copy__", &copy<Version>)
.def("__deepcopy__", &deepcopy<Version>, py::arg("memo"));
// Bindings for VersionSpec currently ignores VersionPredicate and flat_bool_expr_tree
// which would be tedious to bind, and even more to make extendable through Python
py::class_<VersionSpec>(m, "VersionSpec")
.def_readonly_static("and_token", &VersionSpec::and_token)
.def_readonly_static("or_token", &VersionSpec::or_token)
.def_readonly_static("left_parenthesis_token", &VersionSpec::left_parenthesis_token)
.def_readonly_static("right_parenthesis_token", &VersionSpec::right_parenthesis_token)
.def_readonly_static("starts_with_str", &VersionSpec::starts_with_str)
.def_readonly_static("equal_str", &VersionSpec::equal_str)
.def_readonly_static("not_equal_str", &VersionSpec::not_equal_str)
.def_readonly_static("greater_str", &VersionSpec::greater_str)
.def_readonly_static("greater_equal_str", &VersionSpec::greater_equal_str)
.def_readonly_static("less_str", &VersionSpec::less_str)
.def_readonly_static("less_equal_str", &VersionSpec::less_equal_str)
.def_readonly_static("compatible_str", &VersionSpec::compatible_str)
.def_readonly_static("glob_suffix_str", &VersionSpec::glob_suffix_str)
.def_readonly_static("glob_suffix_token", &VersionSpec::glob_suffix_token)
.def_static("parse", &VersionSpec::parse, py::arg("str"))
.def("contains", &VersionSpec::contains, py::arg("point"))
.def("__copy__", &copy<VersionSpec>)
.def("__deepcopy__", &deepcopy<VersionSpec>, py::arg("memo"));
}
}

View File

@ -19,6 +19,16 @@ def test_import_recursive():
_p = mamba.specs.Platform.noarch
def test_archive_extension():
assert libmambapy.specs.archive_extensions() == [".tar.bz2", ".conda"]
assert libmambapy.specs.has_archive_extension("pkg.conda")
assert not libmambapy.specs.has_archive_extension("conda.pkg")
assert libmambapy.specs.strip_archive_extension("pkg.conda") == "pkg"
assert libmambapy.specs.strip_archive_extension("conda.pkg") == "conda.pkg"
def test_Platform():
Platform = libmambapy.specs.Platform
@ -515,3 +525,130 @@ def test_Channel_resolve():
)
assert len(chans) == 2
assert {c.display_name for c in chans} == {"best-forge", "conda-forge"}
def test_VersionPartAtom():
VersionPartAtom = libmambapy.specs.VersionPartAtom
a = VersionPartAtom(numeral=1, literal="alpha")
# Getters
assert a.numeral == 1
assert a.literal == "alpha"
assert str(a) == "1alpha"
# Comparison
b = VersionPartAtom(2)
assert a == a
assert a != b
assert a <= a
assert a <= b
assert a < b
assert a >= a
assert b >= a
assert b > a
# Copy
assert copy.deepcopy(a) == a
def test_VersionPart():
VersionPartAtom = libmambapy.specs.VersionPartAtom
VersionPart = libmambapy.specs.VersionPart
p = VersionPart([VersionPartAtom(1, "a"), VersionPartAtom(3)])
assert len(p) == 2
def test_CommonVersion():
VersionPartAtom = libmambapy.specs.VersionPartAtom
VersionPart = libmambapy.specs.VersionPart
CommonVersion = libmambapy.specs.CommonVersion
p = VersionPart([VersionPartAtom(1, "a"), VersionPartAtom(3)])
v = CommonVersion([p, p])
assert len(v) == 2
def test_Version():
VersionPartAtom = libmambapy.specs.VersionPartAtom
VersionPart = libmambapy.specs.VersionPart
CommonVersion = libmambapy.specs.CommonVersion
Version = libmambapy.specs.Version
# Static data
assert isinstance(Version.epoch_delim, str)
assert isinstance(Version.local_delim, str)
assert isinstance(Version.part_delim, str)
assert isinstance(Version.part_delim_alt, str)
assert isinstance(Version.part_delim_special, str)
# Parse
v = Version.parse("3!1.3ab2.4+42.0alpha")
# Getters
assert v.epoch == 3
assert v.version == CommonVersion(
[
VersionPart([VersionPartAtom(1)]),
VersionPart([VersionPartAtom(3, "ab"), VersionPartAtom(2)]),
VersionPart([VersionPartAtom(4)]),
]
)
assert v.local == CommonVersion(
[
VersionPart([VersionPartAtom(42)]),
VersionPart([VersionPartAtom(0, "alpha")]),
]
)
# str
assert str(v) == "3!1.3ab2.4+42.0alpha"
# Copy
assert copy.deepcopy(v) == v
# Comparison
v1 = Version.parse("1.0.1")
v2 = Version.parse("1.2.3alpha2")
assert v1 == v1
assert v1 != v2
assert v1 <= v1
assert v1 <= v2
assert v2 >= v1
assert v2 >= v2
assert v2 > v1
assert v1.starts_with(Version.parse("1.0"))
assert not v1.starts_with(v2)
assert v2.compatible_with(older=v1, level=1)
assert not v2.compatible_with(older=v1, level=2)
assert not v1.compatible_with(older=v2, level=1)
def test_VersionSpec():
Version = libmambapy.specs.Version
VersionSpec = libmambapy.specs.VersionSpec
# Static data
assert isinstance(VersionSpec.and_token, str)
assert isinstance(VersionSpec.or_token, str)
assert isinstance(VersionSpec.left_parenthesis_token, str)
assert isinstance(VersionSpec.right_parenthesis_token, str)
assert isinstance(VersionSpec.starts_with_str, str)
assert isinstance(VersionSpec.equal_str, str)
assert isinstance(VersionSpec.not_equal_str, str)
assert isinstance(VersionSpec.greater_str, str)
assert isinstance(VersionSpec.greater_equal_str, str)
assert isinstance(VersionSpec.less_str, str)
assert isinstance(VersionSpec.less_equal_str, str)
assert isinstance(VersionSpec.compatible_str, str)
assert isinstance(VersionSpec.glob_suffix_str, str)
assert isinstance(VersionSpec.glob_suffix_token, str)
vs = VersionSpec.parse(">2.0,<3.0")
assert not vs.contains(Version.parse("1.1"))
assert vs.contains(Version.parse("2.1"))
# Copy
copy.deepcopy(vs) # No easy comaprison

View File

@ -2,6 +2,5 @@ import libmambapy
def test_version():
ver_str = "1.0"
ver = libmambapy.Version.parse(ver_str)
assert str(ver) == ver_str
assert isinstance(libmambapy.__version__, str)
assert libmambapy.version.__version__ == libmambapy.__version__