0
# Plugin System
1
2
Extensibility interfaces for creating Poetry plugins. Enables custom commands, functionality extensions, and integration with external tools through a flexible plugin architecture.
3
4
## Capabilities
5
6
### Base Plugin Interface
7
8
Abstract base class for all Poetry plugins providing the foundation for extensibility.
9
10
```python { .api }
11
class Plugin:
12
"""
13
Base plugin interface for Poetry extensions.
14
15
Abstract base class that all Poetry plugins must implement
16
to integrate with Poetry's functionality.
17
"""
18
19
def activate(self, poetry: Poetry, io) -> None:
20
"""
21
Activate plugin with Poetry instance.
22
23
Args:
24
poetry: Poetry instance
25
io: IO interface for output
26
27
Raises:
28
NotImplementedError: Must be implemented by plugins
29
"""
30
```
31
32
### Application Plugin Interface
33
34
Plugin interface for extending Poetry's CLI with custom commands.
35
36
```python { .api }
37
class ApplicationPlugin:
38
"""
39
Plugin interface for CLI command extensions.
40
41
Allows plugins to register custom commands with Poetry's
42
command-line interface.
43
"""
44
45
@property
46
def commands(self) -> list:
47
"""
48
List of command classes to register.
49
50
Returns:
51
List of command class instances
52
"""
53
54
def activate(self, application) -> None:
55
"""
56
Activate plugin with CLI application.
57
58
Args:
59
application: CLI application instance
60
"""
61
```
62
63
### Plugin Manager
64
65
Manages plugin discovery, loading, and activation within Poetry.
66
67
```python { .api }
68
class PluginManager:
69
"""
70
Plugin discovery and management system.
71
72
Handles loading, activating, and managing Poetry plugins
73
with support for entry point discovery and error handling.
74
"""
75
76
def __init__(self, group: str = "poetry.plugin"):
77
"""
78
Initialize plugin manager.
79
80
Args:
81
group: Entry point group for plugin discovery
82
"""
83
84
def load_plugins(self, env: Env = None) -> None:
85
"""
86
Load plugins from entry points.
87
88
Args:
89
env: Environment for plugin loading
90
"""
91
92
def activate_plugins(self, poetry: Poetry, io = None) -> None:
93
"""
94
Activate loaded plugins.
95
96
Args:
97
poetry: Poetry instance
98
io: IO interface
99
"""
100
101
def get_plugin(self, name: str):
102
"""
103
Get plugin by name.
104
105
Args:
106
name: Plugin name
107
108
Returns:
109
Plugin instance or None
110
"""
111
112
def has_plugins(self) -> bool:
113
"""
114
Check if any plugins are loaded.
115
116
Returns:
117
True if plugins are available
118
"""
119
```
120
121
## Plugin Development
122
123
### Creating a Basic Plugin
124
125
```python
126
from poetry.plugins.plugin import Plugin
127
128
class MyPlugin(Plugin):
129
"""Example Poetry plugin."""
130
131
def activate(self, poetry, io):
132
"""Activate plugin functionality."""
133
io.write_line("My plugin activated!")
134
135
# Access Poetry functionality
136
project_name = poetry.package.name
137
io.write_line(f"Working with project: {project_name}")
138
139
# Plugin-specific initialization
140
self.setup_custom_functionality(poetry)
141
142
def setup_custom_functionality(self, poetry):
143
"""Setup plugin-specific functionality."""
144
pass
145
```
146
147
### Creating a Command Plugin
148
149
```python
150
from poetry.plugins.application_plugin import ApplicationPlugin
151
from poetry.console.commands.command import Command
152
153
class MyCommand(Command):
154
"""Custom Poetry command."""
155
156
name = "my-command"
157
description = "My custom command"
158
159
def handle(self):
160
"""Handle command execution."""
161
self.line("Executing my custom command!")
162
163
# Access Poetry instance
164
poetry = self.poetry
165
self.line(f"Project: {poetry.package.name}")
166
167
# Custom command logic
168
return 0
169
170
class MyCommandPlugin(ApplicationPlugin):
171
"""Plugin that adds custom commands."""
172
173
@property
174
def commands(self):
175
"""Return list of commands to register."""
176
return [MyCommand()]
177
178
def activate(self, application):
179
"""Activate plugin with application."""
180
pass # Commands are automatically registered
181
```
182
183
### Plugin Entry Points
184
185
Configure plugins in `pyproject.toml`:
186
187
```toml
188
[tool.poetry.plugins."poetry.plugin"]
189
my-plugin = "my_package.plugins:MyPlugin"
190
191
[tool.poetry.plugins."poetry.application.plugin"]
192
my-commands = "my_package.plugins:MyCommandPlugin"
193
```
194
195
## Advanced Plugin Patterns
196
197
### Configuration Extension Plugin
198
199
```python
200
from poetry.plugins.plugin import Plugin
201
202
class ConfigExtensionPlugin(Plugin):
203
"""Plugin that extends configuration options."""
204
205
def activate(self, poetry, io):
206
"""Add custom configuration handling."""
207
config = poetry.config
208
209
# Add custom configuration defaults
210
custom_config = {
211
"my-plugin.enabled": True,
212
"my-plugin.custom-setting": "value"
213
}
214
config.merge(custom_config)
215
216
# Register configuration validators
217
self.register_validators(config)
218
219
def register_validators(self, config):
220
"""Register custom configuration validators."""
221
pass
222
```
223
224
### Repository Plugin
225
226
```python
227
from poetry.plugins.plugin import Plugin
228
from poetry.repositories.repository import Repository
229
230
class CustomRepository(Repository):
231
"""Custom repository implementation."""
232
233
def __init__(self, name, url, config):
234
"""Initialize custom repository."""
235
super().__init__(name)
236
self.url = url
237
self.config = config
238
239
def find_packages(self, dependency):
240
"""Find packages in custom repository."""
241
# Custom package discovery logic
242
return []
243
244
class RepositoryPlugin(Plugin):
245
"""Plugin that adds custom repository support."""
246
247
def activate(self, poetry, io):
248
"""Register custom repository."""
249
# Add custom repository to pool
250
custom_repo = CustomRepository(
251
name="custom",
252
url="https://custom-repo.example.com",
253
config=poetry.config
254
)
255
256
poetry.pool.add_repository(custom_repo, priority="supplemental")
257
```
258
259
### Build System Integration Plugin
260
261
```python
262
from poetry.plugins.plugin import Plugin
263
264
class BuildIntegrationPlugin(Plugin):
265
"""Plugin that integrates with build systems."""
266
267
def activate(self, poetry, io):
268
"""Setup build system integration."""
269
# Hook into build process
270
self.setup_build_hooks(poetry)
271
272
# Add custom build steps
273
self.register_build_steps(poetry)
274
275
def setup_build_hooks(self, poetry):
276
"""Setup hooks for build process."""
277
# Pre-build hooks
278
# Post-build hooks
279
pass
280
281
def register_build_steps(self, poetry):
282
"""Register custom build steps."""
283
pass
284
```
285
286
## Plugin Testing
287
288
### Mock Plugin Environment
289
290
```python
291
from poetry.plugins.plugin_manager import PluginManager
292
from poetry.factory import Factory
293
294
def test_plugin_activation():
295
"""Test plugin activation."""
296
# Create Poetry instance
297
poetry = Factory().create_poetry()
298
299
# Create plugin manager
300
plugin_manager = PluginManager()
301
302
# Load and activate plugins
303
plugin_manager.load_plugins()
304
plugin_manager.activate_plugins(poetry)
305
306
# Test plugin functionality
307
assert plugin_manager.has_plugins()
308
```
309
310
### Plugin Integration Testing
311
312
```python
313
import pytest
314
from poetry.console.application import Application
315
316
def test_command_plugin():
317
"""Test command plugin integration."""
318
# Create application with plugins
319
app = Application()
320
321
# Test custom command availability
322
assert app.has("my-command")
323
324
# Test command execution
325
tester = ApplicationTester(app)
326
tester.execute("my-command")
327
328
assert tester.status_code == 0
329
```
330
331
## Error Handling
332
333
Plugin system exceptions and error conditions:
334
335
```python { .api }
336
class PluginError(PoetryError):
337
"""Base plugin error."""
338
339
class PluginLoadError(PluginError):
340
"""Plugin loading failures."""
341
342
class PluginActivationError(PluginError):
343
"""Plugin activation errors."""
344
345
class InvalidPluginError(PluginError):
346
"""Invalid plugin configuration or implementation."""
347
```
348
349
Common plugin errors:
350
- Plugin entry points not found or invalid
351
- Plugin dependencies missing or incompatible
352
- Plugin activation failures
353
- Invalid command registration
354
- Configuration conflicts between plugins
355
- Plugin compatibility issues with Poetry versions