From 24464a1cca1bcc1c26a7a882946d6021b052757c Mon Sep 17 00:00:00 2001 From: Pavel Lonkin Date: Tue, 28 May 2024 17:12:56 +0200 Subject: [PATCH] 225740 Add pluggable system to the CLI tool --- swo/mpt/cli/core/plugins.py | 19 +++++++++++++ swo/mpt/cli/plugins/__init__.py | 0 swo/mpt/cli/swocli.py | 6 ++++- tests/plugins/__init__.py | 0 tests/plugins/test_plugin/__init__.py | 1 + tests/plugins/test_plugin/app.py | 13 +++++++++ .../test_cli/test_core/test_plugins.py | 27 +++++++++++++++++++ 7 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 swo/mpt/cli/core/plugins.py create mode 100644 swo/mpt/cli/plugins/__init__.py create mode 100644 tests/plugins/__init__.py create mode 100644 tests/plugins/test_plugin/__init__.py create mode 100644 tests/plugins/test_plugin/app.py create mode 100644 tests/test_swo/test_mpt/test_cli/test_core/test_plugins.py diff --git a/swo/mpt/cli/core/plugins.py b/swo/mpt/cli/core/plugins.py new file mode 100644 index 0000000..39e629c --- /dev/null +++ b/swo/mpt/cli/core/plugins.py @@ -0,0 +1,19 @@ +from importlib.metadata import EntryPoint, entry_points + +from typer import Typer + +PLUGINS_PACKAGE = "swo.mpt.cli.plugins" + + +def list_entrypoints() -> list[EntryPoint]: # pragma: no cover + entrypoints = entry_points().select(group=PLUGINS_PACKAGE) + if not entrypoints: + return [] + + return entrypoints + + +def load_plugins(app: Typer): + for entrypoint in list_entrypoints(): + plugin = entrypoint.load() + app.add_typer(plugin.app, name=entrypoint.name) diff --git a/swo/mpt/cli/plugins/__init__.py b/swo/mpt/cli/plugins/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/swo/mpt/cli/swocli.py b/swo/mpt/cli/swocli.py index 0508a89..34bb9e2 100644 --- a/swo/mpt/cli/swocli.py +++ b/swo/mpt/cli/swocli.py @@ -5,6 +5,7 @@ from swo.mpt.cli.core.accounts import app as accounts_app from swo.mpt.cli.core.alias_group import AliasTyperGroup from swo.mpt.cli.core.console import console, show_banner +from swo.mpt.cli.core.plugins import load_plugins from swo.mpt.cli.core.pricelists import app as pricelists_app from swo.mpt.cli.core.products import app as products_app @@ -15,7 +16,7 @@ try: - VERSION = version("swo-marketplace-cli") + VERSION = version("mpt-cli") except PackageNotFoundError: # pragma: no cover VERSION = "unknown" @@ -43,5 +44,8 @@ def main( show_banner() +load_plugins(app) + + if __name__ == "__main__": app() diff --git a/tests/plugins/__init__.py b/tests/plugins/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/plugins/test_plugin/__init__.py b/tests/plugins/test_plugin/__init__.py new file mode 100644 index 0000000..a457690 --- /dev/null +++ b/tests/plugins/test_plugin/__init__.py @@ -0,0 +1 @@ +from .app import app # noqa diff --git a/tests/plugins/test_plugin/app.py b/tests/plugins/test_plugin/app.py new file mode 100644 index 0000000..fda6ceb --- /dev/null +++ b/tests/plugins/test_plugin/app.py @@ -0,0 +1,13 @@ +import typer +from swo.mpt.cli.core.console import console + +app = typer.Typer() + + +@app.command() +def test(): + console.print("I'm a test plugin") + + +if __name__ == "__main__": + app() diff --git a/tests/test_swo/test_mpt/test_cli/test_core/test_plugins.py b/tests/test_swo/test_mpt/test_cli/test_core/test_plugins.py new file mode 100644 index 0000000..5e0e9e9 --- /dev/null +++ b/tests/test_swo/test_mpt/test_cli/test_core/test_plugins.py @@ -0,0 +1,27 @@ +from importlib.metadata import EntryPoint + +from swo.mpt.cli.core.plugins import load_plugins +from swo.mpt.cli.swocli import app +from typer.testing import CliRunner + +from tests.plugins.test_plugin import app as plugin_app + +runner = CliRunner() + + +def test_load_plugins(mocker): + mocker.patch.object( + EntryPoint, + "load", + side_effect=lambda: mocker.MagicMock(app=plugin_app), + ) + mocker.patch( + "swo.mpt.cli.core.plugins.list_entrypoints", + return_value=[EntryPoint("tests", "tests.plugins.test_plugin", None)], + ) + + load_plugins(app) + result = runner.invoke(app, ["tests", "test"]) + + assert result.exit_code == 0, result.stdout + assert "I'm a test plugin" in result.stdout