poetry/docs/plugins.md

270 lines
7.3 KiB
Markdown

---
title: "Plugins"
draft: false
type: docs
layout: single
menu:
docs:
weight: 80
---
# Plugins
Poetry supports using and building plugins if you wish to
alter or expand Poetry's functionality with your own.
For example if your environment poses special requirements
on the behaviour of Poetry which do not apply to the majority of its users
or if you wish to accomplish something with Poetry in a way that is not desired by most users.
In these cases you could consider creating a plugin to handle your specific logic.
## Creating a plugin
A plugin is a regular Python package which ships its code as part of the package
and may also depend on further packages.
### Plugin package
The plugin package must depend on Poetry
and declare a proper [plugin]({{< relref "pyproject#plugins" >}}) in the `pyproject.toml` file.
```toml
[tool.poetry]
name = "my-poetry-plugin"
version = "1.0.0"
# ...
[tool.poetry.dependencies]
python = "^3.7"
poetry = "^1.2"
[tool.poetry.plugins."poetry.plugin"]
demo = "poetry_demo_plugin.plugin:MyPlugin"
```
### Generic plugins
Every plugin has to supply a class which implements the `poetry.plugins.Plugin` interface.
The `activate()` method of the plugin is called after the plugin is loaded
and receives an instance of `Poetry` as well as an instance of `cleo.io.io.IO`.
Using these two objects all configuration can be read
and all public internal objects and state can be manipulated as desired.
Example:
```python
from cleo.io.io import IO
from poetry.plugins.plugin import Plugin
from poetry.poetry import Poetry
class MyPlugin(Plugin):
def activate(self, poetry: Poetry, io: IO):
io.write_line("Setting readme")
poetry.package.readme = "README.md"
...
```
### Application plugins
If you want to add commands or options to the `poetry` script you need
to create an application plugin which implements the `poetry.plugins.ApplicationPlugin` interface.
The `activate()` method of the application plugin is called after the plugin is loaded
and receives an instance of `poetry.console.Application`.
```python
from cleo.commands.command import Command
from poetry.plugins.application_plugin import ApplicationPlugin
class CustomCommand(Command):
name = "my-command"
def handle(self) -> int:
self.line("My command")
return 0
def factory():
return CustomCommand()
class MyApplicationPlugin(ApplicationPlugin):
def activate(self, application):
application.command_loader.register_factory("my-command", factory)
```
{{% note %}}
It's possible to do the following to register the command:
```python
application.add(MyCommand())
```
However, it is **strongly** recommended to register a new factory
in the command loader to defer the loading of the command when it's actually
called.
This will help keep the performances of Poetry good.
{{% /note %}}
The plugin also must be declared in the `pyproject.toml` file of the plugin package
as a `poetry.application.plugin` plugin:
```toml
[tool.poetry.plugins."poetry.application.plugin"]
foo-command = "poetry_demo_plugin.plugin:MyApplicationPlugin"
```
{{% warning %}}
A plugin **must not** remove or modify in any way the core commands of Poetry.
{{% /warning %}}
### Event handler
Plugins can also listen to specific events and act on them if necessary.
These events are fired by [Cleo](https://github.com/python-poetry/cleo)
and are accessible from the `cleo.events.console_events` module.
- `COMMAND`: this event allows attaching listeners before any command is executed.
- `SIGNAL`: this event allows some actions to be performed after the command execution is interrupted.
- `TERMINATE`: this event allows listeners to be attached after the command.
- `ERROR`: this event occurs when an uncaught exception is raised.
Let's see how to implement an application event handler. For this example
we will see how to load environment variables from a `.env` file before executing
a command.
```python
from cleo.events.console_events import COMMAND
from cleo.events.console_command_event import ConsoleCommandEvent
from cleo.events.event_dispatcher import EventDispatcher
from dotenv import load_dotenv
from poetry.console.application import Application
from poetry.console.commands.env_command import EnvCommand
from poetry.plugins.application_plugin import ApplicationPlugin
class MyApplicationPlugin(ApplicationPlugin):
def activate(self, application: Application):
application.event_dispatcher.add_listener(
COMMAND, self.load_dotenv
)
def load_dotenv(
self,
event: ConsoleCommandEvent,
event_name: str,
dispatcher: EventDispatcher
) -> None:
command = event.command
if not isinstance(command, EnvCommand):
return
io = event.io
if io.is_debug():
io.write_line(
"<debug>Loading environment variables.</debug>"
)
load_dotenv()
```
## Using plugins
Installed plugin packages are automatically loaded when Poetry starts up.
You have multiple ways to install plugins for Poetry
### With `pipx inject`
If you used `pipx` to install Poetry you can add the plugin packages via the `pipx inject` command.
```shell
pipx inject poetry poetry-plugin
```
If you want to uninstall a plugin, you can run:
```shell
pipx runpip poetry uninstall poetry-plugin
```
### With `pip`
The `pip` binary in Poetry's virtual environment can also be used to install and remove plugins.
The environment variable `$POETRY_HOME` here is used to represent the path to the virtual environment.
The [installation instructions](/docs/) can be referenced if you are not
sure where Poetry has been installed.
To add a plugin, you can use `pip install`:
```shell
$POETRY_HOME/bin/pip install --user poetry-plugin
```
If you want to uninstall a plugin, you can run:
```shell
$POETRY_HOME/bin/pip uninstall poetry-plugin
```
### The `self add` command
{{% warning %}}
Especially on Windows, `self add` and `self remove` may be problematic
so that other methods should be preferred.
{{% /warning %}}
```bash
poetry self add poetry-plugin
```
The `self add` command will ensure that the plugin is compatible with the current version of Poetry
and install the needed packages for the plugin to work.
The package specification formats supported by the `self add` command are the same as the ones supported
by the [`add` command]({{< relref "cli#add" >}}).
If you no longer need a plugin and want to uninstall it, you can use the `self remove` command.
```shell
poetry self remove poetry-plugin
```
You can also list all currently installed plugins by running:
```shell
poetry self show plugins
```
## Maintaining a plugin
When writing a plugin, you will probably access internals of Poetry, since there is no
stable public API. Although we try our best to deprecate methods first, before
removing them, sometimes the signature of an internal method has to be changed.
As the author of a plugin, you are probably testing your plugin
against the latest release of Poetry.
Additionally, you should consider testing against the latest release branch and the
master branch of Poetry and schedule a CI job that runs regularly even if you did not
make any changes to your plugin.
This way, you will notice internal changes that break your plugin immediately
and can prepare for the next Poetry release.