Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libmambapy add_constraint adds constraint packages to install #3293

Open
3 tasks done
minrk opened this issue May 8, 2024 · 1 comment
Open
3 tasks done

libmambapy add_constraint adds constraint packages to install #3293

minrk opened this issue May 8, 2024 · 1 comment

Comments

@minrk
Copy link
Contributor

minrk commented May 8, 2024

Troubleshooting docs

  • My problem is not solved in the Troubleshooting docs

Anaconda default channels

  • I do NOT use the Anaconda default channels (pkgs/* etc.)

How did you install Mamba?

Mambaforge or latest Miniforge

Search tried in issue tracker

searched for add_constraint, could find no mentions of issues

Latest version of Mamba

  • My problem is not solved with the latest version

Tried in Conda?

I do not have this problem with Conda, just with Mamba

Describe your issue

libmambapy 1.5.8

attempting to add constraints via solver.add_constraint(["package version"]) results in constraints being installed, instead of merely constraining the install.

test case
import json
from collections import OrderedDict

import libmambapy


def get_index(
    channel_urls=(),
    platform=None,
    use_local=False,
    use_cache=False,
    unknown=None,
    prefix=None,
    repodata_fn="repodata.json",
    *,
    package_cache,
):
    if isinstance(platform, str):
        platform = [platform, "noarch"]

    all_channels = []
    if use_local:
        all_channels.append("local")
    all_channels.extend(channel_urls)
    # check_allowlist(all_channels)

    # Remove duplicates but retain order
    all_channels = list(OrderedDict.fromkeys(all_channels))

    dlist = libmambapy.DownloadTargetList()

    index = []

    def fixup_channel_spec(spec):
        at_count = spec.count("@")
        if at_count > 1:
            first_at = spec.find("@")
            spec = (
                spec[:first_at]
                + urllib.parse.quote(spec[first_at])
                + spec[first_at + 1 :]
            )
        if platform:
            spec = spec + "[" + ",".join(platform) + "]"
        return spec

    all_channels = list(map(fixup_channel_spec, all_channels))
    libmambapy.create_cache_dir(package_cache.first_writable_path)

    for channel in libmambapy.get_channels(all_channels):
        for channel_platform, url in channel.platform_urls(with_credentials=True):
            full_url = url

            sd = libmambapy.SubdirData(
                channel, channel_platform, full_url, package_cache, repodata_fn
            )

            needs_finalising = sd.download_and_check_targets(dlist)
            index.append(
                (
                    sd,
                    {
                        "platform": channel_platform,
                        "url": url,
                        "channel": channel,
                        "needs_finalising": needs_finalising,
                    },
                )
            )

    for sd, info in index:
        if info["needs_finalising"]:
            sd.finalize_checks()
        dlist.add(sd)

    is_downloaded = dlist.download(libmambapy.MAMBA_DOWNLOAD_FAILFAST)

    if not is_downloaded:
        raise RuntimeError("Error downloading repodata.")

    return index


class MambaSolver:
    def __init__(self, prefix="/tmp/mamba-test", channels=("conda-forge",), platform="osx-arm64"):

        api_ctx = libmambapy.Context()
        api_ctx.prefix_params.conda_prefix = prefix
        self.package_cache = libmambapy.MultiPackageCache([str(Path(prefix) / "conda/pkgs")])


        self.channels = channels
        self.platform = platform
        self.index = get_index(channels, platform=platform, package_cache=self.package_cache)
        self.local_index = []
        self.pool = libmambapy.Pool()
        self.repos = []

        start_prio = len(channels)
        priority = start_prio
        subpriority = 0  # wrong! :)
        for subdir, channel in self.index:
            repo = libmambapy.Repo(
                self.pool,
                str(channel),
                subdir.cache_path(),
                channel["url"],
            )
            repo.set_priority(start_prio, subpriority)
            start_prio -= 1
            self.repos.append(repo)

        self.local_repos = {}

    def solve(self, specs, constraints=None):
        """Solve given a set of specs.
        Parameters
        ----------
        specs : list of str
            A list of package specs. You can use `conda.models.match_spec.MatchSpec`
            to get them to the right form by calling
            `MatchSpec(mypec).conda_build_form()`
        Returns
        -------
        solvable : bool
            True if the set of specs has a solution, False otherwise.
        """
        solver_options = [(libmambapy.SOLVER_FLAG_ALLOW_DOWNGRADE, 1)]
        api_solver = libmambapy.Solver(self.pool, solver_options)
        _specs = specs
        for constraint in constraints or []:
            api_solver.add_constraint(constraint)

        api_solver.add_jobs(_specs, libmambapy.SOLVER_INSTALL)
        success = api_solver.try_solve()

        if not success:
            error_string = "Mamba failed to solve:\n"
            for s in _specs:
                error_string += f" - {s}\n"
            error_string += "\nwith channels:\n"
            for c in self.channels:
                error_string += f" - {c}\n"
            pstring = api_solver.problems_to_str()
            pstring = "\n".join(["   " + l for l in pstring.split("\n")])
            error_string += f"\nThe reported errors are:\n{pstring}"
            print(error_string)
            exit(1)

        t = libmambapy.Transaction(self.pool, api_solver, self.package_cache)
        _, to_link, _ = t.to_conda()
        pkgs = []
        for channel, fname, jdata in to_link:
            pkg_info = json.loads(jdata)
            pkgs.append("{name} {version} {build}".format(**pkg_info))
        return pkgs

With the above MambaSolver implementation, the following:

solver = MambaSolver("/tmp/mamba-test", channels=("conda-forge",), platform="osx-arm64")

solver.solve(['zlib'], constraints=["libzlib 1.2.*"])
# ['libzlib 1.2.13 h53f4e23_5', 'zlib 1.2.13 h53f4e23_5'] - correct
solver.solve(['libzlib'], constraints=["libsodium 1.*"])
# ['libsodium 1.0.18 h27ca646_1', 'libzlib 1.3.1 h0d3ecfb_0'] incorrect, includes libsodium

I don't know if this is a libmambapy bug or a libmamba bug, or if I'm just using libmambapy wrong, but I don't see any tests of constraints actually being applied in the solver, nor any examples suggesting how it should be used.

mamba info / micromamba info

mamba version : 1.5.8
     active environment : cf-scripts
    active env location : /Users/minrk/conda/envs/cf-scripts
            shell level : 1
       user config file : /Users/minrk/.condarc
 populated config files : /Users/minrk/conda/.condarc
                          /Users/minrk/.condarc
          conda version : 24.3.0
    conda-build version : 24.3.0
         python version : 3.10.13.final.0
                 solver : libmamba (default)
       virtual packages : __archspec=1=m1
                          __conda=24.3.0=0
                          __osx=14.4.1=0
                          __unix=0=0
       base environment : /Users/minrk/conda  (writable)
      conda av data dir : /Users/minrk/conda/etc/conda
  conda av metadata url : None
           channel URLs : https://conda.anaconda.org/conda-forge/osx-arm64
                          https://conda.anaconda.org/conda-forge/noarch
          package cache : /Users/minrk/conda/pkgs
                          /Users/minrk/.conda/pkgs
       envs directories : /Users/minrk/conda/envs
                          /Users/minrk/.conda/envs
               platform : osx-arm64
             user-agent : conda/24.3.0 requests/2.31.0 CPython/3.10.13 Darwin/23.4.0 OSX/14.4.1 solver/libmamba conda-libmamba-solver/23.11.1 libmambapy/1.5.8
                UID:GID : 501:20
             netrc file : /Users/minrk/.netrc
           offline mode : False

Logs

No response

environment.yml

No response

~/.condarc

No response

@minrk
Copy link
Contributor Author

minrk commented May 9, 2024

FWIW, in regro/conda-forge-feedstock-check-solvable#31 it worked when I switched to add_pin instead of add_constraint, though I had to call repo.set_installed() to avoid an error, which I don't understand, but it worked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant