OLD | NEW |
| (Empty) |
1 # -*- coding: utf-8 -*- | |
2 # Copyright 2011 Google Inc. All Rights Reserved. | |
3 # | |
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 | |
7 # | |
8 # http://www.apache.org/licenses/LICENSE-2.0 | |
9 # | |
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.""" | |
16 | |
17 from __future__ import absolute_import | |
18 | |
19 import itertools | |
20 import os | |
21 import pkgutil | |
22 import re | |
23 from subprocess import PIPE | |
24 from subprocess import Popen | |
25 | |
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 | |
34 | |
35 _SYNOPSIS = """ | |
36 gsutil help [command or topic] | |
37 """ | |
38 | |
39 _DETAILED_HELP_TEXT = (""" | |
40 <B>SYNOPSIS</B> | |
41 """ + _SYNOPSIS + """ | |
42 | |
43 | |
44 <B>DESCRIPTION</B> | |
45 Running: | |
46 | |
47 gsutil help | |
48 | |
49 will provide a summary of all commands and additional topics on which | |
50 help is available. | |
51 | |
52 Running: | |
53 | |
54 gsutil help command or topic | |
55 | |
56 will provide help about the specified command or topic. | |
57 | |
58 Running: | |
59 | |
60 gsutil help command sub-command | |
61 | |
62 will provide help about the specified sub-command. For example, running: | |
63 | |
64 gsutil help acl set | |
65 | |
66 will provide help about the "set" subcommand of the "acl" command. | |
67 | |
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 """) | |
72 | |
73 top_level_usage_string = ( | |
74 'Usage: gsutil [-D] [-DD] [-h header]... ' | |
75 '[-m] [-o] [-q] [command [opts...] args...]' | |
76 ) | |
77 | |
78 | |
79 class HelpCommand(Command): | |
80 """Implementation of gsutil help command.""" | |
81 | |
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, | |
93 ) | |
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={}, | |
102 ) | |
103 | |
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 | |
158 | |
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) | |
169 | |
170 output.append('\n\n\n<B>DEPRECATION WARNING</B>\n') | |
171 output.append(deprecation_warning) | |
172 self._OutputHelp(''.join(output)) | |
173 return 0 | |
174 | |
175 def _OutputHelp(self, help_str): | |
176 """Outputs simply formatted string. | |
177 | |
178 This function paginates if the string is too long, PAGER is defined, and | |
179 the output is a tty. | |
180 | |
181 Args: | |
182 help_str: String to format. | |
183 """ | |
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 | |
205 | |
206 def _LoadHelpMaps(self): | |
207 """Returns tuple of help type and help name. | |
208 | |
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 | |
213 | |
214 Returns: | |
215 (help type, help name) | |
216 """ | |
217 | |
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) | |
224 | |
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) | |
OLD | NEW |