azdev/operations/help/refdoc/extension_docs/helpgen.py (55 lines of code) (raw):
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
from knack.log import get_logger
from knack.help import GroupHelpFile
from azure.cli.core.file_util import _store_parsers, _is_group # pylint: disable=import-error
from azure.cli.core.commands import ExtensionCommandSource # pylint: disable=import-error
from azure.cli.core._help import CliCommandHelpFile # pylint: disable=import-error
from azdev.operations.help.refdoc.common.directives import AbstractHelpGenDirective
from azdev.operations.help.refdoc.common.directives import setup_common_directives
logger = get_logger(__name__)
class ExtensionHelpGenDirective(AbstractHelpGenDirective):
"""
CLI Extensions Sphinx Directive
Ensure that sphinx output is generated from only extension help files
"""
def _get_help_files(self, az_cli):
return get_extension_help_files(az_cli)
def _load_doc_source_map(self):
# no doc source map for extensions
pass
def _get_doc_source_content(self, doc_source_map, help_file):
# no doc source map for extensions
pass
# TODO: move this into core
def get_extension_help_files(cli_ctx):
# 1. Create invoker and load command table and arguments. Remember to turn off applicability check.
invoker = cli_ctx.invocation_cls(cli_ctx=cli_ctx, commands_loader_cls=cli_ctx.commands_loader_cls,
parser_cls=cli_ctx.parser_cls, help_cls=cli_ctx.help_cls)
cli_ctx.invocation = invoker
invoker.commands_loader.skip_applicability = True
cmd_table = invoker.commands_loader.load_command_table(None)
# turn off applicability check for all loaders
for loaders in invoker.commands_loader.cmd_to_loader_map.values():
for loader in loaders:
loader.skip_applicability = True
# filter the command table to only get commands from extensions
cmd_table = {k: v for k, v in cmd_table.items() if isinstance(v.command_source, ExtensionCommandSource)}
invoker.commands_loader.command_table = cmd_table
logger.warning('Found %s command(s) from the extension.\n', len(cmd_table))
for cmd_name in cmd_table:
invoker.commands_loader.load_arguments(cmd_name)
invoker.parser.load_command_table(invoker.commands_loader)
# 2. Now load applicable help files
parser_keys = []
parser_values = []
sub_parser_keys = []
sub_parser_values = []
_store_parsers(invoker.parser, parser_keys, parser_values, sub_parser_keys, sub_parser_values)
for cmd, parser in zip(parser_keys, parser_values):
if cmd not in sub_parser_keys:
sub_parser_keys.append(cmd)
sub_parser_values.append(parser)
help_ctx = cli_ctx.help_cls(cli_ctx=cli_ctx)
help_files = []
for cmd, parser in zip(sub_parser_keys, sub_parser_values):
try:
help_file = GroupHelpFile(help_ctx, cmd, parser) if _is_group(parser) \
else CliCommandHelpFile(help_ctx, cmd, parser)
help_file.load(parser)
help_files.append(help_file)
except Exception as ex: # pylint: disable=broad-except
logger.warning("Skipped '%s' due to '%s'", cmd, ex)
help_files = sorted(help_files, key=lambda x: x.command)
logger.warning('Generated %s help objects from the extension.\n', len(help_files))
return help_files
def setup(app):
""" Setup sphinx app with help generation directive. This is called by sphinx.
:param app: The sphinx app
:return:
"""
app.add_directive('exthelpgen', ExtensionHelpGenDirective)
setup_common_directives(app)