Skip to content

Commit

Permalink
feat(plugins): Switch to an importlib.metadata.EntryPoint-based plugi…
Browse files Browse the repository at this point in the history
…n 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 #495

BREAKING CHANGE: Plugins are now exposed as `commitizen.plugin` entrypoints
  • Loading branch information
noirbizarre authored and Lee-W committed Jan 23, 2023
1 parent 60c4f6a commit 12571fe
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 104 deletions.
47 changes: 24 additions & 23 deletions commitizen/cz/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
from __future__ import annotations

import importlib
import pkgutil
import warnings
from typing import Dict, Iterable, Type
from typing import Iterable, Optional

import importlib_metadata as metadata

from commitizen.cz.base import BaseCommitizen
from commitizen.cz.conventional_commits import ConventionalCommitsCz
from commitizen.cz.customize import CustomizeCommitsCz
from commitizen.cz.jira import JiraSmartCz


def discover_plugins(path: Iterable[str] = None) -> Dict[str, Type[BaseCommitizen]]:
def discover_plugins(
path: Optional[Iterable[str]] = None,
) -> dict[str, type[BaseCommitizen]]:
"""Discover commitizen plugins on the path
Args:
Expand All @@ -19,21 +22,19 @@ def discover_plugins(path: Iterable[str] = None) -> Dict[str, Type[BaseCommitize
Returns:
Dict[str, Type[BaseCommitizen]]: Registry with found plugins
"""
plugins = {}
for _finder, name, _ispkg in pkgutil.iter_modules(path):
try:
if name.startswith("cz_"):
plugins[name] = importlib.import_module(name).discover_this
except AttributeError as e:
warnings.warn(UserWarning(e.args[0]))
continue
return plugins


registry: Dict[str, Type[BaseCommitizen]] = {
"cz_conventional_commits": ConventionalCommitsCz,
"cz_jira": JiraSmartCz,
"cz_customize": CustomizeCommitsCz,
}

registry.update(discover_plugins())
for _, name, _ in pkgutil.iter_modules(path):
if name.startswith("cz_"):
mod = importlib.import_module(name)
if hasattr(mod, "discover_this"):
warnings.warn(
UserWarning(
f"Legacy plugin '{name}' has been ignored: please expose it the 'commitizen.plugin' entrypoint"
)
)

return {
ep.name: ep.load() for ep in metadata.entry_points(group="commitizen.plugin")
}


registry: dict[str, type[BaseCommitizen]] = discover_plugins()
70 changes: 62 additions & 8 deletions docs/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,8 @@ The basic steps are:

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

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

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

### Custom commit rules

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

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

Expand Down Expand Up @@ -257,8 +257,6 @@ class JiraCz(BaseCommitizen):
"""
return 'We use this because is useful'
discover_this = JiraCz # used by the plug-in system
```
The next file required is `setup.py` modified from flask version.
Expand All @@ -272,7 +270,12 @@ setup(
py_modules=['cz_jira'],
license='MIT',
long_description='this is a long description',
install_requires=['commitizen']
install_requires=['commitizen'],
entry_points = {
'commitizen.plugin': [
'cz_jira = cz_jira:JiraCz'
]
}
)
```
Expand All @@ -287,8 +290,6 @@ doing `pip install .`
If you feel like it should be part of this repo, create a PR.
[flask uses]: http://flask.pocoo.org/docs/0.12/extensiondev/
### Custom bump rules
You need to define 2 parameters inside your custom `BaseCommitizen`.
Expand Down Expand Up @@ -381,3 +382,56 @@ from commitizen.cz.exception import CzException
class NoSubjectProvidedException(CzException):
...
```

### Migrating from legacy plugin format

Commitizen migrated to a new plugin format relying on `importlib.metadata.EntryPoint`.
Migration should be straight-forward for legacy plugins:

- Remove the `discover_this` line from you plugin module
- Expose the plugin class under as a `commitizen.plugin` entrypoint.

The name of the plugin is now determined by the name of the entrypoint.

#### Example

If you were having a `CzPlugin` class in a `cz_plugin.py` module like this:

```python
from commitizen.cz.base import BaseCommitizen

class PluginCz(BaseCommitizen):
...

discover_this = PluginCz
```

Then remove the `discover_this` line:

```python
from commitizen.cz.base import BaseCommitizen

class PluginCz(BaseCommitizen):
...
```

and expose the class as entrypoint in you setuptools:

```python
from setuptools import setup

setup(
name='MyPlugin',
version='0.1.0',
py_modules=['cz_plugin'],
...
entry_points = {
'commitizen.plugin': [
'plugin = cz_plugin:PluginCz'
]
}
...
)
```

Then your plugin will be available under the name `plugin`.
Loading

0 comments on commit 12571fe

Please sign in to comment.