def cmd(command, message=False, show_stderr=True, **kwargs): """ Run an arbitrary command. :param command: The entire command line to run. :param message: A custom message to display, or True (bool) to use a default. :param show_stderr: On error, display the contents of STDERR. :param kwargs: Any kwargs supported by subprocess.Popen :returns: CommandResultItem object. """ from azdev.utilities import IS_WINDOWS, display # use default message if custom not provided if message is True: message = 'Running: {}\n'.format(command) if message: display(message) try: output = subprocess.check_output( command.split(), stderr=subprocess.STDOUT if show_stderr else None, shell=IS_WINDOWS, **kwargs).decode('utf-8').strip() return CommandResultItem(output, exit_code=0, error=None) except subprocess.CalledProcessError as err: return CommandResultItem(err.output, exit_code=err.returncode, error=err)
def _run_job(self, expanded_arg, cmd_copy): params = self._filter_params(expanded_arg) try: result = cmd_copy(params) if cmd_copy.supports_no_wait and getattr(expanded_arg, 'no_wait', False): result = None elif cmd_copy.no_wait_param and getattr( expanded_arg, cmd_copy.no_wait_param, False): result = None transform_op = cmd_copy.command_kwargs.get('transform', None) if transform_op: result = transform_op(result) if _is_poller(result): result = LongRunningOperation( cmd_copy.cli_ctx, 'Starting {}'.format(cmd_copy.name))(result) elif _is_paged(result): result = list(result) result = todict(result, AzCliCommandInvoker.remove_additional_prop_layer) event_data = {'result': result} cmd_copy.cli_ctx.raise_event(EVENT_INVOKER_TRANSFORM_RESULT, event_data=event_data) return event_data['result'] except Exception as ex: # pylint: disable=broad-except if cmd_copy.exception_handler: cmd_copy.exception_handler(ex) return CommandResultItem(None, exit_code=1, error=ex) else: six.reraise(*sys.exc_info())
def shell_cmd(command, message=False, stderr=None, stdout=None, check=True, raise_ex=True, timeout=None, executable=None, capture_output=False): # use default message if custom not provided if message is True: message = '\nRunning: {}\n'.format(command) from . import display if message: display(message) try: output = subprocess.run(command, stdout=subprocess.PIPE if capture_output else stdout, stderr=subprocess.PIPE if capture_output else stderr, check=check, timeout=timeout, executable=executable, shell=True) if capture_output: return CommandResultItem(output.stdout.decode('utf-8').strip(), exit_code=0, error=None) except subprocess.CalledProcessError as err: if raise_ex: raise err logger.debug(err) raise CLIError("Command " + command + " failed. Trying running with --debug for more info") return None
def test_out_table_no_query_yes_transformer_order(self): output_producer = OutputProducer(cli_ctx=self.mock_ctx) obj = { 'name': 'qwerty', 'val': '0b1f6472qwerty', 'active': True, 'sub': '0b1f6472' } def transformer(r): return OrderedDict([('Name', r['name']), ('Val', r['val']), ('Active', r['active']), ('Sub', r['sub'])]) result_item = CommandResultItem(obj, table_transformer=transformer, is_query_active=False) output_producer.out(result_item, formatter=format_table, out_file=self.io) # Should be table transformer order self.assertEqual( normalize_newlines(self.io.getvalue()), normalize_newlines("""Name Val Active Sub ------ -------------- -------- -------- qwerty 0b1f6472qwerty True 0b1f6472 """))
def _combine_command_result(cli_result, ext_result): final_result = CommandResultItem(None) def apply_result(item): if item: final_result.exit_code += item.exit_code if item.error: if final_result.error: try: final_result.error.message += item.error.message except AttributeError: final_result.error.message += str(item.error) else: final_result.error = item.error setattr(final_result.error, 'message', '') if item.result: if final_result.result: final_result.result += item.result else: final_result.result = item.result apply_result(cli_result) apply_result(ext_result) return final_result
def execute(self, args): try: return super(EgorInvoker, self).execute(args) except TypeError as e: from knack.log import get_logger logger = get_logger(__name__) logger.error('Could not execute egor command ', e) return CommandResultItem(None, exit_code=0)
def test_output_format_ordereddict_list_not_sorted(self): obj1 = OrderedDict() obj1['B'] = 1 obj1['A'] = 2 obj2 = OrderedDict() obj2['A'] = 3 obj2['B'] = 4 result = format_tsv(CommandResultItem([obj1, obj2])) self.assertEqual(result, '1\t2\n3\t4\n')
def test_out_table_list_of_lists(self): output_producer = OutputProducer(formatter=format_table, file=self.io) obj = [['a', 'b'], ['c', 'd']] output_producer.out(CommandResultItem(obj)) self.assertEqual( normalize_newlines(self.io.getvalue()), normalize_newlines("""Column1 Column2 --------- --------- a b c d """))
def test_out_table(self): output_producer = OutputProducer(formatter=format_table, file=self.io) obj = OrderedDict() obj['active'] = True obj['val'] = '0b1f6472' output_producer.out(CommandResultItem(obj)) self.assertEqual( normalize_newlines(self.io.getvalue()), normalize_newlines("""Active Val -------- -------- True 0b1f6472 """))
def test_out_table_with_number(self): output_producer = OutputProducer(cli_ctx=self.mock_ctx) obj = OrderedDict() obj['Sku'] = '6.10' output_producer.out(CommandResultItem(obj), formatter=format_table, out_file=self.io) self.assertEqual(normalize_newlines(self.io.getvalue()), normalize_newlines("""Sku ----- 6.10 """))
def test_out_yaml_non_ASCII(self): output_producer = OutputProducer(cli_ctx=self.mock_ctx) output_producer.out(CommandResultItem({ 'active': True, 'contents': 'こんにちは' }), formatter=format_yaml, out_file=self.io) self.assertEqual( normalize_newlines(self.io.getvalue()), normalize_newlines("""active: true contents: こんにちは """))
def test_out_yaml_byte(self): output_producer = OutputProducer(cli_ctx=self.mock_ctx) output_producer.out(CommandResultItem({ 'active': True, 'contents': b'0b1f6472' }), formatter=format_yaml, out_file=self.io) self.assertEqual( normalize_newlines(self.io.getvalue()), normalize_newlines("""active: true contents: !!binary | MGIxZjY0NzI= """))
def execute(self, args): try: return super(SFInvoker, self).execute(args) # For exceptions happening while handling http requests, FabricErrorException is thrown with # 'Internal Server Error' message, but here we handle the case where gateway is unable # to find the service. except TypeError: if args[0] == 'events': from knack.log import get_logger logger = get_logger(__name__) logger.error('Service is not installed.') return CommandResultItem(None, exit_code=0) raise
def test_out_json_byte_empty(self): output_producer = OutputProducer(formatter=format_json, file=self.io) output_producer.out( CommandResultItem({ 'active': True, 'contents': b'' })) self.assertEqual( normalize_newlines(self.io.getvalue()), normalize_newlines("""{ "active": true, "contents": "" } """))
def test_out_table_complex_obj(self): output_producer = OutputProducer(formatter=format_table, file=self.io) obj = OrderedDict() obj['name'] = 'qwerty' obj['val'] = '0b1f6472qwerty' obj['sub'] = {'1'} result_item = CommandResultItem(obj) output_producer.out(result_item) self.assertEqual( normalize_newlines(self.io.getvalue()), normalize_newlines("""Name Val ------ -------------- qwerty 0b1f6472qwerty """))
def test_out_json_from_ordered_dict(self): # The JSON output when the input is OrderedDict should be serialized to JSON output_producer = OutputProducer(formatter=format_json, file=self.io) output_producer.out( CommandResultItem(OrderedDict({ 'active': True, 'id': '0b1f6472' }))) self.assertEqual( normalize_newlines(self.io.getvalue()), normalize_newlines("""{ "active": true, "id": "0b1f6472" } """))
def test_out_yaml_valid(self): """ Test Dict serialized to YAML """ output_producer = OutputProducer(cli_ctx=self.mock_ctx) output_producer.out(CommandResultItem({ 'active': True, 'id': '0b1f6472' }), formatter=format_yaml, out_file=self.io) self.assertEqual(normalize_newlines(self.io.getvalue()), normalize_newlines("""active: true id: 0b1f6472 """))
def test_out_table(self): output_producer = OutputProducer(cli_ctx=self.mock_ctx) obj = OrderedDict() obj['active'] = True obj['val'] = '0b1f6472' obj['lun'] = 0 output_producer.out(CommandResultItem(obj), formatter=format_table, out_file=self.io) self.assertEqual( normalize_newlines(self.io.getvalue()), normalize_newlines("""Active Lun Val -------- ----- -------- True 0 0b1f6472 """))
def test_out_json_byte(self): output_producer = OutputProducer(cli_ctx=self.mock_ctx) output_producer.out(CommandResultItem({ 'active': True, 'contents': b'0b1f6472' }), formatter=format_json, out_file=self.io) self.assertEqual( normalize_newlines(self.io.getvalue()), normalize_newlines("""{ "active": true, "contents": "0b1f6472" } """))
def test_out_json_valid(self): """ The JSON output when the input is a dict should be the dict serialized to JSON """ output_producer = OutputProducer(cli_ctx=self.mock_ctx) output_producer.out(CommandResultItem({ 'active': True, 'id': '0b1f6472' }), formatter=format_json, out_file=self.io) self.assertEqual( normalize_newlines(self.io.getvalue()), normalize_newlines("""{ "active": true, "id": "0b1f6472" } """))
def test_out_table_no_query_no_transformer_order(self): output_producer = OutputProducer(formatter=format_table, file=self.io) obj = { 'name': 'qwerty', 'val': '0b1f6472qwerty', 'active': True, 'sub': '0b1f6472' } result_item = CommandResultItem(obj, table_transformer=None, is_query_active=False) output_producer.out(result_item) # Should be alphabetical order as no table transformer and query is not active. self.assertEqual( normalize_newlines(self.io.getvalue()), normalize_newlines("""Active Name Sub Val -------- ------ -------- -------------- True qwerty 0b1f6472 0b1f6472qwerty """))
def _combine_command_result(cli_result, ext_result): final_result = CommandResultItem(None) def apply_result(item): if item: final_result.exit_code += item.exit_code if item.error: if final_result.error: final_result.error.message += item.error.message else: final_result.error = item.error if item.result: if final_result.result: final_result.result += item.result else: final_result.result = item.result apply_result(cli_result) apply_result(ext_result) return final_result
def test_out_table_no_query_yes_jmespath_table_transformer(self): output_producer = OutputProducer(formatter=format_table, file=self.io) obj = { 'name': 'qwerty', 'val': '0b1f6472qwerty', 'active': True, 'sub': '0b1f6472' } result_item = CommandResultItem( obj, table_transformer='{Name:name, Val:val, Active:active}', is_query_active=False) output_producer.out(result_item) # Should be table transformer order self.assertEqual( normalize_newlines(self.io.getvalue()), normalize_newlines("""Name Val Active ------ -------------- -------- qwerty 0b1f6472qwerty True """))
def _run_job(self, expanded_arg, cmd_copy): params = self._filter_params(expanded_arg) try: result = cmd_copy(params) if cmd_copy.supports_no_wait and getattr(expanded_arg, 'no_wait', False): result = None elif cmd_copy.no_wait_param and getattr( expanded_arg, cmd_copy.no_wait_param, False): result = None transform_op = cmd_copy.command_kwargs.get('transform', None) if transform_op: result = transform_op(result) if _is_paged(result): result = list(result) result = todict( result, GraphCliCommandInvoker.remove_additional_prop_layer) # Formatting result so that non utf8 encoded characters are ignored. formatted_json = format_json({'result': result}) result = json.loads(formatted_json) event_data = {'result': result} cmd_copy.cli_ctx.raise_event(EVENT_INVOKER_TRANSFORM_RESULT, event_data=event_data) return event_data['result'] except Exception as ex: # pylint: disable=broad-except if isinstance(ex, HttpResponseError): if ex.status_code == 403: # pylint: disable=no-member self.handle_403() raise CLIError(ex.message) from ex # pylint: disable=no-member if isinstance(ex, AuthenticationException): self.handle_auth_error(ex) if cmd_copy.exception_handler: cmd_copy.exception_handler(ex) return CommandResultItem(None, exit_code=1, error=ex) six.reraise(*sys.exc_info())
def test_out_table(self): output_producer = OutputProducer(cli_ctx=self.mock_ctx) obj1 = OrderedDict() obj1['active'] = True obj1['val'] = '0b1f6472' obj1['lun'] = 0 obj2 = OrderedDict() obj2['active'] = False obj2['val'] = '0b1f6485' obj2['lun'] = 0 output_producer.out(CommandResultItem([obj1, obj2]), formatter=format_table, out_file=self.io) self.assertEqual( normalize_newlines(self.io.getvalue()), normalize_newlines("""Active Val Lun -------- -------- ----- True 0b1f6472 0 False 0b1f6485 0 """))
def test_yaml_output_with_ordered_dict(self): from azure.cli.core._output import AzOutputProducer from azure.cli.core.mock import DummyCli from knack.util import CommandResultItem from collections import OrderedDict import yaml account_dict = { "environmentName": "AzureCloud", "id": "000000-000000", "isDefault": True, "name": "test_sub", "state": "Enabled", "tenantId": "000000-000000-000000", "user": { "name": "*****@*****.**", "type": "user" } } output_producer = AzOutputProducer(DummyCli()) yaml_output = output_producer.format_yaml(CommandResultItem(result=OrderedDict(account_dict))) self.assertEqual(account_dict, yaml.safe_load(yaml_output))
def test_output_format_ordereddict_not_sorted(self): obj = OrderedDict() obj['B'] = 1 obj['A'] = 2 result = format_tsv(CommandResultItem(obj)) self.assertEqual(result, '1\t2\n')
def test_output_format_dict_sort(self): obj = {} obj['B'] = 1 obj['A'] = 2 result = format_tsv(CommandResultItem(obj)) self.assertEqual(result, '2\t1\n')
def execute(self, args): from knack.events import ( EVENT_INVOKER_PRE_CMD_TBL_CREATE, EVENT_INVOKER_POST_CMD_TBL_CREATE, EVENT_INVOKER_CMD_TBL_LOADED, EVENT_INVOKER_PRE_PARSE_ARGS, EVENT_INVOKER_POST_PARSE_ARGS, EVENT_INVOKER_TRANSFORM_RESULT, EVENT_INVOKER_FILTER_RESULT) from knack.util import CommandResultItem, todict from azure.cli.core.commands.events import EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE # TODO: Can't simply be invoked as an event because args are transformed args = _pre_command_table_create(self.cli_ctx, args) self.cli_ctx.raise_event(EVENT_INVOKER_PRE_CMD_TBL_CREATE, args=args) self.commands_loader.load_command_table(args) self.cli_ctx.raise_event( EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE, load_cmd_tbl_func=self.commands_loader.load_command_table, args=args) command = self._rudimentary_get_command(args) telemetry.set_raw_command_name(command) try: self.commands_loader.command_table = { command: self.commands_loader.command_table[command] } except KeyError: # Trim down the command table to reduce the number of subparsers required to optimize the performance. # # When given a command table like this: # # network application-gateway create # network application-gateway delete # network list-usages # storage account create # storage account list # # input: az # output: network application-gateway create # storage account create # # input: az network # output: network application-gateway create # network list-usages cmd_table = {} group_names = set() for cmd_name, cmd in self.commands_loader.command_table.items(): if command and not cmd_name.startswith(command): continue cmd_stub = cmd_name[len(command):].strip() group_name = cmd_stub.split(' ', 1)[0] if group_name not in group_names: cmd_table[cmd_name] = cmd group_names.add(group_name) self.commands_loader.command_table = cmd_table self.commands_loader.command_table = self.commands_loader.command_table # update with the truncated table self.commands_loader.command_name = command self.commands_loader.load_arguments(command) self.cli_ctx.raise_event(EVENT_INVOKER_POST_CMD_TBL_CREATE, cmd_tbl=self.commands_loader.command_table) self.parser.cli_ctx = self.cli_ctx self.parser.load_command_table(self.commands_loader.command_table) self.cli_ctx.raise_event(EVENT_INVOKER_CMD_TBL_LOADED, cmd_tbl=self.commands_loader.command_table, parser=self.parser) if not args: self.cli_ctx.completion.enable_autocomplete(self.parser) subparser = self.parser.subparsers[tuple()] self.help.show_welcome(subparser) # TODO: No event in base with which to target telemetry.set_command_details('az') telemetry.set_success(summary='welcome') return None if args[0].lower() == 'help': args[0] = '--help' self.cli_ctx.completion.enable_autocomplete(self.parser) self.cli_ctx.raise_event(EVENT_INVOKER_PRE_PARSE_ARGS, args=args) parsed_args = self.parser.parse_args(args) self.cli_ctx.raise_event(EVENT_INVOKER_POST_PARSE_ARGS, command=parsed_args.command, args=parsed_args) # TODO: This fundamentally alters the way Knack.invocation works here. Cannot be customized # with an event. Would need to be customized via inheritance. results = [] for expanded_arg in _explode_list_args(parsed_args): cmd = expanded_arg.func if hasattr(expanded_arg, 'cmd'): expanded_arg.cmd = cmd self.cli_ctx.data['command'] = expanded_arg.command self._validation(expanded_arg) params = self._filter_params(expanded_arg) command_source = self.commands_loader.command_table[ command].command_source extension_version = None try: if command_source: extension_version = get_extension( command_source.extension_name).version except Exception: # pylint: disable=broad-except pass telemetry.set_command_details( self.cli_ctx.data['command'], self.data['output'], [(p.split('=', 1)[0] if p.startswith('--') else p[:2]) for p in args if (p.startswith('-') and len(p) > 1)], extension_name=command_source.extension_name if command_source else None, extension_version=extension_version) if command_source: self.data[ 'command_extension_name'] = command_source.extension_name try: result = cmd(params) if cmd.supports_no_wait and getattr(expanded_arg, 'no_wait', False): result = None elif cmd.no_wait_param and getattr(expanded_arg, cmd.no_wait_param, False): result = None # TODO: Not sure how to make this actually work with the TRANSFORM event... transform_op = cmd.command_kwargs.get('transform', None) if transform_op: result = transform_op(result) if _is_poller(result): result = LongRunningOperation( self.cli_ctx, 'Starting {}'.format(cmd.name))(result) elif _is_paged(result): result = list(result) result = todict(result) event_data = {'result': result} self.cli_ctx.raise_event(EVENT_INVOKER_TRANSFORM_RESULT, event_data=event_data) self.cli_ctx.raise_event(EVENT_INVOKER_FILTER_RESULT, event_data=event_data) result = event_data['result'] results.append(result) except Exception as ex: # pylint: disable=broad-except if cmd.exception_handler: cmd.exception_handler(ex) return None else: six.reraise(*sys.exc_info()) if results and len(results) == 1: results = results[0] return CommandResultItem( results, table_transformer=self.commands_loader.command_table[ parsed_args.command].table_transformer, is_query_active=self.data['query_active'])
def parse(self, args, initial_invocation_data=None, out_file=None): """ Invoke a command. :param args: The arguments that represent the command :type args: list, tuple :param initial_invocation_data: Prime the in memory collection of key-value data for this invocation. :type initial_invocation_data: dict :param out_file: The file to send output to. If not used, we use out_file for knack.cli.CLI instance :type out_file: file-like object :return: The exit code of the invocation :rtype: int """ from knack.util import CommandResultItem if not isinstance(args, (list, tuple)): raise TypeError('args should be a list or tuple.') exit_code = 0 try: if self.enable_color: import colorama colorama.init() if self.out_file == sys.__stdout__: # point out_file to the new sys.stdout which is overwritten by colorama self.out_file = sys.stdout args = self.completion.get_completion_args() or args out_file = out_file or self.out_file self.logging.configure(args) logger.debug('Command arguments: %s', args) self.raise_event(EVENT_CLI_PRE_EXECUTE) if CLI._should_show_version(args): self.show_version() self.result = CommandResultItem(None) else: self.invocation = self.invocation_cls( cli_ctx=self, parser_cls=self.parser_cls, commands_loader_cls=self.commands_loader_cls, help_cls=self.help_cls, initial_data=initial_invocation_data) cmd_result = self.invocation.execute(args) self.result = cmd_result exit_code = self.result.exit_code output_type = self.invocation.data['output'] if cmd_result and cmd_result.result is not None: formatter = self.output.get_formatter(output_type) self.output.out(cmd_result, formatter=formatter, out_file=out_file) # print(self.invocation.expanded_arg, self.invocation.cmd_copy) except KeyboardInterrupt as ex: exit_code = 1 self.result = CommandResultItem(None, error=ex, exit_code=exit_code) except Exception as ex: # pylint: disable=broad-except exit_code = self.exception_handler(ex) self.result = CommandResultItem(None, error=ex, exit_code=exit_code) except SystemExit as ex: exit_code = ex.code self.result = CommandResultItem(None, error=ex, exit_code=exit_code) raise ex finally: self.raise_event(EVENT_CLI_POST_EXECUTE) if self.enable_color: colorama.deinit() return exit_code