Source code for conda_pypi.installer

"""
Install a wheel / install a conda.
"""

import os
import subprocess
import tempfile
from pathlib import Path
from unittest.mock import patch
import logging

from conda.cli.main import main_subshell
from conda.core.package_cache_data import PackageCacheData
from installer import install
from installer.destinations import SchemeDictionaryDestination
from installer.records import RecordEntry
from installer.sources import WheelFile

log = logging.getLogger(__name__)


class _CondaWheelDestination(SchemeDictionaryDestination):
    """Suppress entry-point script generation.

    Conda creates entry-point scripts at install time from info/link.json
    (CEP-34), so writing them here would only embed a hardcoded shebang
    that breaks in other environments.
    """

    def write_script(self, name, module, attr, section):
        log.debug(f"Skipping script generation for {name} (handled via link.json)")
        return RecordEntry(path=name, hash_=None, size=None)


[docs] def install_installer(python_executable: str, whl: Path, build_path: Path): # Handler for installation directories and writing into them. # Create site-packages directory if it doesn't exist site_packages = build_path / "site-packages" site_packages.mkdir(parents=True, exist_ok=True) # Scheme keys are defined by PEP 427 (the wheel format spec) and must match # what the installer library extracts from .data/ subdirectory names. # https://packaging.python.org/en/latest/specifications/binary-distribution-format/ scheme = { "purelib": str(site_packages), # Pure Python packages "platlib": str(site_packages), # Platform-specific packages "scripts": str(build_path / "bin"), # Console scripts "data": str(build_path), # Data files (JS, CSS, templates, etc.) "headers": str(build_path / "include"), # C/C++ headers (PEP 427 .data/headers/) } destination = _CondaWheelDestination( scheme_dict=scheme, interpreter=str(python_executable), script_kind="posix", overwrite_existing=True, ) with WheelFile.open(whl) as source: install( source=source, destination=destination, # Additional metadata that is generated by the installation tool. additional_metadata={ "INSTALLER": b"conda-pypi", }, ) log.debug(f"Installed to {build_path}")
[docs] def install_pip(python_executable: str, whl: Path, build_path: Path): command = [ python_executable, "-m", "pip", "install", "--quiet", "--no-deps", "--target", str(build_path / "site-packages"), whl, ] subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) log.debug(f"Installed to {build_path}")
[docs] def install_ephemeral_conda(prefix: Path, package: Path): """ Install [editable] conda package without adding it to the environment's package cache, since we don't want to accidentally re-install "a link to a source checkout" elsewhere. Installing packages directly from a file does not resolve dependencies. Should we automatically install the project's dependencies also? """ persistent_pkgs = PackageCacheData.first_writable().pkgs_dir with ( tempfile.TemporaryDirectory(dir=persistent_pkgs, prefix="ephemeral") as cache_dir, patch.dict(os.environ, {"CONDA_PKGS_DIRS": cache_dir}), ): main_subshell("install", "--prefix", str(prefix), str(package))