Skip to content

Commit 64a693b

Browse files
committed
feat(plugins): Switch to an importlib.metadata.EntryPoint-based plugin loading
Plugins are now loaded using the `commitizen.plugin` entrypoint while legacy plugin are not loaded anymore but a warning is raised when one is seen. Fixes commitizen-tools#495 BREAKING CHANGE: Plugins are now exposed as `commitizen.plugin` entrypoints
1 parent 66e4e62 commit 64a693b

File tree

6 files changed

+689
-583
lines changed

6 files changed

+689
-583
lines changed

commitizen/_compat.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import sys
2+
3+
if sys.version_info < (3, 10):
4+
import importlib_metadata as metadata # noqa: F401
5+
else:
6+
from importlib import metadata # noqa: F401

commitizen/cz/__init__.py

+17-21
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
import warnings
44
from typing import Dict, Iterable, Type
55

6+
from commitizen._compat import metadata
67
from commitizen.cz.base import BaseCommitizen
7-
from commitizen.cz.conventional_commits import ConventionalCommitsCz
8-
from commitizen.cz.customize import CustomizeCommitsCz
9-
from commitizen.cz.jira import JiraSmartCz
108

119

1210
def discover_plugins(path: Iterable[str] = None) -> Dict[str, Type[BaseCommitizen]]:
@@ -19,21 +17,19 @@ def discover_plugins(path: Iterable[str] = None) -> Dict[str, Type[BaseCommitize
1917
Returns:
2018
Dict[str, Type[BaseCommitizen]]: Registry with found plugins
2119
"""
22-
plugins = {}
23-
for _finder, name, _ispkg in pkgutil.iter_modules(path):
24-
try:
25-
if name.startswith("cz_"):
26-
plugins[name] = importlib.import_module(name).discover_this
27-
except AttributeError as e:
28-
warnings.warn(UserWarning(e.args[0]))
29-
continue
30-
return plugins
31-
32-
33-
registry: Dict[str, Type[BaseCommitizen]] = {
34-
"cz_conventional_commits": ConventionalCommitsCz,
35-
"cz_jira": JiraSmartCz,
36-
"cz_customize": CustomizeCommitsCz,
37-
}
38-
39-
registry.update(discover_plugins())
20+
for _, name, _ in pkgutil.iter_modules(path):
21+
if name.startswith("cz_"):
22+
mod = importlib.import_module(name)
23+
if hasattr(mod, "discover_this"):
24+
warnings.warn(
25+
UserWarning(
26+
f"Legacy plugin '{name}' has been ignored: please expose it the 'commitizen.plugin' entrypoint"
27+
)
28+
)
29+
30+
return {
31+
ep.name: ep.load() for ep in metadata.entry_points(group="commitizen.plugin")
32+
}
33+
34+
35+
registry: Dict[str, Type[BaseCommitizen]] = discover_plugins()

docs/customization.md

+62-8
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ The basic steps are:
190190

191191
1. Inheriting from `BaseCommitizen`
192192
2. Give a name to your rules.
193-
3. Expose the class at the end of your file assigning it to `discover_this`
194-
4. Create a python package starting with `cz_` using `setup.py`, `poetry`, etc
193+
3. Create a python package using `setup.py`, `poetry`, etc
194+
4. Expose the class as a `commitizen.plugin` entrypoint
195195

196196
Check an [example](convcomms) on how to configure `BaseCommitizen`.
197197

@@ -205,7 +205,7 @@ See [commitizen_cz_template](https://github.com/commitizen-tools/commitizen_cz_t
205205

206206
### Custom commit rules
207207

208-
Create a file starting with `cz_`, for example `cz_jira.py`. This prefix is used to detect the plug-in. Same method [flask uses]
208+
Create a Python module, for example `cz_jira.py`.
209209

210210
Inherit from `BaseCommitizen`, and you must define `questions` and `message`. The others are optional.
211211

@@ -257,8 +257,6 @@ class JiraCz(BaseCommitizen):
257257
"""
258258
return 'We use this because is useful'
259259
260-
261-
discover_this = JiraCz # used by the plug-in system
262260
```
263261
264262
The next file required is `setup.py` modified from flask version.
@@ -272,7 +270,12 @@ setup(
272270
py_modules=['cz_jira'],
273271
license='MIT',
274272
long_description='this is a long description',
275-
install_requires=['commitizen']
273+
install_requires=['commitizen'],
274+
entry_points = {
275+
'commitizen.plugin': [
276+
'cz_jira = cz_jira:JiraCz'
277+
]
278+
}
276279
)
277280
```
278281
@@ -287,8 +290,6 @@ doing `pip install .`
287290
288291
If you feel like it should be part of this repo, create a PR.
289292
290-
[flask uses]: http://flask.pocoo.org/docs/0.12/extensiondev/
291-
292293
### Custom bump rules
293294
294295
You need to define 2 parameters inside your custom `BaseCommitizen`.
@@ -381,3 +382,56 @@ from commitizen.cz.exception import CzException
381382
class NoSubjectProvidedException(CzException):
382383
...
383384
```
385+
386+
### Migrating from legacy plugin format
387+
388+
Commitizen migrated to a new plugin format relying on `importlib.metadata.EntryPoint`.
389+
Migration should be straight-forward for legacy plugins:
390+
391+
- Remove the `discover_this` line from you plugin module
392+
- Expose the plugin class under as a `commitizen.plugin` entrypoint.
393+
394+
The name of the plugin is now determined by the name of the entrypoint.
395+
396+
#### Example
397+
398+
If you were having a `CzPlugin` class in a `cz_plugin.py` module like this:
399+
400+
```python
401+
from commitizen.cz.base import BaseCommitizen
402+
403+
class PluginCz(BaseCommitizen):
404+
...
405+
406+
discover_this = PluginCz
407+
```
408+
409+
Then remove the `discover_this` line:
410+
411+
```python
412+
from commitizen.cz.base import BaseCommitizen
413+
414+
class PluginCz(BaseCommitizen):
415+
...
416+
```
417+
418+
and expose the class as entrypoint in you setuptools:
419+
420+
```python
421+
from setuptools import setup
422+
423+
setup(
424+
name='MyPlugin',
425+
version='0.1.0',
426+
py_modules=['cz_plugin'],
427+
...
428+
entry_points = {
429+
'commitizen.plugin': [
430+
'plugin = cz_plugin:PluginCz'
431+
]
432+
}
433+
...
434+
)
435+
```
436+
437+
Then your plugin will be available under the name `plugin`.

0 commit comments

Comments
 (0)