Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4de2e7d
Dummy help.yaml file
Oct 11, 2018
79b5663
Parse command for testing yaml parsing
Oct 11, 2018
4226773
Added logic to parse help.yaml file and update help data objects.
Oct 16, 2018
0b73f6c
_help.py now raises error if it fails to parse help.yaml or help.yaml…
Oct 16, 2018
04a4901
updated _help.py: CLI now prints extension messages. Removed parser m…
Oct 17, 2018
5c95622
_help.py: Addressed pep8 warnings. Made some methods static
Oct 17, 2018
01ebec5
changed dir to dir_name as dir is a builtin function.
Oct 18, 2018
86e93bb
Added some comments. Link urls have title field instad of name field.…
Oct 22, 2018
857b004
Fixed bug in _params_equal()
Oct 22, 2018
0894682
checked out devs _help.py file.
Oct 25, 2018
4a90f31
Refactoring code. Todo: update print_header to print links. Fix bug w…
Oct 26, 2018
457f6ed
script to convert _help.py to help/yaml. Parses short and long summar…
Oct 18, 2018
43a036f
Help conversion script uses ruamel.yaml. Output is ordered and visual…
Oct 19, 2018
9e60de9
Conversion script now converts parameters to arguments (bug fix). Out…
Oct 19, 2018
49e6694
Removed unnecessary file.
Nov 1, 2018
2d8c474
Addressed style feedback.
Nov 1, 2018
47e56d6
Changes:
Nov 1, 2018
8548e73
uncommented help import
Nov 1, 2018
bc3a170
Added tests to help script.
Nov 2, 2018
6d5b7f2
PEP8 style changes.
Nov 2, 2018
01ebbf4
Sphinx properly handles help groups.
Nov 13, 2018
c567248
Refactored _help.py and _help_util.py to _help_loaders.py
Nov 14, 2018
8ab7c9c
Removed _help_util.py
Nov 14, 2018
9589f39
Bug fixes
Nov 14, 2018
b5c5279
More refactoring, script tests pass
Nov 15, 2018
a1732d1
Changes to restore _help.py to same state before rebase.
Nov 22, 2018
dc1ad57
Updated AzCliHelp to print links associated with commands and groups.
Nov 22, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions doc/sphinx/azhelpgen/azhelpgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@

from knack.help_files import helps

from knack.help import GroupHelpFile
from azure.cli.core import MainCommandsLoader, AzCli
from azure.cli.core.commands import AzCliCommandInvoker
from azure.cli.core.parser import AzCliCommandParser
from azure.cli.core._help import AzCliHelp, CliCommandHelpFile, ArgumentGroupRegistry
from azure.cli.core._help import AzCliHelp, CliCommandHelpFile, ArgumentGroupRegistry, CliGroupHelpFile

USER_HOME = expanduser('~')

Expand All @@ -44,7 +43,7 @@ def get_help_files(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 = CliGroupHelpFile(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:
Expand Down
147 changes: 137 additions & 10 deletions src/azure-cli-core/azure/cli/core/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

from __future__ import print_function

from knack.help import (HelpExample,
HelpFile as KnackHelpFile,
CommandHelpFile as KnackCommandHelpFile,
CLIHelp,
ArgumentGroupRegistry as KnackArgumentGroupRegistry)
from knack.help import (HelpFile as KnackHelpFile, CommandHelpFile as KnackCommandHelpFile,
GroupHelpFile as KnackGroupHelpFile, ArgumentGroupRegistry as KnackArgumentGroupRegistry,
HelpExample as KnackHelpExample, HelpParameter as KnackHelpParameter,
_print_indent, CLIHelp)

from knack.log import get_logger

from azure.cli.core.commands import ExtensionCommandSource
Expand Down Expand Up @@ -52,6 +52,7 @@ def __init__(self, cli_ctx):
privacy_statement=PRIVACY_STATEMENT,
welcome_message=WELCOME_MESSAGE,
command_help_cls=CliCommandHelpFile,
group_help_cls=CliGroupHelpFile,
help_cls=CliHelpFile)
from knack.help import HelpObject

Expand All @@ -70,6 +71,9 @@ def new_normalize_text(s):

HelpObject._normalize_text = new_normalize_text # pylint: disable=protected-access

self._register_help_loaders()

# print methods
@staticmethod
def _print_extensions_msg(help_file):
if help_file.type != 'command':
Expand All @@ -83,9 +87,39 @@ def _print_detailed_help(self, cli_name, help_file):
AzCliHelp._print_extensions_msg(help_file)
super(AzCliHelp, self)._print_detailed_help(cli_name, help_file)

def _print_header(self, cli_name, help_file):
super(AzCliHelp, self)._print_header(cli_name, help_file)

links = help_file.links
if links:
link_text = "{} and {}".format(", ".join(links[0:-1]), links[-1]) if len(links) > 1 else links[0]
link_text = "For more information, see: {}\n".format(link_text)
_print_indent(link_text, 2, width=self.textwrap_width)


def _register_help_loaders(self):
import azure.cli.core._help_loaders as help_loaders
import inspect

def is_loader_cls(cls):
return inspect.isclass(cls) and cls.__name__ != 'BaseHelpLoader'and issubclass(cls, help_loaders.BaseHelpLoader) # pylint: disable=line-too-long

versioned_loaders = {}
for cls_name, loader_cls in inspect.getmembers(help_loaders, is_loader_cls):
loader = loader_cls(self)
versioned_loaders[cls_name] = loader

self.versioned_loaders = versioned_loaders


class CliHelpFile(KnackHelpFile):

def __init__(self, help_ctx, delimiters):
# Each help file (for a command or group) has a version denoting the source of its data.
self.yaml_help_version = 0
super(CliHelpFile, self).__init__(help_ctx, delimiters)
self.links = []

def _should_include_example(self, ex):
min_profile = ex.get('min_profile')
max_profile = ex.get('max_profile')
Expand All @@ -98,22 +132,92 @@ def _should_include_example(self, ex):
min_api=min_profile, max_api=max_profile)
return True

# Needs to override base implementation
# Needs to override base implementation to exclude unsupported examples.
def _load_from_data(self, data):
super(CliHelpFile, self)._load_from_data(data)
self.examples = [] # clear examples set by knack
if not data:
return

if isinstance(data, str):
self.long_summary = data
return

if 'type' in data:
self.type = data['type']

if 'short-summary' in data:
self.short_summary = data['short-summary']

self.long_summary = data.get('long-summary')

if 'examples' in data:
self.examples = []
for d in data['examples']:
if self._should_include_example(d):
self.examples.append(HelpExample(d))
self.examples.append(HelpExample(**d))

def load(self, options):
ordered_loaders = sorted(self.help_ctx.versioned_loaders.values(), key=lambda ldr: ldr.VERSION)
for loader in ordered_loaders:
loader.versioned_load(self, options)


class CliGroupHelpFile(KnackGroupHelpFile, CliHelpFile):
def __init__(self, help_ctx, delimiters, parser):
super(CliGroupHelpFile, self).__init__(help_ctx, delimiters, parser)

def load(self, options):
# forces class to use this load method even if KnackGroupHelpFile overrides CliHelpFile's method.
CliHelpFile.load(self, options)


class CliCommandHelpFile(KnackCommandHelpFile, CliHelpFile):

def __init__(self, help_ctx, delimiters, parser):
self.command_source = getattr(parser, 'command_source', None)
super(CliCommandHelpFile, self).__init__(help_ctx, delimiters, parser)
import argparse
self.type = 'command'
self.command_source = getattr(parser, 'command_source', None)

self.parameters = []

for action in [a for a in parser._actions if a.help != argparse.SUPPRESS]: # pylint: disable=protected-access
if action.option_strings:
self._add_parameter_help(action)
else:
# use metavar for positional parameters
param_kwargs = {
'name_source': [action.metavar or action.dest],
'deprecate_info': getattr(action, 'deprecate_info', None),
'description': action.help,
'choices': action.choices,
'required': False,
'default': None,
'group_name': 'Positional'
}
self.parameters.append(HelpParameter(**param_kwargs))

help_param = next(p for p in self.parameters if p.name == '--help -h')
help_param.group_name = 'Global Arguments'

def _load_from_data(self, data):
super(CliCommandHelpFile, self)._load_from_data(data)

if isinstance(data, str) or not self.parameters or not data.get('parameters'):
return

loaded_params = []
loaded_param = {}
for param in self.parameters:
loaded_param = next((n for n in data['parameters'] if n['name'] == param.name), None)
if loaded_param:
param.update_from_data(loaded_param)
loaded_params.append(param)

self.parameters = loaded_params

def load(self, options):
# forces class to use this load method even if KnackCommandHelpFile overrides CliHelpFile's method.
CliHelpFile.load(self, options)


class ArgumentGroupRegistry(KnackArgumentGroupRegistry): # pylint: disable=too-few-public-methods
Expand All @@ -133,3 +237,26 @@ def __init__(self, group_list):
for group in other_groups:
self.priorities[group] = priority
priority += 1


class HelpExample(KnackHelpExample): # pylint: disable=too-few-public-methods

def __init__(self, **_data):
_data['name'] = _data.get('name', '')
_data['text'] = _data.get('text', '')
super(HelpExample, self).__init__(_data)

self.min_profile = _data.get('min_profile', '')
self.max_profile = _data.get('max_profile', '')

self.summary = _data.get('summary', '')
self.command = _data.get('command', '')
self.description = _data.get('description', '')


class HelpParameter(KnackHelpParameter): # pylint: disable=too-many-instance-attributes

def __init__(self, **kwargs):
super(HelpParameter, self).__init__(**kwargs)
# new field
self.raw_value_sources = []
Loading