Your first plugin¶
This tutorial walks through creating a simple discovery plugin that locates Python interpreters managed by pyenv.
Create the package structure¶
Set up a new Python package with the following structure:
virtualenv-pyenv/
├── pyproject.toml
└── src/
└── virtualenv_pyenv/
└── __init__.py
Configure the entry point¶
In pyproject.toml, declare your plugin as an entry point under the virtualenv.discovery group:
[project]
name = "virtualenv-pyenv"
version = "0.1.0"
dependencies = ["virtualenv>=20"]
[project.entry-points."virtualenv.discovery"]
pyenv = "virtualenv_pyenv:PyEnvDiscovery"
[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"
Implement the plugin¶
In src/virtualenv_pyenv/__init__.py, implement the discovery plugin by subclassing Discover:
from __future__ import annotations
import subprocess
from pathlib import Path
from virtualenv.discovery.discover import Discover
from virtualenv.discovery.py_info import PythonInfo
class PyEnvDiscovery(Discover):
def __init__(self, options):
super().__init__(options)
self.python_spec = options.python if options.python else "python"
@classmethod
def add_parser_arguments(cls, parser):
parser.add_argument(
"--python",
dest="python",
metavar="py",
type=str,
default=None,
help="pyenv Python version to use (e.g., 3.11.0)",
)
def run(self):
try:
result = subprocess.run(
["pyenv", "which", "python"],
capture_output=True,
text=True,
check=True,
)
python_path = Path(result.stdout.strip())
return PythonInfo.from_exe(str(python_path))
except (subprocess.CalledProcessError, FileNotFoundError) as e:
raise RuntimeError(f"Failed to locate pyenv Python: {e}")
Install the plugin¶
Install your plugin in development mode alongside virtualenv:
$ pip install -e virtualenv-pyenv/
Verify the plugin¶
Check that virtualenv recognizes your plugin by running:
$ virtualenv --discovery help
The output should list pyenv as an available discovery mechanism. You can now use it:
$ virtualenv --discovery=pyenv myenv
created virtual environment CPython3.11.0.final.0-64 in 234ms
creator CPython3Posix(dest=/path/to/myenv, clear=False, no_vcs_ignore=False, global=False)
seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/path)
added seed packages: pip==23.0, setuptools==65.5.0, wheel==0.38.4
activators BashActivator,CShellActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator