def run_commands(args): from knack.cli import CLI from azure.cli.core import MainCommandsLoader, logger from azure.cli.core._config import GLOBAL_CONFIG_DIR, ENV_VAR_PREFIX from azure.cli.core.cloud import get_active_cloud class MockCLI(CLI): def __init__(self): super(MockCLI, self).__init__(cli_name='mock_cli', config_dir=GLOBAL_CONFIG_DIR, config_env_var_prefix=ENV_VAR_PREFIX) self.cloud = get_active_cloud(self) logger.addHandler(logging.NullHandler()) loader = MainCommandsLoader(MockCLI()) table = loader.load_command_table('') if args.prefix: table = dict((each, table[each]) for each in table if each.startswith(args.prefix)) if args.list_only: for each in sorted(table): if args.details: print_command_info(table[each]) else: print(each) sys.exit(0) commands = ['az {} --help'.format(each) for each in table] results = [] pool = multiprocessing.Pool(multiprocessing.cpu_count()) for i, res in enumerate( pool.imap_unordered(run_single_command, commands, 10), 1): sys.stderr.write('{:.2f}% \n'.format(float(i) * 100 / len(commands))) sys.stderr.flush() results.append(res) pool.close() pool.join() if not all(results): sys.stderr.write('Error running --help on commands.\n') sys.exit(1) print('Done')
def run_commands(args): from knack.cli import CLI from azure.cli.core import MainCommandsLoader, logger from azure.cli.core._config import GLOBAL_CONFIG_DIR, ENV_VAR_PREFIX from azure.cli.core.cloud import get_active_cloud class MockCLI(CLI): def __init__(self): super(MockCLI, self).__init__( cli_name='mock_cli', config_dir=GLOBAL_CONFIG_DIR, config_env_var_prefix=ENV_VAR_PREFIX) self.cloud = get_active_cloud(self) logger.addHandler(logging.NullHandler()) loader = MainCommandsLoader(MockCLI()) table = loader.load_command_table('') if args.prefix: table = dict((each, table[each]) for each in table if each.startswith(args.prefix)) if args.list_only: for each in sorted(table): if args.details: print_command_info(table[each]) else: print(each) sys.exit(0) commands = ['az {} --help'.format(each) for each in table] results = [] pool = multiprocessing.Pool(multiprocessing.cpu_count()) for i, res in enumerate(pool.imap_unordered(run_single_command, commands, 10), 1): sys.stderr.write('{:.2f}% \n'.format(float(i) * 100 / len(commands))) sys.stderr.flush() results.append(res) pool.close() pool.join() if not all(results): sys.stderr.write('Error running --help on commands.\n') sys.exit(1) print('Done')
def test_parser_error_spellchecker(self): cli = DummyCli() main_loader = MainCommandsLoader(cli) cli.loader = main_loader cli.loader.load_command_table(None) parser = cli.parser_cls(cli) parser.load_command_table(cli.loader) logger_msgs = [] choice_lists = [] original_get_close_matches = difflib.get_close_matches def mock_log_error(_, msg): logger_msgs.append(msg) def mock_get_close_matches(*args, **kwargs): choice_lists.append(original_get_close_matches(*args, **kwargs)) # run multiple faulty commands and save error logs, as well as close matches with mock.patch('logging.Logger.error', mock_log_error), \ mock.patch('difflib.get_close_matches', mock_get_close_matches): faulty_cmd_args = [ 'test module1 --opt enum_1', 'test extension1 --opt enum_1', 'test foo_bar --opt enum_3', 'test module --opt enum_3', 'test extension --opt enum_3' ] for text in faulty_cmd_args: with self.assertRaises(SystemExit): parser.parse_args(text.split()) parser.parse_args('test module --opt enum_1'.split()) # assert the right type of error msg is logged for command vs argument parsing self.assertEqual(len(logger_msgs), 5) for msg in logger_msgs[:3]: self.assertIn("not in the", msg) self.assertIn("command group", msg) for msg in logger_msgs[3:]: self.assertIn("not a valid value for '--opt'.", msg) # assert the right choices are matched as "close". # If these don't hold, matching algorithm should be deemed flawed. for choices in choice_lists[:2]: self.assertEqual(len(choices), 1) self.assertEqual(len(choice_lists[2]), 0) for choices in choice_lists[3:]: self.assertEqual(len(choices), 2) for choice in ['enum_1', 'enum_2']: self.assertIn(choice, choices)
def test_parser_failure_recovery_recommendations(self): cli = DummyCli() main_loader = MainCommandsLoader(cli) cli.loader = main_loader cli.loader.load_command_table(None) parser = cli.parser_cls(cli) parser.load_command_table(cli.loader) recommendation_provider_parameters = [] version = cli.get_cli_version() expected_recommendation_provider_parameters = [ # version, command, parameters, extension ExpectedParameters(version, 'test module1', ['--opt'], False), ExpectedParameters(version, 'test extension1', ['--opt'], False), ExpectedParameters(version, 'foo_bar', ['--opt'], False), ExpectedParameters(version, 'test module', ['--opt'], False), ExpectedParameters(version, 'test extension', ['--opt'], True) ] def mock_recommendation_provider(*args): recommendation_provider_parameters.append(tuple(args)) return [] AzCliCommandParser.recommendation_provider = mock_recommendation_provider faulty_cmd_args = [ 'test module1 --opt enum_1', 'test extension1 --opt enum_1', 'test foo_bar --opt enum_3', 'test module --opt enum_3', 'test extension --opt enum_3' ] for text in faulty_cmd_args: with self.assertRaises(SystemExit): parser.parse_args(text.split()) for i, parameters in enumerate(recommendation_provider_parameters): version, command, parameters, extension = parameters expected = expected_recommendation_provider_parameters[i] self.assertEqual(expected.version, version) self.assertIn(expected.command, command) self.assertEqual(expected.parameters, parameters) if expected.has_extension: self.assertIsNotNone(extension) else: self.assertIsNone(extension)
def test_register_command_from_extension(self): from azure.cli.core.commands import _load_command_loader cli = DummyCli() main_loader = MainCommandsLoader(cli) cli.loader = main_loader cmd_tbl = cli.loader.load_command_table(None) ext1 = cmd_tbl['hello noodle'] ext2 = cmd_tbl['hello world'] self.assertTrue(isinstance(ext1.command_source, ExtensionCommandSource)) self.assertFalse(ext1.command_source.overrides_command) self.assertTrue(isinstance(ext2.command_source, ExtensionCommandSource)) self.assertTrue(ext2.command_source.overrides_command)
def test_parser_error_spellchecker(self): cli = DummyCli() main_loader = MainCommandsLoader(cli) cli.loader = main_loader cli.loader.load_command_table(None) parser = cli.parser_cls(cli) parser.load_command_table(cli.loader) logger_msgs = [] choice_lists = [] original_get_close_matches = difflib.get_close_matches def mock_log_error(_, msg): logger_msgs.append(msg) def mock_get_close_matches(*args, **kwargs): choice_lists.append(original_get_close_matches(*args, **kwargs)) def mock_ext_cmd_tree_load(*args, **kwargs): return { "test": { "new-ext": { "create": "new-ext-name", "reset": "another-ext-name" } } } def mock_add_extension(*args, **kwargs): pass # run multiple faulty commands and save error logs, as well as close matches with mock.patch('logging.Logger.error', mock_log_error), \ mock.patch('difflib.get_close_matches', mock_get_close_matches): faulty_cmd_args = [ 'test module1 --opt enum_1', 'test extension1 --opt enum_1', 'test foo_bar --opt enum_3', 'test module --opt enum_3', 'test extension --opt enum_3' ] for text in faulty_cmd_args: with self.assertRaises(SystemExit): parser.parse_args(text.split()) parser.parse_args('test module --opt enum_1'.split()) # assert the right type of error msg is logged for command vs argument parsing self.assertEqual(len(logger_msgs), 5) for msg in logger_msgs[:3]: self.assertIn("not in the", msg) self.assertIn("command group", msg) for msg in logger_msgs[3:]: self.assertIn("not a valid value for '--opt'.", msg) # assert the right choices are matched as "close". # If these don't hold, matching algorithm should be deemed flawed. for choices in choice_lists[:2]: self.assertEqual(len(choices), 1) self.assertEqual(len(choice_lists[2]), 0) for choices in choice_lists[3:]: self.assertEqual(len(choices), 2) for choice in ['enum_1', 'enum_2']: self.assertIn(choice, choices) # test dynamic extension install with mock.patch('logging.Logger.error', mock_log_error), \ mock.patch('azure.cli.core.extension.operations.add_extension', mock_add_extension), \ mock.patch('azure.cli.core.parser.AzCliCommandParser._get_extension_command_tree', mock_ext_cmd_tree_load), \ mock.patch('azure.cli.core.parser.AzCliCommandParser._get_extension_use_dynamic_install_config', return_value='yes_without_prompt'): with self.assertRaises(SystemExit): parser.parse_args('test new-ext create --opt enum_2'.split()) self.assertIn( "Extension new-ext-name installed. Please rerun your command.", logger_msgs[5]) with self.assertRaises(SystemExit): parser.parse_args('test new-ext reset pos1 pos2'.split() ) # test positional args self.assertIn( "Extension another-ext-name installed. Please rerun your command.", logger_msgs[6])
def build_command_table(cli_ctx): from azure.cli.core import MainCommandsLoader cmd_table = MainCommandsLoader(cli_ctx).load_command_table(None) for command in cmd_table: cmd_table[command].load_arguments() data = {} for command in cmd_table: com_descip = {} param_descrip = {} com_descip['short-summary'] = cmd_table[command].description() \ if callable(cmd_table[command].description) \ else cmd_table[command].description or '' com_descip['examples'] = '' for key in cmd_table[command].arguments: required = '' help_desc = '' if cmd_table[command].arguments[key].type.settings.get('required'): required = '[REQUIRED]' if cmd_table[command].arguments[key].type.settings.get('help'): help_desc = cmd_table[command].arguments[ key].type.settings.get('help') name_options = [] for name in cmd_table[command].arguments[key].options_list: name_options.append(name) options = { 'name': name_options, 'required': required, 'help': help_desc } param_descrip[ cmd_table[command].arguments[key].options_list[0]] = options com_descip['parameters'] = param_descrip data[command] = com_descip for command in helps: diction_help = yaml.load(helps[command]) if command not in data: data[command] = { 'short-summary': diction_help.get('short-summary', ''), 'long-summary': diction_help.get('long-summary', ''), 'parameters': {} } else: data[command]['short-summary'] = diction_help.get( 'short-summary', data[command].get('short-summary', '')) data[command]['long-summary'] = diction_help.get( "long-summary", '') data[command]['parameters'] = {} if 'parameters' in diction_help: for param in diction_help["parameters"]: if param['name'].split()[0] not in data[command]['parameters']: options = { 'name': name_options, 'required': required, 'help': help_desc } data[command]['parameters'] = { param["name"].split()[0]: options } if 'short-summary' in param: data[command]['parameters'][param['name'].split()[0]]['help'] \ = param['short-summary'] if 'examples' in diction_help: string_example = '' for example in diction_help['examples']: string_example += example.get('name', '') + '\n' + example.get( 'text', '') + '\n' data[command]['examples'] = string_example return data