1# --------------------------------------------------------------------------------------------
2# Copyright (c) Microsoft Corporation. All rights reserved.
3# Licensed under the MIT License. See License.txt in the project root for license information.
4# --------------------------------------------------------------------------------------------
5
6from azure.cli.core._help import CliCommandHelpFile, CliGroupHelpFile
7
8from knack.log import get_logger
9from knack.util import CLIError
10
11logger = get_logger(__name__)
12
13
14def get_all_help(cli_ctx, skip=True):
15    invoker = cli_ctx.invocation
16    help_ctx = cli_ctx.help_cls(cli_ctx)
17    if not invoker:
18        raise CLIError('CLI context does not contain invocation.')
19
20    parser_keys = []
21    parser_values = []
22    sub_parser_keys = []
23    sub_parser_values = []
24    _store_parsers(invoker.parser, parser_keys, parser_values, sub_parser_keys, sub_parser_values)
25    for cmd, parser in zip(parser_keys, parser_values):
26        if cmd not in sub_parser_keys:
27            sub_parser_keys.append(cmd)
28            sub_parser_values.append(parser)
29    help_files = []
30    help_errors = {}
31    for cmd, parser in zip(sub_parser_keys, sub_parser_values):
32        try:
33            help_ctx.update_loaders_with_help_file_contents(cmd.split())
34            help_file = CliGroupHelpFile(help_ctx, cmd, parser) if _is_group(parser) \
35                else CliCommandHelpFile(help_ctx, cmd, parser)
36            help_file.load(parser)
37            help_files.append(help_file)
38        except Exception as ex:  # pylint: disable=broad-except
39            if skip:
40                logger.warning("Skipping '%s': %s", cmd, ex)
41            else:
42                help_errors[cmd] = "Error '{}': {}".format(cmd, ex)
43    if help_errors:
44        raise CLIError(help_errors)
45    help_files = sorted(help_files, key=lambda x: x.command)
46    return help_files
47
48
49def create_invoker_and_load_cmds_and_args(cli_ctx):
50    from knack.events import (
51        EVENT_INVOKER_PRE_CMD_TBL_CREATE, EVENT_INVOKER_POST_CMD_TBL_CREATE)
52    from azure.cli.core.commands import register_cache_arguments
53    from azure.cli.core.commands.arm import register_global_subscription_argument, register_ids_argument
54    from azure.cli.core.commands.events import (
55        EVENT_INVOKER_PRE_LOAD_ARGUMENTS, EVENT_INVOKER_POST_LOAD_ARGUMENTS)
56    import time
57
58    start_time = time.time()
59
60    register_global_subscription_argument(cli_ctx)
61    register_ids_argument(cli_ctx)
62    register_cache_arguments(cli_ctx)
63
64    invoker = cli_ctx.invocation_cls(cli_ctx=cli_ctx, commands_loader_cls=cli_ctx.commands_loader_cls,
65                                     parser_cls=cli_ctx.parser_cls, help_cls=cli_ctx.help_cls)
66    cli_ctx.invocation = invoker
67    invoker.commands_loader.skip_applicability = True
68
69    cli_ctx.raise_event(EVENT_INVOKER_PRE_CMD_TBL_CREATE, args=[])
70    invoker.commands_loader.load_command_table(None)
71    invoker.commands_loader.command_name = ''
72
73    cli_ctx.raise_event(EVENT_INVOKER_PRE_LOAD_ARGUMENTS, commands_loader=invoker.commands_loader)
74    invoker.commands_loader.load_arguments()
75
76    cli_ctx.raise_event(EVENT_INVOKER_POST_LOAD_ARGUMENTS, commands_loader=invoker.commands_loader)
77    cli_ctx.raise_event(EVENT_INVOKER_POST_CMD_TBL_CREATE, commands_loader=invoker.commands_loader)
78    invoker.parser.cli_ctx = cli_ctx
79    invoker.parser.load_command_table(invoker.commands_loader)
80
81    end_time = time.time()
82    logger.info('Time to load entire command table: %.3f sec', end_time - start_time)
83
84
85def _store_parsers(parser, parser_keys, parser_values, sub_parser_keys, sub_parser_values):
86    for s in parser.subparsers.values():
87        parser_keys.append(_get_parser_name(s))
88        parser_values.append(s)
89        if _is_group(s):
90            for c in s.choices.values():
91                sub_parser_keys.append(_get_parser_name(c))
92                sub_parser_values.append(c)
93                _store_parsers(c, parser_keys, parser_values, sub_parser_keys, sub_parser_values)
94
95
96def _get_parser_name(s):
97    return (s._prog_prefix if hasattr(s, '_prog_prefix') else s.prog)[3:]  # pylint: disable=protected-access
98
99
100def _is_group(parser):
101    return getattr(parser, '_subparsers', None) is not None \
102        or getattr(parser, 'choices', None) is not None
103