From ded43dec53481082e68b4854c3f2aa9848be3bec Mon Sep 17 00:00:00 2001 From: Yuming Zhu Date: Tue, 17 Apr 2018 17:36:51 +0800 Subject: [PATCH] cli: also load plugins from ~/.koji/plugins - add plugin_path in koji.conf - add `--plugin-paths` in cli arguments fixes: #887 --- cli/koji | 45 ++++++++++++++++++------- cli/koji.conf | 5 +++ koji/__init__.py | 3 +- tests/test_cli/data/plugins2/plugin2.py | 5 +++ tests/test_cli/data/plugins2/plugin3.py | 7 ++++ tests/test_cli/test_load_plugins.py | 11 ++++-- 6 files changed, 59 insertions(+), 17 deletions(-) create mode 100644 tests/test_cli/data/plugins2/plugin2.py create mode 100644 tests/test_cli/data/plugins2/plugin3.py diff --git a/cli/koji b/cli/koji index 8604bbd0..fc83745c 100755 --- a/cli/koji +++ b/cli/koji @@ -65,20 +65,24 @@ def register_plugin(plugin): globals()[name] = v -def load_plugins(options, path): +def load_plugins(options, paths): """Load plugins specified by our configuration plus system plugins. Order is that system plugins are first, so they can be overridden by user-specified ones with same name.""" logger = logging.getLogger('koji.plugins') - if os.path.exists(path): - tracker = koji.plugin.PluginTracker(path=path) - for name in sorted(os.listdir(path)): - if not name.endswith('.py'): - continue - name = name[:-3] - logger.info('Loading plugin: %s', name) - tracker.load(name) - register_plugin(tracker.get(name)) + tracker = koji.plugin.PluginTracker(path=paths) + names = set() + for path in paths: + if os.path.exists(path): + for name in sorted(os.listdir(path)): + if not name.endswith('.py'): + continue + name = name[:-3] + names.add(name) + for name in names: + logger.info('Loading plugin: %s', name) + tracker.load(name) + register_plugin(tracker.get(name)) def get_options(): @@ -124,6 +128,9 @@ def get_options(): parser.add_option("--weburl", help=_("url of the Koji web interface")) parser.add_option("--topurl", help=_("url for Koji file access")) parser.add_option("--pkgurl", help=SUPPRESS_HELP) + parser.add_option("--plugin-paths", metavar='PATHS', + help=_("specify plugin paths with format as the same as the shell's PATH, " + "'~/.koji/plugins', koji_cli_plugins module are always appended")) parser.add_option("--help-commands", action="store_true", default=False, help=_("list commands")) (options, args) = parser.parse_args() @@ -163,9 +170,21 @@ def get_options(): else: warn("Warning: The pkgurl option is obsolete, please use topurl instead") - plugins_path = '%s/lib/python%s.%s/site-packages/koji_cli_plugins' % \ - (sys.prefix, sys.version_info[0], sys.version_info[1]) - load_plugins(options, plugins_path) + # update plugin_paths to list + plugin_paths = options.plugin_paths + if plugin_paths: + plugin_paths = [os.path.expanduser(p) for p in + plugin_paths.split(':')] + else: + plugin_paths = [] + # always load plugins from ~/.koji/plugins + plugin_paths.append(os.path.expanduser('~/.koji/plugins')) + # always load plugins from koji_cli_plugins module + plugin_paths.append( + '%s/lib/python%s.%s/site-packages/koji_cli_plugins' % + (sys.prefix, sys.version_info[0], sys.version_info[1])) + setattr(options, 'plugin_paths', plugin_paths) + load_plugins(options, plugin_paths) if not args: options.help_commands = True diff --git a/cli/koji.conf b/cli/koji.conf index addd4e31..70aa5432 100644 --- a/cli/koji.conf +++ b/cli/koji.conf @@ -37,6 +37,11 @@ ;certificate of the CA that issued the HTTP server certificate ;serverca = ~/.koji/serverca.crt +;plugin paths, separated by ':' as the same as the shell's PATH +;~/.koji/plugins and koji_cli_plugins module are always be appended +;plugin_paths = ~/.koji/plugins + +;[not_implemented_yet] ;enabled plugins for CLI, runroot and save_failed_tree are available ;plugins = diff --git a/koji/__init__.py b/koji/__init__.py index e9a36627..3fda59e7 100644 --- a/koji/__init__.py +++ b/koji/__init__.py @@ -1701,7 +1701,8 @@ def read_config(profile_name, user_config=None): 'authtype': None, 'debug': False, 'debug_xmlrpc': False, - 'pyver' : None, + 'pyver': None, + 'plugin_paths': None, } result = config_defaults.copy() diff --git a/tests/test_cli/data/plugins2/plugin2.py b/tests/test_cli/data/plugins2/plugin2.py new file mode 100644 index 00000000..336aaf4e --- /dev/null +++ b/tests/test_cli/data/plugins2/plugin2.py @@ -0,0 +1,5 @@ +from koji.plugin import export_cli + +@export_cli +def foo5(): + pass diff --git a/tests/test_cli/data/plugins2/plugin3.py b/tests/test_cli/data/plugins2/plugin3.py new file mode 100644 index 00000000..304db206 --- /dev/null +++ b/tests/test_cli/data/plugins2/plugin3.py @@ -0,0 +1,7 @@ +from koji.plugin import export_cli, export_as + +@export_as('foo6') +@export_cli +def foo(): + pass + diff --git a/tests/test_cli/test_load_plugins.py b/tests/test_cli/test_load_plugins.py index 4747d633..f9368b9b 100644 --- a/tests/test_cli/test_load_plugins.py +++ b/tests/test_cli/test_load_plugins.py @@ -1,23 +1,28 @@ from __future__ import absolute_import -import mock + import os try: import unittest2 as unittest except ImportError: import unittest +import mock + from . import loadcli cli = loadcli.cli class TestLoadPlugins(unittest.TestCase): - @mock.patch('logging.getLogger') def test_load_plugins(self, getLogger): options = mock.MagicMock() - cli.load_plugins(options, os.path.dirname(__file__) + '/data/plugins') + cli.load_plugins(options, [os.path.dirname(__file__) + '/data/plugins', + os.path.dirname( + __file__) + '/data/plugins2']) self.assertTrue(callable(cli.foobar)) self.assertTrue(callable(cli.foo2)) + self.assertTrue(hasattr(cli, 'foo6')) self.assertFalse(hasattr(cli, 'foo3')) self.assertFalse(hasattr(cli, 'foo4')) + self.assertFalse(hasattr(cli, 'foo5')) self.assertFalse(hasattr(cli, 'sth'))