def test_case_insensitive_command_path(self): import argparse def handler(args): return 'PASSED' command = CliCommand('test command', handler) command.add_argument('var', '--var', '-v') cmd_table = {'test command': command} def _test(cmd_line): argv = cmd_line.split() config = Configuration() config.get_command_table = lambda argv: cmd_table application = Application(config) return application.execute(argv[1:]) # case insensitive command paths result = _test('az TEST command --var blah') self.assertEqual(result.result, 'PASSED') result = _test('az test COMMAND --var blah') self.assertEqual(result.result, 'PASSED') result = _test('az test command -v blah') self.assertEqual(result.result, 'PASSED') # verify that long and short options remain case sensitive with self.assertRaises(SystemExit): _test('az test command --vAR blah') with self.assertRaises(SystemExit): _test('az test command -V blah')
def test_help_full_documentations(self, _): app = Application(Configuration()) def test_handler(): pass command = CliCommand('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: - az vm list - 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} config = Configuration() config.get_command_table = lambda args: cmd_table app = Application(config) with self.assertRaises(SystemExit): app.execute('n1 -h'.split()) s = """ Command az 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: az vm list, default. Text, markdown, etc. Global Arguments --help -h : Show this help message and exit. Examples foo example example details """ self.assertEqual(s, io.getvalue())
def test_help_with_param_specified(self, _): app = Application(Configuration([])) def test_handler(): pass command = CliCommand('n1', test_handler) command.add_argument('arg', '--arg', '-a', required=False) command.add_argument('b', '-b', required=False) cmd_table = {'n1': command} config = Configuration([]) config.get_command_table = lambda: cmd_table app = Application(config) with self.assertRaises(SystemExit): app.execute('n1 --arg foo -h'.split()) s = """ Command az n1 Arguments --arg -a -b Global Arguments --help -h: Show this help message and exit. """ self.assertEqual(s, 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('test command', test_handler) command.add_argument('opt', '--opt', required=True, **enum_choice_list(TestEnum)) cmd_table = {'test command': command} parser = AzCliCommandParser() 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_global_params(self, mock_register_extensions, _): def register_globals(global_group): global_group.add_argument( "--query2", dest="_jmespath_query", metavar="JMESPATH", help="JMESPath query string. See http://jmespath.org/ " "for more information and examples.", ) mock_register_extensions.return_value = None def _register_global_parser(appl): # noqa pylint: disable=protected-access appl._event_handlers[appl.GLOBAL_PARSER_CREATED].append(register_globals) mock_register_extensions.side_effect = _register_global_parser def test_handler(): pass command = CliCommand("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} config = Configuration([]) config.get_command_table = lambda: cmd_table app = Application(config) with self.assertRaises(SystemExit): app.execute("n1 -h".split()) s = """ Command az n1 Line1 line2. Arguments --arg -a -b Global Arguments --help -h: Show this help message and exit. --query2 : JMESPath query string. See http://jmespath.org/ for more information and examples. """ self.assertEqual(s, io.getvalue())
def test_help_group_children(self): app = Application(Configuration([])) def test_handler(): pass def test_handler2(): pass command = CliCommand('group1 group3 n1', test_handler) command.add_argument('foobar', '--foobar', '-fb', required=False) command.add_argument('foobar2', '--foobar2', '-fb2', required=True) command2 = CliCommand('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} config = Configuration([]) config.get_command_table = lambda: cmd_table app = Application(config) with self.assertRaises(SystemExit): app.execute('group1 -h'.split()) s = '\nGroup\n az group1\n\nSubgroups:\n group2\n group3\n\n' self.assertEqual(s, io.getvalue())
def test_choice_list_with_ints(self): def test_handler(): pass command = CliCommand('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} config = Configuration([]) config.get_command_table = lambda: cmd_table app = Application() app.initialize(config) with self.assertRaises(SystemExit): app.execute('n1 -h'.split())
def test_choice_list_with_ints(self): def test_handler(): pass command = CliCommand("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} config = Configuration([]) config.get_command_table = lambda: cmd_table app = Application() app.initialize(config) with self.assertRaises(SystemExit): app.execute("n1 -h".split())
def test_help_plain_short_description(self): def test_handler(): pass command = CliCommand('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} config = Configuration([]) config.get_command_table = lambda: cmd_table app = Application(config) with self.assertRaises(SystemExit): app.execute('n1 -h'.split()) self.assertEqual(True, 'n1: The description.' in io.getvalue())
def test_help_plain_short_description(self): def test_handler(): pass command = CliCommand("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} config = Configuration([]) config.get_command_table = lambda: cmd_table app = Application(config) with self.assertRaises(SystemExit): app.execute("n1 -h".split()) self.assertEqual(True, "n1: The description." in io.getvalue())
def test_help_docstring_description_overrides_short_description(self): def test_handler(): pass command = CliCommand('n1', test_handler, description='short description') command.add_argument('arg', '--arg', '-a', required=False) command.add_argument('b', '-b', required=False) command.help = 'short-summary: docstring summary' cmd_table = {'n1': command} config = Configuration() config.get_command_table = lambda args: cmd_table app = Application(config) with self.assertRaises(SystemExit): app.execute('n1 -h'.split()) self.assertEqual(True, 'n1: Docstring summary.' in io.getvalue())
def test_help_long_description_and_short_description(self): def test_handler(): pass command = CliCommand('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} config = Configuration([]) config.get_command_table = lambda: cmd_table app = Application(config) with self.assertRaises(SystemExit): app.execute('n1 -h'.split()) self.assertEqual(True, io.getvalue().startswith('\nCommand\n az n1: Short description.\n Long description.')) # pylint: disable=line-too-long
def test_required_parameter(self): def test_handler(args): # pylint: disable=unused-argument pass command = CliCommand('test command', test_handler) command.add_argument('req', '--req', required=True) cmd_table = {'test command': command} parser = AzCliCommandParser() parser.load_command_table(cmd_table) args = parser.parse_args('test command --req yep'.split()) self.assertIs(args.func, test_handler) AzCliCommandParser.error = VerifyError(self) parser.parse_args('test command'.split()) self.assertTrue(AzCliCommandParser.error.called)
def test_help_global_params(self, mock_register_extensions, _): def register_globals(global_group): global_group.add_argument('--query2', dest='_jmespath_query', metavar='JMESPATH', help='JMESPath query string. See http://jmespath.org/ ' 'for more information and examples.') mock_register_extensions.return_value = None mock_register_extensions.side_effect = lambda app: \ app._event_handlers[app.GLOBAL_PARSER_CREATED].append(register_globals) # pylint: disable=protected-access def test_handler(): pass command = CliCommand('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} config = Configuration([]) config.get_command_table = lambda: cmd_table app = Application(config) with self.assertRaises(SystemExit): app.execute('n1 -h'.split()) s = """ Command az n1 Line1 line2. Arguments --arg -a -b Global Arguments --help -h: Show this help message and exit. --query2 : JMESPath query string. See http://jmespath.org/ for more information and examples. """ self.assertEqual(s, io.getvalue())
def test_nargs_parameter(self): def test_handler(): pass command = CliCommand('test command', test_handler) command.add_argument('req', '--req', required=True, nargs=2) cmd_table = {'test command': command} parser = AzCliCommandParser() parser.load_command_table(cmd_table) args = parser.parse_args('test command --req yep nope'.split()) self.assertIs(args.func, command) AzCliCommandParser.error = VerifyError(self) parser.parse_args('test command -req yep'.split()) self.assertTrue(AzCliCommandParser.error.called)
def test_help_param(self): def test_handler(): pass command = CliCommand("n1", test_handler) command.add_argument("arg", "--arg", "-a", required=False) command.add_argument("b", "-b", required=False) cmd_table = {"n1": command} config = Configuration([]) config.get_command_table = lambda: cmd_table app = Application() app.initialize(config) with self.assertRaises(SystemExit): app.execute("n1 -h".split()) with self.assertRaises(SystemExit): app.execute("n1 --help".split())
def test_help_param(self): def test_handler(): pass command = CliCommand('n1', test_handler) command.add_argument('arg', '--arg', '-a', required=False) command.add_argument('b', '-b', required=False) cmd_table = {'n1': command} config = Configuration([]) config.get_command_table = lambda: cmd_table app = Application() app.initialize(config) with self.assertRaises(SystemExit): app.execute('n1 -h'.split()) with self.assertRaises(SystemExit): app.execute('n1 --help'.split())
def test_help_long_description_and_short_description(self): def test_handler(): pass command = CliCommand("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} config = Configuration([]) config.get_command_table = lambda: cmd_table app = Application(config) with self.assertRaises(SystemExit): app.execute("n1 -h".split()) self.assertEqual( True, io.getvalue().startswith("\nCommand\n az n1: Short description.\n Long description.") ) # pylint: disable=line-too-long
def test_list_value_parameter(self): hellos = [] def handler(args): hellos.append(args) command = CliCommand('test command', handler) command.add_argument('hello', '--hello', nargs='+', action=IterateAction) command.add_argument('something', '--something') cmd_table = {'test command': command} argv = 'az test command --hello world sir --something else'.split() config = Configuration() config.get_command_table = lambda argv: cmd_table application = Application(config) application.execute(argv[1:]) self.assertEqual(2, len(hellos)) self.assertEqual(hellos[0]['hello'], 'world') self.assertEqual(hellos[0]['something'], 'else') self.assertEqual(hellos[1]['hello'], 'sir') self.assertEqual(hellos[1]['something'], 'else')
def test_help_long_description_multi_line(self): def test_handler(): pass command = CliCommand('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} config = Configuration([]) config.get_command_table = lambda: cmd_table app = Application(config) with self.assertRaises(SystemExit): app.execute('n1 -h'.split()) self.assertEqual(True, io.getvalue().startswith('\nCommand\n az n1\n Line1\n line2.')) # pylint: disable=line-too-long
def test_list_value_parameter(self): hellos = [] def handler(args): hellos.append(args) command = CliCommand('test command', handler) command.add_argument('hello', '--hello', nargs='+', action=IterateAction) command.add_argument('something', '--something') cmd_table = {'test command': command} argv = 'az test command --hello world sir --something else'.split() config = Configuration(argv) config.get_command_table = lambda: cmd_table application = Application(config) application.execute(argv[1:]) self.assertEqual(2, len(hellos)) self.assertEqual(hellos[0]['hello'], 'world') self.assertEqual(hellos[0]['something'], 'else') self.assertEqual(hellos[1]['hello'], 'sir') self.assertEqual(hellos[1]['something'], 'else')
def test_help_extra_missing_params(self): app = Application(Configuration([])) def test_handler(foobar2, foobar=None): # pylint: disable=unused-argument pass command = CliCommand("n1", test_handler) command.add_argument("foobar", "--foobar", "-fb", required=False) command.add_argument("foobar2", "--foobar2", "-fb2", required=True) cmd_table = {"n1": command} config = Configuration([]) config.get_command_table = lambda: cmd_table app = Application(config) # 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: app.execute("n1 -fb a --foobar value".split()) except SystemExit: pass try: app.execute("n1 -fb a --foobar2 value --foobar3 extra".split()) except SystemExit: pass else: with self.assertRaises(SystemExit): app.execute("n1 -fb a --foobar value".split()) with self.assertRaises(SystemExit): app.execute("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_extra_missing_params(self): app = Application(Configuration([])) def test_handler(foobar2, foobar=None): # pylint: disable=unused-argument pass command = CliCommand('n1', test_handler) command.add_argument('foobar', '--foobar', '-fb', required=False) command.add_argument('foobar2', '--foobar2', '-fb2', required=True) cmd_table = {'n1': command} config = Configuration([]) config.get_command_table = lambda: cmd_table app = Application(config) # 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: app.execute('n1 -fb a --foobar value'.split()) except SystemExit: pass try: app.execute('n1 -fb a --foobar2 value --foobar3 extra'.split()) except SystemExit: pass else: with self.assertRaises(SystemExit): app.execute('n1 -fb a --foobar value'.split()) with self.assertRaises(SystemExit): app.execute('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_params_documentations(self, _): app = Application(Configuration([])) def test_handler(): pass command = CliCommand('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: - az vm list - default - name: --foobar2 -fb2 type: string required: true short-summary: one line partial sentence long-summary: paragraph(s) """ cmd_table = {'n1': command} config = Configuration([]) config.get_command_table = lambda: cmd_table app = Application(config) with self.assertRaises(SystemExit): app.execute('n1 -h'.split()) s = """ Command az n1 Arguments --foobar2 -fb2 [Required]: One line partial sentence. Paragraph(s). --foobar -fb : One line partial sentence. Values from: az vm list, default. Text, markdown, etc. --foobar3 -fb3 : The foobar3. Global Arguments --help -h : Show this help message and exit. """ self.assertEqual(s, io.getvalue())
def test_help_params_documentations(self, _): app = Application(Configuration()) def test_handler(): pass command = CliCommand('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: - az vm list - default - name: --foobar2 -fb2 type: string required: true short-summary: one line partial sentence long-summary: paragraph(s) """ cmd_table = {'n1': command} config = Configuration() config.get_command_table = lambda argv: cmd_table app = Application(config) with self.assertRaises(SystemExit): app.execute('n1 -h'.split()) s = """ Command az n1 Arguments --foobar2 -fb2 [Required]: One line partial sentence. Paragraph(s). --foobar -fb : One line partial sentence. Values from: az vm list, default. Text, markdown, etc. --foobar3 -fb3 : The foobar3. Global Arguments --help -h : Show this help message and exit. """ self.assertEqual(s, io.getvalue())
def cli_generic_wait_command(module_name, name, getter_op, factory=None): if not isinstance(getter_op, string_types): raise ValueError("Getter operation must be a string. Got '{}'".format(type(getter_op))) def get_arguments_loader(): return dict(extract_args_from_signature(get_op_handler(getter_op))) def arguments_loader(): arguments = {} arguments.update(get_arguments_loader()) return arguments def get_provisioning_state(instance): provisioning_state = getattr(instance, 'provisioning_state', None) if not provisioning_state: # some SDK, like resource-group, has 'provisioning_state' under 'properties' properties = getattr(instance, 'properties', None) if properties: provisioning_state = getattr(properties, 'provisioning_state', None) return provisioning_state def handler(args): from msrest.exceptions import ClientException import time try: client = factory() if factory else None except TypeError: client = factory(None) if factory else None getterargs = {key: val for key, val in args.items() if key in get_arguments_loader()} getter = get_op_handler(getter_op) timeout = args.pop('timeout') interval = args.pop('interval') wait_for_created = args.pop('created') wait_for_deleted = args.pop('deleted') wait_for_updated = args.pop('updated') wait_for_exists = args.pop('exists') custom_condition = args.pop('custom') if not any([wait_for_created, wait_for_updated, wait_for_deleted, wait_for_exists, custom_condition]): raise CLIError( "incorrect usage: --created | --updated | --deleted | --exists | --custom JMESPATH") # pylint: disable=line-too-long for _ in range(0, timeout, interval): try: instance = getter(client, **getterargs) if client else getter(**getterargs) if wait_for_exists: return provisioning_state = get_provisioning_state(instance) # until we have any needs to wait for 'Failed', let us bail out on this if provisioning_state == 'Failed': raise CLIError('The operation failed') if wait_for_created or wait_for_updated: if provisioning_state == 'Succeeded': return if custom_condition and bool(verify_property(instance, custom_condition)): return except ClientException as ex: if getattr(ex, 'status_code', None) == 404: if wait_for_deleted: return if not any([wait_for_created, wait_for_exists, custom_condition]): raise else: raise time.sleep(interval) return CLIError('Wait operation timed-out after {} seconds'.format(timeout)) cmd = CliCommand(name, handler, arguments_loader=arguments_loader) group_name = 'Wait Condition' cmd.add_argument('timeout', '--timeout', default=3600, arg_group=group_name, type=int, help='maximum wait in seconds') cmd.add_argument('interval', '--interval', default=30, arg_group=group_name, type=int, help='polling interval in seconds') cmd.add_argument('deleted', '--deleted', action='store_true', arg_group=group_name, help='wait till deleted') cmd.add_argument('created', '--created', action='store_true', arg_group=group_name, help="wait till created with 'provisioningState' at 'Succeeded'") cmd.add_argument('updated', '--updated', action='store_true', arg_group=group_name, help="wait till updated with provisioningState at 'Succeeded'") cmd.add_argument('exists', '--exists', action='store_true', arg_group=group_name, help="wait till the resource exists") cmd.add_argument('custom', '--custom', arg_group=group_name, help=("Wait until the condition satisfies a custom JMESPath query. E.g. " "provisioningState!='InProgress', " "instanceView.statuses[?code=='PowerState/running']")) main_command_table[name] = cmd main_command_module_map[name] = module_name
def cli_generic_update_command(module_name, name, getter_op, setter_op, factory=None, setter_arg_name='parameters', table_transformer=None, child_collection_prop_name=None, child_collection_key='name', child_arg_name='item_name', custom_function_op=None, no_wait_param=None, transform=None): if not isinstance(getter_op, string_types): raise ValueError("Getter operation must be a string. Got '{}'".format(getter_op)) if not isinstance(setter_op, string_types): raise ValueError("Setter operation must be a string. Got '{}'".format(setter_op)) if custom_function_op and not isinstance(custom_function_op, string_types): raise ValueError("Custom function operation must be a string. Got '{}'".format( custom_function_op)) def get_arguments_loader(): return dict(extract_args_from_signature(get_op_handler(getter_op))) def set_arguments_loader(): return dict(extract_args_from_signature(get_op_handler(setter_op), no_wait_param=no_wait_param)) def function_arguments_loader(): return dict(extract_args_from_signature(get_op_handler(custom_function_op))) \ if custom_function_op else {} def arguments_loader(): arguments = {} arguments.update(set_arguments_loader()) arguments.update(get_arguments_loader()) arguments.update(function_arguments_loader()) arguments.pop('instance', None) # inherited from custom_function(instance, ...) arguments.pop('parent', None) arguments.pop('expand', None) # possibly inherited from the getter arguments.pop(setter_arg_name, None) return arguments def handler(args): # pylint: disable=too-many-branches,too-many-statements from msrestazure.azure_operation import AzureOperationPoller ordered_arguments = args.pop('ordered_arguments', []) for item in ['properties_to_add', 'properties_to_set', 'properties_to_remove']: if args[item]: raise CLIError("Unexpected '{}' was not empty.".format(item)) del args[item] try: client = factory() if factory else None except TypeError: client = factory(None) if factory else None getterargs = {key: val for key, val in args.items() if key in get_arguments_loader()} getter = get_op_handler(getter_op) if child_collection_prop_name: parent = getter(client, **getterargs) if client else getter(**getterargs) instance = _get_child( parent, child_collection_prop_name, args.get(child_arg_name), child_collection_key ) else: parent = None instance = getter(client, **getterargs) if client else getter(**getterargs) # pass instance to the custom_function, if provided if custom_function_op: custom_function = get_op_handler(custom_function_op) custom_func_args = {k: v for k, v in args.items() if k in function_arguments_loader()} if child_collection_prop_name: parent = custom_function(instance, parent, **custom_func_args) else: instance = custom_function(instance, **custom_func_args) # apply generic updates after custom updates setterargs = {key: val for key, val in args.items() if key in set_arguments_loader()} for arg in ordered_arguments: arg_type, arg_values = arg if arg_type == '--set': try: for expression in arg_values: set_properties(instance, expression) except ValueError: raise CLIError('invalid syntax: {}'.format(set_usage)) elif arg_type == '--add': try: add_properties(instance, arg_values) except ValueError: raise CLIError('invalid syntax: {}'.format(add_usage)) elif arg_type == '--remove': try: remove_properties(instance, arg_values) except ValueError: raise CLIError('invalid syntax: {}'.format(remove_usage)) # Done... update the instance! setterargs[setter_arg_name] = parent if child_collection_prop_name else instance setter = get_op_handler(setter_op) opres = setter(client, **setterargs) if client else setter(**setterargs) if setterargs.get(no_wait_param, None): return None result = opres.result() if isinstance(opres, AzureOperationPoller) else opres if child_collection_prop_name: result = _get_child( result, child_collection_prop_name, args.get(child_arg_name), child_collection_key ) # apply results transform if specified if transform: return transform(result) return result class OrderedArgsAction(argparse.Action): # pylint:disable=too-few-public-methods def __call__(self, parser, namespace, values, option_string=None): if not getattr(namespace, 'ordered_arguments', None): setattr(namespace, 'ordered_arguments', []) namespace.ordered_arguments.append((option_string, values)) cmd = CliCommand(name, handler, table_transformer=table_transformer, arguments_loader=arguments_loader) group_name = 'Generic Update' cmd.add_argument('properties_to_set', '--set', nargs='+', action=OrderedArgsAction, default=[], help='Update an object by specifying a property path and value to set.' ' Example: {}'.format(set_usage), metavar='KEY=VALUE', arg_group=group_name) cmd.add_argument('properties_to_add', '--add', nargs='+', action=OrderedArgsAction, default=[], help='Add an object to a list of objects by specifying a path and key' ' value pairs. Example: {}'.format(add_usage), metavar='LIST KEY=VALUE', arg_group=group_name) cmd.add_argument('properties_to_remove', '--remove', nargs='+', action=OrderedArgsAction, default=[], help='Remove a property or an element from a list. Example: ' '{}'.format(remove_usage), metavar='LIST INDEX', arg_group=group_name) main_command_table[name] = cmd main_command_module_map[name] = module_name
def cli_generic_wait_command(module_name, name, getter_op, factory=None, exception_handler=None): if not isinstance(getter_op, string_types): raise ValueError("Getter operation must be a string. Got '{}'".format( type(getter_op))) def get_arguments_loader(): return dict(extract_args_from_signature(get_op_handler(getter_op))) def arguments_loader(): arguments = {} arguments.update(get_arguments_loader()) return arguments def get_provisioning_state(instance): provisioning_state = getattr(instance, 'provisioning_state', None) if not provisioning_state: # some SDK, like resource-group, has 'provisioning_state' under 'properties' properties = getattr(instance, 'properties', None) if properties: provisioning_state = getattr(properties, 'provisioning_state', None) return provisioning_state def _handle_exception(ex): if exception_handler: return exception_handler(ex) else: raise ex def handler(args): from msrest.exceptions import ClientException import time try: client = factory() if factory else None except TypeError: client = factory(None) if factory else None getterargs = { key: val for key, val in args.items() if key in get_arguments_loader() } getter = get_op_handler(getter_op) timeout = args.pop('timeout') interval = args.pop('interval') wait_for_created = args.pop('created') wait_for_deleted = args.pop('deleted') wait_for_updated = args.pop('updated') wait_for_exists = args.pop('exists') custom_condition = args.pop('custom') if not any([ wait_for_created, wait_for_updated, wait_for_deleted, wait_for_exists, custom_condition ]): raise CLIError( "incorrect usage: --created | --updated | --deleted | --exists | --custom JMESPATH" ) for _ in range(0, timeout, interval): try: instance = getter(client, **getterargs) if client else getter( **getterargs) if wait_for_exists: return provisioning_state = get_provisioning_state(instance) # until we have any needs to wait for 'Failed', let us bail out on this if provisioning_state == 'Failed': raise CLIError('The operation failed') if wait_for_created or wait_for_updated: if provisioning_state == 'Succeeded': return if custom_condition and bool( verify_property(instance, custom_condition)): return except ClientException as ex: if getattr(ex, 'status_code', None) == 404: if wait_for_deleted: return if not any( [wait_for_created, wait_for_exists, custom_condition]): _handle_exception(ex) else: _handle_exception(ex) except Exception as ex: # pylint: disable=broad-except _handle_exception(ex) time.sleep(interval) return CLIError( 'Wait operation timed-out after {} seconds'.format(timeout)) cmd = CliCommand(name, handler, arguments_loader=arguments_loader) group_name = 'Wait Condition' cmd.add_argument('timeout', '--timeout', default=3600, arg_group=group_name, type=int, help='maximum wait in seconds') cmd.add_argument('interval', '--interval', default=30, arg_group=group_name, type=int, help='polling interval in seconds') cmd.add_argument('deleted', '--deleted', action='store_true', arg_group=group_name, help='wait till deleted') cmd.add_argument( 'created', '--created', action='store_true', arg_group=group_name, help="wait till created with 'provisioningState' at 'Succeeded'") cmd.add_argument( 'updated', '--updated', action='store_true', arg_group=group_name, help="wait till updated with provisioningState at 'Succeeded'") cmd.add_argument('exists', '--exists', action='store_true', arg_group=group_name, help="wait till the resource exists") cmd.add_argument( 'custom', '--custom', arg_group=group_name, help=( "Wait until the condition satisfies a custom JMESPath query. E.g. " "provisioningState!='InProgress', " "instanceView.statuses[?code=='PowerState/running']")) main_command_table[name] = cmd main_command_module_map[name] = module_name
def cli_generic_update_command(module_name, name, getter_op, setter_op, factory=None, setter_arg_name='parameters', table_transformer=None, child_collection_prop_name=None, child_collection_key='name', child_arg_name='item_name', custom_function_op=None, no_wait_param=None, transform=None, confirmation=None, exception_handler=None, formatter_class=None): if not isinstance(getter_op, string_types): raise ValueError( "Getter operation must be a string. Got '{}'".format(getter_op)) if not isinstance(setter_op, string_types): raise ValueError( "Setter operation must be a string. Got '{}'".format(setter_op)) if custom_function_op and not isinstance(custom_function_op, string_types): raise ValueError( "Custom function operation must be a string. Got '{}'".format( custom_function_op)) def get_arguments_loader(): return dict(extract_args_from_signature(get_op_handler(getter_op))) def set_arguments_loader(): return dict( extract_args_from_signature(get_op_handler(setter_op), no_wait_param=no_wait_param)) def function_arguments_loader(): return dict(extract_args_from_signature(get_op_handler(custom_function_op))) \ if custom_function_op else {} def arguments_loader(): arguments = {} arguments.update(set_arguments_loader()) arguments.update(get_arguments_loader()) arguments.update(function_arguments_loader()) arguments.pop('instance', None) # inherited from custom_function(instance, ...) arguments.pop('parent', None) arguments.pop('expand', None) # possibly inherited from the getter arguments.pop(setter_arg_name, None) return arguments def handler(args): # pylint: disable=too-many-branches,too-many-statements from msrestazure.azure_operation import AzureOperationPoller if confirmation \ and not args.items().get(CONFIRM_PARAM_NAME) \ and not az_config.getboolean('core', 'disable_confirm_prompt', fallback=False) \ and not _user_confirmed(confirmation, args.items()): raise CLIError('Operation cancelled.') ordered_arguments = args.pop('ordered_arguments', []) for item in [ 'properties_to_add', 'properties_to_set', 'properties_to_remove' ]: if args[item]: raise CLIError("Unexpected '{}' was not empty.".format(item)) del args[item] try: client = factory() if factory else None except TypeError: client = factory(None) if factory else None getterargs = { key: val for key, val in args.items() if key in get_arguments_loader() } getter = get_op_handler(getter_op) try: if child_collection_prop_name: parent = getter(client, **getterargs) if client else getter( **getterargs) instance = _get_child(parent, child_collection_prop_name, args.get(child_arg_name), child_collection_key) else: parent = None instance = getter(client, **getterargs) if client else getter( **getterargs) # pass instance to the custom_function, if provided if custom_function_op: custom_function = get_op_handler(custom_function_op) custom_func_args = \ {k: v for k, v in args.items() if k in function_arguments_loader()} if child_collection_prop_name: parent = custom_function(instance, parent, **custom_func_args) else: instance = custom_function(instance, **custom_func_args) # apply generic updates after custom updates setterargs = { key: val for key, val in args.items() if key in set_arguments_loader() } for arg in ordered_arguments: arg_type, arg_values = arg if arg_type == '--set': try: for expression in arg_values: set_properties(instance, expression) except ValueError: raise CLIError('invalid syntax: {}'.format(set_usage)) elif arg_type == '--add': try: add_properties(instance, arg_values) except ValueError: raise CLIError('invalid syntax: {}'.format(add_usage)) elif arg_type == '--remove': try: remove_properties(instance, arg_values) except ValueError: raise CLIError( 'invalid syntax: {}'.format(remove_usage)) # Done... update the instance! setterargs[ setter_arg_name] = parent if child_collection_prop_name else instance setter = get_op_handler(setter_op) opres = setter(client, **setterargs) if client else setter( **setterargs) if setterargs.get(no_wait_param, None): return None result = opres.result() if isinstance( opres, AzureOperationPoller) else opres if child_collection_prop_name: result = _get_child(result, child_collection_prop_name, args.get(child_arg_name), child_collection_key) except Exception as ex: # pylint: disable=broad-except if exception_handler: result = exception_handler(ex) else: raise ex # apply results transform if specified if transform: return transform(result) return result class OrderedArgsAction(argparse.Action): # pylint:disable=too-few-public-methods def __call__(self, parser, namespace, values, option_string=None): if not getattr(namespace, 'ordered_arguments', None): setattr(namespace, 'ordered_arguments', []) namespace.ordered_arguments.append((option_string, values)) cmd = CliCommand(name, handler, table_transformer=table_transformer, arguments_loader=arguments_loader, formatter_class=formatter_class) group_name = 'Generic Update' cmd.add_argument( 'properties_to_set', '--set', nargs='+', action=OrderedArgsAction, default=[], help='Update an object by specifying a property path and value to set.' ' Example: {}'.format(set_usage), metavar='KEY=VALUE', arg_group=group_name) cmd.add_argument( 'properties_to_add', '--add', nargs='+', action=OrderedArgsAction, default=[], help='Add an object to a list of objects by specifying a path and key' ' value pairs. Example: {}'.format(add_usage), metavar='LIST KEY=VALUE', arg_group=group_name) cmd.add_argument( 'properties_to_remove', '--remove', nargs='+', action=OrderedArgsAction, default=[], help='Remove a property or an element from a list. Example: ' '{}'.format(remove_usage), metavar='LIST INDEX', arg_group=group_name) main_command_table[name] = cmd main_command_module_map[name] = module_name
def test_help_group_help(self): app = Application(Configuration([])) def test_handler(): pass azure.cli.core.help_files.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('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: - az vm list - 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} config = Configuration([]) config.get_command_table = lambda: cmd_table app = Application(config) with self.assertRaises(SystemExit): app.execute('test_group1 test_group2 --help'.split()) s = """ Group az 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, io.getvalue()) del azure.cli.core.help_files.helps['test_group1 test_group2']
def cli_generic_update_command(name, getter, setter, factory=None, setter_arg_name='parameters', # pylint: disable=too-many-arguments table_transformer=None, child_collection_prop_name=None, child_collection_key='name', child_arg_name='item_name', custom_function=None): get_arguments = dict(extract_args_from_signature(getter)) set_arguments = dict(extract_args_from_signature(setter)) function_arguments = dict(extract_args_from_signature(custom_function)) \ if custom_function else None def handler(args): ordered_arguments = args.pop('ordered_arguments') if 'ordered_arguments' in args else [] try: client = factory() if factory else None except TypeError: client = factory(None) if factory else None getterargs = {key: val for key, val in args.items() if key in get_arguments} if child_collection_prop_name: parent = getter(client, **getterargs) if client else getter(**getterargs) instance = _get_child( parent, child_collection_prop_name, args.get(child_arg_name), child_collection_key ) else: parent = None instance = getter(client, **getterargs) if client else getter(**getterargs) # pass instance to the custom_function, if provided if custom_function: custom_func_args = {k: v for k, v in args.items() if k in function_arguments} if child_collection_prop_name: parent = custom_function(instance, parent, **custom_func_args) else: instance = custom_function(instance, **custom_func_args) # apply generic updates after custom updates for k in args.copy().keys(): if k in get_arguments or k in set_arguments \ or k in ('properties_to_add', 'properties_to_remove', 'properties_to_set'): args.pop(k) for key, val in args.items(): ordered_arguments.append((key, val)) for arg in ordered_arguments: arg_type, arg_values = arg if arg_type == '--set': try: for expression in arg_values: set_properties(instance, expression) except ValueError: raise CLIError('invalid syntax: {}'.format(set_usage)) elif arg_type == '--add': try: add_properties(instance, arg_values) except ValueError: raise CLIError('invalid syntax: {}'.format(add_usage)) elif arg_type == '--remove': try: remove_properties(instance, arg_values) except ValueError: raise CLIError('invalid syntax: {}'.format(remove_usage)) # Done... update the instance! getterargs[setter_arg_name] = parent if child_collection_prop_name else instance opres = setter(client, **getterargs) if client else setter(**getterargs) result = opres.result() if isinstance(opres, AzureOperationPoller) else opres if child_collection_prop_name: return _get_child( result, child_collection_prop_name, args.get(child_arg_name), child_collection_key ) else: return result class OrderedArgsAction(argparse.Action): #pylint:disable=too-few-public-methods def __call__(self, parser, namespace, values, option_string=None): if not getattr(namespace, 'ordered_arguments', None): setattr(namespace, 'ordered_arguments', []) namespace.ordered_arguments.append((option_string, values)) cmd = CliCommand(name, handler, table_transformer=table_transformer) cmd.arguments.update(set_arguments) cmd.arguments.update(get_arguments) if function_arguments: cmd.arguments.update(function_arguments) cmd.arguments.pop('instance', None) # inherited from custom_function(instance, ...) cmd.arguments.pop('parent', None) cmd.arguments.pop('expand', None) # possibly inherited from the getter cmd.arguments.pop(setter_arg_name, None) group_name = 'Generic Update' cmd.add_argument('properties_to_set', '--set', nargs='+', action=OrderedArgsAction, default=[], help='Update an object by specifying a property path and value to set.' ' Example: {}'.format(set_usage), metavar='KEY=VALUE', arg_group=group_name) cmd.add_argument('properties_to_add', '--add', nargs='+', action=OrderedArgsAction, default=[], help='Add an object to a list of objects by specifying a path and key' ' value pairs. Example: {}'.format(add_usage), metavar='LIST KEY=VALUE', arg_group=group_name) cmd.add_argument('properties_to_remove', '--remove', nargs='+', action=OrderedArgsAction, default=[], help='Remove a property or an element from a list. Example: ' '{}'.format(remove_usage), metavar='LIST INDEX', arg_group=group_name) main_command_table[name] = cmd
def cli_generic_update_command( module_name, name, getter_op, setter_op, factory=None, setter_arg_name="parameters", table_transformer=None, child_collection_prop_name=None, child_collection_key="name", child_arg_name="item_name", custom_function_op=None, no_wait_param=None, transform=None, ): if not isinstance(getter_op, string_types): raise ValueError("Getter operation must be a string. Got '{}'".format(getter_op)) if not isinstance(setter_op, string_types): raise ValueError("Setter operation must be a string. Got '{}'".format(setter_op)) if custom_function_op and not isinstance(custom_function_op, string_types): raise ValueError("Custom function operation must be a string. Got '{}'".format(custom_function_op)) def get_arguments_loader(): return dict(extract_args_from_signature(get_op_handler(getter_op))) def set_arguments_loader(): return dict(extract_args_from_signature(get_op_handler(setter_op), no_wait_param=no_wait_param)) def function_arguments_loader(): return dict(extract_args_from_signature(get_op_handler(custom_function_op))) if custom_function_op else {} def arguments_loader(): arguments = {} arguments.update(set_arguments_loader()) arguments.update(get_arguments_loader()) arguments.update(function_arguments_loader()) arguments.pop("instance", None) # inherited from custom_function(instance, ...) arguments.pop("parent", None) arguments.pop("expand", None) # possibly inherited from the getter arguments.pop(setter_arg_name, None) return arguments def handler(args): # pylint: disable=too-many-branches,too-many-statements from msrestazure.azure_operation import AzureOperationPoller ordered_arguments = args.pop("ordered_arguments") if "ordered_arguments" in args else [] try: client = factory() if factory else None except TypeError: client = factory(None) if factory else None getterargs = {key: val for key, val in args.items() if key in get_arguments_loader()} getter = get_op_handler(getter_op) if child_collection_prop_name: parent = getter(client, **getterargs) if client else getter(**getterargs) instance = _get_child(parent, child_collection_prop_name, args.get(child_arg_name), child_collection_key) else: parent = None instance = getter(client, **getterargs) if client else getter(**getterargs) # pass instance to the custom_function, if provided if custom_function_op: custom_function = get_op_handler(custom_function_op) custom_func_args = {k: v for k, v in args.items() if k in function_arguments_loader()} if child_collection_prop_name: parent = custom_function(instance, parent, **custom_func_args) else: instance = custom_function(instance, **custom_func_args) # apply generic updates after custom updates setterargs = set_arguments_loader() for k in args.copy().keys(): if ( k in get_arguments_loader() or k in setterargs or k in ("properties_to_add", "properties_to_remove", "properties_to_set") ): args.pop(k) for key, val in args.items(): ordered_arguments.append((key, val)) for arg in ordered_arguments: arg_type, arg_values = arg if arg_type == "--set": try: for expression in arg_values: set_properties(instance, expression) except ValueError: raise CLIError("invalid syntax: {}".format(set_usage)) elif arg_type == "--add": try: add_properties(instance, arg_values) except ValueError: raise CLIError("invalid syntax: {}".format(add_usage)) elif arg_type == "--remove": try: remove_properties(instance, arg_values) except ValueError: raise CLIError("invalid syntax: {}".format(remove_usage)) # Done... update the instance! getterargs[setter_arg_name] = parent if child_collection_prop_name else instance setter = get_op_handler(setter_op) no_wait = no_wait_param and setterargs.get(no_wait_param, None) if no_wait: getterargs[no_wait_param] = True opres = setter(client, **getterargs) if client else setter(**getterargs) if no_wait: return None result = opres.result() if isinstance(opres, AzureOperationPoller) else opres if child_collection_prop_name: result = _get_child(result, child_collection_prop_name, args.get(child_arg_name), child_collection_key) # apply results transform if specified if transform: return transform(result) return result class OrderedArgsAction(argparse.Action): # pylint:disable=too-few-public-methods def __call__(self, parser, namespace, values, option_string=None): if not getattr(namespace, "ordered_arguments", None): setattr(namespace, "ordered_arguments", []) namespace.ordered_arguments.append((option_string, values)) cmd = CliCommand(name, handler, table_transformer=table_transformer, arguments_loader=arguments_loader) group_name = "Generic Update" cmd.add_argument( "properties_to_set", "--set", nargs="+", action=OrderedArgsAction, default=[], help="Update an object by specifying a property path and value to set." " Example: {}".format(set_usage), metavar="KEY=VALUE", arg_group=group_name, ) cmd.add_argument( "properties_to_add", "--add", nargs="+", action=OrderedArgsAction, default=[], help="Add an object to a list of objects by specifying a path and key" " value pairs. Example: {}".format(add_usage), metavar="LIST KEY=VALUE", arg_group=group_name, ) cmd.add_argument( "properties_to_remove", "--remove", nargs="+", action=OrderedArgsAction, default=[], help="Remove a property or an element from a list. Example: " "{}".format(remove_usage), metavar="LIST INDEX", arg_group=group_name, ) main_command_table[name] = cmd main_command_module_map[name] = module_name