blob: b6f4a7e5513ce92a22562fe24ea21f2ffd033f66 [file] [edit]
###################
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:
.. code-block:: text
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:
.. code-block:: toml
[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``:
.. code-block:: python
from __future__ import annotations
import subprocess
from argparse import ArgumentParser
from pathlib import Path
from virtualenv.config.cli.parser import VirtualEnvOptions
from virtualenv.discovery.discover import Discover
from virtualenv.discovery.py_info import PythonInfo
class PyEnvDiscovery(Discover):
def __init__(self, options: VirtualEnvOptions) -> None:
super().__init__(options)
self.python_spec = options.python if options.python else "python"
@classmethod
def add_parser_arguments(cls, parser: ArgumentParser) -> None:
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) -> PythonInfo | None:
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}") from e
********************
Install the plugin
********************
Install your plugin in development mode alongside virtualenv:
.. code-block:: console
$ pip install -e virtualenv-pyenv/
*******************
Verify the plugin
*******************
Check that virtualenv recognizes your plugin by running:
.. code-block:: console
$ virtualenv --discovery help
The output should list ``pyenv`` as an available discovery mechanism. You can now use it:
.. code-block:: console
$ 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