コード例 #1
0
    def __init__(self, commands, global_params=True):
        # dictionary of command to descriptions
        self.command_description = commands.descrip
        # from a command to a list of parameters
        self.command_parameters = commands.command_param
        # a list of all the possible parameters
        self.completable_param = commands.completable_param
        # the command tree
        self.command_tree = commands.command_tree
        # a dictionary of parameter (which is command + " " + parameter name)
        # to a description of what it does
        self.param_description = commands.param_descript
        # a dictionary of command to examples of how to use it
        self.command_examples = commands.command_example
        # a dictionary of commands with parameters with multiple names (e.g. {'vm create':{-n: --name}})
        self.same_param_doubles = commands.same_param_doubles or {}

        self._is_command = True

        self.branch = self.command_tree
        self.curr_command = ""

        self.global_param = commands.global_param if global_params else []
        self.output_choices = commands.output_choices if global_params else []
        self.output_options = commands.output_options if global_params else []
        self.global_param_descriptions = commands.global_param_descriptions if global_params else []

        self.global_parser = AzCliCommandParser(add_help=False)
        self.global_parser.add_argument_group('global', 'Global Arguments')
        self.parser = AzCliCommandParser(parents=[self.global_parser])
        self.cmdtab = None
コード例 #2
0
    def __init__(self, commands, global_params=True, outstream=sys.stderr):
        # dictionary of command to descriptions
        self.command_description = commands.descrip
        # from a command to a list of parameters
        self.command_parameters = commands.command_param
        # a list of all the possible parameters
        self.completable_param = commands.completable_param
        # the command tree
        self.command_tree = commands.command_tree
        # a dictionary of parameter (which is command + " " + parameter name)
        # to a description of what it does
        self.param_description = commands.param_descript
        # a dictionary of command to examples of how to use it
        self.command_examples = commands.command_example
        # a dictionary of which parameters mean the same thing
        self.same_param_doubles = commands.same_param_doubles or {}

        self._is_command = True

        self.branch = self.command_tree
        self.curr_command = ""

        self.global_param = commands.global_param if global_params else []
        self.output_choices = commands.output_choices if global_params else []
        self.output_options = commands.output_options if global_params else []
        self.global_param_descriptions = commands.global_param_descriptions if global_params else []

        self.global_parser = AzCliCommandParser(add_help=False)
        self.global_parser.add_argument_group('global', 'Global Arguments')
        self.parser = AzCliCommandParser(parents=[self.global_parser])

        from azclishell._dump_commands import CMD_TABLE
        self.cmdtab = CMD_TABLE
        self.parser.load_command_table(CMD_TABLE)
        self.argsfinder = ArgsFinder(self.parser, outstream)
コード例 #3
0
ファイル: application.py プロジェクト: JasonRShaver/azure-cli
    def __init__(self, configuration=None):
        self._event_handlers = defaultdict(lambda: [])
        self.session = {
            'headers': {
                'x-ms-client-request-id': str(uuid.uuid1())
            },
            'command': 'unknown',
            'completer_active': ARGCOMPLETE_ENV_NAME in os.environ,
            'query_active': False
        }

        # Register presence of and handlers for global parameters
        self.register(self.GLOBAL_PARSER_CREATED, Application._register_builtin_arguments)
        self.register(self.COMMAND_PARSER_PARSED, self._handle_builtin_arguments)

        # Let other extensions make their presence known
        azure.cli.core.extensions.register_extensions(self)

        self.global_parser = AzCliCommandParser(prog='az', add_help=False)
        global_group = self.global_parser.add_argument_group('global', 'Global Arguments')
        self.raise_event(self.GLOBAL_PARSER_CREATED, global_group=global_group)

        self.parser = AzCliCommandParser(prog='az', parents=[self.global_parser])
        self.configuration = configuration
        self.progress_controller = progress.ProgressHook()
コード例 #4
0
    def __init__(self, commands):
        # dictionary of command to descriptions
        self.command_description = commands.descrip
        # from a command to a list of parameters
        self.command_parameters = commands.command_param
        # a list of all the possible parameters
        self.completable_param = commands.completable_param
        # the command tree
        self.command_tree = commands.command_tree
        # a dictionary of parameter (which is command + " " + parameter name)
        # to a description of what it does
        self.param_description = commands.param_descript
        # a dictionary of command to examples of how to use it
        self.command_examples = commands.command_example
        # a dictionary of which parameters mean the same thing
        self.same_param_doubles = commands.same_param_doubles or {}

        self.global_parser = AzCliCommandParser(prog='az', add_help=False)
        self.global_parser.add_argument_group('global', 'Global Arguments')
        self.parser = AzCliCommandParser(prog='az',
                                         parents=[self.global_parser])

        from azclishell._dump_commands import CMD_TABLE as cmd_table
        self.cmdtab = cmd_table
        self.parser.load_command_table(self.cmdtab)
コード例 #5
0
    def __init__(self, config=None):
        self._event_handlers = defaultdict(lambda: [])
        self.session = {
            'headers': {
                'x-ms-client-request-id': str(uuid.uuid1())
            },
            'command': 'unknown',
            'completer_active': ARGCOMPLETE_ENV_NAME in os.environ,
            'query_active': False
        }

        # Register presence of and handlers for global parameters
        self.register(self.GLOBAL_PARSER_CREATED, Application._register_builtin_arguments)
        self.register(self.COMMAND_PARSER_PARSED, self._handle_builtin_arguments)

        # Let other extensions make their presence known
        azure.cli.core.extensions.register_extensions(self)

        self.global_parser = AzCliCommandParser(prog='az', add_help=False)
        global_group = self.global_parser.add_argument_group('global', 'Global Arguments')
        self.raise_event(self.GLOBAL_PARSER_CREATED, global_group=global_group)

        self.parser = AzCliCommandParser(prog='az', parents=[self.global_parser])

        self.initialize(config or Configuration([]))
コード例 #6
0
    def __init__(self, commands, global_params=True, outstream=sys.stderr):
        # dictionary of command to descriptions
        self.command_description = commands.descrip
        # from a command to a list of parameters
        self.command_parameters = commands.command_param
        # a list of all the possible parameters
        self.completable_param = commands.completable_param
        # the command tree
        self.command_tree = commands.command_tree
        # a dictionary of parameter (which is command + " " + parameter name)
        # to a description of what it does
        self.param_description = commands.param_descript
        # a dictionary of command to examples of how to use it
        self.command_examples = commands.command_example
        # a dictionary of which parameters mean the same thing
        self.same_param_doubles = commands.same_param_doubles or {}

        self._is_command = True

        self.branch = self.command_tree
        self.curr_command = ""

        self.global_param = commands.global_param if global_params else []
        self.output_choices = commands.output_choices if global_params else []
        self.output_options = commands.output_options if global_params else []
        self.global_param_descriptions = commands.global_param_descriptions if global_params else []

        self.global_parser = AzCliCommandParser(add_help=False)
        self.global_parser.add_argument_group('global', 'Global Arguments')
        self.parser = AzCliCommandParser(parents=[self.global_parser])

        from azclishell._dump_commands import CMD_TABLE
        self.cmdtab = CMD_TABLE
        self.parser.load_command_table(CMD_TABLE)
        self.argsfinder = ArgsFinder(self.parser, outstream)
コード例 #7
0
ファイル: test_parser.py プロジェクト: balajikris/azure-cli
    def test_register_simple_commands(self):
        def test_handler1():
            pass

        def test_handler2():
            pass

        command = CliCommand('command the-name', test_handler1)
        command2 = CliCommand('sub-command the-second-name', test_handler2)
        cmd_table = {
            'command the-name': command,
            'sub-command the-second-name': command2
        }

        parser = AzCliCommandParser()
        parser.load_command_table(cmd_table)
        args = parser.parse_args('command the-name'.split())
        self.assertIs(args.func, test_handler1)

        args = parser.parse_args('sub-command the-second-name'.split())
        self.assertIs(args.func, test_handler2)

        AzCliCommandParser.error = VerifyError(self, )
        parser.parse_args('sub-command'.split())
        self.assertTrue(AzCliCommandParser.error.called)
コード例 #8
0
ファイル: test_parser.py プロジェクト: mevtorres/Azure-CLI
    def test_register_simple_commands(self):
        def test_handler1():
            pass

        def test_handler2():
            pass

        cli = DummyCli()
        cli.loader = mock.MagicMock()
        cli.loader.cli_ctx = cli

        command = AzCliCommand(cli.loader, 'command the-name', test_handler1)
        command2 = AzCliCommand(cli.loader, 'sub-command the-second-name',
                                test_handler2)
        cmd_table = {
            'command the-name': command,
            'sub-command the-second-name': command2
        }
        cli.commands_loader.command_table = cmd_table

        parser = AzCliCommandParser(cli)
        parser.load_command_table(cli.commands_loader)
        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)

        AzCliCommandParser.error = VerifyError(self, )
        parser.parse_args('sub-command'.split())
        self.assertTrue(AzCliCommandParser.error.called)
コード例 #9
0
ファイル: az_completer.py プロジェクト: LukaszStem/azure-cli
    def __init__(self, commands, global_params=True):
        # dictionary of command to descriptions
        self.command_description = commands.descrip
        # from a command to a list of parameters
        self.command_parameters = commands.command_param
        # a list of all the possible parameters
        self.completable_param = commands.completable_param
        # the command tree
        self.command_tree = commands.command_tree
        # a dictionary of parameter (which is command + " " + parameter name)
        # to a description of what it does
        self.param_description = commands.param_descript
        # a dictionary of command to examples of how to use it
        self.command_examples = commands.command_example
        # a dictionary of commands with parameters with multiple names (e.g. {'vm create':{-n: --name}})
        self.same_param_doubles = commands.same_param_doubles or {}

        self._is_command = True

        self.branch = self.command_tree
        self.curr_command = ""

        self.global_param = commands.global_param if global_params else []
        self.output_choices = commands.output_choices if global_params else []
        self.output_options = commands.output_options if global_params else []
        self.global_param_descriptions = commands.global_param_descriptions if global_params else []

        self.global_parser = AzCliCommandParser(add_help=False)
        self.global_parser.add_argument_group('global', 'Global Arguments')
        self.parser = AzCliCommandParser(parents=[self.global_parser])
        self.cmdtab = None
コード例 #10
0
ファイル: test_parser.py プロジェクト: mevtorres/Azure-CLI
    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

        cli = DummyCli()
        cli.loader = mock.MagicMock()
        cli.loader.cli_ctx = cli

        command = AzCliCommand(cli.loader, 'test command', test_handler)
        command.add_argument('opt',
                             '--opt',
                             required=True,
                             **enum_choice_list(TestEnum))
        cmd_table = {'test command': command}
        cli.commands_loader.command_table = cmd_table

        parser = AzCliCommandParser(cli)
        parser.load_command_table(cli.commands_loader)

        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')
コード例 #11
0
ファイル: test_parser.py プロジェクト: jiayexie/azure-cli
    def test_register_simple_commands(self):
        def test_handler1():
            pass

        def test_handler2():
            pass

        cli = TestCli()
        cli.loader = mock.MagicMock()
        cli.loader.cli_ctx = cli

        command = AzCliCommand(cli.loader, 'command the-name', test_handler1)
        command2 = AzCliCommand(cli.loader, 'sub-command the-second-name', test_handler2)
        cmd_table = {'command the-name': command, 'sub-command the-second-name': command2}
        cli.commands_loader.command_table = cmd_table

        parser = AzCliCommandParser(cli)
        parser.load_command_table(cli.commands_loader)
        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)

        AzCliCommandParser.error = VerifyError(self,)
        parser.parse_args('sub-command'.split())
        self.assertTrue(AzCliCommandParser.error.called)
コード例 #12
0
ファイル: test_parser.py プロジェクト: LukaszStem/azure-cli
    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')
コード例 #13
0
ファイル: az_completer.py プロジェクト: yorek/azure-cli
    def __init__(self, shell_ctx, commands, global_params=True):
        self.shell_ctx = shell_ctx
        self.started = False

        # dictionary of command to descriptions
        self.command_description = {}
        # a list of all the possible parameters
        self.completable_param = None
        # the command tree
        self.command_tree = None
        # a dictionary of parameter (which is command + " " + parameter name)
        # to a description of what it does
        self.param_description = None
        # a dictionary of command to examples of how to use it
        self.command_examples = None
        # a dictionary of commands with parameters with multiple names (e.g. {'vm create':{-n: --name}})
        self.command_param_info = {}

        # information about what completions to generate
        self.current_command = ''
        self.unfinished_word = ''
        self.subtree = None
        self.leftover_args = None
        self.complete_command = False

        self.global_param = []
        self.output_choices = []
        self.output_options = []
        self.global_param_descriptions = []

        self.global_parser = AzCliCommandParser(add_help=False)
        self.global_parser.add_argument_group('global', 'Global Arguments')
        self.parser = AzCliCommandParser(parents=[self.global_parser])
        self.argsfinder = ArgsFinder(self.parser)
        self.cmdtab = {}

        if commands:
            self.start(commands, global_params=global_params)
コード例 #14
0
ファイル: application.py プロジェクト: LukaszStem/azure-cli
    def __init__(self, configuration=None):
        self._event_handlers = defaultdict(lambda: [])
        self.session = {
            'headers': {},  # the x-ms-client-request-id is generated before a command is to execute
            'command': 'unknown',
            'completer_active': ARGCOMPLETE_ENV_NAME in os.environ,
            'query_active': False,
            'az_interactive_active': False
        }

        # Register presence of and handlers for global parameters
        self.register(self.GLOBAL_PARSER_CREATED, Application._register_builtin_arguments)
        self.register(self.COMMAND_PARSER_PARSED, self._handle_builtin_arguments)

        # Let other extensions make their presence known
        azure.cli.core.extensions.register_extensions(self)

        self.global_parser = AzCliCommandParser(prog='az', add_help=False)
        global_group = self.global_parser.add_argument_group('global', 'Global Arguments')
        self.raise_event(self.GLOBAL_PARSER_CREATED, global_group=global_group)

        self.parser = AzCliCommandParser(prog='az', parents=[self.global_parser])
        self.configuration = configuration
        self.progress_controller = progress.ProgressHook()
コード例 #15
0
ファイル: test_parser.py プロジェクト: balajikris/azure-cli
    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, test_handler)

        AzCliCommandParser.error = VerifyError(self)
        parser.parse_args('test command -req yep'.split())
        self.assertTrue(AzCliCommandParser.error.called)
コード例 #16
0
ファイル: test_parser.py プロジェクト: balajikris/azure-cli
    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)
コード例 #17
0
ファイル: test_parser.py プロジェクト: BurtBiel/azure-cli
    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)
コード例 #18
0
ファイル: test_parser.py プロジェクト: LukaszStem/azure-cli
    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)
コード例 #19
0
ファイル: test_parser.py プロジェクト: zooba/azure-cli
    def test_nargs_parameter(self):
        def test_handler():
            pass

        cli = TestCli()
        cli.loader = mock.MagicMock()
        cli.loader.cli_ctx = cli

        command = AzCliCommand(cli.loader, 'test command', test_handler)
        command.add_argument('req', '--req', required=True, nargs=2)
        cmd_table = {'test command': command}

        parser = AzCliCommandParser(cli)
        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)
コード例 #20
0
ファイル: test_parser.py プロジェクト: mevtorres/Azure-CLI
    def test_required_parameter(self):
        def test_handler(args):  # pylint: disable=unused-argument
            pass

        cli = DummyCli()
        cli.loader = mock.MagicMock()
        cli.loader.cli_ctx = cli

        command = AzCliCommand(cli.loader, 'test command', test_handler)
        command.add_argument('req', '--req', required=True)
        cmd_table = {'test command': command}
        cli.commands_loader.command_table = cmd_table

        parser = AzCliCommandParser(cli)
        parser.load_command_table(cli.commands_loader)

        args = parser.parse_args('test command --req yep'.split())
        self.assertIs(args.func, command)

        AzCliCommandParser.error = VerifyError(self)
        parser.parse_args('test command'.split())
        self.assertTrue(AzCliCommandParser.error.called)
コード例 #21
0
ファイル: test_parser.py プロジェクト: LukaszStem/azure-cli
    def test_register_simple_commands(self):
        def test_handler1():
            pass

        def test_handler2():
            pass

        command = CliCommand('command the-name', test_handler1)
        command2 = CliCommand('sub-command the-second-name', test_handler2)
        cmd_table = {'command the-name': command, 'sub-command the-second-name': command2}

        parser = AzCliCommandParser()
        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)

        AzCliCommandParser.error = VerifyError(self,)
        parser.parse_args('sub-command'.split())
        self.assertTrue(AzCliCommandParser.error.called)
コード例 #22
0
ファイル: test_parser.py プロジェクト: wiebeck/azure-cli
    def test_nargs_parameter(self):
        def test_handler():
            pass

        cli = DummyCli()
        cli.loader = mock.MagicMock()
        cli.loader.cli_ctx = cli

        command = AzCliCommand(cli.loader, 'test command', test_handler)
        command.add_argument('req', '--req', required=True, nargs=2)
        cmd_table = {'test command': command}
        cli.commands_loader.command_table = cmd_table

        parser = AzCliCommandParser(cli)
        parser.load_command_table(cli.commands_loader)

        args = parser.parse_args('test command --req yep nope'.split())
        self.assertIs(args.func, command)

        with mock.patch('azure.cli.core.parser.AzCliCommandParser.error', new=VerifyError(self)):
            parser.parse_args('test command -req yep'.split())
            self.assertTrue(AzCliCommandParser.error.called)
コード例 #23
0
ファイル: test_parser.py プロジェクト: sptramer/azure-cli
    def test_required_parameter(self):
        def test_handler(args):  # pylint: disable=unused-argument
            pass

        cli = DummyCli()
        cli.loader = mock.MagicMock()
        cli.loader.cli_ctx = cli

        command = AzCliCommand(cli.loader, 'test command', test_handler)
        command.add_argument('req', '--req', required=True)
        cmd_table = {'test command': command}
        cli.commands_loader.command_table = cmd_table

        parser = AzCliCommandParser(cli)
        parser.load_command_table(cli.commands_loader)

        args = parser.parse_args('test command --req yep'.split())
        self.assertIs(args.func, command)

        AzCliCommandParser.error = VerifyError(self)
        parser.parse_args('test command'.split())
        self.assertTrue(AzCliCommandParser.error.called)
コード例 #24
0
ファイル: test_parser.py プロジェクト: jiayexie/azure-cli
    def test_nargs_parameter(self):
        def test_handler():
            pass

        cli = TestCli()
        cli.loader = mock.MagicMock()
        cli.loader.cli_ctx = cli

        command = AzCliCommand(cli.loader, 'test command', test_handler)
        command.add_argument('req', '--req', required=True, nargs=2)
        cmd_table = {'test command': command}
        cli.commands_loader.command_table = cmd_table

        parser = AzCliCommandParser(cli)
        parser.load_command_table(cli.commands_loader)

        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)
コード例 #25
0
class AzCompleter(Completer):
    """ Completes Azure CLI commands """
    def __init__(self, shell_ctx, commands, global_params=True):
        self.shell_ctx = shell_ctx
        # dictionary of command to descriptions
        self.command_description = commands.descrip
        # from a command to a list of parameters
        self.command_parameters = commands.command_param
        # a list of all the possible parameters
        self.completable_param = commands.completable_param
        # the command tree
        self.command_tree = commands.command_tree
        # a dictionary of parameter (which is command + " " + parameter name)
        # to a description of what it does
        self.param_description = commands.param_descript
        # a dictionary of command to examples of how to use it
        self.command_examples = commands.command_example
        # a dictionary of commands with parameters with multiple names (e.g. {'vm create':{-n: --name}})
        self.same_param_doubles = commands.same_param_doubles or {}

        self._is_command = True

        self.branch = self.command_tree
        self.curr_command = ""

        self.global_param = commands.global_param if global_params else []
        self.output_choices = commands.output_choices if global_params else []
        self.output_options = commands.output_options if global_params else []
        self.global_param_descriptions = commands.global_param_descriptions if global_params else []

        self.global_parser = AzCliCommandParser(add_help=False)
        self.global_parser.add_argument_group('global', 'Global Arguments')
        self.parser = AzCliCommandParser(parents=[self.global_parser])
        self.argsfinder = ArgsFinder(self.parser)
        self.cmdtab = {}

    def validate_completion(self,
                            param,
                            words,
                            text_before_cursor,
                            check_double=True):
        """ validates that a param should be completed """
        # validates the position of the parameter
        position = param.lower().startswith(
            words.lower()) and not text_before_cursor[-1].isspace()
        # cancels parameters that are already in the in line
        canceling_positions = param.lower() != words.lower(
        ) and param not in text_before_cursor.split()

        found_double = True
        # checks for aliasing of parameters

        if check_double:
            for double_sets in self.same_param_doubles.get(
                    self.curr_command, []):
                # if the parameter is in any of the sets
                if param in double_sets:
                    # if any of the other aliases are in the line already
                    found_double = not any(
                        alias in text_before_cursor.split() and alias != param
                        for alias in double_sets)

        return position and canceling_positions and found_double

    def get_completions(self, document, complete_event):
        text = document.text_before_cursor
        self.branch = self.command_tree
        self.curr_command = ''
        self._is_command = True
        text = self.reformat_cmd(text)
        if text.split():

            for comp in sort_completions(
                    self.gen_cmd_and_param_completions(text)):
                yield comp

        for cmd in sort_completions(self.gen_cmd_completions(text)):
            yield cmd

        if self.cmdtab:
            for val in sort_completions(self.gen_dynamic_completions(text)):
                yield val

        for param in sort_completions(self.gen_global_param_completions(text)):
            yield param

    def gen_enum_completions(self, arg_name, text, started_param, prefix):
        """ generates dynamic enumeration completions """
        try:  # if enum completion
            for choice in self.cmdtab[
                    self.curr_command].arguments[arg_name].choices:
                if started_param:
                    if choice.lower().startswith(
                            prefix.lower()) and choice not in text.split():
                        yield Completion(choice, -len(prefix))
                else:
                    yield Completion(choice, -len(prefix))

        except TypeError:  # there is no choices option
            pass

    def get_arg_name(self, is_param, param):
        """ gets the argument name used in the command table for a parameter """
        if self.curr_command in self.cmdtab and is_param:
            for arg in self.cmdtab[self.curr_command].arguments:

                for name in self.cmdtab[
                        self.curr_command].arguments[arg].options_list:
                    if name == param:
                        return arg
        return None

    def mute_parse_args(self, text):
        """ mutes the parser error when parsing, then puts it back """
        error = AzCliCommandParser.error
        AzCliCommandParser.error = error_pass

        parse_args = self.argsfinder.get_parsed_args(
            parse_quotes(text, quotes=False, string=False))

        AzCliCommandParser.error = error
        return parse_args

    # pylint: disable=too-many-branches
    def gen_dynamic_completions(self, text):
        """ generates the dynamic values, like the names of resource groups """
        try:  # pylint: disable=too-many-nested-blocks

            is_param, started_param, prefix, param = dynamic_param_logic(text)

            # command table specific name
            arg_name = self.get_arg_name(is_param, param)

            if arg_name and (
                (text.split()[-1].startswith('-') and text[-1].isspace())
                    or text.split()[-2].startswith('-')):

                for comp in self.gen_enum_completions(arg_name, text,
                                                      started_param, prefix):
                    yield comp

                parse_args = self.mute_parse_args(text)

                # there are 3 formats for completers the cli uses
                # this try catches which format it is
                if self.cmdtab[
                        self.curr_command].arguments[arg_name].completer:
                    try:
                        for comp in self.cmdtab[self.curr_command].arguments[
                                arg_name].completer(prefix=prefix,
                                                    action=None,
                                                    parsed_args=parse_args):
                            for completion in verify_dynamic_completion(
                                    comp, started_param, prefix, text):
                                yield completion
                    except TypeError:
                        try:
                            for comp in self.cmdtab[self.curr_command].\
                                    arguments[arg_name].completer(prefix=prefix):

                                for completion in verify_dynamic_completion(
                                        comp, started_param, prefix, text):
                                    yield completion
                        except TypeError:
                            try:
                                for comp in self.cmdtab[self.curr_command].\
                                        arguments[arg_name].completer():

                                    for completion in verify_dynamic_completion(
                                            comp, started_param, prefix, text):
                                        yield completion

                            except TypeError:
                                pass  # other completion method used

        # if the user isn't logged in
        except Exception:  # pylint: disable=broad-except
            pass

    def gen_cmd_completions(self, text):
        """ whether is a space or no text typed, send the current branch """
        # if nothing, so first level commands
        if not text.split() and self._is_command:
            if self.branch.children is not None:
                for com in self.branch.children:
                    yield Completion(com.data)

        # if space show current level commands
        elif text.split() and text[-1].isspace() and self._is_command:
            if self.branch is not self.command_tree:
                for com in self.branch.children:
                    yield Completion(com.data)

    def yield_param_completion(self, param, last_word):
        """ yields a parameter """
        return Completion(param,
                          -len(last_word),
                          display_meta=self.param_description.get(
                              self.curr_command + " " + str(param),
                              '').replace('\n', ''))

    def gen_cmd_and_param_completions(self, text):
        """ generates command and parameter completions """
        temp_command = str('')
        txtspt = text.split()

        for word in txtspt:
            if word.startswith("-"):
                self._is_command = False

            # building what the command is
            elif self._is_command:
                temp_command += ' ' + str(word) if temp_command else str(word)

            mid_val = text.find(word) + len(word)
            # moving down command tree
            if self.branch.has_child(
                    word) and len(text) > mid_val and text[mid_val].isspace():
                self.branch = self.branch.get_child(word, self.branch.children)

        if text and text[-1].isspace():
            if in_tree(self.command_tree, temp_command):
                self.curr_command = temp_command
            else:
                self._is_command = False
        else:
            self.curr_command = temp_command

        last_word = txtspt[-1]
        # this is for single char parameters
        if last_word.startswith("-") and not last_word.startswith("--"):
            self._is_command = False
            if self.curr_command in self.command_parameters:
                for param in self.command_parameters[self.curr_command]:
                    if self.validate_completion(param, last_word, text) and\
                            not param.startswith("--"):
                        yield self.yield_param_completion(param, last_word)

        elif last_word.startswith("--"):  # for regular parameters
            self._is_command = False

            if self.curr_command in self.command_parameters:  # Everything should, map to empty list
                for param in self.command_parameters[self.curr_command]:
                    if self.validate_completion(param, last_word, text):
                        yield self.yield_param_completion(param, last_word)

        if self.branch.children and self._is_command:  # all underneath commands
            for kid in self.branch.children:
                if self.validate_completion(kid.data, txtspt[-1], text, False):
                    yield Completion(str(kid.data), -len(txtspt[-1]))
        elif self._is_command and self.curr_command.strip(
        ) in self.command_parameters:
            for param in self.command_parameters[self.curr_command.strip()]:
                if param.startswith('--'):
                    yield self.yield_param_completion(param, '')

    def gen_global_param_completions(self, text):
        """ Global parameter stuff hard-coded in """
        txtspt = text.split()
        if txtspt and txtspt:
            for param in self.global_param:
                # for single dash global parameters
                if txtspt[-1].startswith('-') \
                        and not txtspt[-1].startswith('--') and \
                        param.startswith('-') and not param.startswith('--') and\
                        self.validate_completion(param, txtspt[-1], text, check_double=False):
                    yield Completion(
                        param,
                        -len(txtspt[-1]),
                        display_meta=self.global_param_descriptions[param])
                # for double dash global parameters
                elif txtspt[-1].startswith('--') and \
                        self.validate_completion(param, txtspt[-1], text, check_double=False):
                    yield Completion(
                        param,
                        -len(txtspt[-1]),
                        display_meta=self.global_param_descriptions[param])
            # if there is an output, gets the options without user typing
            if txtspt[-1] in self.output_options:
                for opt in self.output_choices:
                    yield Completion(opt)
            # if there is an output option, if they have started typing
            if len(txtspt) > 1 and\
                    txtspt[-2] in self.output_options:
                for opt in self.output_choices:
                    if self.validate_completion(opt,
                                                txtspt[-1],
                                                text,
                                                check_double=False):
                        yield Completion(opt, -len(txtspt[-1]))

    def is_completable(self, symbol):
        """ whether the word can be completed as a command or parameter """
        return symbol in self.command_parameters or symbol in self.param_description.keys(
        )

    def has_description(self, param):
        """ if a parameter has a description """
        return param in self.param_description.keys() and \
            not self.param_description[param].isspace()

    def reformat_cmd(self, text):
        """ reformat the text to be stripped of noise """
        # remove az if there
        text = text.replace('az', '')
        # disregard defaulting symbols
        if text and SELECT_SYMBOL['scope'] == text[0:2]:
            text = text.replace(SELECT_SYMBOL['scope'], "")

        if self.shell_ctx.default_command:
            text = self.shell_ctx.default_command + ' ' + text
        return text
コード例 #26
0
ファイル: application.py プロジェクト: LukaszStem/azure-cli
class Application(object):

    TRANSFORM_RESULT = 'Application.TransformResults'
    FILTER_RESULT = 'Application.FilterResults'
    GLOBAL_PARSER_CREATED = 'GlobalParser.Created'
    COMMAND_PARSER_LOADED = 'CommandParser.Loaded'
    COMMAND_PARSER_PARSING = 'CommandParser.Parsing'
    COMMAND_PARSER_PARSED = 'CommandParser.Parsed'
    COMMAND_TABLE_LOADED = 'CommandTable.Loaded'
    COMMAND_TABLE_PARAMS_LOADED = 'CommandTableParams.Loaded'

    def __init__(self, configuration=None):
        self._event_handlers = defaultdict(lambda: [])
        self.session = {
            'headers': {},  # the x-ms-client-request-id is generated before a command is to execute
            'command': 'unknown',
            'completer_active': ARGCOMPLETE_ENV_NAME in os.environ,
            'query_active': False,
            'az_interactive_active': False
        }

        # Register presence of and handlers for global parameters
        self.register(self.GLOBAL_PARSER_CREATED, Application._register_builtin_arguments)
        self.register(self.COMMAND_PARSER_PARSED, self._handle_builtin_arguments)

        # Let other extensions make their presence known
        azure.cli.core.extensions.register_extensions(self)

        self.global_parser = AzCliCommandParser(prog='az', add_help=False)
        global_group = self.global_parser.add_argument_group('global', 'Global Arguments')
        self.raise_event(self.GLOBAL_PARSER_CREATED, global_group=global_group)

        self.parser = AzCliCommandParser(prog='az', parents=[self.global_parser])
        self.configuration = configuration
        self.progress_controller = progress.ProgressHook()

    def get_progress_controller(self, det=False):
        self.progress_controller.init_progress(progress.get_progress_view(det))
        return self.progress_controller

    def initialize(self, configuration):
        self.configuration = configuration

    def execute(self, unexpanded_argv):  # pylint: disable=too-many-statements
        self.refresh_request_id()

        argv = Application._expand_file_prefixed_files(unexpanded_argv)
        command_table = self.configuration.get_command_table(argv)
        self.raise_event(self.COMMAND_TABLE_LOADED, command_table=command_table)
        self.parser.load_command_table(command_table)
        self.raise_event(self.COMMAND_PARSER_LOADED, parser=self.parser)

        if not argv:
            enable_autocomplete(self.parser)
            az_subparser = self.parser.subparsers[tuple()]
            _help.show_welcome(az_subparser)

            # TODO: Question, is this needed?
            telemetry.set_command_details('az')
            telemetry.set_success(summary='welcome')

            return None

        if argv[0].lower() == 'help':
            argv[0] = '--help'

        # Rudimentary parsing to get the command
        nouns = []
        for i, current in enumerate(argv):
            try:
                if current[0] == '-':
                    break
            except IndexError:
                pass
            argv[i] = current.lower()
            nouns.append(argv[i])

        command = ' '.join(nouns)

        if argv[-1] in ('--help', '-h') or command in command_table:
            self.configuration.load_params(command)
            self.raise_event(self.COMMAND_TABLE_PARAMS_LOADED, command_table=command_table)
            self.parser.load_command_table(command_table)

        if self.session['completer_active']:
            enable_autocomplete(self.parser)

        self.raise_event(self.COMMAND_PARSER_PARSING, argv=argv)
        args = self.parser.parse_args(argv)

        self.raise_event(self.COMMAND_PARSER_PARSED, command=args.command, args=args)
        results = []
        for expanded_arg in _explode_list_args(args):
            self.session['command'] = expanded_arg.command
            try:
                _validate_arguments(expanded_arg)
            except CLIError:
                raise
            except:  # pylint: disable=bare-except
                err = sys.exc_info()[1]
                getattr(expanded_arg, '_parser', self.parser).validation_error(str(err))

            # Consider - we are using any args that start with an underscore (_) as 'private'
            # arguments and remove them from the arguments that we pass to the actual function.
            # This does not feel quite right.
            params = dict([(key, value)
                           for key, value in expanded_arg.__dict__.items()
                           if not key.startswith('_')])
            params.pop('subcommand', None)
            params.pop('func', None)
            params.pop('command', None)

            telemetry.set_command_details(expanded_arg.command,
                                          self.configuration.output_format,
                                          [p for p in unexpanded_argv if p.startswith('-')])

            result = expanded_arg.func(params)
            result = todict(result)
            results.append(result)

        if len(results) == 1:
            results = results[0]

        event_data = {'result': results}
        self.raise_event(self.TRANSFORM_RESULT, event_data=event_data)
        self.raise_event(self.FILTER_RESULT, event_data=event_data)

        return CommandResultItem(event_data['result'],
                                 table_transformer=command_table[args.command].table_transformer,
                                 is_query_active=self.session['query_active'])

    def raise_event(self, name, **kwargs):
        '''Raise the event `name`.
        '''
        data = truncate_text(str(kwargs), width=500)
        logger.debug("Application event '%s' with event data %s", name, data)
        for func in list(self._event_handlers[name]):  # Make copy in case handler modifies the list
            func(**kwargs)

    def register(self, name, handler):
        '''Register a callable that will be called when the
        event `name` is raised.

        param: name: The name of the event
        param: handler: Function that takes two parameters;
          name: name of the event raised
          event_data: `dict` with event specific data.
        '''
        self._event_handlers[name].append(handler)
        logger.debug("Registered application event handler '%s' at %s", name, handler)

    def remove(self, name, handler):
        '''Remove a callable that is registered to be called when the
        event `name` is raised.

        param: name: The name of the event
        param: handler: Function that takes two parameters;
          name: name of the event raised
          event_data: `dict` with event specific data.
        '''
        self._event_handlers[name].remove(handler)
        logger.debug("Removed application event handler '%s' at %s", name, handler)

    def refresh_request_id(self):
        """Assign a new randome GUID as x-ms-client-request-id

        The method must be invoked before each command execution in order to ensure unique client side request ID is
        generated.
        """
        self.session['headers']['x-ms-client-request-id'] = str(uuid.uuid1())

    @staticmethod
    def _register_builtin_arguments(**kwargs):
        global_group = kwargs['global_group']
        global_group.add_argument('--output', '-o', dest='_output_format',
                                  choices=['json', 'tsv', 'table', 'jsonc'],
                                  default=az_config.get('core', 'output', fallback='json'),
                                  help='Output format',
                                  type=str.lower)
        # The arguments for verbosity don't get parsed by argparse but we add it here for help.
        global_group.add_argument('--verbose', dest='_log_verbosity_verbose', action='store_true',
                                  help='Increase logging verbosity. Use --debug for full debug logs.')
        global_group.add_argument('--debug', dest='_log_verbosity_debug', action='store_true',
                                  help='Increase logging verbosity to show all debug logs.')

    @staticmethod
    def _maybe_load_file(arg):
        ix = arg.find('@')
        if ix == -1:  # no @ found
            return arg

        poss_file = arg[ix + 1:]
        if not poss_file:  # if nothing after @ then it can't be a file
            return arg
        elif ix == 0:
            return Application._load_file(poss_file)

        # if @ not at the start it can't be a file
        return arg

    @staticmethod
    def _expand_file_prefix(arg):
        arg_split = arg.split('=', 1)
        try:
            return '='.join([arg_split[0], Application._maybe_load_file(arg_split[1])])
        except IndexError:
            return Application._maybe_load_file(arg_split[0])

    @staticmethod
    def _expand_file_prefixed_files(argv):
        return list([Application._expand_file_prefix(arg) for arg in argv])

    @staticmethod
    def _load_file(path):
        if path == '-':
            content = sys.stdin.read()
        else:
            content = read_file_content(os.path.expanduser(path),
                                        allow_binary=True)

        return content[0:-1] if content and content[-1] == '\n' else content

    def _handle_builtin_arguments(self, **kwargs):
        args = kwargs['args']
        self.configuration.output_format = args._output_format  # pylint: disable=protected-access
        del args._output_format
コード例 #27
0
ファイル: az_completer.py プロジェクト: yorek/azure-cli
class AzCompleter(Completer):
    """ Completes Azure CLI commands """

    def __init__(self, shell_ctx, commands, global_params=True):
        self.shell_ctx = shell_ctx
        self.started = False

        # dictionary of command to descriptions
        self.command_description = {}
        # a list of all the possible parameters
        self.completable_param = None
        # the command tree
        self.command_tree = None
        # a dictionary of parameter (which is command + " " + parameter name)
        # to a description of what it does
        self.param_description = None
        # a dictionary of command to examples of how to use it
        self.command_examples = None
        # a dictionary of commands with parameters with multiple names (e.g. {'vm create':{-n: --name}})
        self.command_param_info = {}

        # information about what completions to generate
        self.current_command = ''
        self.unfinished_word = ''
        self.subtree = None
        self.leftover_args = None
        self.complete_command = False

        self.global_param = []
        self.output_choices = []
        self.output_options = []
        self.global_param_descriptions = []

        self.global_parser = AzCliCommandParser(add_help=False)
        self.global_parser.add_argument_group('global', 'Global Arguments')
        self.parser = AzCliCommandParser(parents=[self.global_parser])
        self.argsfinder = ArgsFinder(self.parser)
        self.cmdtab = {}

        if commands:
            self.start(commands, global_params=global_params)

    def __bool__(self):
        return self.started

    def start(self, commands, global_params=True):
        self.started = True
        self.command_description = commands.descrip
        self.completable_param = commands.completable_param
        self.command_tree = commands.command_tree
        self.param_description = commands.param_descript
        self.command_examples = commands.command_example
        self.command_param_info = commands.command_param_info or self.command_param_info

        if global_params:
            self.global_param = commands.global_param
            self.output_choices = commands.output_choices
            self.output_options = commands.output_options
            self.global_param_descriptions = commands.global_param_descriptions

    def initialize_command_table_attributes(self):
        from ._dump_commands import FreshTable
        loader = FreshTable(self.shell_ctx).loader
        if loader and loader.command_table:
            self.cmdtab = loader.command_table
            self.parser.load_command_table(loader)
            self.argsfinder = ArgsFinder(self.parser)

    def validate_param_completion(self, param, leftover_args):
        """ validates that a param should be completed """
        # validates param starts with unfinished word
        completes = self.validate_completion(param)

        # show parameter completions when started
        full_param = self.unfinished_word.startswith("--") and param.startswith("--")
        char_param = self.unfinished_word.startswith("-") and not param.startswith("--")

        # show full parameters before any are used
        new_param = not self.unfinished_word and not leftover_args and param.startswith("--")

        # checks for parameters already in the line as well as aliases
        no_doubles = True
        command_doubles = self.command_param_info.get(self.current_command, {})
        for alias in command_doubles.get(param, []):
            if alias in leftover_args:
                no_doubles = False

        return completes and no_doubles and any((full_param, char_param, new_param))

    def validate_completion(self, completion):
        return completion.lower().startswith(self.unfinished_word.lower())

    def process_dynamic_completion(self, completion):
        """ how to validate and generate completion for dynamic params """
        if len(completion.split()) > 1:
            completion = '\"' + completion + '\"'

        if self.validate_completion(completion):
            yield Completion(completion, -len(self.unfinished_word))

    def get_completions(self, document, complete_event):
        if not self.started:
            return

        text = self.reformat_cmd(document.text_before_cursor)
        event_payload = {
            'text': text
        }
        self.shell_ctx.cli_ctx.raise_event(EVENT_INTERACTIVE_PRE_COMPLETER_TEXT_PARSING, event_payload=event_payload)
        # Reload various attributes from event_payload
        text = event_payload.get('text', text)
        text_split = text.split()
        self.unfinished_word = ''
        new_word = text and text[-1].isspace()
        if not new_word and text_split:
            self.unfinished_word = text_split[-1]
            text_split = text_split[:-1]

        self.subtree, self.current_command, self.leftover_args = self.command_tree.get_sub_tree(text_split)
        self.shell_ctx.cli_ctx.raise_event(EVENT_INTERACTIVE_POST_SUB_TREE_CREATE, subtree=self.subtree)
        self.complete_command = not self.subtree.children

        for comp in sort_completions(self.gen_cmd_and_param_completions()):
            yield comp

        for comp in sort_completions(self.gen_global_params_and_arg_completions()):
            yield comp

        if self.complete_command and self.cmdtab and self.leftover_args and self.leftover_args[-1].startswith('-'):
            for comp in sort_completions(self.gen_dynamic_completions(text)):
                yield comp

    def gen_enum_completions(self, arg_name):
        """ generates dynamic enumeration completions """
        try:  # if enum completion
            for choice in self.cmdtab[self.current_command].arguments[arg_name].choices:
                if self.validate_completion(choice):
                    yield Completion(choice, -len(self.unfinished_word))

        except TypeError:  # there is no choices option
            pass

    def get_arg_name(self, param):
        """ gets the argument name used in the command table for a parameter """
        if self.current_command in self.cmdtab:
            for arg in self.cmdtab[self.current_command].arguments:

                for name in self.cmdtab[self.current_command].arguments[arg].options_list:
                    if name == param:
                        return arg
        return None

    # pylint: disable=protected-access
    def mute_parse_args(self, text):
        """ mutes the parser error when parsing, then puts it back """
        error = AzCliCommandParser.error
        _check_value = AzCliCommandParser._check_value

        AzCliCommandParser.error = error_pass
        AzCliCommandParser._check_value = _check_value_muted

        # No exception is expected. However, we add this try-catch block, as this may have far-reaching effects.
        try:
            parse_args = self.argsfinder.get_parsed_args(parse_quotes(text, quotes=False, string=False))
        except Exception:  # pylint: disable=broad-except
            pass

        AzCliCommandParser.error = error
        AzCliCommandParser._check_value = _check_value
        return parse_args

    # pylint: disable=too-many-branches
    def gen_dynamic_completions(self, text):
        """ generates the dynamic values, like the names of resource groups """
        try:  # pylint: disable=too-many-nested-blocks
            param = self.leftover_args[-1]

            # command table specific name
            arg_name = self.get_arg_name(param)

            for comp in self.gen_enum_completions(arg_name):
                yield comp

            parsed_args = self.mute_parse_args(text)

            # there are 3 formats for completers the cli uses
            # this try catches which format it is
            if self.cmdtab[self.current_command].arguments[arg_name].completer:
                completions = []
                try:
                    completions = self.cmdtab[self.current_command].arguments[arg_name].completer(
                        prefix=self.unfinished_word, action=None, parsed_args=parsed_args)
                except TypeError:
                    try:
                        completions = self.cmdtab[self.current_command].arguments[arg_name].completer(
                            prefix=self.unfinished_word)
                    except TypeError:
                        try:
                            completions = self.cmdtab[self.current_command].arguments[arg_name].completer()
                        except TypeError:
                            pass  # other completion method used

                for comp in completions:
                    for completion in self.process_dynamic_completion(comp):
                        yield completion

        # if the user isn't logged in
        except Exception:  # pylint: disable=broad-except
            pass

    def yield_param_completion(self, param, last_word):
        """ yields a parameter """
        return Completion(param, -len(last_word), display_meta=self.param_description.get(
            self.current_command + " " + str(param), '').replace(os.linesep, ''))

    def gen_cmd_and_param_completions(self):
        """ generates command and parameter completions """
        if self.complete_command:
            for param in self.command_param_info.get(self.current_command, []):
                if self.validate_param_completion(param, self.leftover_args):
                    yield self.yield_param_completion(param, self.unfinished_word)
        elif not self.leftover_args:
            for child_command in self.subtree.children:
                if self.validate_completion(child_command):
                    yield Completion(child_command, -len(self.unfinished_word))

    def gen_global_params_and_arg_completions(self):
        # global parameters
        for param in self.global_param:
            if self.validate_param_completion(param, self.leftover_args) and self.unfinished_word:
                if param in self.output_options and not self.complete_command:
                    continue
                yield Completion(param, -len(self.unfinished_word), display_meta=self.global_param_descriptions[param])

        # global parameter args
        if self.leftover_args and self.leftover_args[-1] in self.output_options:
            for opt in self.output_choices:
                if self.validate_completion(opt):
                    yield Completion(opt, -len(self.unfinished_word))

    def is_completable(self, symbol):
        """ whether the word can be completed as a command or parameter """
        return symbol in self.command_description or symbol in self.param_description

    def has_description(self, param):
        """ if a parameter has a description """
        return param in self.param_description.keys() and \
            not self.param_description[param].isspace()

    def reformat_cmd(self, text):
        """ reformat the text to be stripped of noise """
        # remove az if there
        text = text.replace('az', '')
        # disregard defaulting symbols
        if text and SELECT_SYMBOL['scope'] == text[0:2]:
            text = text.replace(SELECT_SYMBOL['scope'], "")

        if self.shell_ctx.default_command:
            text = self.shell_ctx.default_command + ' ' + text
        return text
コード例 #28
0
class AzCompleter(Completer):
    """ Completes Azure CLI commands """

    def __init__(self, commands, global_params=True, outstream=sys.stderr):
        # dictionary of command to descriptions
        self.command_description = commands.descrip
        # from a command to a list of parameters
        self.command_parameters = commands.command_param
        # a list of all the possible parameters
        self.completable_param = commands.completable_param
        # the command tree
        self.command_tree = commands.command_tree
        # a dictionary of parameter (which is command + " " + parameter name)
        # to a description of what it does
        self.param_description = commands.param_descript
        # a dictionary of command to examples of how to use it
        self.command_examples = commands.command_example
        # a dictionary of which parameters mean the same thing
        self.same_param_doubles = commands.same_param_doubles or {}

        self._is_command = True

        self.branch = self.command_tree
        self.curr_command = ""

        self.global_param = commands.global_param if global_params else []
        self.output_choices = commands.output_choices if global_params else []
        self.output_options = commands.output_options if global_params else []
        self.global_param_descriptions = commands.global_param_descriptions if global_params else []

        self.global_parser = AzCliCommandParser(add_help=False)
        self.global_parser.add_argument_group('global', 'Global Arguments')
        self.parser = AzCliCommandParser(parents=[self.global_parser])

        from azclishell._dump_commands import CMD_TABLE
        self.cmdtab = CMD_TABLE
        self.parser.load_command_table(CMD_TABLE)
        self.argsfinder = ArgsFinder(self.parser, outstream)

    def validate_completion(self, param, words, text_before_cursor, double=True):
        """ validates that a param should be completed """
        return param.lower().startswith(words.lower()) and param.lower() != words.lower() and\
            param not in text_before_cursor.split() and not \
            text_before_cursor[-1].isspace() and\
            (not (double and param in self.same_param_doubles) or
             self.same_param_doubles[param] not in text_before_cursor.split())

    def get_completions(self, document, complete_event):
        text = document.text_before_cursor
        self.branch = self.command_tree
        self.curr_command = ''
        self._is_command = True

        text = reformat_cmd(text)
        if text.split():

            for comp in sort_completions(self.gen_cmd_and_param_completions(text)):
                yield comp

        for cmd in sort_completions(self.gen_cmd_completions(text)):
            yield cmd

        for val in sort_completions(self.gen_dynamic_completions(text)):
            yield val

        for param in sort_completions(self.gen_global_param_completions(text)):
            yield param

    def gen_enum_completions(self, arg_name, text, started_param, prefix):
        """ generates dynamic enumeration completions """
        try:  # if enum completion
            for choice in self.cmdtab[
                    self.curr_command].arguments[arg_name].choices:
                if started_param:
                    if choice.lower().startswith(prefix.lower())\
                       and choice not in text.split():
                        yield Completion(choice, -len(prefix))
                else:
                    yield Completion(choice, -len(prefix))

        except TypeError:  # there is no choices option
            pass

    def get_arg_name(self, is_param, param):
        """ gets the argument name used in the command table for a parameter """
        if self.curr_command in self.cmdtab and is_param:
            for arg in self.cmdtab[self.curr_command].arguments:

                for name in self.cmdtab[self.curr_command].arguments[arg].options_list:
                    if name == param:
                        return arg

    def mute_parse_args(self, text):
        """ mutes the parser error when parsing, the puts it back """
        error = AzCliCommandParser.error
        AzCliCommandParser.error = error_pass

        parse_args = self.argsfinder.get_parsed_args(
            parse_quotes(text, quotes=False, string=False))

        AzCliCommandParser.error = error
        return parse_args

    # pylint: disable=too-many-branches
    def gen_dynamic_completions(self, text):
        """ generates the dynamic values, like the names of resource groups """
        try:
            is_param, started_param, prefix, param = dynamic_param_logic(text)

            # command table specific name
            arg_name = self.get_arg_name(is_param, param)

            if arg_name and ((text.split()[-1].startswith('-') and text[-1].isspace()) or
                             text.split()[-2].startswith('-')):

                for comp in self.gen_enum_completions(arg_name, text, started_param, prefix):
                    yield comp

                parse_args = self.mute_parse_args(text)

                # there are 3 formats for completers the cli uses
                # this try catches which format it is
                if self.cmdtab[self.curr_command].arguments[arg_name].completer:
                    try:
                        for comp in self.cmdtab[self.curr_command].arguments[arg_name].completer(
                                prefix=prefix, action=None, parsed_args=parse_args):

                            for comp in gen_dyn_completion(
                                    comp, started_param, prefix, text):
                                yield comp
                    except TypeError:
                        try:
                            for comp in self.cmdtab[self.curr_command].\
                                    arguments[arg_name].completer(prefix=prefix):

                                for comp in gen_dyn_completion(
                                        comp, started_param, prefix, text):
                                    yield comp
                        except TypeError:
                            try:
                                for comp in self.cmdtab[self.curr_command].\
                                        arguments[arg_name].completer():

                                    for comp in gen_dyn_completion(
                                            comp, started_param, prefix, text):
                                        yield comp

                            except TypeError:
                                pass  # other completion method used

        # if the user isn't logged in
        except Exception:  # pylint: disable=broad-except
            pass

    def gen_cmd_completions(self, text):
        """ whether is a space or no text typed, send the current branch """
        # if nothing, so first level commands
        if not text.split() and self._is_command:
            if self.branch.children is not None:
                for com in self.branch.children:
                    yield Completion(com.data)

        # if space show current level commands
        elif len(text.split()) > 0 and text[-1].isspace() and self._is_command:
            if self.branch is not self.command_tree:
                for com in self.branch.children:
                    yield Completion(com.data)

    def yield_param_completion(self, param, last_word):
        """ yields a parameter """
        return Completion(param, -len(last_word), display_meta=self.get_param_description(
            self.curr_command + " " + str(param)).replace('\n', ''))

    def gen_cmd_and_param_completions(self, text):
        """ generates command and parameter completions """
        temp_command = str('')
        txtspt = text.split()

        for word in txtspt:
            if word.startswith("-"):
                self._is_command = False

            # building what the command is
            elif self._is_command:
                temp_command += ' ' + str(word) if temp_command else str(word)

            mid_val = text.find(word) + len(word)
            # moving down command tree
            if self.branch.has_child(word) and len(text) > mid_val and text[mid_val].isspace():
                self.branch = self.branch.get_child(word, self.branch.children)

        if len(text) > 0 and text[-1].isspace():
            if in_tree(self.command_tree, temp_command):
                self.curr_command = temp_command
            else:
                self._is_command = False
        else:
            self.curr_command = temp_command

        last_word = txtspt[-1]
        # this is for single char parameters
        if last_word.startswith("-") and not last_word.startswith("--"):
            self._is_command = False
            if self.has_parameters(self.curr_command):
                for param in self.command_parameters[self.curr_command]:
                    if self.validate_completion(param, last_word, text) and\
                            not param.startswith("--"):
                        yield self.yield_param_completion(param, last_word)

        elif last_word.startswith("--"):  # for regular parameters
            self._is_command = False

            if self.has_parameters(self.curr_command):  # Everything should, map to empty list
                for param in self.command_parameters[self.curr_command]:
                    if self.validate_completion(param, last_word, text):
                        yield self.yield_param_completion(param, last_word)

        if self.branch.children and self._is_command:  # all underneath commands
            for kid in self.branch.children:
                if self.validate_completion(kid.data, txtspt[-1], text, False):
                    yield Completion(
                        str(kid.data), -len(txtspt[-1]))
        elif self._is_command and self.curr_command.strip() in self.command_parameters:
            for param in self.command_parameters[self.curr_command.strip()]:
                if param.startswith('--'):
                    yield self.yield_param_completion(param, '')

    def gen_global_param_completions(self, text):
        """ Global parameter stuff hard-coded in """
        txtspt = text.split()
        if txtspt and len(txtspt) > 0:
            for param in self.global_param:
                # for single dash global parameters
                if txtspt[-1].startswith('-') \
                        and not txtspt[-1].startswith('--') and \
                        param.startswith('-') and not param.startswith('--') and\
                        self.validate_completion(param, txtspt[-1], text, double=False):
                    yield Completion(
                        param, -len(txtspt[-1]),
                        display_meta=self.global_param_descriptions[param])
                # for double dash global parameters
                elif txtspt[-1].startswith('--') and \
                        self.validate_completion(param, txtspt[-1], text, double=False):
                    yield Completion(
                        param, -len(txtspt[-1]),
                        display_meta=self.global_param_descriptions[param])
            # if there is an output, gets the options without user typing
            if txtspt[-1] in self.output_options:
                for opt in self.output_choices:
                    yield Completion(opt)
            # if there is an output option, if they have started typing
            if len(txtspt) > 1 and\
                    txtspt[-2] in self.output_options:
                for opt in self.output_choices:
                    if self.validate_completion(opt, txtspt[-1], text, double=False):
                        yield Completion(opt, -len(txtspt[-1]))

    def is_completable(self, symbol):
        """ whether the word can be completed as a command or parameter """
        return self.has_parameters(symbol) or symbol in self.param_description.keys()

    def get_param_description(self, param):
        """ gets a description of an empty string """
        if param in self.param_description:
            return self.param_description[param]
        else:
            return ""

    def has_parameters(self, command):
        """ returns whether given command is valid """
        return command in self.command_parameters.keys()

    def has_description(self, param):
        """ if a parameter has a description """
        return param in self.param_description.keys() and \
            not self.param_description[param].isspace()
コード例 #29
0
ファイル: application.py プロジェクト: Azure/azure-cli
class Application(object):

    TRANSFORM_RESULT = "Application.TransformResults"
    FILTER_RESULT = "Application.FilterResults"
    GLOBAL_PARSER_CREATED = "GlobalParser.Created"
    COMMAND_PARSER_LOADED = "CommandParser.Loaded"
    COMMAND_PARSER_PARSED = "CommandParser.Parsed"
    COMMAND_TABLE_LOADED = "CommandTable.Loaded"
    COMMAND_TABLE_PARAMS_LOADED = "CommandTableParams.Loaded"

    def __init__(self, config=None):
        self._event_handlers = defaultdict(lambda: [])
        self.session = {
            "headers": {"x-ms-client-request-id": str(uuid.uuid1())},
            "command": "unknown",
            "completer_active": ARGCOMPLETE_ENV_NAME in os.environ,
            "query_active": False,
        }

        # Register presence of and handlers for global parameters
        self.register(self.GLOBAL_PARSER_CREATED, Application._register_builtin_arguments)
        self.register(self.COMMAND_PARSER_PARSED, self._handle_builtin_arguments)

        # Let other extensions make their presence known
        azure.cli.core.extensions.register_extensions(self)

        self.global_parser = AzCliCommandParser(prog="az", add_help=False)
        global_group = self.global_parser.add_argument_group("global", "Global Arguments")
        self.raise_event(self.GLOBAL_PARSER_CREATED, global_group=global_group)

        self.parser = AzCliCommandParser(prog="az", parents=[self.global_parser])

        self.initialize(config or Configuration([]))

    def initialize(self, configuration):
        self.configuration = configuration

    def execute(self, unexpanded_argv):
        argv = Application._expand_file_prefixed_files(unexpanded_argv)
        command_table = self.configuration.get_command_table()
        self.raise_event(self.COMMAND_TABLE_LOADED, command_table=command_table)
        self.parser.load_command_table(command_table)
        self.raise_event(self.COMMAND_PARSER_LOADED, parser=self.parser)

        if len(argv) == 0:
            enable_autocomplete(self.parser)
            az_subparser = self.parser.subparsers[tuple()]
            _help.show_welcome(az_subparser)
            log_telemetry("welcome")
            return None

        if argv[0].lower() == "help":
            argv[0] = "--help"

        # Rudimentary parsing to get the command
        nouns = []
        for noun in argv:
            if noun[0] == "-":
                break
            nouns.append(noun)
        command = " ".join(nouns)

        if argv[-1] in ("--help", "-h") or command in command_table:
            self.configuration.load_params(command)
            self.raise_event(self.COMMAND_TABLE_PARAMS_LOADED, command_table=command_table)
            self.parser.load_command_table(command_table)

        if self.session["completer_active"]:
            enable_autocomplete(self.parser)

        args = self.parser.parse_args(argv)

        self.raise_event(self.COMMAND_PARSER_PARSED, command=args.command, args=args)
        results = []
        for expanded_arg in _explode_list_args(args):
            self.session["command"] = expanded_arg.command
            try:
                _validate_arguments(expanded_arg)
            except CLIError:
                raise
            except:  # pylint: disable=bare-except
                err = sys.exc_info()[1]
                getattr(expanded_arg, "_parser", self.parser).validation_error(str(err))

            # Consider - we are using any args that start with an underscore (_) as 'private'
            # arguments and remove them from the arguments that we pass to the actual function.
            # This does not feel quite right.
            params = dict([(key, value) for key, value in expanded_arg.__dict__.items() if not key.startswith("_")])
            params.pop("subcommand", None)
            params.pop("func", None)
            params.pop("command", None)
            log_telemetry(
                expanded_arg.command,
                log_type="pageview",
                output_type=self.configuration.output_format,
                parameters=[p for p in unexpanded_argv if p.startswith("-")],
            )

            result = expanded_arg.func(params)
            result = todict(result)
            results.append(result)

        if len(results) == 1:
            results = results[0]

        event_data = {"result": results}
        self.raise_event(self.TRANSFORM_RESULT, event_data=event_data)
        self.raise_event(self.FILTER_RESULT, event_data=event_data)
        return CommandResultItem(
            event_data["result"],
            table_transformer=command_table[args.command].table_transformer,
            is_query_active=self.session["query_active"],
        )

    def raise_event(self, name, **kwargs):
        """Raise the event `name`.
        """
        logger.info("Application event '%s' with event data %s", name, kwargs)
        for func in list(self._event_handlers[name]):  # Make copy in case handler modifies the list
            func(**kwargs)

    def register(self, name, handler):
        """Register a callable that will be called when the
        event `name` is raised.

        param: name: The name of the event
        param: handler: Function that takes two parameters;
          name: name of the event raised
          event_data: `dict` with event specific data.
        """
        self._event_handlers[name].append(handler)
        logger.info("Registered application event handler '%s' at %s", name, handler)

    def remove(self, name, handler):
        """Remove a callable that is registered to be called when the
        event `name` is raised.

        param: name: The name of the event
        param: handler: Function that takes two parameters;
          name: name of the event raised
          event_data: `dict` with event specific data.
        """
        self._event_handlers[name].remove(handler)
        logger.info("Removed application event handler '%s' at %s", name, handler)

    @staticmethod
    def _register_builtin_arguments(**kwargs):
        global_group = kwargs["global_group"]
        global_group.add_argument(
            "--output",
            "-o",
            dest="_output_format",
            choices=["json", "tsv", "list", "table", "jsonc"],
            default=az_config.get("core", "output", fallback="json"),
            help="Output format",
            type=str.lower,
        )
        # The arguments for verbosity don't get parsed by argparse but we add it here for help.
        global_group.add_argument(
            "--verbose",
            dest="_log_verbosity_verbose",
            action="store_true",
            help="Increase logging verbosity. Use --debug for full debug logs.",
        )  # pylint: disable=line-too-long
        global_group.add_argument(
            "--debug",
            dest="_log_verbosity_debug",
            action="store_true",
            help="Increase logging verbosity to show all debug logs.",
        )

    @staticmethod
    def _expand_file_prefixed_files(argv):
        return list([Application._load_file(arg[1:]) if arg.startswith("@") else arg for arg in argv])

    @staticmethod
    def _load_file(path):
        try:
            if path == "-":
                content = sys.stdin.read()
            else:
                with open(path, "r") as input_file:
                    content = input_file.read()

            return content[0:-1] if content[-1] == "\n" else content
        except:
            raise CLIError("Failed to open file {}".format(path))

    def _handle_builtin_arguments(self, **kwargs):
        args = kwargs["args"]
        self.configuration.output_format = args._output_format  # pylint: disable=protected-access
        del args._output_format
コード例 #30
0
class AzCompleter(Completer):
    """ Completes Azure CLI commands """
    def __init__(self, commands, global_params=True, outstream=sys.stderr):
        # dictionary of command to descriptions
        self.command_description = commands.descrip
        # from a command to a list of parameters
        self.command_parameters = commands.command_param
        # a list of all the possible parameters
        self.completable_param = commands.completable_param
        # the command tree
        self.command_tree = commands.command_tree
        # a dictionary of parameter (which is command + " " + parameter name)
        # to a description of what it does
        self.param_description = commands.param_descript
        # a dictionary of command to examples of how to use it
        self.command_examples = commands.command_example
        # a dictionary of which parameters mean the same thing
        self.same_param_doubles = commands.same_param_doubles or {}

        self._is_command = True

        self.branch = self.command_tree
        self.curr_command = ""

        self.global_param = commands.global_param if global_params else []
        self.output_choices = commands.output_choices if global_params else []
        self.output_options = commands.output_options if global_params else []
        self.global_param_descriptions = commands.global_param_descriptions if global_params else []

        self.global_parser = AzCliCommandParser(add_help=False)
        self.global_parser.add_argument_group('global', 'Global Arguments')
        self.parser = AzCliCommandParser(parents=[self.global_parser])

        from azclishell._dump_commands import CMD_TABLE
        self.cmdtab = CMD_TABLE
        self.parser.load_command_table(CMD_TABLE)
        self.argsfinder = ArgsFinder(self.parser, outstream)

    def validate_completion(self,
                            param,
                            words,
                            text_before_cursor,
                            double=True):
        """ validates that a param should be completed """
        return param.lower().startswith(words.lower()) and param.lower() != words.lower() and\
            param not in text_before_cursor.split() and not \
            text_before_cursor[-1].isspace() and\
            (not (double and param in self.same_param_doubles) or
             self.same_param_doubles[param] not in text_before_cursor.split())

    def get_completions(self, document, complete_event):
        text = document.text_before_cursor
        self.branch = self.command_tree
        self.curr_command = ''
        self._is_command = True

        text = reformat_cmd(text)
        if text.split():

            for comp in sort_completions(
                    self.gen_cmd_and_param_completions(text)):
                yield comp

        for cmd in sort_completions(self.gen_cmd_completions(text)):
            yield cmd

        for val in sort_completions(self.gen_dynamic_completions(text)):
            yield val

        for param in sort_completions(self.gen_global_param_completions(text)):
            yield param

    def gen_enum_completions(self, arg_name, text, started_param, prefix):
        """ generates dynamic enumeration completions """
        try:  # if enum completion
            for choice in self.cmdtab[
                    self.curr_command].arguments[arg_name].choices:
                if started_param:
                    if choice.lower().startswith(prefix.lower())\
                       and choice not in text.split():
                        yield Completion(choice, -len(prefix))
                else:
                    yield Completion(choice, -len(prefix))

        except TypeError:  # there is no choices option
            pass

    def get_arg_name(self, is_param, param):
        """ gets the argument name used in the command table for a parameter """
        if self.curr_command in self.cmdtab and is_param:
            for arg in self.cmdtab[self.curr_command].arguments:

                for name in self.cmdtab[
                        self.curr_command].arguments[arg].options_list:
                    if name == param:
                        return arg

    def mute_parse_args(self, text):
        """ mutes the parser error when parsing, the puts it back """
        error = AzCliCommandParser.error
        AzCliCommandParser.error = error_pass

        parse_args = self.argsfinder.get_parsed_args(
            parse_quotes(text, quotes=False, string=False))

        AzCliCommandParser.error = error
        return parse_args

    # pylint: disable=too-many-branches
    def gen_dynamic_completions(self, text):
        """ generates the dynamic values, like the names of resource groups """
        try:
            is_param, started_param, prefix, param = dynamic_param_logic(text)

            # command table specific name
            arg_name = self.get_arg_name(is_param, param)

            if arg_name and (
                (text.split()[-1].startswith('-') and text[-1].isspace())
                    or text.split()[-2].startswith('-')):

                for comp in self.gen_enum_completions(arg_name, text,
                                                      started_param, prefix):
                    yield comp

                parse_args = self.mute_parse_args(text)

                # there are 3 formats for completers the cli uses
                # this try catches which format it is
                if self.cmdtab[
                        self.curr_command].arguments[arg_name].completer:
                    try:
                        for comp in self.cmdtab[self.curr_command].arguments[
                                arg_name].completer(prefix=prefix,
                                                    action=None,
                                                    parsed_args=parse_args):

                            for comp in gen_dyn_completion(
                                    comp, started_param, prefix, text):
                                yield comp
                    except TypeError:
                        try:
                            for comp in self.cmdtab[self.curr_command].\
                                    arguments[arg_name].completer(prefix=prefix):

                                for comp in gen_dyn_completion(
                                        comp, started_param, prefix, text):
                                    yield comp
                        except TypeError:
                            try:
                                for comp in self.cmdtab[self.curr_command].\
                                        arguments[arg_name].completer():

                                    for comp in gen_dyn_completion(
                                            comp, started_param, prefix, text):
                                        yield comp

                            except TypeError:
                                pass  # other completion method used

        # if the user isn't logged in
        except (CLIError, AttributeError, ValueError):  # service client throws
            pass

    def gen_cmd_completions(self, text):
        """ whether is a space or no text typed, send the current branch """
        # if nothing, so first level commands
        if not text.split() and self._is_command:
            if self.branch.children is not None:
                for com in self.branch.children:
                    yield Completion(com.data)

        # if space show current level commands
        elif len(text.split()) > 0 and text[-1].isspace() and self._is_command:
            if self.branch is not self.command_tree:
                for com in self.branch.children:
                    yield Completion(com.data)

    def yield_param_completion(self, param, last_word):
        """ yields a parameter """
        return Completion(
            param,
            -len(last_word),
            display_meta=self.get_param_description(self.curr_command + " " +
                                                    str(param)).replace(
                                                        '\n', ''))

    def gen_cmd_and_param_completions(self, text):
        """ generates command and parameter completions """
        temp_command = str('')
        txtspt = text.split()

        for word in txtspt:
            if word.startswith("-"):
                self._is_command = False

            # building what the command is
            elif self._is_command:
                temp_command += ' ' + str(word) if temp_command else str(word)

            mid_val = text.find(word) + len(word)
            # moving down command tree
            if self.branch.has_child(
                    word) and len(text) > mid_val and text[mid_val].isspace():
                self.branch = self.branch.get_child(word, self.branch.children)

        if len(text) > 0 and text[-1].isspace():
            if in_tree(self.command_tree, temp_command):
                self.curr_command = temp_command
            else:
                self._is_command = False
        else:
            self.curr_command = temp_command

        last_word = txtspt[-1]
        # this is for single char parameters
        if last_word.startswith("-") and not last_word.startswith("--"):
            self._is_command = False
            if self.has_parameters(self.curr_command):
                for param in self.command_parameters[self.curr_command]:
                    if self.validate_completion(param, last_word, text) and\
                            not param.startswith("--"):
                        yield self.yield_param_completion(param, last_word)

        elif last_word.startswith("--"):  # for regular parameters
            self._is_command = False

            if self.has_parameters(
                    self.curr_command):  # Everything should, map to empty list
                for param in self.command_parameters[self.curr_command]:
                    if self.validate_completion(param, last_word, text):
                        yield self.yield_param_completion(param, last_word)

        if self.branch.children and self._is_command:  # all underneath commands
            for kid in self.branch.children:
                if self.validate_completion(kid.data, txtspt[-1], text, False):
                    yield Completion(str(kid.data), -len(txtspt[-1]))
        elif self._is_command and self.curr_command.strip(
        ) in self.command_parameters:
            for param in self.command_parameters[self.curr_command.strip()]:
                if param.startswith('--'):
                    yield self.yield_param_completion(param, '')

    def gen_global_param_completions(self, text):
        """ Global parameter stuff hard-coded in """
        txtspt = text.split()
        if txtspt and len(txtspt) > 0:
            for param in self.global_param:
                # for single dash global parameters
                if txtspt[-1].startswith('-') \
                        and not txtspt[-1].startswith('--') and \
                        param.startswith('-') and not param.startswith('--') and\
                        self.validate_completion(param, txtspt[-1], text, double=False):
                    yield Completion(
                        param,
                        -len(txtspt[-1]),
                        display_meta=self.global_param_descriptions[param])
                # for double dash global parameters
                elif txtspt[-1].startswith('--') and \
                        self.validate_completion(param, txtspt[-1], text, double=False):
                    yield Completion(
                        param,
                        -len(txtspt[-1]),
                        display_meta=self.global_param_descriptions[param])
            # if there is an output, gets the options without user typing
            if txtspt[-1] in self.output_options:
                for opt in self.output_choices:
                    yield Completion(opt)
            # if there is an output option, if they have started typing
            if len(txtspt) > 1 and\
                    txtspt[-2] in self.output_options:
                for opt in self.output_choices:
                    if self.validate_completion(opt,
                                                txtspt[-1],
                                                text,
                                                double=False):
                        yield Completion(opt, -len(txtspt[-1]))

    def is_completable(self, symbol):
        """ whether the word can be completed as a command or parameter """
        return self.has_parameters(
            symbol) or symbol in self.param_description.keys()

    def get_param_description(self, param):
        """ gets a description of an empty string """
        if param in self.param_description:
            return self.param_description[param]
        else:
            return ""

    def has_parameters(self, command):
        """ returns whether given command is valid """
        return command in self.command_parameters.keys()

    def has_description(self, param):
        """ if a parameter has a description """
        return param in self.param_description.keys() and \
            not self.param_description[param].isspace()
コード例 #31
0
class AzCompleter(Completer):
    """ Completes Azure CLI commands """
    def __init__(self, commands):
        # dictionary of command to descriptions
        self.command_description = commands.descrip
        # from a command to a list of parameters
        self.command_parameters = commands.command_param
        # a list of all the possible parameters
        self.completable_param = commands.completable_param
        # the command tree
        self.command_tree = commands.command_tree
        # a dictionary of parameter (which is command + " " + parameter name)
        # to a description of what it does
        self.param_description = commands.param_descript
        # a dictionary of command to examples of how to use it
        self.command_examples = commands.command_example
        # a dictionary of which parameters mean the same thing
        self.same_param_doubles = commands.same_param_doubles or {}

        self.global_parser = AzCliCommandParser(prog='az', add_help=False)
        self.global_parser.add_argument_group('global', 'Global Arguments')
        self.parser = AzCliCommandParser(prog='az',
                                         parents=[self.global_parser])

        from azclishell._dump_commands import CMD_TABLE as cmd_table
        self.cmdtab = cmd_table
        self.parser.load_command_table(self.cmdtab)

    def validate_param_completion(self, param, words, text_before_cursor):
        """ validates that a param should be completed """
        double_flag = True
        if param in self.same_param_doubles:
            double_flag = self.same_param_doubles[
                param] not in text_before_cursor.split()
        return param.lower().startswith(words.lower()) and \
                param.lower() != words.lower() and\
                param not in text_before_cursor.split()\
                and double_flag

    def get_completions(self, document, complete_event):
        text_before_cursor = document.text_before_cursor
        command = ""
        is_command = True
        branch = self.command_tree
        not_command = False
        if len(text_before_cursor.split()) > 0\
        and text_before_cursor.split()[0] == 'az': # remove optional az
            text_before_cursor = ' '.join(text_before_cursor.split()[1:])

        if SELECT_SYMBOL['default'] in text_before_cursor:
            text_before_cursor = text_before_cursor.replace(
                SELECT_SYMBOL['default'], "")
        if SELECT_SYMBOL['undefault'] in text_before_cursor:
            text_before_cursor = text_before_cursor.replace(
                SELECT_SYMBOL['undefault'], "")

        if default_command():
            # print(DEFAULT_COMMAND)
            text_before_cursor = default_command() + ' ' + text_before_cursor

        if text_before_cursor.split():
            if text_before_cursor.split():
                for words in text_before_cursor.split():
                    if words.startswith("-") and not words.startswith("--"):
                        is_command = False
                        if self.has_parameters(command):
                            for param in self.get_param(command):
                                if self.validate_param_completion(param, words, text_before_cursor)\
                                and not param.startswith("--"):
                                    yield Completion(param, -len(words), display_meta=\
                                    self.get_param_description(
                                        command + " " + str(param)).replace('\n', ''))

                    elif words.startswith("--"):
                        is_command = False
                        if self.has_parameters(
                                command
                        ):  # Everything should, map to empty list
                            for param in self.get_param(command):
                                if self.validate_param_completion(
                                        param, words, text_before_cursor):
                                    yield Completion(param, -len(words),\
                                    display_meta=self.get_param_description(
                                        command + " " + str(param)).replace('\n', ''))
                    else:
                        if is_command:
                            if command:
                                command += " " + str(words)
                            else:
                                command += str(words)
                        try:
                            if branch.has_child(words):
                                branch = branch.get_child(
                                    words, branch.children)
                            elif text_before_cursor.split(
                            )[0] in SELECT_SYMBOL.values():
                                # print('help')
                                not_command = True
                        except ValueError:
                            continue  # do something

                if branch.children is not None and not not_command:
                    for kid in branch.children:
                        if kid.data.lower().startswith(
                                text_before_cursor.split()[-1].lower()):
                            yield Completion(str(kid.data),\
                                -len(text_before_cursor.split()[-1]))

        if not text_before_cursor:
            if branch.children is not None:
                for com in branch.children:
                    yield Completion(com.data)
        elif text_before_cursor[-1].isspace():
            if branch is not self.command_tree:
                for com in branch.children:
                    yield Completion(com.data)

        is_param = False
        started_param = False
        prefix = ""
        param = ""
        if text_before_cursor.split():
            param = text_before_cursor.split()[-1]
            if param.startswith("-"):
                is_param = True
            elif len(text_before_cursor.split()) > 2 and text_before_cursor.split()[-2]\
            and text_before_cursor.split()[-2].startswith('-'):
                is_param = True
                param = text_before_cursor.split()[-2]
                started_param = True
                prefix = text_before_cursor.split()[-1]

        arg_name = ""
        if command in self.cmdtab:
            if is_param:
                for arg in self.cmdtab[command].arguments:
                    for name in self.cmdtab[command].arguments[
                            arg].options_list:
                        if name == param:
                            arg_name = arg
                            break
                    if arg_name:
                        break
                if arg_name and (text_before_cursor.split()[-1].startswith('-') or\
                text_before_cursor.split()[-2].startswith('-')):
                    try:
                        for choice in self.cmdtab[command].arguments[
                                arg_name].choices:
                            if started_param:
                                if choice.lower().startswith(prefix.lower())\
                                   and choice not in text_before_cursor.split():
                                    yield Completion(choice, -len(prefix))
                            else:
                                yield Completion(choice, -len(prefix))
                    except TypeError:
                        pass
                    if self.cmdtab[command].arguments[arg_name].completer:
                        try:
                            for comp in self.cmdtab[command].\
                            arguments[arg_name].completer(prefix=prefix, action=None,\
                            parser=self.parser, parsed_args=None):
                                if started_param:
                                    if comp.lower().startswith(prefix.lower())\
                                        and comp not in text_before_cursor.split():
                                        yield Completion(comp, -len(prefix))
                                else:
                                    yield Completion(comp, -len(prefix))
                        except TypeError:
                            try:
                                for comp in self.cmdtab[command].\
                                arguments[arg_name].completer(prefix):
                                    if started_param:
                                        if comp.lower().startswith(prefix.lower())\
                                            and comp not in text_before_cursor.split():
                                            yield Completion(
                                                comp, -len(prefix))
                                    else:
                                        yield Completion(comp, -len(prefix))
                            except TypeError:
                                try:
                                    for comp in self.cmdtab[command].\
                                    arguments[arg_name].completer():
                                        if started_param:
                                            if comp.lower().startswith(prefix.lower())\
                                                and comp not in text_before_cursor.split():
                                                yield Completion(
                                                    comp, -len(prefix))
                                        else:
                                            yield Completion(
                                                comp, -len(prefix))
                                except TypeError:
                                    print("TypeError: " + TypeError.message)

    def is_completable(self, symbol):
        """ whether the word can be completed as a command or parameter """
        return self.has_parameters(
            symbol) or symbol in self.param_description.keys()

    def get_param(self, command):
        """ returns the parameters for a given command """
        return self.command_parameters[command]

    def get_param_description(self, param):
        """ gets a description of an empty string """
        if param in self.param_description:
            return self.param_description[param]
        else:
            return ""

    def get_description(self, command):
        """ returns the description for a given command """
        return self.command_description[command]

    def has_parameters(self, command):
        """ returns whether given command is valid """
        return command in self.command_parameters.keys()

    def has_description(self, param):
        """ if a parameter has a description """
        return param in self.param_description.keys() and \
        not self.param_description[param].isspace()
コード例 #32
0
ファイル: application.py プロジェクト: JasonRShaver/azure-cli
class Application(object):

    TRANSFORM_RESULT = 'Application.TransformResults'
    FILTER_RESULT = 'Application.FilterResults'
    GLOBAL_PARSER_CREATED = 'GlobalParser.Created'
    COMMAND_PARSER_LOADED = 'CommandParser.Loaded'
    COMMAND_PARSER_PARSING = 'CommandParser.Parsing'
    COMMAND_PARSER_PARSED = 'CommandParser.Parsed'
    COMMAND_TABLE_LOADED = 'CommandTable.Loaded'
    COMMAND_TABLE_PARAMS_LOADED = 'CommandTableParams.Loaded'

    def __init__(self, configuration=None):
        self._event_handlers = defaultdict(lambda: [])
        self.session = {
            'headers': {
                'x-ms-client-request-id': str(uuid.uuid1())
            },
            'command': 'unknown',
            'completer_active': ARGCOMPLETE_ENV_NAME in os.environ,
            'query_active': False
        }

        # Register presence of and handlers for global parameters
        self.register(self.GLOBAL_PARSER_CREATED, Application._register_builtin_arguments)
        self.register(self.COMMAND_PARSER_PARSED, self._handle_builtin_arguments)

        # Let other extensions make their presence known
        azure.cli.core.extensions.register_extensions(self)

        self.global_parser = AzCliCommandParser(prog='az', add_help=False)
        global_group = self.global_parser.add_argument_group('global', 'Global Arguments')
        self.raise_event(self.GLOBAL_PARSER_CREATED, global_group=global_group)

        self.parser = AzCliCommandParser(prog='az', parents=[self.global_parser])
        self.configuration = configuration
        self.progress_controller = progress.ProgressHook()

    def get_progress_controller(self, det=False):
        self.progress_controller.init_progress(progress.get_progress_view(det))
        return self.progress_controller

    def initialize(self, configuration):
        self.configuration = configuration

    def execute(self, unexpanded_argv):  # pylint: disable=too-many-statements
        argv = Application._expand_file_prefixed_files(unexpanded_argv)
        command_table = self.configuration.get_command_table(argv)
        self.raise_event(self.COMMAND_TABLE_LOADED, command_table=command_table)
        self.parser.load_command_table(command_table)
        self.raise_event(self.COMMAND_PARSER_LOADED, parser=self.parser)

        if not argv:
            enable_autocomplete(self.parser)
            az_subparser = self.parser.subparsers[tuple()]
            _help.show_welcome(az_subparser)

            # TODO: Question, is this needed?
            telemetry.set_command_details('az')
            telemetry.set_success(summary='welcome')

            return None

        if argv[0].lower() == 'help':
            argv[0] = '--help'

        # Rudimentary parsing to get the command
        nouns = []
        for i, current in enumerate(argv):
            try:
                if current[0] == '-':
                    break
            except IndexError:
                pass
            argv[i] = current.lower()
            nouns.append(argv[i])

        command = ' '.join(nouns)

        if argv[-1] in ('--help', '-h') or command in command_table:
            self.configuration.load_params(command)
            self.raise_event(self.COMMAND_TABLE_PARAMS_LOADED, command_table=command_table)
            self.parser.load_command_table(command_table)

        if self.session['completer_active']:
            enable_autocomplete(self.parser)

        self.raise_event(self.COMMAND_PARSER_PARSING, argv=argv)
        args = self.parser.parse_args(argv)

        self.raise_event(self.COMMAND_PARSER_PARSED, command=args.command, args=args)
        results = []
        for expanded_arg in _explode_list_args(args):
            self.session['command'] = expanded_arg.command
            try:
                _validate_arguments(expanded_arg)
            except CLIError:
                raise
            except:  # pylint: disable=bare-except
                err = sys.exc_info()[1]
                getattr(expanded_arg, '_parser', self.parser).validation_error(str(err))

            # Consider - we are using any args that start with an underscore (_) as 'private'
            # arguments and remove them from the arguments that we pass to the actual function.
            # This does not feel quite right.
            params = dict([(key, value)
                           for key, value in expanded_arg.__dict__.items()
                           if not key.startswith('_')])
            params.pop('subcommand', None)
            params.pop('func', None)
            params.pop('command', None)

            telemetry.set_command_details(expanded_arg.command,
                                          self.configuration.output_format,
                                          [p for p in unexpanded_argv if p.startswith('-')])

            result = expanded_arg.func(params)
            result = todict(result)
            results.append(result)

        if len(results) == 1:
            results = results[0]

        event_data = {'result': results}
        self.raise_event(self.TRANSFORM_RESULT, event_data=event_data)
        self.raise_event(self.FILTER_RESULT, event_data=event_data)

        return CommandResultItem(event_data['result'],
                                 table_transformer=command_table[args.command].table_transformer,
                                 is_query_active=self.session['query_active'])

    def raise_event(self, name, **kwargs):
        '''Raise the event `name`.
        '''
        data = truncate_text(str(kwargs), width=500)
        logger.debug("Application event '%s' with event data %s", name, data)
        for func in list(self._event_handlers[name]):  # Make copy in case handler modifies the list
            func(**kwargs)

    def register(self, name, handler):
        '''Register a callable that will be called when the
        event `name` is raised.

        param: name: The name of the event
        param: handler: Function that takes two parameters;
          name: name of the event raised
          event_data: `dict` with event specific data.
        '''
        self._event_handlers[name].append(handler)
        logger.debug("Registered application event handler '%s' at %s", name, handler)

    def remove(self, name, handler):
        '''Remove a callable that is registered to be called when the
        event `name` is raised.

        param: name: The name of the event
        param: handler: Function that takes two parameters;
          name: name of the event raised
          event_data: `dict` with event specific data.
        '''
        self._event_handlers[name].remove(handler)
        logger.debug("Removed application event handler '%s' at %s", name, handler)

    @staticmethod
    def _register_builtin_arguments(**kwargs):
        global_group = kwargs['global_group']
        global_group.add_argument('--output', '-o', dest='_output_format',
                                  choices=['json', 'tsv', 'table', 'jsonc','pandas'],
                                  default=az_config.get('core', 'output', fallback='json'),
                                  help='Output format',
                                  type=str.lower)
        # The arguments for verbosity don't get parsed by argparse but we add it here for help.
        global_group.add_argument('--verbose', dest='_log_verbosity_verbose', action='store_true',
                                  help='Increase logging verbosity. Use --debug for full debug logs.')  # pylint: disable=line-too-long
        global_group.add_argument('--debug', dest='_log_verbosity_debug', action='store_true',
                                  help='Increase logging verbosity to show all debug logs.')

    @staticmethod
    def _maybe_load_file(arg):
        ix = arg.find('@')
        if ix == -1:  # no @ found
            return arg

        poss_file = arg[ix + 1:]
        if not poss_file:  # if nothing after @ then it can't be a file
            return arg
        elif ix == 0:
            return Application._load_file(poss_file)

        # if @ not at the start it can't be a file
        return arg

    @staticmethod
    def _expand_file_prefix(arg):
        arg_split = arg.split('=', 1)
        try:
            return '='.join([arg_split[0], Application._maybe_load_file(arg_split[1])])
        except IndexError:
            return Application._maybe_load_file(arg_split[0])

    @staticmethod
    def _expand_file_prefixed_files(argv):
        return list([Application._expand_file_prefix(arg) for arg in argv])

    @staticmethod
    def _load_file(path):
        if path == '-':
            content = sys.stdin.read()
        else:
            content = read_file_content(os.path.expanduser(path),
                                        allow_binary=True)

        return content[0:-1] if content and content[-1] == '\n' else content

    def _handle_builtin_arguments(self, **kwargs):
        args = kwargs['args']
        self.configuration.output_format = args._output_format  # pylint: disable=protected-access
        del args._output_format
コード例 #33
0
class Application(object):

    TRANSFORM_RESULT = 'Application.TransformResults'
    FILTER_RESULT = 'Application.FilterResults'
    GLOBAL_PARSER_CREATED = 'GlobalParser.Created'
    COMMAND_PARSER_LOADED = 'CommandParser.Loaded'
    COMMAND_PARSER_PARSED = 'CommandParser.Parsed'
    COMMAND_TABLE_LOADED = 'CommandTable.Loaded'

    def __init__(self, config=None):
        self._event_handlers = defaultdict(lambda: [])
        self.session = {
            'headers': {
                'x-ms-client-request-id': str(uuid.uuid1())
                },
            'command': 'unknown',
            'completer_active': ARGCOMPLETE_ENV_NAME in os.environ,
            'query_active': False
            }

        # Register presence of and handlers for global parameters
        self.register(self.GLOBAL_PARSER_CREATED, Application._register_builtin_arguments)
        self.register(self.COMMAND_PARSER_PARSED, self._handle_builtin_arguments)

        # Let other extensions make their presence known
        azure.cli.core.extensions.register_extensions(self)

        self.global_parser = AzCliCommandParser(prog='az', add_help=False)
        global_group = self.global_parser.add_argument_group('global', 'Global Arguments')
        self.raise_event(self.GLOBAL_PARSER_CREATED, global_group=global_group)

        self.parser = AzCliCommandParser(prog='az', parents=[self.global_parser])

        self.initialize(config or Configuration([]))

    def initialize(self, configuration):
        self.configuration = configuration

    def execute(self, unexpanded_argv):
        argv = Application._expand_file_prefixed_files(unexpanded_argv)
        command_table = self.configuration.get_command_table()
        self.raise_event(self.COMMAND_TABLE_LOADED, command_table=command_table)
        self.parser.load_command_table(command_table)
        self.raise_event(self.COMMAND_PARSER_LOADED, parser=self.parser)

        if len(argv) == 0:
            az_subparser = self.parser.subparsers[tuple()]
            _help.show_welcome(az_subparser)
            log_telemetry('welcome')
            return None

        if argv[0].lower() == 'help':
            argv[0] = '--help'

        args = self.parser.parse_args(argv)
        self.raise_event(self.COMMAND_PARSER_PARSED, command=args.command, args=args)
        results = []
        for expanded_arg in _explode_list_args(args):
            self.session['command'] = expanded_arg.command
            try:
                _validate_arguments(expanded_arg)
            except CLIError:
                raise
            except: # pylint: disable=bare-except
                err = sys.exc_info()[1]
                getattr(expanded_arg, '_parser', self.parser).validation_error(str(err))

            # Consider - we are using any args that start with an underscore (_) as 'private'
            # arguments and remove them from the arguments that we pass to the actual function.
            # This does not feel quite right.
            params = dict([(key, value)
                           for key, value in expanded_arg.__dict__.items()
                           if not key.startswith('_')])
            params.pop('subcommand', None)
            params.pop('func', None)
            params.pop('command', None)
            log_telemetry(expanded_arg.command, log_type='pageview',
                          output_type=self.configuration.output_format,
                          parameters=[p for p in unexpanded_argv if p.startswith('-')])

            result = expanded_arg.func(params)
            result = todict(result)
            results.append(result)

        if len(results) == 1:
            results = results[0]

        event_data = {'result': results}
        self.raise_event(self.TRANSFORM_RESULT, event_data=event_data)
        self.raise_event(self.FILTER_RESULT, event_data=event_data)
        return CommandResultItem(event_data['result'],
                                 table_transformer=
                                 command_table[args.command].table_transformer,
                                 is_query_active=self.session['query_active'])

    def raise_event(self, name, **kwargs):
        '''Raise the event `name`.
        '''
        logger.info("Application event '%s' with event data %s", name, kwargs)
        for func in list(self._event_handlers[name]): # Make copy in case handler modifies the list
            func(**kwargs)

    def register(self, name, handler):
        '''Register a callable that will be called when the
        event `name` is raised.

        param: name: The name of the event
        param: handler: Function that takes two parameters;
          name: name of the event raised
          event_data: `dict` with event specific data.
        '''
        self._event_handlers[name].append(handler)
        logger.info("Registered application event handler '%s' at %s", name, handler)

    def remove(self, name, handler):
        '''Remove a callable that is registered to be called when the
        event `name` is raised.

        param: name: The name of the event
        param: handler: Function that takes two parameters;
          name: name of the event raised
          event_data: `dict` with event specific data.
        '''
        self._event_handlers[name].remove(handler)
        logger.info("Removed application event handler '%s' at %s", name, handler)

    @staticmethod
    def _register_builtin_arguments(**kwargs):
        global_group = kwargs['global_group']
        global_group.add_argument('--subscription', dest='_subscription_id', help=argparse.SUPPRESS)
        global_group.add_argument('--output', '-o', dest='_output_format',
                                  choices=['json', 'tsv', 'list', 'table', 'jsonc'],
                                  default=az_config.get('core', 'output', fallback='json'),
                                  help='Output format',
                                  type=str.lower)
        # The arguments for verbosity don't get parsed by argparse but we add it here for help.
        global_group.add_argument('--verbose', dest='_log_verbosity_verbose', action='store_true',
                                  help='Increase logging verbosity. Use --debug for full debug logs.') #pylint: disable=line-too-long
        global_group.add_argument('--debug', dest='_log_verbosity_debug', action='store_true',
                                  help='Increase logging verbosity to show all debug logs.')

    @staticmethod
    def _expand_file_prefixed_files(argv):
        return list(
            [Application._load_file(arg[1:]) if arg.startswith('@') else arg for arg in argv]
            )

    @staticmethod
    def _load_file(path):
        try:
            if path == '-':
                content = sys.stdin.read()
            else:
                with open(path, 'r') as input_file:
                    content = input_file.read()

            return content[0:-1] if content[-1] == '\n' else content
        except:
            raise CLIError('Failed to open file {}'.format(path))

    def _handle_builtin_arguments(self, **kwargs):
        args = kwargs['args']
        self.configuration.output_format = args._output_format #pylint: disable=protected-access
        del args._output_format