File: //snap/google-cloud-cli/394/lib/googlecloudsdk/calliope/cli_tree_markdown.py
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""The cli_tree command help document markdown generator.
This module generates command help markdown from the tree generated by:
gcloud --quiet alpha # make sure the alpha component is installed
gcloud --quiet beta # make sure the beta component is installed
gcloud meta list-gcloud --format=json |
python -c "
import json
import sys
data = json.load(sys.stdin)
print 'gcloud_tree =', data" > gcloud_tree.py
Usage:
from googlecloudsdk.calliope import cli_tree_markdown
from googlecloudsdk.command_lib.shell import gcloud_tree
command = <command node in gcloud tree>
flag = <flag node in gcloud tree>
generator = cli_tree_markdown.CliTreeMarkdownGenerator(command, gcloud_tree)
generator.PrintSynopsisSection()
generator.PrintFlagDefinition(flag)
...
markdown = generator.Edit()
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import cli_tree
from googlecloudsdk.calliope import markdown
from googlecloudsdk.calliope import usage_text
from googlecloudsdk.core import properties
import six
if six.PY2:
FLAG_TYPE_NAME = b'flag'
POSITIONAL_TYPE_NAME = b'positional'
GROUP_TYPE_NAME = b'group'
else:
FLAG_TYPE_NAME = 'flag'
POSITIONAL_TYPE_NAME = 'positional'
GROUP_TYPE_NAME = 'group'
def _GetReleaseTrackFromId(release_id):
"""Returns the base.ReleaseTrack for release_id."""
if release_id == 'INTERNAL':
release_id = 'GA'
return base.ReleaseTrack.FromId(release_id)
def Flag(d):
"""Returns a flag object suitable for the calliope.markdown module."""
flag = type(FLAG_TYPE_NAME, (object,), d)
flag.is_group = False
flag.is_hidden = d.get(cli_tree.LOOKUP_IS_HIDDEN, d.get('hidden', False))
flag.hidden = flag.is_hidden
flag.is_positional = False
flag.is_required = d.get(
cli_tree.LOOKUP_IS_REQUIRED, d.get(cli_tree.LOOKUP_REQUIRED, False)
)
flag.required = flag.is_required
flag.help = flag.description
flag.dest = flag.name.lower().replace('-', '_')
flag.metavar = flag.value
flag.option_strings = [flag.name]
if not hasattr(flag, 'default'):
flag.default = None
if flag.type == 'bool':
flag.nargs = 0
elif flag.nargs not in ('?', '*', '+'):
flag.nargs = 1
if flag.type == 'dict':
flag.type = arg_parsers.ArgDict()
elif flag.type == 'list':
flag.type = arg_parsers.ArgList()
elif flag.type == 'string':
flag.type = None
if flag.attr.get(cli_tree.LOOKUP_INVERTED_SYNOPSIS):
flag.inverted_synopsis = True
prop = flag.attr.get('property')
if prop:
if cli_tree.LOOKUP_VALUE in prop:
kind = 'value'
value = prop[cli_tree.LOOKUP_VALUE]
else:
value = None
kind = 'bool' if flag.type == 'bool' else None
flag.store_property = (
properties.FromString(prop[cli_tree.LOOKUP_NAME]),
kind,
value,
)
return flag
def Positional(d):
"""Returns a positional object suitable for the calliope.markdown module."""
positional = type(POSITIONAL_TYPE_NAME, (object,), d)
positional.help = positional.description
positional.is_group = False
positional.is_hidden = False
positional.is_positional = True
positional.is_required = positional.nargs != '*'
positional.dest = positional.value.lower().replace('-', '_')
positional.metavar = positional.value
positional.option_strings = []
try:
positional.nargs = int(positional.nargs)
except ValueError:
pass
return positional
def Argument(d):
"""Returns an argument object suitable for the calliope.markdown module."""
if d.get(cli_tree.LOOKUP_IS_POSITIONAL, False):
return Positional(d)
if not d.get(cli_tree.LOOKUP_IS_GROUP, False):
return Flag(d)
group = type(GROUP_TYPE_NAME, (object,), d)
group.arguments = [Argument(a) for a in d.get(cli_tree.LOOKUP_ARGUMENTS, [])]
group.category = None
group.help = group.description
group.is_global = False
group.is_hidden = False
group.sort_args = True
group.disable_default_heading = False
return group
class CliTreeMarkdownGenerator(markdown.MarkdownGenerator):
"""cli_tree command help markdown document generator.
Attributes:
_capsule: The help text capsule.
_command: The tree node for command.
_command_path: The command path list.
_tree: The (sub)tree root.
_sections: The help text sections indexed by SECTION name.
_subcommands: The dict of subcommand help indexed by subcommand name.
_subgroups: The dict of subgroup help indexed by subcommand name.
"""
def __init__(self, command, tree):
"""Constructor.
Args:
command: The command node in the root tree.
tree: The (sub)tree root.
"""
self._tree = tree
self._command = command
self._command_path = command[cli_tree.LOOKUP_PATH]
super(CliTreeMarkdownGenerator, self).__init__(
self._command_path,
_GetReleaseTrackFromId(self._command[cli_tree.LOOKUP_RELEASE]),
self._command.get(
cli_tree.LOOKUP_IS_HIDDEN, self._command.get('hidden', False)
),
)
self._capsule = self._command[cli_tree.LOOKUP_CAPSULE]
self._sections = self._command[cli_tree.LOOKUP_SECTIONS]
self._subcommands = self.GetSubCommandHelp()
self._subgroups = self.GetSubGroupHelp()
self._sort_top_level_args = True
def _GetCommandFromPath(self, command_path):
"""Returns the command node for command_path."""
path = self._tree[cli_tree.LOOKUP_PATH]
if path:
# self._tree is not a super root. The first path name must match.
if command_path[:1] != path:
return None
# Already checked the first name.
command_path = command_path[1:]
command = self._tree
for name in command_path:
commands = command[cli_tree.LOOKUP_COMMANDS]
if name not in commands:
return None
command = commands[name]
return command
def IsValidSubPath(self, command_path):
"""Returns True if the given command path after the top is valid."""
return (
self._GetCommandFromPath([cli_tree.DEFAULT_CLI_NAME] + command_path)
is not None
)
def GetArguments(self):
"""Returns the command arguments."""
command = self._GetCommandFromPath(self._command_path)
try:
return [
Argument(a)
for a in command[cli_tree.LOOKUP_CONSTRAINTS][
cli_tree.LOOKUP_ARGUMENTS
]
]
except (KeyError, TypeError):
return []
def GetArgDetails(self, arg, depth=None):
"""Returns the help text with auto-generated details for arg.
The help text was already generated on the cli_tree generation side.
Args:
arg: The arg to auto-generate help text for.
depth: The indentation depth at which the details should be printed. Added
here only to maintain consistency with superclass during testing.
Returns:
The help text with auto-generated details for arg.
"""
return arg.help
def _GetSubHelp(self, is_group=False):
"""Returns the help dict indexed by command for sub commands or groups."""
return {
name: usage_text.HelpInfo(
help_text=subcommand[cli_tree.LOOKUP_CAPSULE],
is_hidden=subcommand.get(
cli_tree.LOOKUP_IS_HIDDEN, subcommand.get('hidden', False)
),
release_track=_GetReleaseTrackFromId(
subcommand[cli_tree.LOOKUP_RELEASE]
),
)
for name, subcommand in six.iteritems(
self._command[cli_tree.LOOKUP_COMMANDS]
)
if subcommand[cli_tree.LOOKUP_IS_GROUP] == is_group
}
def GetSubCommandHelp(self):
"""Returns the subcommand help dict indexed by subcommand."""
return self._GetSubHelp(is_group=False)
def GetSubGroupHelp(self):
"""Returns the subgroup help dict indexed by subgroup."""
return self._GetSubHelp(is_group=True)
def PrintFlagDefinition(self, flag, disable_header=False):
"""Prints a flags definition list item."""
if isinstance(flag, dict):
flag = Flag(flag)
super(CliTreeMarkdownGenerator, self).PrintFlagDefinition(
flag, disable_header=disable_header
)
def _ExpandHelpText(self, doc):
"""{...} references were done when the tree was generated."""
return doc
def Markdown(command, tree):
"""Returns the help markdown document string for the command node in tree.
Args:
command: The command node in the root tree.
tree: The (sub)tree root.
Returns:
The markdown document string.
"""
return CliTreeMarkdownGenerator(command, tree).Generate()