Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / tools / telemetry / third_party / gsutilz / gslib / commands / help.py
blob5f4486fcdd1e48ba2613178c54b83dd79a3d9e9f
1 # -*- coding: utf-8 -*-
2 # Copyright 2011 Google Inc. All Rights Reserved.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 """Implementation of gsutil help command."""
17 from __future__ import absolute_import
19 import itertools
20 import os
21 import pkgutil
22 import re
23 from subprocess import PIPE
24 from subprocess import Popen
26 import gslib.addlhelp
27 from gslib.command import Command
28 from gslib.command import OLD_ALIAS_MAP
29 import gslib.commands
30 from gslib.exception import CommandException
31 from gslib.help_provider import HelpProvider
32 from gslib.help_provider import MAX_HELP_NAME_LEN
33 from gslib.util import IsRunningInteractively
35 _SYNOPSIS = """
36 gsutil help [command or topic]
37 """
39 _DETAILED_HELP_TEXT = ("""
40 <B>SYNOPSIS</B>
41 """ + _SYNOPSIS + """
44 <B>DESCRIPTION</B>
45 Running:
47 gsutil help
49 will provide a summary of all commands and additional topics on which
50 help is available.
52 Running:
54 gsutil help command or topic
56 will provide help about the specified command or topic.
58 Running:
60 gsutil help command sub-command
62 will provide help about the specified sub-command. For example, running:
64 gsutil help acl set
66 will provide help about the "set" subcommand of the "acl" command.
68 If you set the PAGER environment variable to the path to a pager program
69 (such as /bin/less on Linux), long help sections will be piped through
70 the specified pager.
71 """)
73 top_level_usage_string = (
74 'Usage: gsutil [-D] [-DD] [-h header]... '
75 '[-m] [-o] [-q] [command [opts...] args...]'
79 class HelpCommand(Command):
80 """Implementation of gsutil help command."""
82 # Command specification. See base class for documentation.
83 command_spec = Command.CreateCommandSpec(
84 'help',
85 command_name_aliases=['?', 'man'],
86 usage_synopsis=_SYNOPSIS,
87 min_args=0,
88 max_args=2,
89 supported_sub_args='',
90 file_url_ok=True,
91 provider_url_ok=False,
92 urls_start_arg=0,
94 # Help specification. See help_provider.py for documentation.
95 help_spec = Command.HelpSpec(
96 help_name='help',
97 help_name_aliases=['?'],
98 help_type='command_help',
99 help_one_line_summary='Get help about commands and topics',
100 help_text=_DETAILED_HELP_TEXT,
101 subcommand_help_text={},
104 def RunCommand(self):
105 """Command entry point for the help command."""
106 (help_type_map, help_name_map) = self._LoadHelpMaps()
107 output = []
108 if not self.args:
109 output.append('%s\nAvailable commands:\n' % top_level_usage_string)
110 format_str = ' %-' + str(MAX_HELP_NAME_LEN) + 's%s\n'
111 for help_prov in sorted(help_type_map['command_help'],
112 key=lambda hp: hp.help_spec.help_name):
113 output.append(format_str % (
114 help_prov.help_spec.help_name,
115 help_prov.help_spec.help_one_line_summary))
116 output.append('\nAdditional help topics:\n')
117 for help_prov in sorted(help_type_map['additional_help'],
118 key=lambda hp: hp.help_spec.help_name):
119 output.append(format_str % (
120 help_prov.help_spec.help_name,
121 help_prov.help_spec.help_one_line_summary))
122 output.append('\nUse gsutil help <command or topic> for detailed help.')
123 else:
124 invalid_subcommand = False
125 arg = self.args[0]
126 if arg not in help_name_map:
127 output.append('No help available for "%s"' % arg)
128 else:
129 help_prov = help_name_map[arg]
130 help_name = None
131 if len(self.args) > 1: # We also have a subcommand argument.
132 subcommand_map = help_prov.help_spec.subcommand_help_text
133 if subcommand_map and self.args[1] in subcommand_map:
134 help_name = arg + ' ' + self.args[1]
135 help_text = subcommand_map[self.args[1]]
136 else:
137 invalid_subcommand = True
138 if not subcommand_map:
139 output.append((
140 'The "%s" command has no subcommands. You can ask for the '
141 'full help by running:\n\n\tgsutil help %s\n') %
142 (arg, arg))
143 else:
144 subcommand_examples = []
145 for subcommand in subcommand_map:
146 subcommand_examples.append(
147 '\tgsutil help %s %s' % (arg, subcommand))
148 output.append(
149 ('Subcommand "%s" does not exist for command "%s".\n'
150 'You can either ask for the full help about the command by '
151 'running:\n\n\tgsutil help %s\n\n'
152 'Or you can ask for help about one of the subcommands:\n\n%s'
153 ) % (self.args[1], arg, arg, '\n'.join(subcommand_examples)))
154 if not invalid_subcommand:
155 if not help_name: # No subcommand or invalid subcommand.
156 help_name = help_prov.help_spec.help_name
157 help_text = help_prov.help_spec.help_text
159 output.append('<B>NAME</B>\n')
160 output.append(' %s - %s\n' % (
161 help_name, help_prov.help_spec.help_one_line_summary))
162 output.append('\n\n')
163 output.append(help_text.strip('\n'))
164 new_alias = OLD_ALIAS_MAP.get(arg, [None])[0]
165 if new_alias:
166 deprecation_warning = """
167 The "%s" alias is deprecated, and will eventually be removed completely.
168 Please use the "%s" command instead.""" % (arg, new_alias)
170 output.append('\n\n\n<B>DEPRECATION WARNING</B>\n')
171 output.append(deprecation_warning)
172 self._OutputHelp(''.join(output))
173 return 0
175 def _OutputHelp(self, help_str):
176 """Outputs simply formatted string.
178 This function paginates if the string is too long, PAGER is defined, and
179 the output is a tty.
181 Args:
182 help_str: String to format.
184 # Replace <B> and </B> with terminal formatting strings if connected to tty.
185 if not IsRunningInteractively():
186 help_str = re.sub('<B>', '', help_str)
187 help_str = re.sub('</B>', '', help_str)
188 print help_str
189 return
190 help_str = re.sub('<B>', '\033[1m', help_str)
191 help_str = re.sub('</B>', '\033[0;0m', help_str)
192 num_lines = len(help_str.split('\n'))
193 if 'PAGER' in os.environ and num_lines >= gslib.util.GetTermLines():
194 # Use -r option for less to make bolding work right.
195 pager = os.environ['PAGER'].split(' ')
196 if pager[0].endswith('less'):
197 pager.append('-r')
198 try:
199 Popen(pager, stdin=PIPE).communicate(input=help_str)
200 except OSError, e:
201 raise CommandException('Unable to open pager (%s): %s' %
202 (' '.join(pager), e))
203 else:
204 print help_str
206 def _LoadHelpMaps(self):
207 """Returns tuple of help type and help name.
209 help type is a dict with key: help type
210 value: list of HelpProviders
211 help name is a dict with key: help command name or alias
212 value: HelpProvider
214 Returns:
215 (help type, help name)
218 # Import all gslib.commands submodules.
219 for _, module_name, _ in pkgutil.iter_modules(gslib.commands.__path__):
220 __import__('gslib.commands.%s' % module_name)
221 # Import all gslib.addlhelp submodules.
222 for _, module_name, _ in pkgutil.iter_modules(gslib.addlhelp.__path__):
223 __import__('gslib.addlhelp.%s' % module_name)
225 help_type_map = {}
226 help_name_map = {}
227 for s in gslib.help_provider.ALL_HELP_TYPES:
228 help_type_map[s] = []
229 # Only include HelpProvider subclasses in the dict.
230 for help_prov in itertools.chain(
231 HelpProvider.__subclasses__(), Command.__subclasses__()):
232 if help_prov is Command:
233 # Skip the Command base class itself; we just want its subclasses,
234 # where the help command text lives (in addition to non-Command
235 # HelpProviders, like naming.py).
236 continue
237 gslib.help_provider.SanityCheck(help_prov, help_name_map)
238 help_name_map[help_prov.help_spec.help_name] = help_prov
239 for help_name_aliases in help_prov.help_spec.help_name_aliases:
240 help_name_map[help_name_aliases] = help_prov
241 help_type_map[help_prov.help_spec.help_type].append(help_prov)
242 return (help_type_map, help_name_map)