Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/conda/conda/llms.txt

Use this file to discover all available pages before exploring further.

Conda’s plugin system allows you to extend its functionality by creating custom plugins. Plugins are registered using the pluggy framework and can add new capabilities to conda.

Plugin System Overview

Plugins are defined using hook specifications (hookspecs) and implemented using hook implementations (hookimpls).
From conda/plugins/hookspec.py:55:
hookimpl = pluggy.HookimplMarker("conda")
"""Decorator to mark plugin hook implementations, used to register plugins."""

Available Plugin Types

Solvers

Custom dependency solvers

Subcommands

Add new CLI commands

Virtual Packages

Define system virtual packages

Pre/Post Commands

Hook into command execution

Auth Handlers

Custom authentication methods

Health Checks

Diagnostic checks for conda doctor

Transaction Actions

Custom package transaction steps

Settings

Register new configuration options

Reporter Backends

Custom output formatters

Request Headers

Add HTTP headers to requests

Environment Specs

Parse custom env file formats

Package Extractors

Handle custom package formats

Creating a Plugin

1. Subcommand Plugin

Add a custom command to conda. From conda/plugins/hookspec.py:97:
@_hookspec
def conda_subcommands(self) -> Iterable[CondaSubcommand]:
    """
    Register external subcommands in conda.
    
    :return: An iterable of subcommand entries.
    """
1

Create the Plugin File

my_conda_plugin.py
from conda import plugins

def example_command(args):
    """Implementation of your subcommand."""
    print("This is an example command!")
    print(f"Arguments: {args}")

@plugins.hookimpl
def conda_subcommands():
    yield plugins.types.CondaSubcommand(
        name="example",
        summary="Example subcommand",
        action=example_command,
    )
2

Install the Plugin

Create a setup.py or pyproject.toml:
pyproject.toml
[project]
name = "my-conda-plugin"
version = "0.1.0"

[project.entry-points.conda]
my-plugin = "my_conda_plugin"
Then install:
pip install -e .
3

Use the Plugin

conda example
# Output: This is an example command!

2. Solver Plugin

Create a custom dependency solver. From conda/plugins/hookspec.py:63:
@_hookspec
def conda_solvers(self) -> Iterable[CondaSolver]:
    """
    Register solvers in conda.
    
    :return: An iterable of solver entries.
    """
import logging
from conda import plugins
from conda.core import solve

log = logging.getLogger(__name__)

class VerboseSolver(solve.Solver):
    """A solver that logs detailed information."""
    
    def solve_final_state(self, *args, **kwargs):
        log.info("Starting verbose solver!")
        log.info(f"Solving with args: {args}")
        result = super().solve_final_state(*args, **kwargs)
        log.info(f"Solved {len(result)} packages")
        return result

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

3. Virtual Package Plugin

Define system capabilities as virtual packages. From conda/plugins/hookspec.py:125:
@_hookspec
def conda_virtual_packages(self) -> Iterable[CondaVirtualPackage]:
    """
    Register virtual packages in Conda.
    
    :return: An iterable of virtual package entries.
    """
from conda import plugins

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

4. Pre/Post Command Hooks

Execute code before or after conda commands. From conda/plugins/hookspec.py:149:
@_hookspec
def conda_pre_commands(self) -> Iterable[CondaPreCommand]:
    """Register pre-command functions in conda."""

@_hookspec
def conda_post_commands(self) -> Iterable[CondaPostCommand]:
    """Register post-command functions in conda."""
from conda import plugins
import logging

log = logging.getLogger(__name__)

def log_install_start(command):
    """Log when install operations begin."""
    log.info(f"Starting {command} operation")
    log.info("Validating environment...")

@plugins.hookimpl
def conda_pre_commands():
    yield plugins.types.CondaPreCommand(
        name="example-pre-command",
        action=log_install_start,
        run_for={"install", "create"},  # Only run for these commands
    )

5. Authentication Handler

Custom authentication for channel access. From conda/plugins/hookspec.py:201:
@_hookspec
def conda_auth_handlers(self) -> Iterable[CondaAuthHandler]:
    """
    Register a conda auth handler derived from the requests API.
    """
import os
from conda import plugins
from requests.auth import AuthBase

class EnvironmentHeaderAuth(AuthBase):
    """Add authentication headers from environment variables."""
    
    def __init__(self, *args, **kwargs):
        self.username = os.environ.get("EXAMPLE_CONDA_AUTH_USERNAME", "")
        self.password = os.environ.get("EXAMPLE_CONDA_AUTH_PASSWORD", "")
    
    def __call__(self, request):
        """Modify the request to include authentication."""
        request.headers["X-Username"] = self.username
        request.headers["X-Password"] = self.password
        return request

@plugins.hookimpl
def conda_auth_handlers():
    yield plugins.types.CondaAuthHandler(
        name="environment-header-auth",
        handler=EnvironmentHeaderAuth,
    )

6. Health Check Plugin

Add diagnostic checks for conda doctor. From conda/plugins/hookspec.py:239:
@_hookspec
def conda_health_checks(self) -> Iterable[CondaHealthCheck]:
    """
    Register health checks for conda doctor.
    
    Health checks can optionally provide a ``fixer`` callable.
    """
from conda import plugins

def check_disk_space(prefix: str, verbose: bool):
    """Check if sufficient disk space is available."""
    import shutil
    
    stat = shutil.disk_usage(prefix)
    free_gb = stat.free / (1024**3)
    
    if free_gb < 1.0:
        print(f"⚠️  Low disk space: {free_gb:.2f} GB free")
    else:
        print(f"✓ Disk space OK: {free_gb:.2f} GB free")

@plugins.hookimpl
def conda_health_checks():
    yield plugins.types.CondaHealthCheck(
        name="disk-space",
        action=check_disk_space,
        summary="Check available disk space",
    )

7. Transaction Action Hooks

Custom actions during package transactions. From conda/plugins/hookspec.py:301 and conda/plugins/hookspec.py:353:
@_hookspec
def conda_pre_transaction_actions(self) -> Iterable[CondaPreTransactionAction]:
    """Register pre-transaction hooks."""

@_hookspec  
def conda_post_transaction_actions(self) -> Iterable[CondaPostTransactionAction]:
    """Register post-transaction hooks."""
from conda import plugins
from conda.core.path_actions import Action

class BackupAction(Action):
    """Backup environment before making changes."""
    
    def verify(self):
        print("Verifying backup requirements...")
        self._verified = True
    
    def execute(self):
        print(f"Backing up {self.target_prefix}...")
        # Perform backup
        print(f"Backing up {len(self.link_precs)} packages")
    
    def reverse(self):
        print("Restore from backup if needed")
    
    def cleanup(self):
        print("Cleanup backup resources")

@plugins.hookimpl
def conda_pre_transaction_actions():
    yield plugins.types.CondaPreTransactionAction(
        name="backup-pre-transaction",
        action=BackupAction,
    )

8. Custom Settings

Register new configuration options. From conda/plugins/hookspec.py:470:
@_hookspec
def conda_settings(self) -> Iterable[CondaSetting]:
    """Register new setting."""
from conda import plugins
from conda.common.configuration import PrimitiveParameter, SequenceParameter

@plugins.hookimpl
def conda_settings():
    yield plugins.types.CondaSetting(
        name="my_custom_setting",
        description="Custom plugin configuration option",
        parameter=PrimitiveParameter("default_value", element_type=str),
        aliases=("my_setting_alias",),
    )
    
    yield plugins.types.CondaSetting(
        name="custom_list_setting",
        description="List of custom values",
        parameter=SequenceParameter(["value1", "value2"], element_type=str),
    )
Users can configure your settings:
conda config --set my_custom_setting "my_value"
conda config --append custom_list_setting "value3"

9. Environment Exporters

Export environments to custom formats. From conda/plugins/hookspec.py:718:
@_hookspec
def conda_environment_exporters(self) -> Iterable[CondaEnvironmentExporter]:
    """Register new conda environment exporter."""
import json
from conda import plugins
from conda.models.environment import Environment

def export_json(env: Environment) -> str:
    """Export environment to JSON format."""
    data = {
        "name": env.name,
        "channels": env.channels,
        "dependencies": [str(dep) for dep in env.dependencies],
    }
    return json.dumps(data, indent=2)

@plugins.hookimpl
def conda_environment_exporters():
    yield plugins.types.CondaEnvironmentExporter(
        name="environment-json",
        aliases=("json",),
        default_filenames=("environment.json",),
        export=export_json,
    )
Usage:
conda export --format json > environment.json

10. Package Extractors

Handle custom package archive formats. From conda/plugins/hookspec.py:775:
@_hookspec
def conda_package_extractors(self) -> Iterable[CondaPackageExtractor]:
    """Register package extractors for different archive formats."""
from conda import plugins
from conda.common.path import PathType
import zipfile

def extract_custom_format(source_path: PathType, destination_directory: PathType) -> None:
    """Extract a custom package format."""
    print(f"Extracting {source_path} to {destination_directory}")
    
    # Custom extraction logic
    with zipfile.ZipFile(source_path, 'r') as zip_ref:
        zip_ref.extractall(destination_directory)

@plugins.hookimpl
def conda_package_extractors():
    yield plugins.types.CondaPackageExtractor(
        name="custom-package",
        extensions=[".custom", ".cpkg"],
        extract=extract_custom_format,
    )

Plugin Types Reference

From conda/plugins/types.py, here are the key plugin type definitions:
@dataclass
class CondaSolver(CondaPlugin):
    name: str
    backend: type[Solver]
@dataclass
class CondaSubcommand(CondaPlugin):
    name: str
    summary: str
    action: Callable[[Namespace | tuple[str]], int | None]
    configure_parser: Callable[[ArgumentParser], None] | None = None
@dataclass
class CondaVirtualPackage(CondaPlugin):
    name: str
    version: str | None | Callable[[], str | None | _Null]
    build: str | None | Callable[[], str | None | _Null]
    override_entity: Literal["version", "build"] | None = None
@dataclass
class CondaHealthCheck(CondaPlugin):
    name: str
    action: Callable[[str, bool], None]
    fixer: Callable[[str, Namespace, ConfirmCallback], int] | None = None
    summary: str | None = None
    fix: str | None = None

Best Practices

Use Type Hints

Include proper type hints for better IDE support and documentation.

Handle Errors

Catch and handle exceptions appropriately in your plugin code.

Add Documentation

Document your plugin’s purpose, usage, and configuration options.

Test Thoroughly

Test your plugin with different conda versions and scenarios.
Important Considerations:
  • Plugins run in the same process as conda - be careful with global state
  • Plugin names must be unique
  • Consider backward compatibility when updating plugins
  • Test offline behavior if your plugin makes network requests

Plugin Distribution

Via PyPI

pyproject.toml
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

[project]
name = "my-conda-plugin"
version = "0.1.0"
description = "My conda plugin"
requires-python = ">=3.8"
dependencies = ["conda>=23.0"]

[project.entry-points.conda]
my-plugin = "my_conda_plugin"

Via Conda Package

meta.yaml
package:
  name: my-conda-plugin
  version: 0.1.0

source:
  path: .

requirements:
  host:
    - python
    - setuptools
  run:
    - python
    - conda >=23.0

test:
  imports:
    - my_conda_plugin

Real-World Examples

conda-libmamba-solver

Alternative solver using libmambaGitHub

conda-auth

Authentication plugin for private channelsGitHub

Debugging Plugins

1

List Registered Plugins

conda plugins --list
2

Enable Debug Logging

conda install --debug
3

Check Plugin Registration

from conda.plugins.manager import CondaPluginManager

manager = CondaPluginManager()
print(manager.get_plugins())

Next Steps

Python API

Learn about the Python API for deeper integration

CLI Reference

Command-line interface reference