hookspec#

Pluggy hook specifications ("hookspecs") to register conda plugins.

Each hookspec defined in CondaSpecs contains an example of how to use it.

Classes#

CondaSpecs

The conda plugin hookspecs, to be used by developers.

Attributes#

spec_name

Name used for organizing conda hook specifications

_hookspec

The conda plugin hook specifications, to be used by developers

hookimpl

Decorator used to mark plugin hook implementations

spec_name = 'conda'#

Name used for organizing conda hook specifications

_hookspec#

The conda plugin hook specifications, to be used by developers

hookimpl#

Decorator used to mark plugin hook implementations

class CondaSpecs#

The conda plugin hookspecs, to be used by developers.

conda_solvers() collections.abc.Iterable[conda.plugins.types.CondaSolver]#

Register solvers in conda.

Example:

import logging

from conda import plugins
from conda.core import solve

log = logging.getLogger(__name__)


class VerboseSolver(solve.Solver):
    def solve_final_state(self, *args, **kwargs):
        log.info("My verbose solver!")
        return super().solve_final_state(*args, **kwargs)


@plugins.hookimpl
def conda_solvers():
    yield plugins.CondaSolver(
        name="verbose-classic",
        backend=VerboseSolver,
    )
Returns:

An iterable of solver entries.

conda_subcommands() collections.abc.Iterable[conda.plugins.types.CondaSubcommand]#

Register external subcommands in conda.

Example:

from conda import plugins


def example_command(args):
    print("This is an example command!")


@plugins.hookimpl
def conda_subcommands():
    yield plugins.CondaSubcommand(
        name="example",
        summary="example command",
        action=example_command,
    )
Returns:

An iterable of subcommand entries.

conda_virtual_packages() collections.abc.Iterable[conda.plugins.types.CondaVirtualPackage]#

Register virtual packages in Conda.

Example:

from conda import plugins


@plugins.hookimpl
def conda_virtual_packages():
    yield plugins.CondaVirtualPackage(
        name="my_custom_os",
        version="1.2.3",
        build="x86_64",
    )
Returns:

An iterable of virtual package entries.

conda_pre_commands() collections.abc.Iterable[conda.plugins.types.CondaPreCommand]#

Register pre-command functions in conda.

Example:

from conda import plugins


def example_pre_command(command):
    print("pre-command action")


@plugins.hookimpl
def conda_pre_commands():
    yield plugins.CondaPreCommand(
        name="example-pre-command",
        action=example_pre_command,
        run_for={"install", "create"},
    )
conda_post_commands() collections.abc.Iterable[conda.plugins.types.CondaPostCommand]#

Register post-command functions in conda.

Example:

from conda import plugins


def example_post_command(command):
    print("post-command action")


@plugins.hookimpl
def conda_post_commands():
    yield plugins.CondaPostCommand(
        name="example-post-command",
        action=example_post_command,
        run_for={"install", "create"},
    )
conda_auth_handlers() collections.abc.Iterable[conda.plugins.types.CondaAuthHandler]#

Register a conda auth handler derived from the requests API.

This plugin hook allows attaching requests auth handler subclasses, e.g. when authenticating requests against individual channels hosted at HTTP/HTTPS services.

Example:

import os
from conda import plugins
from requests.auth import AuthBase


class EnvironmentHeaderAuth(AuthBase):
    def __init__(self, *args, **kwargs):
        self.username = os.environ["EXAMPLE_CONDA_AUTH_USERNAME"]
        self.password = os.environ["EXAMPLE_CONDA_AUTH_PASSWORD"]

    def __call__(self, request):
        request.headers["X-Username"] = self.username
        request.headers["X-Password"] = self.password
        return request


@plugins.hookimpl
def conda_auth_handlers():
    yield plugins.CondaAuthHandler(
        name="environment-header-auth",
        handler=EnvironmentHeaderAuth,
    )
conda_health_checks() collections.abc.Iterable[conda.plugins.types.CondaHealthCheck]#

Register health checks for conda doctor.

This plugin hook allows you to add more "health checks" to conda doctor that you can write to diagnose problems in your conda environment. Check out the health checks already shipped with conda for inspiration.

Example:

from conda import plugins


def example_health_check(prefix: str, verbose: bool):
    print("This is an example health check!")


@plugins.hookimpl
def conda_health_checks():
    yield plugins.CondaHealthCheck(
        name="example-health-check",
        action=example_health_check,
    )
conda_pre_solves() collections.abc.Iterable[conda.plugins.types.CondaPreSolve]#

Register pre-solve functions in conda that are used in the general solver API, before the solver processes the package specs in search of a solution.

Example:

from conda import plugins
from conda.models.match_spec import MatchSpec


def example_pre_solve(
    specs_to_add: frozenset[MatchSpec],
    specs_to_remove: frozenset[MatchSpec],
):
    print(f"Adding {len(specs_to_add)} packages")
    print(f"Removing {len(specs_to_remove)} packages")


@plugins.hookimpl
def conda_pre_solves():
    yield plugins.CondaPreSolve(
        name="example-pre-solve",
        action=example_pre_solve,
    )
conda_post_solves() collections.abc.Iterable[conda.plugins.types.CondaPostSolve]#

Register post-solve functions in conda that are used in the general solver API, after the solver has provided the package records to add or remove from the conda environment.

Example:

from conda import plugins
from conda.models.records import PackageRecord


def example_post_solve(
    repodata_fn: str,
    unlink_precs: tuple[PackageRecord, ...],
    link_precs: tuple[PackageRecord, ...],
):
    print(f"Uninstalling {len(unlink_precs)} packages")
    print(f"Installing {len(link_precs)} packages")


@plugins.hookimpl
def conda_post_solves():
    yield plugins.CondaPostSolve(
        name="example-post-solve",
        action=example_post_solve,
    )
conda_settings() collections.abc.Iterable[conda.plugins.types.CondaSetting]#

Register new setting

The example below defines a simple string type parameter

Example:

from conda import plugins
from conda.common.configuration import PrimitiveParameter, SequenceParameter


@plugins.hookimpl
def conda_settings():
    yield plugins.CondaSetting(
        name="example_option",
        description="This is an example option",
        parameter=PrimitiveParameter("default_value", element_type=str),
        aliases=("example_option_alias",),
    )
conda_reporter_backends() collections.abc.Iterable[conda.plugins.types.CondaReporterBackend]#

Register new reporter backend

The example below defines a reporter backend that uses the pprint module in Python.

Example:

from pprint import pformat

from conda import plugins
from conda.plugins.types import (
    CondaReporterBackend,
    ReporterRendererBase,
    ProgressBarBase,
)


class PprintReporterRenderer(ReporterRendererBase):
    "Implementation of the ReporterRendererBase"

    def detail_view(self, data):
        return pformat(data)

    def envs_list(self, data):
        formatted_data = pformat(data)
        return f"Environments: {formatted_data}"

    def progress_bar(self, description, io_context_manager) -> ProgressBarBase:
        "Returns our custom progress bar implementation"
        return PprintProgressBar(description, io_context_manager)


class PprintProgressBar(ProgressBarBase):
    "Blank implementation of ProgressBarBase which does nothing"

    def update_to(self, fraction) -> None:
        pass

    def refresh(self) -> None:
        pass

    def close(self) -> None:
        pass


@plugins.hookimpl
def conda_reporter_backends():
    yield CondaReporterBackend(
        name="pprint",
        description="Reporter backend based on the pprint module",
        renderer=PprintReporterRenderer,
    )
conda_session_headers(host: str) collections.abc.Iterable[conda.plugins.types.CondaRequestHeader]#

Register new HTTP request headers

The example below defines how to add HTTP headers for all requests with the hostname of example.com.

Example:

from conda import plugins

HOSTS = {"example.com", "sub.example.com"}


@plugins.hookimpl
def conda_session_headers(host: str):
    if host in HOSTS:
        yield plugins.CondaRequestHeader(
            name="Example-Header",
            value="example",
        )
conda_request_headers(host: str, path: str) collections.abc.Iterable[conda.plugins.types.CondaRequestHeader]#

Register new HTTP request headers

The example below defines how to add HTTP headers for all requests with the hostname of example.com and a path/to/endpoint.json path.

Example:

from conda import plugins

HOSTS = {"example.com", "sub.example.com"}
ENDPOINT = "/path/to/endpoint.json"


@plugins.hookimpl
def conda_request_headers(host: str, path: str):
    if host in HOSTS and path == ENDPOINT:
        yield plugins.CondaRequestHeader(
            name="Example-Header",
            value="example",
        )
conda_prefix_data_loaders() collections.abc.Iterable[conda.plugins.types.CondaPrefixDataLoader]#

Register new loaders for PrefixData

The example below defines how to expose the packages installed by a hypothetical 'penguin' tool as conda packages.

Example:

from pathlib import Path

from conda import plugins
from conda.common.path import PathType
from conda.models.records import PrefixRecord
from conda.plugins.types import CondaPrefixDataLoader


@plugins.hookimpl
def conda_prefix_data_loaders():
    yield CondaPrefixDataLoader(
        "hypothetical",
        load_hypothetical_packages,
    )


def load_hypothetical_packages(
    prefix: PathType, records: dict[str, PrefixRecord]
) -> dict[str, PrefixRecord]:
    penguin_records = {}
    for info in Path(prefix).glob("lib/penguin/*.penguin-info"):
        name, version = info.name.rsplit("-", 1)
        kwargs = {}  # retrieve extra fields here
        penguin_records[name] = PrefixRecord(
            name=name, version=version, build_number=0, build="0", **kwargs
        )
    records.update(penguin_records)
    return penguin_records
conda_environment_specifiers() collections.abc.Iterable[conda.plugins.types.CondaEnvironmentSpecifier]#

Register new conda env spec type

The example below defines a type of conda env file called "random". It can parse a file with the file extension .random. This plugin will ignore whatever is in the input environment file and produce an environment with a random name and with random packages.

Example:

import json
import random
from pathlib import Path
from subprocess import run
from conda import plugins
from ...plugins.types import EnvironmentSpecBase
from conda.env.env import Environment

packages = ["python", "numpy", "scipy", "matplotlib", "pandas", "scikit-learn"]


class RandomSpec(EnvironmentSpecBase):
    extensions = {".random"}

    def __init__(self, filename: str):
        self.filename = filename

    def can_handle(self):
        # Return early if no filename was provided
        if self.filename is None:
            return False

        # Extract the file extension (e.g., '.txt' or '' if no extension)
        file_ext = os.path.splitext(self.filename)[1]

        # Check if the file has a supported extension and exists
        return any(
            spec_ext == file_ext and os.path.exists(self.filename)
            for spec_ext in RandomSpec.extensions
        )

    def environment(self):
        return Environment(
            name="".join(random.choice("0123456789abcdef") for i in range(6)),
            dependencies=[random.choice(packages) for i in range(6)],
        )


@plugins.hookimpl
def conda_environment_specifiers():
    yield plugins.CondaEnvSpec(
        name="random",
        environment_spec=RandomSpec,
    )