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
23 from subprocess
import PIPE
24 from subprocess
import Popen
27 from gslib
.command
import Command
28 from gslib
.command
import OLD_ALIAS_MAP
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
36 gsutil help [command or topic]
39 _DETAILED_HELP_TEXT
= ("""
49 will provide a summary of all commands and additional topics on which
54 gsutil help command or topic
56 will provide help about the specified command or topic.
60 gsutil help command sub-command
62 will provide help about the specified sub-command. For example, running:
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
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(
85 command_name_aliases
=['?', 'man'],
86 usage_synopsis
=_SYNOPSIS
,
89 supported_sub_args
='',
91 provider_url_ok
=False,
94 # Help specification. See help_provider.py for documentation.
95 help_spec
= Command
.HelpSpec(
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
()
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.')
124 invalid_subcommand
= False
126 if arg
not in help_name_map
:
127 output
.append('No help available for "%s"' % arg
)
129 help_prov
= help_name_map
[arg
]
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]]
137 invalid_subcommand
= True
138 if not subcommand_map
:
140 'The "%s" command has no subcommands. You can ask for the '
141 'full help by running:\n\n\tgsutil help %s\n') %
144 subcommand_examples
= []
145 for subcommand
in subcommand_map
:
146 subcommand_examples
.append(
147 '\tgsutil help %s %s' % (arg
, subcommand
))
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]
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
))
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
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
)
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'):
199 Popen(pager
, stdin
=PIPE
).communicate(input=help_str
)
201 raise CommandException('Unable to open pager (%s): %s' %
202 (' '.join(pager
), e
))
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
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
)
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).
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
)