From bada3b1e6aafa8992e07bf8db6ef9f3af9ce3c12 Mon Sep 17 00:00:00 2001 From: Ken Odegard Date: Tue, 17 Oct 2023 22:13:26 -0500 Subject: [PATCH] Refactor which_package & which_prefix --- conda_build/conda_interface.py | 50 ++++++++++++++-------------------- conda_build/inspect_pkg.py | 45 ++++++++++++++++++------------ conda_build/post.py | 8 ++---- 3 files changed, 50 insertions(+), 53 deletions(-) diff --git a/conda_build/conda_interface.py b/conda_build/conda_interface.py index dba4e4b1a7..623a73db4c 100644 --- a/conda_build/conda_interface.py +++ b/conda_build/conda_interface.py @@ -6,6 +6,8 @@ import os from functools import partial from importlib import import_module # noqa: F401 +from pathlib import Path +from typing import Iterable from conda import __version__ as CONDA_VERSION # noqa: F401 from conda.auxlib.packaging import ( # noqa: F401 @@ -125,46 +127,36 @@ class SignatureError(Exception): pass -@deprecated("3.28.0", "4.0.0") -def which_package(path): - """ - Given the path (of a (presumably) conda installed file) iterate over - the conda packages the file came from. Usually the iteration yields - only one package. - """ - from os.path import abspath, join +@deprecated( + "3.28.0", + "4.0.0", + addendum="Use `conda_build.inspect_pkg.which_package` instead.", +) +def which_package(path: str | os.PathLike | Path) -> Iterable[PackageRecord]: + from .inspect_pkg import which_package - path = abspath(path) - prefix = which_prefix(path) - if prefix is None: - raise RuntimeError("could not determine conda prefix from: %s" % path) - for dist in linked(prefix): - meta = is_linked(prefix, dist) - if any(abspath(join(prefix, f)) == path for f in meta["files"]): - yield dist + return which_package(path, which_prefix(path)) @deprecated("3.28.0", "4.0.0") -def which_prefix(path): +def which_prefix(path: str | os.PathLike | Path) -> Path: """ Given the path (to a (presumably) conda installed file) return the environment prefix in which the file in located """ - from os.path import abspath, dirname, isdir, join + from conda.gateways.disk.test import is_conda_environment - prefix = abspath(path) - iteration = 0 - while iteration < 20: - if isdir(join(prefix, "conda-meta")): - # we found it, so let's return it - break - if prefix == dirname(prefix): + prefix = Path(path) + for _ in range(20): + if is_conda_environment(prefix): + return prefix + elif prefix == (parent := prefix.parent): # we cannot chop off any more directories, so we didn't find it - prefix = None break - prefix = dirname(prefix) - iteration += 1 - return prefix + else: + prefix = parent + + raise RuntimeError("could not determine conda prefix from: %s" % path) @deprecated("3.28.0", "4.0.0") diff --git a/conda_build/inspect_pkg.py b/conda_build/inspect_pkg.py index e38c5aa9e7..e2ac8f6492 100644 --- a/conda_build/inspect_pkg.py +++ b/conda_build/inspect_pkg.py @@ -1,5 +1,7 @@ # Copyright (C) 2014 Anaconda, Inc # SPDX-License-Identifier: BSD-3-Clause +from __future__ import annotations + import json import os import re @@ -9,7 +11,12 @@ from functools import lru_cache from itertools import groupby from operator import itemgetter -from os.path import abspath, basename, dirname, exists, join, normcase +from os.path import abspath, basename, dirname, exists, join +from pathlib import Path +from typing import Iterable + +from conda.models.dist import Dist +from conda.models.records import PackageRecord from conda_build.conda_interface import ( display_actions, @@ -34,32 +41,35 @@ rm_rf, ) +from .deprecations import deprecated + +@deprecated("3.28.0", "4.0.0") @lru_cache(maxsize=None) -def dist_files(prefix, dist): +def dist_files(prefix: str | os.PathLike | Path, dist: Dist) -> set[str]: meta = is_linked(prefix, dist) return set(meta["files"]) if meta else set() -def which_package(in_prefix_path, prefix, avoid_canonical_channel_name=False): +@deprecated.argument("3.28.0", "4.0.0", "avoid_canonical_channel_name") +def which_package( + path: str | os.PathLike | Path, + prefix: str | os.PathLike | Path, +) -> Iterable[PackageRecord]: """ - given the path of a conda installed file iterate over + Given the path (of a (presumably) conda installed file) iterate over the conda packages the file came from. Usually the iteration yields only one package. """ - norm_ipp = normcase(in_prefix_path.replace(os.sep, "/")) - from conda_build.utils import linked_data_no_multichannels + from conda.core.prefix_data import PrefixData - if avoid_canonical_channel_name: - fn = linked_data_no_multichannels - else: - fn = linked_data - for dist in fn(prefix): - # dfiles = set(dist.get('files', [])) - dfiles = dist_files(prefix, dist) - # TODO :: This is completely wrong when the env is on a case-sensitive FS! - if any(norm_ipp == normcase(w) for w in dfiles): - yield dist + prefix = Path(prefix) + for prec in PrefixData(prefix).iter_records(): + for file in prec["files"]: + # historically, path was relative to prefix just to be safe we append to prefix + # (pathlib correctly handles this even if path is absolute) + if (prefix / file).samefile(prefix / path): + yield prec def print_object_info(info, key): @@ -278,8 +288,7 @@ def inspect_linkages( else path ) if path.startswith(prefix): - in_prefix_path = re.sub("^" + prefix + "/", "", path) - deps = list(which_package(in_prefix_path, prefix)) + deps = list(which_package(path, prefix)) if len(deps) > 1: deps_str = [str(dep) for dep in deps] get_logger(__name__).warn( diff --git a/conda_build/post.py b/conda_build/post.py index 290779385d..5a8e5b439e 100644 --- a/conda_build/post.py +++ b/conda_build/post.py @@ -926,9 +926,7 @@ def _map_file_to_package( if not len(owners): if any(rp == normpath(w) for w in files): owners.append(pkg_vendored_dist) - new_pkgs = list( - which_package(rp, prefix, avoid_canonical_channel_name=True) - ) + new_pkgs = list(which_package(rp, prefix)) # Cannot filter here as this means the DSO (eg libomp.dylib) will not be found in any package # [owners.append(new_pkg) for new_pkg in new_pkgs if new_pkg not in owners # and not any([fnmatch(new_pkg.name, i) for i in ignore_for_statics])] @@ -1125,9 +1123,7 @@ def _lookup_in_prefix_packages( in_prefix_dso = normpath(needed_dso) n_dso_p = "Needed DSO {}".format(in_prefix_dso.replace("\\", "/")) and_also = " (and also in this package)" if in_prefix_dso in files else "" - pkgs = list( - which_package(in_prefix_dso, run_prefix, avoid_canonical_channel_name=True) - ) + pkgs = list(which_package(in_prefix_dso, run_prefix)) in_pkgs_in_run_reqs = [pkg for pkg in pkgs if pkg.quad[0] in requirements_run] # TODO :: metadata build/inherit_child_run_exports (for vc, mro-base-impl). for pkg in in_pkgs_in_run_reqs: