Plugin architecture

This page explains how virtualenv’s plugin system works internally.

Entry points

virtualenv uses Python entry points (setuptools / importlib.metadata) to discover plugins. Each plugin registers under one of four entry point groups:

  • virtualenv.discovery

  • virtualenv.create

  • virtualenv.seed

  • virtualenv.activate

At startup, virtualenv loads all registered entry points from these groups and makes them available as CLI options. Built-in implementations are registered in virtualenv’s own pyproject.toml, while third-party plugins register their entry points in their own package metadata.

When a package with virtualenv plugins is installed in the same environment as virtualenv, the plugins become immediately available without additional configuration.

Plugin lifecycle

The following diagram shows how plugins are discovered and executed:

        sequenceDiagram
    participant User
    participant CLI
    participant EntryPoints
    participant Discovery
    participant Creator
    participant Seeder
    participant Activator

    rect rgba(37, 99, 235, 0.15)
    User->>CLI: virtualenv myenv
    CLI->>EntryPoints: Load plugins from all groups
    EntryPoints-->>CLI: Available plugins
    CLI->>CLI: Build argument parser with plugin options
    CLI->>User: Parse CLI arguments
    User-->>CLI: Selected options
    end

    rect rgba(22, 163, 106, 0.15)
    CLI->>Discovery: Run selected discovery plugin
    Discovery-->>CLI: PythonInfo
    CLI->>Creator: Create environment with PythonInfo
    Creator-->>CLI: Created environment
    CLI->>Seeder: Seed packages into environment
    Seeder-->>CLI: Seeded environment
    CLI->>Activator: Generate activation scripts
    Activator-->>CLI: Complete environment
    end
    

The lifecycle follows these stages:

  1. virtualenv starts and discovers all entry points from the four plugin groups

  2. The CLI parser is built dynamically, incorporating options from all discovered plugins

  3. User arguments are parsed to select which discovery, creator, seeder, and activator plugins to use

  4. Selected plugins execute in sequence: discover → create → seed → activate

  5. Each stage passes its output to the next stage

Extension point design

Each extension point follows a consistent pattern:

Base abstract class

Each extension point defines a base abstract class (Discover, Creator, Seeder, Activator) that specifies the interface plugins must implement.

Built-in implementations

virtualenv includes built-in implementations registered as entry points in its own pyproject.toml. For example, the built-in CPython creator is registered as cpython3-posix.

Third-party plugins

External packages implement the base interface and register their own entry points under the same group. When installed, they appear alongside built-in options.

CLI selection

Command-line flags (--discovery, --creator, --seeder, --activators) allow users to select which implementation to use. Multiple activators can be selected simultaneously.

Parser integration

Each plugin can contribute CLI arguments through the add_parser_arguments classmethod. These arguments appear in virtualenv --help and are available when the plugin is selected.

How plugins interact

Plugins execute in a pipeline where each stage depends on the previous one:

Discovery → Creator

The discovery plugin produces a PythonInfo object describing the source Python interpreter. This object contains metadata about the Python version, platform, paths, and capabilities. The creator plugin receives this PythonInfo and uses it to determine how to build the virtual environment structure.

Creator → Seeder

The creator plugin produces a Creator object representing the newly created virtual environment. This includes paths to the environment’s bin directory, site-packages, and Python executable. The seeder plugin uses these paths to install packages.

Seeder → Activator

After seeding completes, activator plugins use the Creator object to generate shell activation scripts. These scripts reference the environment’s bin directory and other paths to configure the shell environment.

This pipeline ensures that each plugin has the information it needs from previous stages. The PythonInfo flows from discovery to creator, and the Creator object flows from creator to both seeder and activators.

Plugin isolation

Plugins within the same extension point do not interact with each other. Only one discovery and one creator plugin can run per invocation, though multiple activators can run simultaneously. This isolation keeps plugins simple and focused on their specific task.