diff --git a/conda/cli/install.py b/conda/cli/install.py index c0dd67dc6c0..b459ff89168 100644 --- a/conda/cli/install.py +++ b/conda/cli/install.py @@ -18,6 +18,7 @@ from ..common.constants import NULL from ..common.path import is_package_file, paths_equal from ..core.index import calculate_channel_urls, get_index +from ..core.link import revert_actions from ..core.prefix_data import PrefixData from ..exceptions import ( CondaExitZero, @@ -42,7 +43,6 @@ from ..gateways.disk.delete import delete_trash, path_is_clean from ..misc import clone_env, explicit, touch_nonadmin from ..models.match_spec import MatchSpec -from ..plan import revert_actions from . import common from .common import check_non_admin from .python_api import Commands, run_command diff --git a/conda/core/link.py b/conda/core/link.py index 4fd9834e8ee..94eb0a44950 100644 --- a/conda/core/link.py +++ b/conda/core/link.py @@ -16,6 +16,11 @@ from traceback import format_exception_only from typing import Iterable, NamedTuple +try: + from boltons.setutils import IndexedSet +except ImportError: # pragma: no cover + from ._vendor.boltons.setutils import IndexedSet + from .. import CondaError, CondaMultiError, conda_signal_handler from ..auxlib.collection import first from ..auxlib.ish import dals @@ -38,11 +43,13 @@ ) from ..common.signals import signal_handler from ..exceptions import ( + CondaIndexError, CondaSystemExit, DisallowedPackageError, EnvironmentNotWritableError, KnownPackageClobberError, LinkError, + PackagesNotFoundError, RemoveError, SharedLinkPathClobberError, UnknownPackageClobberError, @@ -57,12 +64,15 @@ softlink_supported, ) from ..gateways.subprocess import subprocess_call +from ..history import History from ..models.enums import LinkType +from ..models.match_spec import ChannelMatch, MatchSpec from ..models.package_info import PackageInfo +from ..models.prefix_graph import PrefixGraph from ..models.records import PackageRecord from ..models.version import VersionOrder -from ..resolve import MatchSpec from ..utils import get_comspec, human_bytes, wrap_subprocess_call +from .index import _supplement_index_with_prefix from .package_cache_data import PackageCacheData from .path_actions import ( AggregateCompileMultiPycAction, @@ -81,6 +91,7 @@ _Action, ) from .prefix_data import PrefixData, get_python_version_for_prefix +from .solve import diff_for_unlink_link_precs log = getLogger(__name__) @@ -1614,3 +1625,57 @@ def messages(prefix): return m finally: rm_rf(path) + + +def _get_best_prec_match(precs): + assert precs + for chn in context.channels: + channel_matcher = ChannelMatch(chn) + prec_matches = tuple( + prec for prec in precs if channel_matcher.match(prec.channel.name) + ) + if prec_matches: + break + else: + prec_matches = precs + log.warn("Multiple packages found:%s", dashlist(prec_matches)) + return prec_matches[0] + + +def revert_actions(prefix, revision=-1, index=None): + # TODO: If revision raise a revision error, should always go back to a safe revision + h = History(prefix) + # TODO: need a History method to get user-requested specs for revision number + # Doing a revert right now messes up user-requested spec history. + # Either need to wipe out history after ``revision``, or add the correct + # history information to the new entry about to be created. + # TODO: This is wrong!!!!!!!!!! + user_requested_specs = h.get_requested_specs_map().values() + try: + target_state = { + MatchSpec.from_dist_str(dist_str) for dist_str in h.get_state(revision) + } + except IndexError: + raise CondaIndexError("no such revision: %d" % revision) + + _supplement_index_with_prefix(index, prefix) + + not_found_in_index_specs = set() + link_precs = set() + for spec in target_state: + precs = tuple(prec for prec in index.values() if spec.match(prec)) + if not precs: + not_found_in_index_specs.add(spec) + elif len(precs) > 1: + link_precs.add(_get_best_prec_match(precs)) + else: + link_precs.add(precs[0]) + + if not_found_in_index_specs: + raise PackagesNotFoundError(not_found_in_index_specs) + + final_precs = IndexedSet(PrefixGraph(link_precs).graph) # toposort + unlink_precs, link_precs = diff_for_unlink_link_precs(prefix, final_precs) + stp = PrefixSetup(prefix, unlink_precs, link_precs, (), user_requested_specs, ()) + txn = UnlinkLinkTransaction(stp) + return txn diff --git a/conda/misc.py b/conda/misc.py index 5895e09e416..1f401ca4cf6 100644 --- a/conda/misc.py +++ b/conda/misc.py @@ -13,7 +13,7 @@ from .common.path import expand from .common.url import is_url, join_url, path_to_url from .core.index import get_index -from .core.link import PrefixSetup, UnlinkLinkTransaction +from .core.link import _get_best_prec_match, PrefixSetup, UnlinkLinkTransaction from .core.package_cache_data import PackageCacheData, ProgressiveFetchExtract from .core.prefix_data import PrefixData from .exceptions import ( @@ -27,7 +27,6 @@ from .gateways.disk.link import islink, readlink, symlink from .models.match_spec import MatchSpec from .models.prefix_graph import PrefixGraph -from .plan import _get_best_prec_match def conda_installed_files(prefix, exclude_self_build=False): diff --git a/conda/plan.py b/conda/plan.py index 2c5e1417e39..2ea4f5d04d3 100644 --- a/conda/plan.py +++ b/conda/plan.py @@ -23,17 +23,13 @@ from .base.context import context, reset_context from .common.io import dashlist, env_vars, time_recorder from .common.iterators import groupby_to_dict as groupby -from .core.index import LAST_CHANNEL_URLS, _supplement_index_with_prefix -from .core.link import PrefixSetup, UnlinkLinkTransaction -from .core.solve import diff_for_unlink_link_precs -from .exceptions import CondaIndexError, PackagesNotFoundError -from .history import History +from .core.index import LAST_CHANNEL_URLS +from .core.link import revert_actions from .instructions import FETCH, LINK, SYMLINK_CONDA, UNLINK from .models.channel import Channel, prioritize_channels from .models.dist import Dist from .models.enums import LinkType from .models.match_spec import ChannelMatch -from .models.prefix_graph import PrefixGraph from .models.records import PackageRecord from .models.version import normalized_version from .resolve import MatchSpec @@ -280,60 +276,6 @@ def add_defaults_to_specs(r, linked, specs, update=False, prefix=None): return -def _get_best_prec_match(precs): - assert precs - for chn in context.channels: - channel_matcher = ChannelMatch(chn) - prec_matches = tuple( - prec for prec in precs if channel_matcher.match(prec.channel.name) - ) - if prec_matches: - break - else: - prec_matches = precs - log.warn("Multiple packages found:%s", dashlist(prec_matches)) - return prec_matches[0] - - -def revert_actions(prefix, revision=-1, index=None): - # TODO: If revision raise a revision error, should always go back to a safe revision - h = History(prefix) - # TODO: need a History method to get user-requested specs for revision number - # Doing a revert right now messes up user-requested spec history. - # Either need to wipe out history after ``revision``, or add the correct - # history information to the new entry about to be created. - # TODO: This is wrong!!!!!!!!!! - user_requested_specs = h.get_requested_specs_map().values() - try: - target_state = { - MatchSpec.from_dist_str(dist_str) for dist_str in h.get_state(revision) - } - except IndexError: - raise CondaIndexError("no such revision: %d" % revision) - - _supplement_index_with_prefix(index, prefix) - - not_found_in_index_specs = set() - link_precs = set() - for spec in target_state: - precs = tuple(prec for prec in index.values() if spec.match(prec)) - if not precs: - not_found_in_index_specs.add(spec) - elif len(precs) > 1: - link_precs.add(_get_best_prec_match(precs)) - else: - link_precs.add(precs[0]) - - if not_found_in_index_specs: - raise PackagesNotFoundError(not_found_in_index_specs) - - final_precs = IndexedSet(PrefixGraph(link_precs).graph) # toposort - unlink_precs, link_precs = diff_for_unlink_link_precs(prefix, final_precs) - stp = PrefixSetup(prefix, unlink_precs, link_precs, (), user_requested_specs, ()) - txn = UnlinkLinkTransaction(stp) - return txn - - # ---------------------------- Backwards compat for conda-build --------------------------