diff --git a/commitizen/cz/__init__.py b/commitizen/cz/__init__.py index a14ea95edf..1699649d3e 100644 --- a/commitizen/cz/__init__.py +++ b/commitizen/cz/__init__.py @@ -1,21 +1,40 @@ import importlib import pkgutil -from typing import Dict, Type +import warnings +from pathlib import Path +from typing import Dict, Iterable, Type 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[Path] = None) -> Dict[str, Type[BaseCommitizen]]: + """Discover commitizen plugins on the path + + Args: + path (Path, optional): If provided, 'path' should be either None or a list of paths to look for + modules in. If path is None, all top-level modules on sys.path.. Defaults to None. + + 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, } -plugins = { - name: importlib.import_module(name).discover_this # type: ignore - for finder, name, ispkg in pkgutil.iter_modules() - if name.startswith("cz_") -} -registry.update(plugins) +registry.update(discover_plugins()) diff --git a/tests/test_factory.py b/tests/test_factory.py index 5fbd2deebb..a78fe65a6e 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -1,7 +1,10 @@ +import sys + import pytest from commitizen import BaseCommitizen, defaults, factory from commitizen.config import BaseConfig +from commitizen.cz import discover_plugins from commitizen.exceptions import NoCommitizenFoundException @@ -19,3 +22,27 @@ def test_factory_fails(): factory.commiter_factory(config) assert "The committer has not been found in the system." in str(excinfo) + + +@pytest.mark.parametrize( + "module_content, plugin_name, expected_plugins", + [ + ("", "cz_no_plugin", {}), + ], +) +def test_discover_plugins(module_content, plugin_name, expected_plugins, tmp_path): + no_plugin_folder = tmp_path / plugin_name + no_plugin_folder.mkdir() + init_file = no_plugin_folder / "__init__.py" + init_file.write_text(module_content) + + sys.path.append(tmp_path.as_posix()) + with pytest.warns(UserWarning) as record: + discovered_plugins = discover_plugins([tmp_path]) + sys.path.pop() + + assert ( + record[0].message.args[0] + == f"module '{plugin_name}' has no attribute 'discover_this'" + ) + assert expected_plugins == discovered_plugins