From 10026284dbf8f1b8a6eedf3b1c6ce05da568b4fa Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Mon, 27 Oct 2008 14:48:02 -0600 Subject: [PATCH] Started cleanup work on CLI class, added unit tests for CLI.parse_globals() --- ipa | 4 +- ipalib/cli.py | 113 ++++++++++++++++++++-------------- tests/test_ipalib/test_cli.py | 103 ++++++++++++++++--------------- 3 files changed, 122 insertions(+), 98 deletions(-) diff --git a/ipa b/ipa index 9fb302017..39ab81a33 100755 --- a/ipa +++ b/ipa @@ -31,5 +31,7 @@ from ipalib.cli import CLI api.load_plugins() if __name__ == '__main__': - cli = CLI(api) + cli = CLI(api, + (s.decode('utf-8') for s in sys.args[1:]) + ) sys.exit(cli.run()) diff --git a/ipalib/cli.py b/ipalib/cli.py index a802f8ef0..21b02299e 100644 --- a/ipalib/cli.py +++ b/ipalib/cli.py @@ -84,6 +84,24 @@ class help(frontend.Application): print 'Purpose: %s' % cmd.doc self.application.build_parser(cmd).print_help() + def print_commands(self): + std = set(self.api.Command) - set(self.api.Application) + print '\nStandard IPA commands:' + for key in sorted(std): + cmd = self.api.Command[key] + self.print_cmd(cmd) + print '\nSpecial CLI commands:' + for cmd in self.api.Application(): + self.print_cmd(cmd) + print '\nUse the --help option to see all the global options' + print '' + + def print_cmd(self, cmd): + print ' %s %s' % ( + to_cli(cmd.name).ljust(self.mcl), + cmd.doc, + ) + class console(frontend.Application): 'Start the IPA interactive Python console.' @@ -207,32 +225,24 @@ class CLI(object): __d = None __mcl = None - def __init__(self, api): - self.__api = api - self.__all_interactive = False - self.__not_interactive = False + def __init__(self, api, argv): + self.api = api + self.argv = tuple(argv) + self.__done = set() - def __get_api(self): - return self.__api - api = property(__get_api) + def __doing(self, name): + if name in self.__done: + raise StandardError( + '%s.%s() already called' % (self.__class__.__name__, name) + ) + self.__done.add(name) - def print_commands(self): - std = set(self.api.Command) - set(self.api.Application) - print '\nStandard IPA commands:' - for key in sorted(std): - cmd = self.api.Command[key] - self.print_cmd(cmd) - print '\nSpecial CLI commands:' - for cmd in self.api.Application(): - self.print_cmd(cmd) - print '\nUse the --help option to see all the global options' - print '' + def __do_if_not_done(self, name): + if name not in self.__done: + getattr(self, name)() - def print_cmd(self, cmd): - print ' %s %s' % ( - to_cli(cmd.name).ljust(self.mcl), - cmd.doc, - ) + def isdone(self, name): + return name in self.__done def __contains__(self, key): assert self.__d is not None, 'you must call finalize() first' @@ -360,10 +370,11 @@ class CLI(object): parser.add_option(o) return parser - def parse_globals(self, argv=sys.argv[1:]): + def parse_globals(self): + self.__doing('parse_globals') parser = optparse.OptionParser() parser.disable_interspersed_args() - parser.add_option('-a', dest='interactive', action='store_true', + parser.add_option('-a', dest='prompt_all', action='store_true', help='Prompt for all missing options interactively') parser.add_option('-n', dest='interactive', action='store_false', help='Don\'t prompt for any options interactively') @@ -373,29 +384,39 @@ class CLI(object): help='Specify or override environment variables') parser.add_option('-v', dest='verbose', action='store_true', help='Verbose output') - (options, args) = parser.parse_args(argv) + parser.set_defaults( + prompt_all=False, + interactive=True, + verbose=False, + ) + (options, args) = parser.parse_args(list(self.argv)) + self.options = options + self.cmd_argv = tuple(args) - if options.interactive == True: - self.__all_interactive = True - elif options.interactive == False: - self.__not_interactive = True - if options.verbose != None: - self.api.env.verbose = True - if options.environment: - env_dict = {} - for a in options.environment.split(','): - a = a.split('=', 1) - if len(a) < 2: - parser.error('badly specified environment string,'\ - 'use var1=val1[,var2=val2]..') - env_dict[a[0].strip()] = a[1].strip() - self.api.env.update(env_dict, True) - if options.config_file: - self.api.env.update(read_config(options.config_file), True) - else: - self.api.env.update(read_config(), True) + def bootstrap(self): + pass - return args +# if options.interactive == True: +# self.__all_interactive = True +# elif options.interactive == False: +# self.__not_interactive = True +# if options.verbose != None: +# self.api.env.verbose = True +# if options.environment: +# env_dict = {} +# for a in options.environment.split(','): +# a = a.split('=', 1) +# if len(a) < 2: +# parser.error('badly specified environment string,'\ +# 'use var1=val1[,var2=val2]..') +# env_dict[a[0].strip()] = a[1].strip() +# self.api.env.update(env_dict, True) +# if options.config_file: +# self.api.env.update(read_config(options.config_file), True) +# else: +# self.api.env.update(read_config(), True) + +# return args def get_usage(self, cmd): return ' '.join(self.get_usage_iter(cmd)) diff --git a/tests/test_ipalib/test_cli.py b/tests/test_ipalib/test_cli.py index 50bfb932f..10f239897 100644 --- a/tests/test_ipalib/test_cli.py +++ b/tests/test_ipalib/test_cli.py @@ -22,7 +22,7 @@ Test the `ipalib.cli` module. """ from tests.util import raises, getitem, no_set, no_del, read_only, ClassChecker -from ipalib import cli, plugable +from ipalib import cli, plugable, frontend, backend def test_to_cli(): @@ -81,60 +81,61 @@ class test_CLI(ClassChecker): """ _cls = cli.CLI - def test_class(self): - """ - Test the `ipalib.cli.CLI` class. - """ - assert type(self.cls.api) is property - - def test_api(self): - """ - Test the `ipalib.cli.CLI.api` property. - """ - api = 'the plugable.API instance' - o = self.cls(api) - assert read_only(o, 'api') is api - - def dont_parse(self): - """ - Test the `ipalib.cli.CLI.parse` method. - """ - o = self.cls(None) - args = ['hello', 'naughty', 'nurse'] - kw = dict( - first_name='Naughty', - last_name='Nurse', + def new(self, argv): + api = plugable.API( + frontend.Command, + frontend.Object, + frontend.Method, + frontend.Property, + frontend.Application, + backend.Backend, ) - opts = ['--%s=%s' % (k.replace('_', '-'), v) for (k, v) in kw.items()] - assert o.parse(args + []) == (args, {}) - assert o.parse(opts + []) == ([], kw) - assert o.parse(args + opts) == (args, kw) - assert o.parse(opts + args) == (args, kw) + o = self.cls(api, argv) + assert o.api is api + return o - def test_mcl(self): + def test_init(self): """ - Test the `ipalib.cli.CLI.mcl` property . + Test the `ipalib.cli.CLI.__init__` method. """ - cnt = 100 - api = DummyAPI(cnt) - len(api.Command) == cnt - o = self.cls(api) - assert o.mcl is None - o.build_map() - assert o.mcl == 6 # len('cmd_99') + argv = ['-v', 'user-add', '--first=Jonh', '--last=Doe'] + o = self.new(argv) + assert type(o.api) is plugable.API + assert o.argv == tuple(argv) - def test_dict(self): + def test_parse_globals(self): """ - Test container emulation of `ipalib.cli.CLI` class. + Test the `ipalib.cli.CLI.parse_globals` method. """ - cnt = 25 - api = DummyAPI(cnt) - assert len(api.Command) == cnt - o = self.cls(api) - o.build_map() - for cmd in api.Command(): - key = cli.to_cli(cmd.name) - assert key in o - assert o[key] is cmd - assert cmd.name not in o - raises(KeyError, getitem, o, cmd.name) + # Test with empty argv + o = self.new([]) + assert not hasattr(o, 'options') + assert not hasattr(o, 'cmd_argv') + assert o.isdone('parse_globals') is False + o.parse_globals() + assert o.isdone('parse_globals') is True + assert o.options.interactive is True + assert o.options.verbose is False + assert o.options.config_file is None + assert o.options.environment is None + assert o.cmd_argv == tuple() + e = raises(StandardError, o.parse_globals) + assert str(e) == 'CLI.parse_globals() already called' + + # Test with a populated argv + argv = ('-a', '-n', '-v', '-c', '/my/config.conf', '-e', 'my_key=my_val') + cmd_argv = ('user-add', '--first', 'John', '--last', 'Doe') + o = self.new(argv + cmd_argv) + assert not hasattr(o, 'options') + assert not hasattr(o, 'cmd_argv') + assert o.isdone('parse_globals') is False + o.parse_globals() + assert o.isdone('parse_globals') is True + assert o.options.prompt_all is True + assert o.options.interactive is False + assert o.options.verbose is True + assert o.options.config_file == '/my/config.conf' + assert o.options.environment == 'my_key=my_val' + assert o.cmd_argv == cmd_argv + e = raises(StandardError, o.parse_globals) + assert str(e) == 'CLI.parse_globals() already called'