mamba-org / mamba

The Fast Cross-Platform Package Manager
https://mamba.readthedocs.io
BSD 3-Clause "New" or "Revised" License
6.53k stars 342 forks source link

libmambapy add_constraint adds constraint packages to install #3293

Open minrk opened 2 months ago

minrk commented 2 months ago

Troubleshooting docs

Anaconda default channels

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

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 ```python 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 commented 1 month ago

FWIW, in https://github.com/regro/conda-forge-feedstock-check-solvable/pull/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.