Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(268)

Side by Side Diff: gslib/commands/help.py

Issue 698893003: Update checked in version of gsutil to version 4.6 (Closed) Base URL: http://dart.googlecode.com/svn/third_party/gsutil/
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « gslib/commands/hash.py ('k') | gslib/commands/lifecycle.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # -*- coding: utf-8 -*-
1 # Copyright 2011 Google Inc. All Rights Reserved. 2 # Copyright 2011 Google Inc. All Rights Reserved.
2 # 3 #
3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License. 5 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at 6 # You may obtain a copy of the License at
6 # 7 #
7 # http://www.apache.org/licenses/LICENSE-2.0 8 # http://www.apache.org/licenses/LICENSE-2.0
8 # 9 #
9 # Unless required by applicable law or agreed to in writing, software 10 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and 13 # See the License for the specific language governing permissions and
13 # limitations under the License. 14 # limitations under the License.
15 """Implementation of gsutil help command."""
16
17 from __future__ import absolute_import
14 18
15 import itertools 19 import itertools
16 import os 20 import os
17 import pkgutil 21 import pkgutil
18 import re 22 import re
19 import struct
20 from subprocess import PIPE 23 from subprocess import PIPE
21 from subprocess import Popen 24 from subprocess import Popen
22 25
23 import gslib.commands
24 import gslib.addlhelp 26 import gslib.addlhelp
25 from gslib.command import Command 27 from gslib.command import Command
26 from gslib.command import COMMAND_NAME
27 from gslib.command import COMMAND_NAME_ALIASES
28 from gslib.command import FILE_URIS_OK
29 from gslib.command import MAX_ARGS
30 from gslib.command import MIN_ARGS
31 from gslib.command import OLD_ALIAS_MAP 28 from gslib.command import OLD_ALIAS_MAP
32 from gslib.command import PROVIDER_URIS_OK 29 import gslib.commands
33 from gslib.command import SUPPORTED_SUB_ARGS
34 from gslib.command import URIS_START_ARG
35 from gslib.exception import CommandException 30 from gslib.exception import CommandException
36 from gslib.help_provider import ALL_HELP_TYPES
37 from gslib.help_provider import HELP_NAME
38 from gslib.help_provider import HELP_NAME_ALIASES
39 from gslib.help_provider import HELP_ONE_LINE_SUMMARY
40 from gslib.help_provider import HelpProvider 31 from gslib.help_provider import HelpProvider
41 from gslib.help_provider import HELP_TEXT
42 from gslib.help_provider import HelpType
43 from gslib.help_provider import HELP_TYPE
44 from gslib.help_provider import MAX_HELP_NAME_LEN 32 from gslib.help_provider import MAX_HELP_NAME_LEN
45 from gslib.help_provider import SUBCOMMAND_HELP_TEXT
46 from gslib.util import IsRunningInteractively 33 from gslib.util import IsRunningInteractively
47 34
48 _detailed_help_text = (""" 35 _DETAILED_HELP_TEXT = ("""
49 <B>SYNOPSIS</B> 36 <B>SYNOPSIS</B>
50 gsutil help [command or topic] 37 gsutil help [command or topic]
51 38
52 39
53 <B>DESCRIPTION</B> 40 <B>DESCRIPTION</B>
54 Running: 41 Running:
55 42
56 gsutil help 43 gsutil help
57 44
58 will provide a summary of all commands and additional topics on which 45 will provide a summary of all commands and additional topics on which
(...skipping 14 matching lines...) Expand all
73 gsutil help acl set 60 gsutil help acl set
74 61
75 will provide help about the "set" subcommand of the "acl" command. 62 will provide help about the "set" subcommand of the "acl" command.
76 63
77 If you set the PAGER environment variable to the path to a pager program 64 If you set the PAGER environment variable to the path to a pager program
78 (such as /bin/less on Linux), long help sections will be piped through 65 (such as /bin/less on Linux), long help sections will be piped through
79 the specified pager. 66 the specified pager.
80 """) 67 """)
81 68
82 top_level_usage_string = ( 69 top_level_usage_string = (
83 "Usage: gsutil [-d][-D] [-h header]... [-m] [command [opts...] args...] [-q] " 70 'Usage: gsutil [-D] [-DD] [-h header]... '
71 '[-m] [-o] [-q] [command [opts...] args...]'
84 ) 72 )
85 73
74
86 class HelpCommand(Command): 75 class HelpCommand(Command):
87 """Implementation of gsutil help command.""" 76 """Implementation of gsutil help command."""
88 77
89 # Command specification (processed by parent class). 78 # Command specification. See base class for documentation.
90 command_spec = { 79 command_spec = Command.CreateCommandSpec(
91 # Name of command. 80 'help',
92 COMMAND_NAME : 'help', 81 command_name_aliases=['?', 'man'],
93 # List of command name aliases. 82 min_args=0,
94 COMMAND_NAME_ALIASES : ['?', 'man'], 83 max_args=2,
95 # Min number of args required by this command. 84 supported_sub_args='',
96 MIN_ARGS : 0, 85 file_url_ok=True,
97 # Max number of args required by this command, or NO_MAX. 86 provider_url_ok=False,
98 MAX_ARGS : 2, 87 urls_start_arg=0,
99 # Getopt-style string specifying acceptable sub args. 88 )
100 SUPPORTED_SUB_ARGS : '', 89 # Help specification. See help_provider.py for documentation.
101 # True if file URIs acceptable for this command. 90 help_spec = Command.HelpSpec(
102 FILE_URIS_OK : True, 91 help_name='help',
103 # True if provider-only URIs acceptable for this command. 92 help_name_aliases=['?'],
104 PROVIDER_URIS_OK : False, 93 help_type='command_help',
105 # Index in args of first URI arg. 94 help_one_line_summary='Get help about commands and topics',
106 URIS_START_ARG : 0, 95 help_text=_DETAILED_HELP_TEXT,
107 } 96 subcommand_help_text={},
108 help_spec = { 97 )
109 # Name of command or auxiliary help info for which this help applies.
110 HELP_NAME : 'help',
111 # List of help name aliases.
112 HELP_NAME_ALIASES : ['?'],
113 # Type of help:
114 HELP_TYPE : HelpType.COMMAND_HELP,
115 # One line summary of this help.
116 HELP_ONE_LINE_SUMMARY : 'Get help about commands and topics',
117 # The full help text.
118 HELP_TEXT : _detailed_help_text,
119 }
120 98
121 # Command entry point.
122 def RunCommand(self): 99 def RunCommand(self):
100 """Command entry point for the help command."""
123 (help_type_map, help_name_map) = self._LoadHelpMaps() 101 (help_type_map, help_name_map) = self._LoadHelpMaps()
124 output = [] 102 output = []
125 if not len(self.args): 103 if not self.args:
126 output.append('%s\nAvailable commands:\n' % top_level_usage_string) 104 output.append('%s\nAvailable commands:\n' % top_level_usage_string)
127 format_str = ' %-' + str(MAX_HELP_NAME_LEN) + 's%s\n' 105 format_str = ' %-' + str(MAX_HELP_NAME_LEN) + 's%s\n'
128 for help_prov in sorted(help_type_map[HelpType.COMMAND_HELP], 106 for help_prov in sorted(help_type_map['command_help'],
129 key=lambda hp: hp.help_spec[HELP_NAME]): 107 key=lambda hp: hp.help_spec.help_name):
130 output.append(format_str % (help_prov.help_spec[HELP_NAME], 108 output.append(format_str % (
131 help_prov.help_spec[HELP_ONE_LINE_SUMMARY])) 109 help_prov.help_spec.help_name,
110 help_prov.help_spec.help_one_line_summary))
132 output.append('\nAdditional help topics:\n') 111 output.append('\nAdditional help topics:\n')
133 for help_prov in sorted(help_type_map[HelpType.ADDITIONAL_HELP], 112 for help_prov in sorted(help_type_map['additional_help'],
134 key=lambda hp: hp.help_spec[HELP_NAME]): 113 key=lambda hp: hp.help_spec.help_name):
135 output.append(format_str % (help_prov.help_spec[HELP_NAME], 114 output.append(format_str % (
136 help_prov.help_spec[HELP_ONE_LINE_SUMMARY])) 115 help_prov.help_spec.help_name,
116 help_prov.help_spec.help_one_line_summary))
137 output.append('\nUse gsutil help <command or topic> for detailed help.') 117 output.append('\nUse gsutil help <command or topic> for detailed help.')
138 else: 118 else:
139 invalid_subcommand = False 119 invalid_subcommand = False
140 arg = self.args[0] 120 arg = self.args[0]
141 if arg not in help_name_map: 121 if arg not in help_name_map:
142 output.append('No help available for "%s"' % arg) 122 output.append('No help available for "%s"' % arg)
143 else: 123 else:
144 help_prov = help_name_map[arg] 124 help_prov = help_name_map[arg]
145 help_name = None 125 help_name = None
146 if len(self.args) > 1: # We also have a subcommand argument. 126 if len(self.args) > 1: # We also have a subcommand argument.
147 subcommand_map = help_prov.help_spec.get(SUBCOMMAND_HELP_TEXT, None) 127 subcommand_map = help_prov.help_spec.subcommand_help_text
148 if subcommand_map and self.args[1] in subcommand_map: 128 if subcommand_map and self.args[1] in subcommand_map:
149 help_name = arg + ' ' + self.args[1] 129 help_name = arg + ' ' + self.args[1]
150 help_text = subcommand_map[self.args[1]] 130 help_text = subcommand_map[self.args[1]]
151 else: 131 else:
152 invalid_subcommand = True 132 invalid_subcommand = True
153 if not subcommand_map: 133 if not subcommand_map:
154 output.append(( 134 output.append((
155 'The "%s" command has no subcommands. You can ask for the ' + 135 'The "%s" command has no subcommands. You can ask for the '
156 'full help by running:\n\n\tgsutil help %s\n') % 136 'full help by running:\n\n\tgsutil help %s\n') %
157 (arg, arg)) 137 (arg, arg))
158 else: 138 else:
159 subcommand_examples = [] 139 subcommand_examples = []
160 for subcommand in subcommand_map: 140 for subcommand in subcommand_map:
161 subcommand_examples.append( 141 subcommand_examples.append(
162 '\tgsutil help %s %s' % (arg, subcommand)) 142 '\tgsutil help %s %s' % (arg, subcommand))
163 output.append( 143 output.append(
164 ('Subcommand "%s" does not exist for command "%s".\n' + 144 ('Subcommand "%s" does not exist for command "%s".\n'
165 'You can either ask for the full help about the command by ' + 145 'You can either ask for the full help about the command by '
166 'running:\n\n\tgsutil help %s\n\n' 146 'running:\n\n\tgsutil help %s\n\n'
167 'Or you can ask for help about one of the subcommands:\n\n%s' 147 'Or you can ask for help about one of the subcommands:\n\n%s'
168 ) % (self.args[1], arg, arg, '\n'.join(subcommand_examples))) 148 ) % (self.args[1], arg, arg, '\n'.join(subcommand_examples)))
169 if not invalid_subcommand: 149 if not invalid_subcommand:
170 if not help_name: # No subcommand or invalid subcommand. 150 if not help_name: # No subcommand or invalid subcommand.
171 help_name = help_prov.help_spec[HELP_NAME] 151 help_name = help_prov.help_spec.help_name
172 help_text = help_prov.help_spec[HELP_TEXT] 152 help_text = help_prov.help_spec.help_text
173 153
174 output.append('<B>NAME</B>\n') 154 output.append('<B>NAME</B>\n')
175 output.append(' %s - %s\n' % 155 output.append(' %s - %s\n' % (
176 (help_name, help_prov.help_spec[HELP_ONE_LINE_SUMMARY])) 156 help_name, help_prov.help_spec.help_one_line_summary))
177 output.append('\n\n') 157 output.append('\n\n')
178 output.append(help_text.strip('\n')) 158 output.append(help_text.strip('\n'))
179 new_alias = OLD_ALIAS_MAP.get(arg, [None])[0] 159 new_alias = OLD_ALIAS_MAP.get(arg, [None])[0]
180 if new_alias: 160 if new_alias:
181 deprecation_warning = """ 161 deprecation_warning = """
182 The "%s" alias is deprecated, and will eventually be removed completely. 162 The "%s" alias is deprecated, and will eventually be removed completely.
183 Please use the "%s" command instead.""" % (arg, new_alias) 163 Please use the "%s" command instead.""" % (arg, new_alias)
184 164
185 output.append('\n\n\n<B>DEPRECATION WARNING</B>\n') 165 output.append('\n\n\n<B>DEPRECATION WARNING</B>\n')
186 output.append(deprecation_warning) 166 output.append(deprecation_warning)
187 self._OutputHelp(''.join(output)) 167 self._OutputHelp(''.join(output))
188 return 0 168 return 0
189 169
190 def _OutputHelp(self, str): 170 def _OutputHelp(self, help_str):
191 """Outputs simply formatted string, paginating if long and PAGER defined and 171 """Outputs simply formatted string.
192 output is a tty""" 172
173 This function paginates if the string is too long, PAGER is defined, and
174 the output is a tty.
175
176 Args:
177 help_str: String to format.
178 """
193 # Replace <B> and </B> with terminal formatting strings if connected to tty. 179 # Replace <B> and </B> with terminal formatting strings if connected to tty.
194 if not IsRunningInteractively(): 180 if not IsRunningInteractively():
195 str = re.sub('<B>', '', str) 181 help_str = re.sub('<B>', '', help_str)
196 str = re.sub('</B>', '', str) 182 help_str = re.sub('</B>', '', help_str)
197 print str 183 print help_str
198 return 184 return
199 str = re.sub('<B>', '\033[1m', str) 185 help_str = re.sub('<B>', '\033[1m', help_str)
200 str = re.sub('</B>', '\033[0;0m', str) 186 help_str = re.sub('</B>', '\033[0;0m', help_str)
201 num_lines = len(str.split('\n')) 187 num_lines = len(help_str.split('\n'))
202 if 'PAGER' in os.environ and num_lines >= self.getTermLines(): 188 if 'PAGER' in os.environ and num_lines >= gslib.util.GetTermLines():
203 # Use -r option for less to make bolding work right. 189 # Use -r option for less to make bolding work right.
204 pager = os.environ['PAGER'].split(' ') 190 pager = os.environ['PAGER'].split(' ')
205 if pager[0].endswith('less'): 191 if pager[0].endswith('less'):
206 pager.append('-r') 192 pager.append('-r')
207 try: 193 try:
208 Popen(pager, stdin=PIPE).communicate(input=str) 194 Popen(pager, stdin=PIPE).communicate(input=help_str)
209 except OSError, e: 195 except OSError, e:
210 raise CommandException('Unable to open pager (%s): %s' % 196 raise CommandException('Unable to open pager (%s): %s' %
211 (' '.join(pager), e)) 197 (' '.join(pager), e))
212 else: 198 else:
213 print str 199 print help_str
214
215 _DEFAULT_LINES = 25
216
217 def getTermLines(self):
218 """Returns number of terminal lines"""
219 # fcntl isn't supported in Windows.
220 try:
221 import fcntl
222 import termios
223 except ImportError:
224 return self._DEFAULT_LINES
225 def ioctl_GWINSZ(fd):
226 try:
227 return struct.unpack(
228 'hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))[0]
229 except:
230 return 0 # Failure (so will retry on different file descriptor below).
231 # Try to find a valid number of lines from termio for stdin, stdout,
232 # or stderr, in that order.
233 ioc = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
234 if not ioc:
235 try:
236 fd = os.open(os.ctermid(), os.O_RDONLY)
237 ioc = ioctl_GWINSZ(fd)
238 os.close(fd)
239 except:
240 pass
241 if not ioc:
242 ioc = os.environ.get('LINES', self._DEFAULT_LINES)
243 return int(ioc)
244 200
245 def _LoadHelpMaps(self): 201 def _LoadHelpMaps(self):
246 """Returns tuple (help type -> [HelpProviders], 202 """Returns tuple of help type and help name.
247 help name->HelpProvider dict, 203
248 ).""" 204 help type is a dict with key: help type
205 value: list of HelpProviders
206 help name is a dict with key: help command name or alias
207 value: HelpProvider
208
209 Returns:
210 (help type, help name)
211 """
249 212
250 # Import all gslib.commands submodules. 213 # Import all gslib.commands submodules.
251 for _, module_name, _ in pkgutil.iter_modules(gslib.commands.__path__): 214 for _, module_name, _ in pkgutil.iter_modules(gslib.commands.__path__):
252 __import__('gslib.commands.%s' % module_name) 215 __import__('gslib.commands.%s' % module_name)
253 # Import all gslib.addlhelp submodules. 216 # Import all gslib.addlhelp submodules.
254 for _, module_name, _ in pkgutil.iter_modules(gslib.addlhelp.__path__): 217 for _, module_name, _ in pkgutil.iter_modules(gslib.addlhelp.__path__):
255 __import__('gslib.addlhelp.%s' % module_name) 218 __import__('gslib.addlhelp.%s' % module_name)
256 219
257 help_type_map = {} 220 help_type_map = {}
258 help_name_map = {} 221 help_name_map = {}
259 for s in gslib.help_provider.ALL_HELP_TYPES: 222 for s in gslib.help_provider.ALL_HELP_TYPES:
260 help_type_map[s] = [] 223 help_type_map[s] = []
261 # Only include HelpProvider subclasses in the dict. 224 # Only include HelpProvider subclasses in the dict.
262 for help_prov in itertools.chain( 225 for help_prov in itertools.chain(
263 HelpProvider.__subclasses__(), Command.__subclasses__()): 226 HelpProvider.__subclasses__(), Command.__subclasses__()):
264 if help_prov is Command: 227 if help_prov is Command:
265 # Skip the Command base class itself; we just want its subclasses, 228 # Skip the Command base class itself; we just want its subclasses,
266 # where the help command text lives (in addition to non-Command 229 # where the help command text lives (in addition to non-Command
267 # HelpProviders, like naming.py). 230 # HelpProviders, like naming.py).
268 continue 231 continue
269 gslib.help_provider.SanityCheck(help_prov, help_name_map) 232 gslib.help_provider.SanityCheck(help_prov, help_name_map)
270 help_name_map[help_prov.help_spec[HELP_NAME]] = help_prov 233 help_name_map[help_prov.help_spec.help_name] = help_prov
271 for help_name_aliases in help_prov.help_spec[HELP_NAME_ALIASES]: 234 for help_name_aliases in help_prov.help_spec.help_name_aliases:
272 help_name_map[help_name_aliases] = help_prov 235 help_name_map[help_name_aliases] = help_prov
273 help_type_map[help_prov.help_spec[HELP_TYPE]].append(help_prov) 236 help_type_map[help_prov.help_spec.help_type].append(help_prov)
274 return (help_type_map, help_name_map) 237 return (help_type_map, help_name_map)
OLDNEW
« no previous file with comments | « gslib/commands/hash.py ('k') | gslib/commands/lifecycle.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698