def test_register_simple_commands(self): def test_handler1(): pass def test_handler2(): pass command = CLICommand(self.mock_ctx, 'command the-name', test_handler1) command2 = CLICommand(self.mock_ctx, 'sub-command the-second-name', test_handler2) cmd_table = { 'command the-name': command, 'sub-command the-second-name': command2 } parser = CLICommandParser() parser.load_command_table(cmd_table) args = parser.parse_args('command the-name'.split()) self.assertIs(args.func, command) args = parser.parse_args('sub-command the-second-name'.split()) self.assertIs(args.func, command2) CLICommandParser.error = VerifyError(self, ) parser.parse_args('sub-command'.split()) self.assertTrue(CLICommandParser.error.called)
def test_case_insensitive_command_path(self): def handler(_): return 'PASSED' command = CLICommand(self.mock_ctx, 'test command', handler) command.add_argument('var', '--var', '-v') cmd_table = {'test command': command} def _test(cmd_line): ci = CommandInvoker(cli_ctx=self.mock_ctx) self.mock_ctx.invocation = ci self.mock_ctx.invocation.commands_loader.command_table = cmd_table return self.mock_ctx.invocation.execute(cmd_line.split()) # case insensitive command paths result = _test('TEST command --var blah') self.assertEqual(result.result, 'PASSED') result = _test('test COMMAND --var blah') self.assertEqual(result.result, 'PASSED') result = _test('test command -v blah') self.assertEqual(result.result, 'PASSED') # verify that long and short options remain case sensitive with mock.patch('sys.stderr', new_callable=StringIO): with self.assertRaises(SystemExit): _test('test command --vAR blah') with self.assertRaises(SystemExit): _test('test command -V blah')
def test_prefix_file_expansion(self): import json, os def test_handler(): pass def create_test_file(file, contents): with open(file, 'w') as f: f.write(contents) def remove_test_file(file): os.remove(file) json_test_data = json.dumps({'one': 1, 'two': 2, 'three': 3}) create_test_file('test.json', json_test_data) command = CLICommand(self.mock_ctx, 'test command', test_handler) command.add_argument('json_data', '--param') cmd_table = {'test command': command} self.mock_ctx.commands_loader.command_table = cmd_table parser = CLICommandParser() parser.load_command_table(self.mock_ctx.commands_loader) args = parser.parse_args('test command --param @test.json'.split()) self.assertEqual(json_test_data, args.json_data) remove_test_file('test.json')
def test_help_extra_missing_params(self): def test_handler(foobar2, foobar=None): # pylint: disable=unused-argument pass command = CLICommand(self.mock_ctx, 'n1', test_handler) command.add_argument('foobar', '--foobar', '-fb', required=False) command.add_argument('foobar2', '--foobar2', '-fb2', required=True) cmd_table = {'n1': command} with mock.patch.object(CLICommandsLoader, 'load_command_table', return_value=cmd_table): # work around an argparse behavior where output is not printed and SystemExit # is not raised on Python 2.7.9 if sys.version_info < (2, 7, 10): try: self.mock_ctx.invoke('n1 -fb a --foobar value'.split()) except SystemExit: pass try: self.mock_ctx.invoke('n1 -fb a --foobar2 value --foobar3 extra'.split()) except SystemExit: pass else: with self.assertRaises(SystemExit): self.mock_ctx.invoke('n1 -fb a --foobar value'.split()) with self.assertRaises(SystemExit): self.mock_ctx.invoke('n1 -fb a --foobar2 value --foobar3 extra'.split()) self.assertTrue('required' in io.getvalue() and '--foobar/-fb' not in io.getvalue() and '--foobar2/-fb2' in io.getvalue() and 'unrecognized arguments: --foobar3 extra' in io.getvalue())
def test_help_with_param_specified(self, _): def test_handler(): pass self.mock_ctx = MockContext() command = CLICommand(self.mock_ctx, 'n1', test_handler) command.add_argument('arg', '--arg', '-a', required=False) command.add_argument('b', '-b', required=False) cmd_table = {'n1': command} with mock.patch.object(CLICommandsLoader, 'load_command_table', return_value=cmd_table): with self.assertRaises(SystemExit): self.mock_ctx.invoke('n1 --arg foo -h'.split()) s = """ Command {} n1 Arguments --arg -a -b Global Arguments --help -h: Show this help message and exit. """ self.assertEqual(s.format(self.cliname), io.getvalue())
def test_case_insensitive_enum_choices(self): from enum import Enum class TestEnum(Enum): # pylint: disable=too-few-public-methods opt1 = "ALL_CAPS" opt2 = "camelCase" opt3 = "snake_case" def test_handler(): pass command = CLICommand(self.mock_ctx, 'test command', test_handler) command.add_argument('opt', '--opt', required=True, **enum_choice_list(TestEnum)) cmd_table = {'test command': command} parser = CLICommandParser() parser.load_command_table(cmd_table) args = parser.parse_args('test command --opt alL_cAps'.split()) self.assertEqual(args.opt, 'ALL_CAPS') args = parser.parse_args('test command --opt CAMELCASE'.split()) self.assertEqual(args.opt, 'camelCase') args = parser.parse_args('test command --opt sNake_CASE'.split()) self.assertEqual(args.opt, 'snake_case')
def test_help_full_documentations(self, _): def test_handler(): pass self.mock_ctx = MockContext() command = CLICommand(self.mock_ctx, 'n1', test_handler) command.add_argument('foobar', '--foobar', '-fb', required=False) command.add_argument('foobar2', '--foobar2', '-fb2', required=True) command.help = """ short-summary: this module does xyz one-line or so long-summary: | this module.... kjsdflkj... klsfkj paragraph1 this module.... kjsdflkj... klsfkj paragraph2 parameters: - name: --foobar -fb type: string required: false short-summary: one line partial sentence long-summary: text, markdown, etc. populator-commands: - mycli abc xyz - default - name: --foobar2 -fb2 type: string required: true short-summary: one line partial sentence long-summary: paragraph(s) examples: - name: foo example text: example details """ cmd_table = {'n1': command} with mock.patch.object(CLICommandsLoader, 'load_command_table', return_value=cmd_table): with self.assertRaises(SystemExit): self.mock_ctx.invoke('n1 -h'.split()) s = """ Command {} n1: This module does xyz one-line or so. This module.... kjsdflkj... klsfkj paragraph1 this module.... kjsdflkj... klsfkj paragraph2. Arguments --foobar2 -fb2 [Required]: One line partial sentence. Paragraph(s). --foobar -fb : One line partial sentence. Values from: mycli abc xyz, default. Text, markdown, etc. Global Arguments --help -h : Show this help message and exit. Examples foo example example details """ self.assertEqual(s.format(self.cliname), io.getvalue())
def test_choice_list_with_ints(self): def test_handler(): pass command = CLICommand(self.mock_ctx, 'n1', test_handler) command.add_argument('arg', '--arg', '-a', required=False, choices=[1, 2, 3]) command.add_argument('b', '-b', required=False, choices=['a', 'b', 'c']) cmd_table = {'n1': command} with mock.patch.object(CLICommandsLoader, 'load_command_table', return_value=cmd_table): with self.assertRaises(SystemExit): self.mock_ctx.invoke('n1 -h'.split())
def test_help_group_children(self): def test_handler(): pass def test_handler2(): pass command = CLICommand(self.mock_ctx, 'group1 group3 n1', test_handler) command.add_argument('foobar', '--foobar', '-fb', required=False) command.add_argument('foobar2', '--foobar2', '-fb2', required=True) command2 = CLICommand(self.mock_ctx, 'group1 group2 n1', test_handler2) command2.add_argument('foobar', '--foobar', '-fb', required=False) command2.add_argument('foobar2', '--foobar2', '-fb2', required=True) cmd_table = {'group1 group3 n1': command, 'group1 group2 n1': command2} with mock.patch.object(CLICommandsLoader, 'load_command_table', return_value=cmd_table): with self.assertRaises(SystemExit): self.mock_ctx.invoke('group1 -h'.split()) s = '\nGroup\n {} group1\n\nSubgroups:\n group2\n group3\n\n'.format(self.cliname) self.assertEqual(s, io.getvalue())
def test_help_plain_short_description(self): def test_handler(): pass command = CLICommand(self.mock_ctx, 'n1', test_handler, description='the description') command.add_argument('arg', '--arg', '-a', required=False) command.add_argument('b', '-b', required=False) cmd_table = {'n1': command} with mock.patch.object(CLICommandsLoader, 'load_command_table', return_value=cmd_table): with self.assertRaises(SystemExit): self.mock_ctx.invoke('n1 -h'.split()) self.assertTrue('n1: The description.' in io.getvalue())
def test_list_value_parameter(self): handler_args = {} def handler(args): handler_args.update(args) command = CLICommand(self.mock_ctx, 'test command', handler) command.add_argument('hello', '--hello', nargs='+') command.add_argument('something', '--something') cmd_table = {'test command': command} with mock.patch.object(CLICommandsLoader, 'load_command_table', return_value=cmd_table): self.mock_ctx.invoke('test command --hello world sir --something else'.split()) self.assertEqual(handler_args['something'], 'else') self.assertEqual(handler_args['hello'][0], 'world') self.assertEqual(handler_args['hello'][1], 'sir')
def test_help_long_description_and_short_description(self): def test_handler(): pass command = CLICommand(self.mock_ctx, 'n1', test_handler, description='short description') command.add_argument('arg', '--arg', '-a', required=False) command.add_argument('b', '-b', required=False) command.help = 'long description' cmd_table = {'n1': command} with mock.patch.object(CLICommandsLoader, 'load_command_table', return_value=cmd_table): with self.assertRaises(SystemExit): self.mock_ctx.invoke('n1 -h'.split()) self.assertTrue(io.getvalue().startswith('\nCommand\n {} n1: Short description.\n Long description.'.format(self.cliname))) # pylint: disable=line-too-long
def test_help_param(self): def test_handler(): pass command = CLICommand(self.mock_ctx, 'n1', test_handler) command.add_argument('arg', '--arg', '-a', required=False) command.add_argument('b', '-b', required=False) cmd_table = {'n1': command} with mock.patch.object(CLICommandsLoader, 'load_command_table', return_value=cmd_table): with self.assertRaises(SystemExit): self.mock_ctx.invoke('n1 -h'.split()) with self.assertRaises(SystemExit): self.mock_ctx.invoke('n1 --help'.split())
def test_extra_nonargparse_parameters(self): """ Add argument that has non argparse parameters. 'mycustomarg' should be filtered out and load_command_table should complete successfully instead of throwing TypeError: __init__() got an unexpected keyword argument 'mycustomarg' """ def test_handler(): pass command = CLICommand(self.mock_ctx, 'test command', test_handler) command.add_argument('req', '--req', required=True, mycustomarg=True) cmd_table = {'test command': command} parser = CLICommandParser() parser.load_command_table(cmd_table)
def test_list_value_parameter(self): handler_args = {} def handler(args): handler_args.update(args) command = CLICommand(self.mock_ctx, 'test command', handler) command.add_argument('hello', '--hello', nargs='+') command.add_argument('something', '--something') cmd_table = {'test command': command} self.invoke_with_command_table( 'test command --hello world sir --something else', cmd_table) self.assertEqual(handler_args['something'], 'else') self.assertEqual(handler_args['hello'][0], 'world') self.assertEqual(handler_args['hello'][1], 'sir')
def test_nargs_parameter(self): def test_handler(): pass command = CLICommand(self.mock_ctx, 'test command', test_handler) command.add_argument('req', '--req', required=True, nargs=2) cmd_table = {'test command': command} parser = CLICommandParser() parser.load_command_table(cmd_table) args = parser.parse_args('test command --req yep nope'.split()) self.assertIs(args.func, command) CLICommandParser.error = VerifyError(self) parser.parse_args('test command -req yep'.split()) self.assertTrue(CLICommandParser.error.called)
def test_help_params_documentations(self, _): def test_handler(): pass self.mock_ctx = MockContext() command = CLICommand(self.mock_ctx, 'n1', test_handler) command.add_argument('foobar', '--foobar', '-fb', required=False) command.add_argument('foobar2', '--foobar2', '-fb2', required=True) command.add_argument('foobar3', '--foobar3', '-fb3', required=False, help='the foobar3') command.help = """ parameters: - name: --foobar -fb type: string required: false short-summary: one line partial sentence long-summary: text, markdown, etc. populator-commands: - mycli abc xyz - default - name: --foobar2 -fb2 type: string required: true short-summary: one line partial sentence long-summary: paragraph(s) """ cmd_table = {'n1': command} with mock.patch.object(CLICommandsLoader, 'load_command_table', return_value=cmd_table): with self.assertRaises(SystemExit): self.mock_ctx.invoke('n1 -h'.split()) s = """ Command {} n1 Arguments --foobar2 -fb2 [Required]: One line partial sentence. Paragraph(s). --foobar -fb : One line partial sentence. Values from: mycli abc xyz, default. Text, markdown, etc. --foobar3 -fb3 : The foobar3. Global Arguments --help -h : Show this help message and exit. """ self.assertEqual(s.format(self.cliname), io.getvalue())
def test_required_parameter(self): def test_handler(args): # pylint: disable=unused-argument pass command = CLICommand(self.mock_ctx, 'test command', test_handler) command.add_argument('req', '--req', required=True) cmd_table = {'test command': command} self.mock_ctx.commands_loader.command_table = cmd_table parser = CLICommandParser() parser.load_command_table(self.mock_ctx.commands_loader) args = parser.parse_args('test command --req yep'.split()) self.assertIs(args.func, command) CLICommandParser.error = VerifyError(self) parser.parse_args('test command'.split()) self.assertTrue(CLICommandParser.error.called)
def test_help_global_params(self, _): def register_globals(_, **kwargs): arg_group = kwargs.get('arg_group') arg_group.add_argument('--exampl', help='This is a new global argument.') self.mock_ctx = MockContext() self.mock_ctx._event_handlers[EVENT_PARSER_GLOBAL_CREATE].append(register_globals) # pylint: disable=protected-access def test_handler(): pass command = CLICommand(self.mock_ctx, 'n1', test_handler) command.add_argument('arg', '--arg', '-a', required=False) command.add_argument('b', '-b', required=False) command.help = """ long-summary: | line1 line2 """ cmd_table = {'n1': command} with mock.patch.object(CLICommandsLoader, 'load_command_table', return_value=cmd_table): with self.assertRaises(SystemExit): self.mock_ctx.invoke('n1 -h'.split()) s = """ Command {} n1 Line1 line2. Arguments --arg -a -b Global Arguments --exampl : This is a new global argument. --help -h: Show this help message and exit. """ self.assertEqual(s.format(self.cliname), io.getvalue())
def test_help_group_help(self): def test_handler(): pass from knack.help_files import helps helps['test_group1 test_group2'] = """ type: group short-summary: this module does xyz one-line or so long-summary: | this module.... kjsdflkj... klsfkj paragraph1 this module.... kjsdflkj... klsfkj paragraph2 examples: - name: foo example text: example details """ command = CLICommand(self.mock_ctx, 'test_group1 test_group2 n1', test_handler) command.add_argument('foobar', '--foobar', '-fb', required=False) command.add_argument('foobar2', '--foobar2', '-fb2', required=True) command.help = """ short-summary: this module does xyz one-line or so long-summary: | this module.... kjsdflkj... klsfkj paragraph1 this module.... kjsdflkj... klsfkj paragraph2 parameters: - name: --foobar -fb type: string required: false short-summary: one line partial sentence long-summary: text, markdown, etc. populator-commands: - mycli abc xyz - default - name: --foobar2 -fb2 type: string required: true short-summary: one line partial sentence long-summary: paragraph(s) examples: - name: foo example text: example details """ cmd_table = {'test_group1 test_group2 n1': command} with mock.patch.object(CLICommandsLoader, 'load_command_table', return_value=cmd_table): with self.assertRaises(SystemExit): self.mock_ctx.invoke('test_group1 test_group2 --help'.split()) s = """ Group {} test_group1 test_group2: This module does xyz one-line or so. This module.... kjsdflkj... klsfkj paragraph1 this module.... kjsdflkj... klsfkj paragraph2. Commands: n1: This module does xyz one-line or so. Examples foo example example details """ self.assertEqual(s.format(self.cliname), io.getvalue()) del helps['test_group1 test_group2']
def load_command_table(self, args): self.command_table['abc xyz'] = CLICommand( self.cli_ctx, 'abc xyz', a_test_command_handler) self.command_table['abc list'] = CLICommand( self.cli_ctx, 'abc list', a_test_command_handler) return OrderedDict(self.command_table)