From 930b751540b9463b23f1239058fef7cb1eb98874 Mon Sep 17 00:00:00 2001 From: Yuming Zhu Date: Mar 12 2018 08:57:37 +0000 Subject: [PATCH 1/3] cli: add `no_cmd` option to get_options --- diff --git a/cli/koji b/cli/koji index ab4cdc5..847b63c 100755 --- a/cli/koji +++ b/cli/koji @@ -81,7 +81,7 @@ def load_plugins(options, path): register_plugin(tracker.get(name)) -def get_options(): +def get_options(no_cmd=False): """process options from command line and config file""" common_commands = ['build', 'help', 'download-build', @@ -167,6 +167,8 @@ def get_options(): (sys.prefix, sys.version_info[0], sys.version_info[1]) load_plugins(options, plugins_path) + if no_cmd: + return options, args if options.help_commands: list_commands() sys.exit(0) From cfee8c52db2795aa5c1b476bce6a680faf96462b Mon Sep 17 00:00:00 2001 From: Yuming Zhu Date: Mar 13 2018 13:09:27 +0000 Subject: [PATCH 2/3] cli: misc refactoring of cli - add multiple plugin paths supporting - move function to lib - add a`CommandExports` to move cli handlers into this namespace instead of `globals()` --- diff --git a/cli/koji b/cli/koji index 847b63c..b640c83 100755 --- a/cli/koji +++ b/cli/koji @@ -26,210 +26,20 @@ from __future__ import absolute_import from __future__ import division + import logging -import optparse import os -import re -import six import sys -import types -import six.moves.configparser import six.moves.xmlrpc_client import koji -import koji.util -import koji.plugin - -from koji_cli.lib import _, OptionParser, get_epilog_str, greetings, \ - warn, categories -from koji_cli.commands import * - - -def register_plugin(plugin): - """Scan a given plugin for handlers - - Handlers are functions marked with one of the decorators defined in koji.plugin - """ - for v in six.itervalues(vars(plugin)): - if isinstance(v, six.class_types): - #skip classes - continue - if callable(v): - if getattr(v, 'exported_cli', False): - if hasattr(v, 'export_alias'): - name = getattr(v, 'export_alias') - else: - name = v.__name__ - # copy object to local namespace - globals()[name] = v - - -def load_plugins(options, path): - """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)) - +from koji_cli import commands +from koji_cli.lib import CommandExports, get_options -def get_options(no_cmd=False): - """process options from command line and config file""" - - common_commands = ['build', 'help', 'download-build', - 'latest-pkg', 'search', 'list-targets'] - usage = _("%%prog [global-options] command [command-options-and-arguments]" - "\n\nCommon commands: %s" % ', '.join(sorted(common_commands))) - parser = OptionParser(usage=usage) - parser.disable_interspersed_args() - progname = os.path.basename(sys.argv[0]) or 'koji' - parser.__dict__['origin_format_help'] = parser.format_help - parser.__dict__['format_help'] = lambda formatter=None: ( - "%(origin_format_help)s%(epilog)s" % ({ - 'origin_format_help': parser.origin_format_help(formatter), - 'epilog': get_epilog_str()})) - parser.add_option("-c", "--config", dest="configFile", - help=_("use alternate configuration file"), metavar="FILE") - parser.add_option("-p", "--profile", default=progname, - help=_("specify a configuration profile")) - parser.add_option("--keytab", help=_("specify a Kerberos keytab to use"), metavar="FILE") - parser.add_option("--principal", help=_("specify a Kerberos principal to use")) - parser.add_option("--krbservice", help=_("specify the Kerberos service name for the hub")) - parser.add_option("--runas", help=_("run as the specified user (requires special privileges)")) - parser.add_option("--user", help=_("specify user")) - parser.add_option("--password", help=_("specify password")) - parser.add_option("--noauth", action="store_true", default=False, - help=_("do not authenticate")) - parser.add_option("--force-auth", action="store_true", default=False, - help=_("authenticate even for read-only operations")) - parser.add_option("--authtype", help=_("force use of a type of authentication, options: noauth, ssl, password, or kerberos")) - parser.add_option("-d", "--debug", action="store_true", - help=_("show debug output")) - parser.add_option("--debug-xmlrpc", action="store_true", - help=_("show xmlrpc debug output")) - parser.add_option("-q", "--quiet", action="store_true", default=False, - help=_("run quietly")) - parser.add_option("--skip-main", action="store_true", default=False, - help=_("don't actually run main")) - parser.add_option("-s", "--server", help=_("url of XMLRPC server")) - parser.add_option("--topdir", help=_("specify topdir")) - 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=optparse.SUPPRESS_HELP) - parser.add_option("--help-commands", action="store_true", default=False, help=_("list commands")) - (options, args) = parser.parse_args() - - # load local config - try: - result = koji.read_config(options.profile, user_config=options.configFile) - except koji.ConfigurationError as e: - parser.error(e.args[0]) - assert False # pragma: no cover - - # update options according to local config - for name, value in six.iteritems(result): - if getattr(options, name, None) is None: - setattr(options, name, value) - - dir_opts = ('topdir', 'cert', 'serverca') - for name in dir_opts: - # expand paths here, so we don't have to worry about it later - value = os.path.expanduser(getattr(options, name)) - setattr(options, name, value) - - #honor topdir - if options.topdir: - koji.BASEDIR = options.topdir - koji.pathinfo.topdir = options.topdir - - #pkgurl is obsolete - if options.pkgurl: - if options.topurl: - warn("Warning: the pkgurl option is obsolete") - else: - suggest = re.sub(r'/packages/?$', '', options.pkgurl) - if suggest != options.pkgurl: - warn("Warning: the pkgurl option is obsolete, using topurl=%r" - % suggest) - options.topurl = suggest - 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) - - if no_cmd: - return options, args - if options.help_commands: - list_commands() - sys.exit(0) - if not args: - list_commands() - sys.exit(0) - - aliases = { - 'cancel-task' : 'cancel', - 'cxl' : 'cancel', - 'list-commands' : 'help', - 'move-pkg': 'move-build', - 'move': 'move-build', - 'latest-pkg': 'latest-build', - 'tag-pkg': 'tag-build', - 'tag': 'tag-build', - 'untag-pkg': 'untag-build', - 'untag': 'untag-build', - 'watch-tasks': 'watch-task', - } - cmd = args[0] - cmd = aliases.get(cmd, cmd) - if cmd.lower() in greetings: - cmd = "moshimoshi" - cmd = cmd.replace('-', '_') - if ('anon_handle_' + cmd) in globals(): - if not options.force_auth and '--mine' not in args: - options.noauth = True - cmd = 'anon_handle_' + cmd - elif ('handle_' + cmd) in globals(): - cmd = 'handle_' + cmd - else: - list_commands() - parser.error('Unknown command: %s' % args[0]) - assert False # pragma: no cover - - return options, cmd, args[1:] - - -def handle_help(options, session, args): - "[info] List available commands" - usage = _("usage: %prog help ...") - usage += _("\n(Specify the --help global option for a list of other help options)") - parser = OptionParser(usage=usage) - # the --admin opt is for backwards compatibility. It is equivalent to: koji help admin - parser.add_option("--admin", action="store_true", help=optparse.SUPPRESS_HELP) - - (options, args) = parser.parse_args(args) - - chosen = set(args) - if options.admin: - chosen.add('admin') - avail = set(list(categories.keys()) + ['all']) - unavail = chosen - avail - for arg in unavail: - print("No such help category: %s" % arg) - - if not chosen: - list_commands() - else: - list_commands(chosen) +for n, v in six.iteritems(vars(commands)): + if not isinstance(v, six.class_types) and callable(v): + setattr(CommandExports, n, staticmethod(v)) def fix_pyver(options, logger): @@ -237,7 +47,7 @@ def fix_pyver(options, logger): pyver = getattr(options, 'pyver', None) if not pyver: return - if pyver not in [2,3]: + if pyver not in [2, 3]: logger.warning('Invalid python version requested: %s', pyver) if sys.version_info[0] == pyver: return @@ -255,45 +65,14 @@ def fix_pyver(options, logger): logger.exception('Unable to execute with requested python version') -def list_commands(categories_chosen=None): - if categories_chosen is None or "all" in categories_chosen: - categories_chosen = list(categories.keys()) - else: - # copy list since we're about to modify it - categories_chosen = list(categories_chosen) - categories_chosen.sort() - handlers = [] - for name,value in globals().items(): - if name.startswith('handle_'): - alias = name.replace('handle_','') - alias = alias.replace('_','-') - handlers.append((alias,value)) - elif name.startswith('anon_handle_'): - alias = name.replace('anon_handle_','') - alias = alias.replace('_','-') - handlers.append((alias,value)) - handlers.sort() - print(_("Available commands:")) - for category in categories_chosen: - print(_("\n%s:" % categories[category])) - for alias,handler in handlers: - desc = handler.__doc__ or '' - if desc.startswith('[%s] ' % category): - desc = desc[len('[%s] ' % category):] - elif category != 'misc' or desc.startswith('['): - continue - print(" %-25s %s" % (alias, desc)) - - print("%s" % get_epilog_str().rstrip("\n")) - - if __name__ == "__main__": global options options, command, args = get_options() logger = logging.getLogger("koji") handler = logging.StreamHandler(sys.stderr) - handler.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s')) + handler.setFormatter( + logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s')) handler.setLevel(logging.DEBUG) logger.addHandler(handler) if options.debug: @@ -309,7 +88,7 @@ if __name__ == "__main__": session = koji.ClientSession(options.server, session_opts) rv = 0 try: - rv = locals()[command].__call__(options, session, args) + rv = getattr(CommandExports, command).__call__(options, session, args) if not rv: rv = 0 except (KeyboardInterrupt, SystemExit): diff --git a/cli/koji.conf b/cli/koji.conf index 26c24cf..17cf244 100644 --- a/cli/koji.conf +++ b/cli/koji.conf @@ -37,5 +37,9 @@ ;certificate of the CA that issued the HTTP server certificate ;serverca = ~/.koji/serverca.crt +;plugin paths, separated by ':' +;plugin_paths = ~/.koji/plugins + +;[not_implemented_yet] ;enabled plugins for CLI, runroot and save_failed_tree are available ;plugins = diff --git a/cli/koji_cli/lib.py b/cli/koji_cli/lib.py index 341cecd..626ba0e 100644 --- a/cli/koji_cli/lib.py +++ b/cli/koji_cli/lib.py @@ -1,16 +1,20 @@ # coding=utf-8 from __future__ import absolute_import from __future__ import division + +import logging import optparse import os import random -import requests -import six +import re import socket import string import sys import time from contextlib import closing + +import requests +import six from six.moves import range try: @@ -19,6 +23,7 @@ except ImportError: # pragma: no cover krbV = None import koji +import koji.plugin # fix OptionParser for python 2.3 (optparse verion 1.4.1+) # code taken from optparse version 1.5a2 @@ -636,3 +641,247 @@ def _list_tasks(options, session): t['sub'] = True return tasklist + + +def register_plugin(plugin): + """Scan a given plugin for handlers + + Handlers are functions marked with one of the decorators defined in koji.plugin + """ + for v in six.itervalues(vars(plugin)): + if isinstance(v, six.class_types): + #skip classes + continue + if callable(v): + if getattr(v, 'exported_cli', False): + if hasattr(v, 'export_alias'): + name = getattr(v, 'export_alias') + else: + name = v.__name__ + # copy object to local namespace + setattr(CommandExports, name, staticmethod(v)) + + +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') + 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(no_cmd=False): + """process options from command line and config file. + + If no_cmd is False, command is required, and it must be registered. + Returns (options, cmd, args) + + If no_cmd is True, command would be an option. + Returns (options, args) + It's usually used by standalone cli script, + but global options and customized options should be divided by '--'. + """ + + common_commands = ['build', 'help', 'download-build', + 'latest-pkg', 'search', 'list-targets'] + usage = _("%%prog [global-options] command [command-options-and-arguments]" + "\n\nCommon commands: %s" % ', '.join(sorted(common_commands))) + parser = OptionParser(usage=usage) + parser.disable_interspersed_args() + progname = os.path.basename(sys.argv[0]) or 'koji' + parser.__dict__['origin_format_help'] = parser.format_help + parser.__dict__['format_help'] = lambda formatter=None: ( + "%(origin_format_help)s%(epilog)s" % ({ + 'origin_format_help': parser.origin_format_help(formatter), + 'epilog': get_epilog_str()})) + parser.add_option("-c", "--config", dest="configFile", + help=_("use alternate configuration file"), metavar="FILE") + parser.add_option("-p", "--profile", default=progname, + help=_("specify a configuration profile")) + parser.add_option("--keytab", help=_("specify a Kerberos keytab to use"), metavar="FILE") + parser.add_option("--principal", help=_("specify a Kerberos principal to use")) + parser.add_option("--krbservice", help=_("specify the Kerberos service name for the hub")) + parser.add_option("--runas", help=_("run as the specified user (requires special privileges)")) + parser.add_option("--user", help=_("specify user")) + parser.add_option("--password", help=_("specify password")) + parser.add_option("--noauth", action="store_true", default=False, + help=_("do not authenticate")) + parser.add_option("--force-auth", action="store_true", default=False, + help=_("authenticate even for read-only operations")) + parser.add_option("--authtype", help=_("force use of a type of authentication, options: noauth, ssl, password, or kerberos")) + parser.add_option("-d", "--debug", action="store_true", + help=_("show debug output")) + parser.add_option("--debug-xmlrpc", action="store_true", + help=_("show xmlrpc debug output")) + parser.add_option("-q", "--quiet", action="store_true", default=False, + help=_("run quietly")) + parser.add_option("--skip-main", action="store_true", default=False, + help=_("don't actually run main")) + parser.add_option("-s", "--server", help=_("url of XMLRPC server")) + parser.add_option("--topdir", help=_("specify topdir")) + 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=optparse.SUPPRESS_HELP) + parser.add_option("--plugin-paths", help=_("specify plugin paths divided by ':'")) + parser.add_option("--help-commands", action="store_true", default=False, help=_("list commands")) + (options, args) = parser.parse_args() + + # load local config + try: + result = koji.read_config(options.profile, user_config=options.configFile) + except koji.ConfigurationError as e: + parser.error(e.args[0]) + assert False # pragma: no cover + + # update options according to local config + for name, value in six.iteritems(result): + if getattr(options, name, None) is None: + setattr(options, name, value) + + dir_opts = ('topdir', 'cert', 'serverca') + for name in dir_opts: + # expand paths here, so we don't have to worry about it later + value = os.path.expanduser(getattr(options, name)) + setattr(options, name, value) + + #honor topdir + if options.topdir: + koji.BASEDIR = options.topdir + koji.pathinfo.topdir = options.topdir + + #pkgurl is obsolete + if options.pkgurl: + if options.topurl: + warn("Warning: the pkgurl option is obsolete") + else: + suggest = re.sub(r'/packages/?$', '', options.pkgurl) + if suggest != options.pkgurl: + warn("Warning: the pkgurl option is obsolete, using topurl=%r" + % suggest) + options.topurl = suggest + else: + warn("Warning: The pkgurl option is obsolete, please use topurl instead") + + + # update plugin_paths to list + plugin_paths = options.plugin_paths or [] + if plugin_paths: + plugin_paths = [os.path.expanduser(p) for p in plugin_paths.split(':')] + # 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 no_cmd: + return options, args + if options.help_commands: + list_commands() + sys.exit(0) + if not args: + list_commands() + sys.exit(0) + + aliases = { + 'cancel-task' : 'cancel', + 'cxl' : 'cancel', + 'list-commands' : 'help', + 'move-pkg': 'move-build', + 'move': 'move-build', + 'latest-pkg': 'latest-build', + 'tag-pkg': 'tag-build', + 'tag': 'tag-build', + 'untag-pkg': 'untag-build', + 'untag': 'untag-build', + 'watch-tasks': 'watch-task', + } + cmd = args[0] + cmd = aliases.get(cmd, cmd) + if cmd.lower() in greetings: + cmd = "moshimoshi" + cmd = cmd.replace('-', '_') + if hasattr(CommandExports, 'anon_handle_' + cmd): + if not options.force_auth and '--mine' not in args: + options.noauth = True + cmd = 'anon_handle_' + cmd + elif hasattr(CommandExports, 'handle_' + cmd): + cmd = 'handle_' + cmd + else: + list_commands() + parser.error('Unknown command: %s' % args[0]) + assert False # pragma: no cover + + return options, cmd, args[1:] + + +def list_commands(categories_chosen=None): + if categories_chosen is None or "all" in categories_chosen: + categories_chosen = list(categories.keys()) + else: + # copy list since we're about to modify it + categories_chosen = list(categories_chosen) + categories_chosen.sort() + handlers = [] + for name, value in six.iteritems(vars(CommandExports)): + if name.startswith('handle_'): + alias = name.replace('handle_', '') + alias = alias.replace('_', '-') + handlers.append((alias, value)) + elif name.startswith('anon_handle_'): + alias = name.replace('anon_handle_', '') + alias = alias.replace('_', '-') + handlers.append((alias, value)) + handlers.sort() + print(_("Available commands:")) + for category in categories_chosen: + print(_("\n%s:" % categories[category])) + for alias, handler in handlers: + if isinstance(handler, staticmethod): + handler = handler.__func__ + desc = handler.__doc__ or '' + if desc.startswith('[%s] ' % category): + desc = desc[len('[%s] ' % category):] + elif category != 'misc' or desc.startswith('['): + continue + print(" %-25s %s" % (alias, desc)) + + print("%s" % get_epilog_str().rstrip("\n")) + + +class CommandExports(object): + + @staticmethod + def handle_help(options, session, args): + "[info] List available commands" + usage = _("usage: %prog help ...") + usage += _("\n(Specify the --help global option for a list of other help options)") + parser = OptionParser(usage=usage) + # the --admin opt is for backwards compatibility. It is equivalent to: koji help admin + parser.add_option("--admin", action="store_true", help=optparse.SUPPRESS_HELP) + + (options, args) = parser.parse_args(args) + + chosen = set(args) + if options.admin: + chosen.add('admin') + avail = set(list(categories.keys()) + ['all']) + unavail = chosen - avail + for arg in unavail: + print("No such help category: %s" % arg) + + if not chosen: + list_commands() + else: + list_commands(chosen) diff --git a/koji/__init__.py b/koji/__init__.py index 8e40e30..8037a7d 100644 --- a/koji/__init__.py +++ b/koji/__init__.py @@ -1661,7 +1661,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 0000000..336aaf4 --- /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 0000000..304db20 --- /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_list_commands.py b/tests/test_cli/test_list_commands.py index 49a9140..95aa83c 100644 --- a/tests/test_cli/test_list_commands.py +++ b/tests/test_cli/test_list_commands.py @@ -1,25 +1,30 @@ from __future__ import absolute_import -import mock + import os -import six import unittest +import mock +import six + +import koji_cli.lib from . import loadcli + cli = loadcli.cli class TestListCommands(unittest.TestCase): - def setUp(self): self.options = mock.MagicMock() self.session = mock.MagicMock() self.args = mock.MagicMock() self.original_parser = cli.OptionParser cli.OptionParser = mock.MagicMock() + koji_cli.lib.OptionParser = cli.OptionParser self.parser = cli.OptionParser.return_value def tearDown(self): cli.OptionParser = self.original_parser + koji_cli.lib.OptionParser = self.original_parser # Show long diffs in error output... maxDiff = None @@ -42,7 +47,7 @@ class TestListCommands(unittest.TestCase): options, arguments = mock.MagicMock(), mock.MagicMock() options.admin = True self.parser.parse_args.return_value = [options, arguments] - cli.handle_help(self.options, self.session, self.args) + cli.CommandExports.handle_help(self.options, self.session, self.args) actual = stdout.getvalue() if six.PY2: actual = actual.replace('nosetests', 'koji') diff --git a/tests/test_cli/test_load_plugins.py b/tests/test_cli/test_load_plugins.py index 411ee96..a4d85af 100644 --- a/tests/test_cli/test_load_plugins.py +++ b/tests/test_cli/test_load_plugins.py @@ -1,20 +1,24 @@ from __future__ import absolute_import -import mock + import os import unittest -from . import loadcli -cli = loadcli.cli +import mock +from koji_cli import lib -class TestLoadPlugins(unittest.TestCase): +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') - self.assertTrue(callable(cli.foobar)) - self.assertTrue(callable(cli.foo2)) - self.assertFalse(hasattr(cli, 'foo3')) - self.assertFalse(hasattr(cli, 'foo4')) - self.assertFalse(hasattr(cli, 'sth')) + lib.load_plugins(options, [os.path.dirname(__file__) + '/data/plugins', + os.path.dirname( + __file__) + '/data/plugins2']) + self.assertTrue(callable(lib.CommandExports.foobar)) + self.assertTrue(callable(lib.CommandExports.foo2)) + self.assertTrue(hasattr(lib.CommandExports, 'foo6')) + self.assertFalse(hasattr(lib.CommandExports, 'foo3')) + self.assertFalse(hasattr(lib.CommandExports, 'foo4')) + self.assertFalse(hasattr(lib.CommandExports, 'foo5')) + self.assertFalse(hasattr(lib.CommandExports, 'sth')) From c7466dd4802a11a199afd2758b8cac40bbe56dcd Mon Sep 17 00:00:00 2001 From: Yuming Zhu Date: Mar 13 2018 13:47:14 +0000 Subject: [PATCH 3/3] fix unittest: cli/test_list_commands --- diff --git a/tests/test_cli/test_list_commands.py b/tests/test_cli/test_list_commands.py index 95aa83c..b53b072 100644 --- a/tests/test_cli/test_list_commands.py +++ b/tests/test_cli/test_list_commands.py @@ -17,13 +17,11 @@ class TestListCommands(unittest.TestCase): self.options = mock.MagicMock() self.session = mock.MagicMock() self.args = mock.MagicMock() - self.original_parser = cli.OptionParser - cli.OptionParser = mock.MagicMock() - koji_cli.lib.OptionParser = cli.OptionParser - self.parser = cli.OptionParser.return_value + self.original_parser = koji_cli.lib.OptionParser + koji_cli.lib.OptionParser = mock.MagicMock() + self.parser = koji_cli.lib.OptionParser.return_value def tearDown(self): - cli.OptionParser = self.original_parser koji_cli.lib.OptionParser = self.original_parser # Show long diffs in error output... @@ -31,7 +29,7 @@ class TestListCommands(unittest.TestCase): @mock.patch('sys.stdout', new_callable=six.StringIO) def test_list_commands(self, stdout): - cli.list_commands() + koji_cli.lib.list_commands() actual = stdout.getvalue() if six.PY2: actual = actual.replace('nosetests', 'koji') @@ -47,7 +45,7 @@ class TestListCommands(unittest.TestCase): options, arguments = mock.MagicMock(), mock.MagicMock() options.admin = True self.parser.parse_args.return_value = [options, arguments] - cli.CommandExports.handle_help(self.options, self.session, self.args) + koji_cli.lib.CommandExports.handle_help(self.options, self.session, self.args) actual = stdout.getvalue() if six.PY2: actual = actual.replace('nosetests', 'koji')