Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a plugin system #3733

Merged
merged 6 commits into from
Mar 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions docs/docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -538,3 +538,46 @@ To only remove a specific package from a cache, you have to specify the cache en
```bash
poetry cache clear pypi:requests:2.24.0
```

## plugin

The `plugin` namespace regroups sub commands to manage Poetry plugins.

### `plugin add`

The `plugin add` command installs Poetry plugins and make them available at runtime.

For example, to install the `poetry-plugin` plugin, you can run:

```bash
poetry plugin add poetry-plugin
```

The package specification formats supported by the `plugin add` command are the same as the ones supported
by the [`add` command](#add).

If you just want to check what would happen by installing a plugin, you can use the `--dry-run` option

```bash
poetry plugin add poetry-plugin --dry-run
```

#### Options

* `--dry-run`: Outputs the operations but will not execute anything (implicitly enables --verbose).

### `plugin show`

The `plugin show` command lists all the currently installed plugins.

```bash
poetry plugin show
```

### `plugin remove`

The `plugin remove` command removes installed plugins.

```bash
poetry plugin remove poetry-plugin
sdispater marked this conversation as resolved.
Show resolved Hide resolved
```
231 changes: 231 additions & 0 deletions docs/docs/plugins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
# 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.

This comment was marked as outdated.


## 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](/docs/pyproject/#plugins) in the `pyproject.toml` file.

```toml
[tool.poetry]
name = "my-poetry-plugin"
version = "1.0.0"

# ...
[tool.poetry.dependency]
python = "~2.7 || ^3.7"
poetry = "^1.0"

[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`.

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):
version = self.get_custom_version()
io.write_line(f"Setting package version to <b>{version}</b>")
poetry.package.set_version(version)

def get_custom_version(self) -> str:
...
```

### 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 `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.

The plugin also must be declared in the `pyproject.toml` file of the plugin package
as an `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.
Comment on lines +121 to +123

This comment was marked as resolved.



### Event handler

Plugins can also listen to specific events and act on them if necessary.

These events are fired by [Cleo](https://github.com/sdispater/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

### The `plugin add` command

This is the easiest way and should account for all the ways Poetry can be installed.

```bash
poetry plugin add poetry-plugin
```

The `plugin 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 `plugin add` command are the same as the ones supported
by the [`add` command](/docs/cli/#add).

If you no longer need a plugin and want to uninstall it, you can use the `plugin remove` command.

```shell
poetry plugin remove poetry-plugin
```

You can also list all currently installed plugins by running:

```shell
poetry plugin show
```

### 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`

If you used `pip` to install Poetry you can add the plugin packages via the `pip install` command.

```shell
pip install --user poetry-plugin
```

If you want to uninstall a plugin, you can run:

```shell
pip uninstall poetry-plugin
```
1 change: 1 addition & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ nav:
- Repositories: repositories.md
- Managing environments: managing-environments.md
- Dependency specification: dependency-specification.md
- Plugins: plugins.md
- The pyproject.toml file: pyproject.md
- Contributing: contributing.md
- FAQ: faq.md
Expand Down
14 changes: 13 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading