From 12779bca05e8091263beeb9f3ae5efdd52e2d99d Mon Sep 17 00:00:00 2001 From: AntoinePrv Date: Thu, 21 Dec 2023 15:54:24 +0100 Subject: [PATCH 1/5] Bind archive --- libmamba/src/specs/version.cpp | 1 - libmambapy/src/libmambapy/bindings/specs.cpp | 21 ++++++++++++++++++++ libmambapy/tests/test_specs.py | 10 ++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/libmamba/src/specs/version.cpp b/libmamba/src/specs/version.cpp index 44e2773c37..08a49a91fc 100644 --- a/libmamba/src/specs/version.cpp +++ b/libmamba/src/specs/version.cpp @@ -11,7 +11,6 @@ #include #include -#include "mamba/core/error_handling.hpp" #include "mamba/specs/version.hpp" #include "mamba/util/cast.hpp" #include "mamba/util/string.hpp" diff --git a/libmambapy/src/libmambapy/bindings/specs.cpp b/libmambapy/src/libmambapy/bindings/specs.cpp index 3ba7c2d819..db7b6e3fce 100644 --- a/libmambapy/src/libmambapy/bindings/specs.cpp +++ b/libmambapy/src/libmambapy/bindings/specs.cpp @@ -8,6 +8,7 @@ #include #include +#include "mamba/specs/archive.hpp" #include "mamba/specs/authentication_info.hpp" #include "mamba/specs/channel.hpp" #include "mamba/specs/channel_spec.hpp" @@ -26,6 +27,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_(m, "Platform") .value("noarch", Platform::noarch) .value("linux_32", Platform::linux_32) diff --git a/libmambapy/tests/test_specs.py b/libmambapy/tests/test_specs.py index d998f0b243..3e1c829488 100644 --- a/libmambapy/tests/test_specs.py +++ b/libmambapy/tests/test_specs.py @@ -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 From 86c8ba0dbfa87e71d22cc8ac45ac3c14a884f730 Mon Sep 17 00:00:00 2001 From: AntoinePrv Date: Thu, 21 Dec 2023 16:34:24 +0100 Subject: [PATCH 2/5] Minor specs::Version improvements --- libmamba/include/mamba/specs/version.hpp | 12 ++++++------ libmamba/src/specs/version.cpp | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libmamba/include/mamba/specs/version.hpp b/libmamba/include/mamba/specs/version.hpp index d717ce1a1d..358e1b3ccc 100644 --- a/libmamba/include/mamba/specs/version.hpp +++ b/libmamba/include/mamba/specs/version.hpp @@ -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 - VersionPartAtom(std::size_t numeral, std::basic_string&& literal); + VersionPartAtom(std::size_t numeral, std::basic_string 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&; diff --git a/libmamba/src/specs/version.cpp b/libmamba/src/specs/version.cpp index 08a49a91fc..6221fcfe9a 100644 --- a/libmamba/src/specs/version.cpp +++ b/libmamba/src/specs/version.cpp @@ -64,13 +64,13 @@ namespace mamba::specs } template - VersionPartAtom::VersionPartAtom(std::size_t numeral, std::basic_string&& literal) + VersionPartAtom::VersionPartAtom(std::size_t numeral, std::basic_string 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 { @@ -179,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 } From 4073f62b5fe5443c640e6303a8e6bf1381b28638 Mon Sep 17 00:00:00 2001 From: AntoinePrv Date: Thu, 21 Dec 2023 17:47:16 +0100 Subject: [PATCH 3/5] Bind specs::Version --- libmambapy/src/libmambapy/bindings/legacy.cpp | 5 - libmambapy/src/libmambapy/bindings/specs.cpp | 57 ++++++++++- libmambapy/tests/test_specs.py | 98 +++++++++++++++++++ libmambapy/tests/test_version.py | 5 +- 4 files changed, 156 insertions(+), 9 deletions(-) diff --git a/libmambapy/src/libmambapy/bindings/legacy.cpp b/libmambapy/src/libmambapy/bindings/legacy.cpp index 49b43c7abd..4802d492df 100644 --- a/libmambapy/src/libmambapy/bindings/legacy.cpp +++ b/libmambapy/src/libmambapy/bindings/legacy.cpp @@ -263,11 +263,6 @@ bind_submodule_impl(pybind11::module_ m) { using namespace mamba; - py::class_(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_(m, "PackageInfo"); auto pyPrefixData = py::class_(m, "PrefixData"); diff --git a/libmambapy/src/libmambapy/bindings/specs.cpp b/libmambapy/src/libmambapy/bindings/specs.cpp index db7b6e3fce..f01e3492c3 100644 --- a/libmambapy/src/libmambapy/bindings/specs.cpp +++ b/libmambapy/src/libmambapy/bindings/specs.cpp @@ -14,12 +14,16 @@ #include "mamba/specs/channel_spec.hpp" #include "mamba/specs/conda_url.hpp" #include "mamba/specs/platform.hpp" +#include "mamba/specs/version.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) @@ -474,9 +478,60 @@ 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) .def("__copy__", ©) .def("__deepcopy__", &deepcopy, pybind11::arg("memo")); + + py::class_(m, "VersionPartAtom") + .def(py::init<>()) + .def(py::init(), 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__", ©) + .def("__deepcopy__", &deepcopy, pybind11::arg("memo")); + + // Type made opaque at the top of this file + py::bind_vector(m, "VersionPart"); + + // Type made opaque at the top of this file + py::bind_vector(m, "CommonVersion"); + + py::class_(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(), + 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__", ©) + .def("__deepcopy__", &deepcopy, pybind11::arg("memo")); } } diff --git a/libmambapy/tests/test_specs.py b/libmambapy/tests/test_specs.py index 3e1c829488..c9ff4b930f 100644 --- a/libmambapy/tests/test_specs.py +++ b/libmambapy/tests/test_specs.py @@ -525,3 +525,101 @@ 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) diff --git a/libmambapy/tests/test_version.py b/libmambapy/tests/test_version.py index 7924f6f1ba..918f4c7a33 100644 --- a/libmambapy/tests/test_version.py +++ b/libmambapy/tests/test_version.py @@ -2,6 +2,5 @@ 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__ From 692c89a64b74cfa4e8a12eb66df42b6380611677 Mon Sep 17 00:00:00 2001 From: AntoinePrv Date: Thu, 21 Dec 2023 18:21:24 +0100 Subject: [PATCH 4/5] Bind VersionSpec --- libmambapy/src/libmambapy/bindings/specs.cpp | 24 ++++++++++++++++ libmambapy/tests/test_specs.py | 29 ++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/libmambapy/src/libmambapy/bindings/specs.cpp b/libmambapy/src/libmambapy/bindings/specs.cpp index f01e3492c3..547d9ceb67 100644 --- a/libmambapy/src/libmambapy/bindings/specs.cpp +++ b/libmambapy/src/libmambapy/bindings/specs.cpp @@ -15,6 +15,7 @@ #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" @@ -533,5 +534,28 @@ namespace mambapy .def(py::self >= py::self) .def("__copy__", ©) .def("__deepcopy__", &deepcopy, pybind11::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_(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__", ©) + .def("__deepcopy__", &deepcopy, pybind11::arg("memo")); } } diff --git a/libmambapy/tests/test_specs.py b/libmambapy/tests/test_specs.py index c9ff4b930f..6af55330ef 100644 --- a/libmambapy/tests/test_specs.py +++ b/libmambapy/tests/test_specs.py @@ -623,3 +623,32 @@ def test_Version(): 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 From 757219904db4d4eab8021dfce8b1ffb67209d3e5 Mon Sep 17 00:00:00 2001 From: AntoinePrv Date: Thu, 21 Dec 2023 18:22:02 +0100 Subject: [PATCH 5/5] Use alias --- libmambapy/src/libmambapy/bindings/specs.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libmambapy/src/libmambapy/bindings/specs.cpp b/libmambapy/src/libmambapy/bindings/specs.cpp index 547d9ceb67..7336dfe8f5 100644 --- a/libmambapy/src/libmambapy/bindings/specs.cpp +++ b/libmambapy/src/libmambapy/bindings/specs.cpp @@ -311,7 +311,7 @@ namespace mambapy py::arg("type") = ChannelSpec::Type::Unknown ) .def("__copy__", ©) - .def("__deepcopy__", &deepcopy, pybind11::arg("memo")) + .def("__deepcopy__", &deepcopy, py::arg("memo")) .def_property_readonly("type", &ChannelSpec::type) .def_property_readonly("location", &ChannelSpec::location) .def_property_readonly("platform_filters", &ChannelSpec::platform_filters); @@ -335,7 +335,7 @@ namespace mambapy .def(py::self == py::self) .def(py::self != py::self) .def("__copy__", ©) - .def("__deepcopy__", &deepcopy, pybind11::arg("memo")) + .def("__deepcopy__", &deepcopy, py::arg("memo")) .def("__hash__", &hash); py::class_(m, "BearerToken") @@ -347,7 +347,7 @@ namespace mambapy .def(py::self == py::self) .def(py::self != py::self) .def("__copy__", ©) - .def("__deepcopy__", &deepcopy, pybind11::arg("memo")) + .def("__deepcopy__", &deepcopy, py::arg("memo")) .def("__hash__", &hash); py::class_(m, "CondaToken") @@ -359,7 +359,7 @@ namespace mambapy .def(py::self == py::self) .def(py::self != py::self) .def("__copy__", ©) - .def("__deepcopy__", &deepcopy, pybind11::arg("memo")) + .def("__deepcopy__", &deepcopy, py::arg("memo")) .def("__hash__", &hash); bind_weakening_map(m, "AuthenticationDataBase"); @@ -410,7 +410,7 @@ namespace mambapy .def_readwrite("home_dir", &ChannelResolveParams::home_dir) .def_readwrite("current_working_dir", &ChannelResolveParams::current_working_dir) .def("__copy__", ©) - .def("__deepcopy__", &deepcopy, pybind11::arg("memo")); + .def("__deepcopy__", &deepcopy, py::arg("memo")); py_channel // .def_property_readonly_static( @@ -481,7 +481,7 @@ namespace mambapy .def(py::self != py::self) .def("__hash__", &hash) .def("__copy__", ©) - .def("__deepcopy__", &deepcopy, pybind11::arg("memo")); + .def("__deepcopy__", &deepcopy, py::arg("memo")); py::class_(m, "VersionPartAtom") .def(py::init<>()) @@ -499,7 +499,7 @@ namespace mambapy .def(py::self > py::self) .def(py::self >= py::self) .def("__copy__", ©) - .def("__deepcopy__", &deepcopy, pybind11::arg("memo")); + .def("__deepcopy__", &deepcopy, py::arg("memo")); // Type made opaque at the top of this file py::bind_vector(m, "VersionPart"); @@ -533,7 +533,7 @@ namespace mambapy .def(py::self > py::self) .def(py::self >= py::self) .def("__copy__", ©) - .def("__deepcopy__", &deepcopy, pybind11::arg("memo")); + .def("__deepcopy__", &deepcopy, 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 @@ -556,6 +556,6 @@ namespace mambapy .def_static("parse", &VersionSpec::parse, py::arg("str")) .def("contains", &VersionSpec::contains, py::arg("point")) .def("__copy__", ©) - .def("__deepcopy__", &deepcopy, pybind11::arg("memo")); + .def("__deepcopy__", &deepcopy, py::arg("memo")); } }