gcloud 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. data.tar.gz.sig +2 -3
  2. data/CHANGELOG +4 -0
  3. data/LICENSE +674 -0
  4. data/Manifest +111 -0
  5. data/README.md +4 -3
  6. data/bin/gcutil +53 -0
  7. data/gcloud.gemspec +4 -3
  8. data/packages/gcutil-1.7.1/CHANGELOG +197 -0
  9. data/packages/gcutil-1.7.1/LICENSE +202 -0
  10. data/packages/gcutil-1.7.1/VERSION +1 -0
  11. data/packages/gcutil-1.7.1/gcutil +53 -0
  12. data/packages/gcutil-1.7.1/lib/google_api_python_client/LICENSE +23 -0
  13. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/__init__.py +1 -0
  14. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/discovery.py +743 -0
  15. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/errors.py +123 -0
  16. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/ext/__init__.py +0 -0
  17. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/http.py +1443 -0
  18. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/mimeparse.py +172 -0
  19. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/model.py +385 -0
  20. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/schema.py +303 -0
  21. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/__init__.py +1 -0
  22. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/anyjson.py +32 -0
  23. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/appengine.py +528 -0
  24. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.py +1139 -0
  25. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/clientsecrets.py +105 -0
  26. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/crypt.py +244 -0
  27. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/django_orm.py +124 -0
  28. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/file.py +107 -0
  29. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/locked_file.py +343 -0
  30. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/multistore_file.py +379 -0
  31. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/tools.py +174 -0
  32. data/packages/gcutil-1.7.1/lib/google_api_python_client/uritemplate/__init__.py +147 -0
  33. data/packages/gcutil-1.7.1/lib/google_apputils/LICENSE +202 -0
  34. data/packages/gcutil-1.7.1/lib/google_apputils/google/__init__.py +3 -0
  35. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/__init__.py +3 -0
  36. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/app.py +356 -0
  37. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/appcommands.py +783 -0
  38. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/basetest.py +1260 -0
  39. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/datelib.py +421 -0
  40. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/debug.py +60 -0
  41. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/file_util.py +181 -0
  42. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/resources.py +67 -0
  43. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/run_script_module.py +217 -0
  44. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/setup_command.py +159 -0
  45. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/shellutil.py +49 -0
  46. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/stopwatch.py +204 -0
  47. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/__init__.py +0 -0
  48. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auth_helper.py +140 -0
  49. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auth_helper_test.py +149 -0
  50. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auto_auth.py +130 -0
  51. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auto_auth_test.py +75 -0
  52. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/basic_cmds.py +128 -0
  53. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/basic_cmds_test.py +111 -0
  54. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/command_base.py +1808 -0
  55. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/command_base_test.py +1651 -0
  56. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/compute/v1beta13.json +2851 -0
  57. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/compute/v1beta14.json +3361 -0
  58. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/disk_cmds.py +342 -0
  59. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/disk_cmds_test.py +474 -0
  60. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/firewall_cmds.py +344 -0
  61. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/firewall_cmds_test.py +231 -0
  62. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/flags_cache.py +274 -0
  63. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/gcutil +89 -0
  64. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/gcutil_logging.py +69 -0
  65. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/image_cmds.py +262 -0
  66. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/image_cmds_test.py +172 -0
  67. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/instance_cmds.py +1506 -0
  68. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/instance_cmds_test.py +1904 -0
  69. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/kernel_cmds.py +91 -0
  70. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/kernel_cmds_test.py +56 -0
  71. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/machine_type_cmds.py +106 -0
  72. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/machine_type_cmds_test.py +59 -0
  73. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata.py +96 -0
  74. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata_lib.py +357 -0
  75. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata_test.py +84 -0
  76. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/mock_api.py +420 -0
  77. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/mock_metadata.py +58 -0
  78. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/move_cmds.py +824 -0
  79. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/move_cmds_test.py +307 -0
  80. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/network_cmds.py +178 -0
  81. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/network_cmds_test.py +133 -0
  82. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/operation_cmds.py +181 -0
  83. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/operation_cmds_test.py +196 -0
  84. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/path_initializer.py +38 -0
  85. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/project_cmds.py +173 -0
  86. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/project_cmds_test.py +111 -0
  87. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/scopes.py +61 -0
  88. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/scopes_test.py +50 -0
  89. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/snapshot_cmds.py +276 -0
  90. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/snapshot_cmds_test.py +260 -0
  91. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/ssh_keys.py +266 -0
  92. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/ssh_keys_test.py +128 -0
  93. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/table_formatter.py +563 -0
  94. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/thread_pool.py +188 -0
  95. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/thread_pool_test.py +88 -0
  96. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/utils.py +208 -0
  97. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/utils_test.py +193 -0
  98. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version.py +17 -0
  99. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version_checker.py +246 -0
  100. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version_checker_test.py +271 -0
  101. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/zone_cmds.py +151 -0
  102. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/zone_cmds_test.py +60 -0
  103. data/packages/gcutil-1.7.1/lib/httplib2/LICENSE +21 -0
  104. data/packages/gcutil-1.7.1/lib/httplib2/httplib2/__init__.py +1630 -0
  105. data/packages/gcutil-1.7.1/lib/httplib2/httplib2/cacerts.txt +714 -0
  106. data/packages/gcutil-1.7.1/lib/httplib2/httplib2/iri2uri.py +110 -0
  107. data/packages/gcutil-1.7.1/lib/httplib2/httplib2/socks.py +438 -0
  108. data/packages/gcutil-1.7.1/lib/iso8601/LICENSE +20 -0
  109. data/packages/gcutil-1.7.1/lib/iso8601/iso8601/__init__.py +1 -0
  110. data/packages/gcutil-1.7.1/lib/iso8601/iso8601/iso8601.py +102 -0
  111. data/packages/gcutil-1.7.1/lib/iso8601/iso8601/test_iso8601.py +111 -0
  112. data/packages/gcutil-1.7.1/lib/python_gflags/AUTHORS +2 -0
  113. data/packages/gcutil-1.7.1/lib/python_gflags/LICENSE +28 -0
  114. data/packages/gcutil-1.7.1/lib/python_gflags/gflags.py +2862 -0
  115. data/packages/gcutil-1.7.1/lib/python_gflags/gflags2man.py +544 -0
  116. data/packages/gcutil-1.7.1/lib/python_gflags/gflags_validators.py +187 -0
  117. metadata +118 -5
  118. metadata.gz.sig +0 -0
@@ -0,0 +1,783 @@
1
+ #!/usr/bin/env python
2
+ # Copyright 2007 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
+
16
+ """This module is the base for programs that provide multiple commands.
17
+
18
+ This provides command line tools that have a few shared global flags,
19
+ followed by a command name, followed by command specific flags,
20
+ then by arguments. That is:
21
+ tool [--global_flags] command [--command_flags] [args]
22
+
23
+ The module is built on top of app.py and 'overrides' a bit of it. However
24
+ the interface is mostly the same. The main difference is that your main
25
+ is supposed to register commands and return without further execution
26
+ of the commands; pre checking is of course welcome! Also your
27
+ global initialization should call appcommands.Run() rather than app.run().
28
+
29
+ To register commands use AddCmd() or AddCmdFunc(). AddCmd() is used
30
+ for commands that derive from class Cmd and the AddCmdFunc() is used
31
+ to wrap simple functions.
32
+
33
+ This module itself registers the command 'help' that allows users
34
+ to retrieve help for all or specific commands.
35
+
36
+ Example:
37
+
38
+ <code>
39
+ from mx import DateTime
40
+
41
+
42
+ class CmdDate(appcommands.Cmd):
43
+ \"\"\"This docstring contains the help for the date command.\"\"\"
44
+
45
+ def Run(self, argv):
46
+ print DateTime.now()
47
+
48
+
49
+ def main(argv):
50
+ appcommands.AddCmd('date', CmdDate, command_aliases=['data_now'])
51
+
52
+
53
+ if __name__ == '__main__':
54
+ appcommands.Run()
55
+ </code>
56
+
57
+ In the above example the name of the registered command on the command line is
58
+ 'date'. Thus, to get the date you would execute:
59
+ tool date
60
+ The above example also added the command alias 'data_now' which allows to
61
+ replace 'tool date' with 'tool data_now'.
62
+
63
+ To get a list of available commands run:
64
+ tool help
65
+ For help with a specific command, you would execute:
66
+ tool help date
67
+ For help on flags run one of the following:
68
+ tool --help
69
+ Note that 'tool --help' gives you information on global flags, just like for
70
+ applications that do not use appcommand. Likewise 'tool --helpshort' and the
71
+ other help-flags from app.py are also available.
72
+
73
+ The above example also demonstrates that you only have to call
74
+ appcommands.Run()
75
+ and register your commands in main() to initialize your program with appcommands
76
+ (and app).
77
+
78
+ Handling of flags:
79
+ Flags can be registered just as with any other google tool using flags.py.
80
+ In addition you can also provide command specific flags. To do so simply add
81
+ flags registering code into the __init__ function of your Cmd classes passing
82
+ parameter flag_values to any flags registering calls. These flags will get
83
+ copied to the global flag list, so that once the command is detected they
84
+ behave just like any other flag. That means these flags won't be available
85
+ for other commands. Note that it is possible to register flags with more
86
+ than one command.
87
+
88
+ Getting help:
89
+ This module activates formatting and wrapping to help output. That is
90
+ the main difference to help created from app.py. So just as with app.py,
91
+ appcommands.py will create help from the main modules main __doc__.
92
+ But it adds the new 'help' command that allows you to get a list of
93
+ all available commands. Each command's help will be followed by the
94
+ registered command specific flags along with their defaults and help.
95
+ After help for all commands there will also be a list of all registered
96
+ global flags with their defaults and help.
97
+
98
+ The text for the command's help can best be supplied by overwriting the
99
+ __doc__ property of the Cmd classes for commands registered with AddCmd() or
100
+ the __doc__ property of command functions registered AddCmdFunc().
101
+
102
+ Inner working:
103
+ This module interacts with app.py by replacing its inner start dispatcher.
104
+ The replacement version basically does the same, registering help flags,
105
+ checking whether help flags were present, and calling the main module's main
106
+ function. However unlike app.py, this module epxpects main() to only register
107
+ commands and then to return. After having all commands registered
108
+ appcommands.py will then parse the remaining arguments for any registered
109
+ command. If one is found it will get executed. Otherwise a short usage info
110
+ will be displayed.
111
+
112
+ Each provided command must be an instance of Cmd. If commands get registered
113
+ from global functions using AddCmdFunc() then the helper class _FunctionalCmd
114
+ will be used in the registering process.
115
+ """
116
+
117
+
118
+
119
+ import os
120
+ import pdb
121
+ import sys
122
+ import traceback
123
+
124
+ from google.apputils import app
125
+ import gflags as flags
126
+
127
+ FLAGS = flags.FLAGS
128
+
129
+
130
+ # module exceptions:
131
+ class AppCommandsError(Exception):
132
+ """The base class for all flags errors."""
133
+ pass
134
+
135
+
136
+ _cmd_argv = None # remaining arguments with index 0 = sys.argv[0]
137
+ _cmd_list = {} # list of commands index by name (_Cmd instances)
138
+ _cmd_alias_list = {} # list of command_names index by command_alias
139
+
140
+
141
+ def GetAppBasename():
142
+ """Returns the friendly basename of this application."""
143
+ return os.path.basename(sys.argv[0])
144
+
145
+
146
+ def ShortHelpAndExit(message=None):
147
+ """Display optional message, followed by a note on how to get help, then exit.
148
+
149
+ Args:
150
+ message: optional message to display
151
+ """
152
+ sys.stdout.flush()
153
+ if message is not None:
154
+ sys.stderr.write('%s\n' % message)
155
+ sys.stderr.write("Run '%s help' to get help\n" % GetAppBasename())
156
+ sys.exit(1)
157
+
158
+
159
+ def GetCommandList():
160
+ """Return list of registered commands."""
161
+ # pylint: disable-msg=W0602
162
+ global _cmd_list
163
+ return _cmd_list
164
+
165
+
166
+ def GetCommandAliasList():
167
+ """Return list of registered command aliases."""
168
+ # pylint: disable-msg=W0602
169
+ global _cmd_alias_list
170
+ return _cmd_alias_list
171
+
172
+
173
+ def GetFullCommandList():
174
+ """Return list of registered commands, including aliases."""
175
+ all_cmds = dict(GetCommandList())
176
+ for cmd_alias, cmd_name in GetCommandAliasList().iteritems():
177
+ all_cmds[cmd_alias] = all_cmds.get(cmd_name)
178
+ return all_cmds
179
+
180
+
181
+ def GetCommandByName(name):
182
+ """Get the command or None if name is not a registered command.
183
+
184
+ Args:
185
+ name: name of command to look for
186
+
187
+ Returns:
188
+ Cmd instance holding the command or None
189
+ """
190
+ return GetCommandList().get(GetCommandAliasList().get(name))
191
+
192
+
193
+ def GetCommandArgv():
194
+ """Return list of remaining args."""
195
+ return _cmd_argv
196
+
197
+
198
+ def GetMaxCommandLength():
199
+ """Returns the length of the longest registered command."""
200
+ return max([len(cmd_name) for cmd_name in GetCommandList()])
201
+
202
+
203
+ class Cmd(object):
204
+ """Abstract class describing and implementing a command.
205
+
206
+ When creating code for a command, at least you have to derive this class
207
+ and override method Run(). The other methods of this class might be
208
+ overridden as well. Check their documentation for details. If the command
209
+ needs any specific flags, use __init__ for registration.
210
+ """
211
+
212
+ def __init__(self, name, flag_values, command_aliases=None):
213
+ """Initialize and check whether self is actually a Cmd instance.
214
+
215
+ This can be used to register command specific flags. If you do so
216
+ remember that you have to provide the 'flag_values=flag_values'
217
+ parameter to any flags.DEFINE_*() call.
218
+
219
+ Args:
220
+ name: Name of the command
221
+ flag_values: FlagValues() instance that needs to be passed as
222
+ flag_values parameter to any flags registering call.
223
+ command_aliases: A list of command aliases that the command can be run as.
224
+ Raises:
225
+ AppCommandsError: if self is Cmd (Cmd is abstract)
226
+ """
227
+ self._command_name = name
228
+ self._command_aliases = command_aliases
229
+ self._command_flags = flag_values
230
+ self._all_commands_help = None
231
+ if type(self) is Cmd:
232
+ raise AppCommandsError('Cmd is abstract and cannot be instantiated')
233
+
234
+ def Run(self, argv):
235
+ """Execute the command. Must be provided by the implementing class.
236
+
237
+ Args:
238
+ argv: Remaining command line arguments after parsing flags and command
239
+ (that is a copy of sys.argv at the time of the function call with
240
+ all parsed flags removed).
241
+
242
+ Returns:
243
+ 0 for success, anything else for failure (must return with integer).
244
+ Alternatively you may return None (or not use a return statement at all).
245
+
246
+ Raises:
247
+ AppCommandsError: Always as in must be overwritten
248
+ """
249
+ raise AppCommandsError('%s.%s.Run() is not implemented' % (
250
+ type(self).__module__, type(self).__name__))
251
+
252
+ def CommandRun(self, argv):
253
+ """Execute the command with given arguments.
254
+
255
+ First register and parse additional flags. Then run the command.
256
+
257
+ Returns:
258
+ Command return value.
259
+
260
+ Args:
261
+ argv: Remaining command line arguments after parsing command and flags
262
+ (that is a copy of sys.argv at the time of the function call with
263
+ all parsed flags removed).
264
+ """
265
+ # Register flags global when run normally
266
+ FLAGS.AppendFlagValues(self._command_flags)
267
+ # Prepare flags parsing, to redirect help, to show help for command
268
+ orig_app_usage = app.usage
269
+
270
+ def ReplacementAppUsage(shorthelp=0, writeto_stdout=1, detailed_error=None,
271
+ exitcode=None):
272
+ AppcommandsUsage(shorthelp, writeto_stdout, detailed_error, exitcode=1,
273
+ show_cmd=self._command_name, show_global_flags=True)
274
+ app.usage = ReplacementAppUsage
275
+ # Parse flags and restore app.usage afterwards
276
+ try:
277
+ try:
278
+ argv = ParseFlagsWithUsage(argv)
279
+ # Run command
280
+ if FLAGS.run_with_pdb:
281
+ ret = pdb.runcall(self.Run, argv)
282
+ else:
283
+ ret = self.Run(argv)
284
+ if ret is None:
285
+ ret = 0
286
+ else:
287
+ assert isinstance(ret, int)
288
+ return ret
289
+ except app.UsageError, error:
290
+ app.usage(shorthelp=1, detailed_error=error, exitcode=error.exitcode)
291
+ finally:
292
+ # Restore app.usage and remove this command's flags from the global flags.
293
+ app.usage = orig_app_usage
294
+ for flag_name in self._command_flags.FlagDict():
295
+ delattr(FLAGS, flag_name)
296
+
297
+ def CommandGetHelp(self, unused_argv, cmd_names=None):
298
+ """Get help string for command.
299
+
300
+ Args:
301
+ unused_argv: Remaining command line flags and arguments after parsing
302
+ command (that is a copy of sys.argv at the time of the
303
+ function call with all parsed flags removed); unused in this
304
+ default implementation, but may be used in subclasses.
305
+ cmd_names: Complete list of commands for which help is being shown at
306
+ the same time. This is used to determine whether to return
307
+ _all_commands_help, or the command's docstring.
308
+ (_all_commands_help is used, if not None, when help is being
309
+ shown for more than one command, otherwise the command's
310
+ docstring is used.)
311
+
312
+ Returns:
313
+ Help string, one of the following (by order):
314
+ - Result of the registered 'help' function (if any)
315
+ - Doc string of the Cmd class (if any)
316
+ - Default fallback string
317
+ """
318
+ if (type(cmd_names) is list and len(cmd_names) > 1 and
319
+ self._all_commands_help is not None):
320
+ return flags.DocToHelp(self._all_commands_help)
321
+ elif self.__doc__:
322
+ return flags.DocToHelp(self.__doc__)
323
+ else:
324
+ return 'No help available'
325
+
326
+ def CommandGetAliases(self):
327
+ """Get aliases for command.
328
+
329
+ Returns:
330
+ aliases: list of aliases for the command.
331
+ """
332
+ return self._command_aliases
333
+
334
+
335
+ class _FunctionalCmd(Cmd):
336
+ """Class to wrap functions as CMD instances.
337
+
338
+ Args:
339
+ cmd_func: command function
340
+ """
341
+
342
+ def __init__(self, name, flag_values, cmd_func, all_commands_help=None,
343
+ **kargs):
344
+ """Create a functional command.
345
+
346
+ Args:
347
+ name: Name of command
348
+ flag_values: FlagValues() instance that needs to be passed as flag_values
349
+ parameter to any flags registering call.
350
+ cmd_func: Function to call when command is to be executed.
351
+ """
352
+ Cmd.__init__(self, name, flag_values, **kargs)
353
+ self._all_commands_help = all_commands_help
354
+ self._cmd_func = cmd_func
355
+
356
+ def CommandGetHelp(self, unused_argv, cmd_names=None):
357
+ """Get help for command.
358
+
359
+ Args:
360
+ unused_argv: Remaining command line flags and arguments after parsing
361
+ command (that is a copy of sys.argv at the time of the
362
+ function call with all parsed flags removed); unused in this
363
+ implementation.
364
+ cmd_names: By default, if help is being shown for more than one command,
365
+ and this command defines _all_commands_help, then
366
+ _all_commands_help will be displayed instead of the class
367
+ doc. cmd_names is used to determine the number of commands
368
+ being displayed and if only a single command is display then
369
+ the class doc is returned.
370
+
371
+ Returns:
372
+ __doc__ property for command function or a message stating there is no
373
+ help.
374
+ """
375
+ if (type(cmd_names) is list and len(cmd_names) > 1 and
376
+ self._all_commands_help is not None):
377
+ return flags.DocToHelp(self._all_commands_help)
378
+ if self._cmd_func.__doc__ is not None:
379
+ return flags.DocToHelp(self._cmd_func.__doc__)
380
+ else:
381
+ return 'No help available'
382
+
383
+ def Run(self, argv):
384
+ """Execute the command with given arguments.
385
+
386
+ Args:
387
+ argv: Remaining command line flags and arguments after parsing command
388
+ (that is a copy of sys.argv at the time of the function call with
389
+ all parsed flags removed).
390
+
391
+ Returns:
392
+ Command return value.
393
+ """
394
+ return self._cmd_func(argv)
395
+
396
+
397
+ def _AddCmdInstance(command_name, cmd, command_aliases=None):
398
+ """Add a command from a Cmd instance.
399
+
400
+ Args:
401
+ command_name: name of the command which will be used in argument parsing
402
+ cmd: Cmd instance to register
403
+ command_aliases: A list of command aliases that the command can be run as.
404
+
405
+ Raises:
406
+ AppCommandsError: is command is already registered OR cmd is not a subclass
407
+ of Cmd
408
+ AppCommandsError: if name is already registered OR name is not a string OR
409
+ name is too short OR name does not start with a letter OR
410
+ name contains any non alphanumeric characters besides
411
+ '_'.
412
+ """
413
+ # Update global command list.
414
+ # pylint: disable-msg=W0602
415
+ global _cmd_list
416
+ global _cmd_alias_list
417
+ if not issubclass(cmd.__class__, Cmd):
418
+ raise AppCommandsError('Command must be an instance of commands.Cmd')
419
+
420
+ for name in [command_name] + (command_aliases or []):
421
+ _CheckCmdName(name)
422
+ _cmd_alias_list[name] = command_name
423
+
424
+ _cmd_list[command_name] = cmd
425
+
426
+
427
+ def _CheckCmdName(name_or_alias):
428
+ """Only allow strings for command names and aliases (reject unicode as well).
429
+
430
+ Args:
431
+ name_or_alias: properly formatted string name or alias.
432
+
433
+ Raises:
434
+ AppCommandsError: is command is already registered OR cmd is not a subclass
435
+ of Cmd
436
+ AppCommandsError: if name is already registered OR name is not a string OR
437
+ name is too short OR name does not start with a letter OR
438
+ name contains any non alphanumeric characters besides
439
+ '_'.
440
+ """
441
+ if name_or_alias in GetCommandAliasList():
442
+ raise AppCommandsError("Command or Alias '%s' already defined" %
443
+ name_or_alias)
444
+ if not isinstance(name_or_alias, str) or len(name_or_alias) <= 1:
445
+ raise AppCommandsError("Command '%s' not a string or too short"
446
+ % str(name_or_alias))
447
+ if not name_or_alias[0].isalpha():
448
+ raise AppCommandsError("Command '%s' does not start with a letter"
449
+ % name_or_alias)
450
+ if [c for c in name_or_alias if not (c.isalnum() or c == '_')]:
451
+ raise AppCommandsError("Command '%s' contains non alphanumeric characters"
452
+ % name_or_alias)
453
+
454
+
455
+ def AddCmd(command_name, cmd_factory, **kargs):
456
+ """Add a command from a Cmd subclass or factory.
457
+
458
+ Args:
459
+ command_name: name of the command which will be used in argument parsing
460
+ cmd_factory: A callable whose arguments match those of Cmd.__init__ and
461
+ returns a Cmd. In the simplest case this is just a subclass
462
+ of Cmd.
463
+ command_aliases: A list of command aliases that the command can be run as.
464
+
465
+ Raises:
466
+ AppCommandsError: if calling cmd_factory does not return an instance of Cmd.
467
+ """
468
+ cmd = cmd_factory(command_name, flags.FlagValues(), **kargs)
469
+
470
+ if not isinstance(cmd, Cmd):
471
+ raise AppCommandsError('Command must be an instance of commands.Cmd')
472
+
473
+ _AddCmdInstance(command_name, cmd, **kargs)
474
+
475
+
476
+ def AddCmdFunc(command_name, cmd_func, command_aliases=None,
477
+ all_commands_help=None):
478
+ """Add a new command to the list of registered commands.
479
+
480
+ Args:
481
+ command_name: name of the command which will be used in argument
482
+ parsing
483
+ cmd_func: command function, this function received the remaining
484
+ arguments as its only parameter. It is supposed to do the
485
+ command work and then return with the command result that
486
+ is being used as the shell exit code.
487
+ command_aliases: A list of command aliases that the command can be run as.
488
+ all_commands_help: Help message to be displayed in place of func.__doc__
489
+ when all commands are displayed.
490
+ """
491
+ _AddCmdInstance(command_name,
492
+ _FunctionalCmd(command_name, flags.FlagValues(), cmd_func,
493
+ command_aliases=command_aliases,
494
+ all_commands_help=all_commands_help),
495
+ command_aliases)
496
+
497
+
498
+ class _CmdHelp(Cmd):
499
+ """Standard help command.
500
+
501
+ Allows to provide help for all or specific commands.
502
+ """
503
+
504
+ def Run(self, argv):
505
+ """Execute help command.
506
+
507
+ If an argument is given and that argument is a registered command
508
+ name, then help specific to that command is being displayed.
509
+ If the command is unknown then a fatal error will be displayed. If
510
+ no argument is present then help for all commands will be presented.
511
+
512
+ If a specific command help is being generated, the list of commands is
513
+ temporarily replaced with one containing only that command. Thus the call
514
+ to usage() will only show help for that command. Otherwise call usage()
515
+ will show help for all registered commands as it sees all commands.
516
+
517
+ Args:
518
+ argv: Remaining command line flags and arguments after parsing command
519
+ (that is a copy of sys.argv at the time of the function call with
520
+ all parsed flags removed).
521
+ So argv[0] is the program and argv[1] will be the first argument to
522
+ the call. For instance 'tool.py help command' will result in argv
523
+ containing ('tool.py', 'command'). In this case the list of
524
+ commands is searched for 'command'.
525
+
526
+ Returns:
527
+ 1 for failure
528
+ """
529
+ if len(argv) > 1 and argv[1] in GetFullCommandList():
530
+ show_cmd = argv[1]
531
+ else:
532
+ show_cmd = None
533
+ AppcommandsUsage(shorthelp=0, writeto_stdout=1, detailed_error=None,
534
+ exitcode=1, show_cmd=show_cmd, show_global_flags=False)
535
+
536
+ def CommandGetHelp(self, unused_argv, cmd_names=None):
537
+ """Returns: Help for command."""
538
+ cmd_help = ('Help for all or selected command:\n'
539
+ '\t%(prog)s help [<command>]\n\n'
540
+ 'To retrieve help with global flags:\n'
541
+ '\t%(prog)s --help\n\n'
542
+ 'To retrieve help with flags only from the main module:\n'
543
+ '\t%(prog)s --helpshort [<command>]\n\n'
544
+ % {'prog': GetAppBasename()})
545
+ return flags.DocToHelp(cmd_help)
546
+
547
+
548
+ def GetSynopsis():
549
+ """Get synopsis for program.
550
+
551
+ Returns:
552
+ Synopsis including program basename.
553
+ """
554
+ return '%s [--global_flags] <command> [--command_flags] [args]' % (
555
+ GetAppBasename())
556
+
557
+
558
+ def _UsageFooter(detailed_error, cmd_names):
559
+ """Output a footer at the end of usage or help output.
560
+
561
+ Args:
562
+ detailed_error: additional detail about why usage info was presented.
563
+ cmd_names: list of command names for which help was shown or None.
564
+ Returns:
565
+ Generated footer that contains 'Run..' messages if appropriate.
566
+ """
567
+ footer = []
568
+ if not cmd_names or len(cmd_names) == 1:
569
+ footer.append("Run '%s help' to see the list of available commands."
570
+ % GetAppBasename())
571
+ if not cmd_names or len(cmd_names) == len(GetCommandList()):
572
+ footer.append("Run '%s help <command>' to get help for <command>."
573
+ % GetAppBasename())
574
+ if detailed_error is not None:
575
+ if footer:
576
+ footer.append('')
577
+ footer.append('%s' % detailed_error)
578
+ return '\n'.join(footer)
579
+
580
+
581
+ def AppcommandsUsage(shorthelp=0, writeto_stdout=0, detailed_error=None,
582
+ exitcode=None, show_cmd=None, show_global_flags=False):
583
+ """Output usage or help information.
584
+
585
+ Extracts the __doc__ string from the __main__ module and writes it to
586
+ stderr. If that string contains a '%s' then that is replaced by the command
587
+ pathname. Otherwise a default usage string is being generated.
588
+
589
+ The output varies depending on the following:
590
+ - FLAGS.help
591
+ - FLAGS.helpshort
592
+ - show_cmd
593
+ - show_global_flags
594
+
595
+ Args:
596
+ shorthelp: print only command and main module flags, rather than all.
597
+ writeto_stdout: write help message to stdout, rather than to stderr.
598
+ detailed_error: additional details about why usage info was presented.
599
+ exitcode: if set, exit with this status code after writing help.
600
+ show_cmd: show help for this command only (name of command).
601
+ show_global_flags: show help for global flags.
602
+ """
603
+ if writeto_stdout:
604
+ stdfile = sys.stdout
605
+ else:
606
+ stdfile = sys.stderr
607
+
608
+ prefix = ''.rjust(GetMaxCommandLength() + 2)
609
+ # Deal with header, containing general tool documentation
610
+ doc = sys.modules['__main__'].__doc__
611
+ if doc:
612
+ help_msg = flags.DocToHelp(doc.replace('%s', sys.argv[0]))
613
+ stdfile.write(flags.TextWrap(help_msg, flags.GetHelpWidth()))
614
+ stdfile.write('\n\n\n')
615
+ if not doc or doc.find('%s') == -1:
616
+ synopsis = 'USAGE: ' + GetSynopsis()
617
+ stdfile.write(flags.TextWrap(synopsis, flags.GetHelpWidth(), ' ',
618
+ ''))
619
+ stdfile.write('\n\n\n')
620
+ # Special case just 'help' registered, that means run as 'tool --help'.
621
+ if len(GetCommandList()) == 1:
622
+ cmd_names = []
623
+ else:
624
+ # Show list of commands
625
+ if show_cmd is None or show_cmd == 'help':
626
+ cmd_names = GetCommandList().keys()
627
+ cmd_names.sort()
628
+ stdfile.write('Any of the following commands:\n')
629
+ doc = ', '.join(cmd_names)
630
+ stdfile.write(flags.TextWrap(doc, flags.GetHelpWidth(), ' '))
631
+ stdfile.write('\n\n\n')
632
+ # Prepare list of commands to show help for
633
+ if show_cmd is not None:
634
+ cmd_names = [show_cmd] # show only one command
635
+ elif FLAGS.help or FLAGS.helpshort or shorthelp:
636
+ cmd_names = []
637
+ else:
638
+ cmd_names = GetCommandList().keys() # show all commands
639
+ cmd_names.sort()
640
+ # Show the command help (none, one specific, or all)
641
+ for name in cmd_names:
642
+ command = GetCommandByName(name)
643
+ cmd_help = command.CommandGetHelp(GetCommandArgv(), cmd_names=cmd_names)
644
+ cmd_help = cmd_help.strip()
645
+ all_names = ', '.join([name] + (command.CommandGetAliases() or []))
646
+ if len(all_names) + 1 >= len(prefix) or not cmd_help:
647
+ # If command/alias list would reach over help block-indent
648
+ # start the help block on a new line.
649
+ stdfile.write(flags.TextWrap(all_names, flags.GetHelpWidth()))
650
+ stdfile.write('\n')
651
+ prefix1 = prefix
652
+ else:
653
+ prefix1 = all_names.ljust(GetMaxCommandLength() + 2)
654
+ if cmd_help:
655
+ stdfile.write(flags.TextWrap(cmd_help, flags.GetHelpWidth(), prefix,
656
+ prefix1))
657
+ stdfile.write('\n\n')
658
+ else:
659
+ stdfile.write('\n')
660
+ # When showing help for exactly one command we show its flags
661
+ if len(cmd_names) == 1:
662
+ # Need to register flags for command prior to be able to use them.
663
+ # We do not register them globally so that they do not reappear.
664
+ # pylint: disable-msg=W0212
665
+ cmd_flags = command._command_flags
666
+ if cmd_flags.RegisteredFlags():
667
+ stdfile.write('%sFlags for %s:\n' % (prefix, name))
668
+ stdfile.write(cmd_flags.GetHelp(prefix+' '))
669
+ stdfile.write('\n\n')
670
+ stdfile.write('\n')
671
+ # Now show global flags as asked for
672
+ if show_global_flags:
673
+ stdfile.write('Global flags:\n')
674
+ if shorthelp:
675
+ stdfile.write(FLAGS.MainModuleHelp())
676
+ else:
677
+ stdfile.write(FLAGS.GetHelp())
678
+ stdfile.write('\n')
679
+ else:
680
+ stdfile.write("Run '%s --help' to get help for global flags."
681
+ % GetAppBasename())
682
+ stdfile.write('\n%s\n' % _UsageFooter(detailed_error, cmd_names))
683
+ if exitcode is not None:
684
+ sys.exit(exitcode)
685
+
686
+
687
+ def ParseFlagsWithUsage(argv):
688
+ """Parse the flags, exiting (after printing usage) if they are unparseable.
689
+
690
+ Args:
691
+ argv: command line arguments
692
+
693
+ Returns:
694
+ remaining command line arguments after parsing flags
695
+ """
696
+ # Update the global commands.
697
+ # pylint: disable-msg=W0603
698
+ global _cmd_argv
699
+ try:
700
+ _cmd_argv = FLAGS(argv)
701
+ return _cmd_argv
702
+ except flags.FlagsError, error:
703
+ ShortHelpAndExit('FATAL Flags parsing error: %s' % error)
704
+
705
+
706
+ def GetCommand(command_required):
707
+ """Get the command or return None (or issue an error) if there is none.
708
+
709
+ Args:
710
+ command_required: whether to issue an error if no command is present
711
+
712
+ Returns:
713
+ command or None, if command_required is True then return value is a valid
714
+ command or the program will exit. The program also exits if a command was
715
+ specified but that command does not exist.
716
+ """
717
+ # Update the global commands.
718
+ # pylint: disable-msg=W0603
719
+ global _cmd_argv
720
+ _cmd_argv = ParseFlagsWithUsage(_cmd_argv)
721
+ if len(_cmd_argv) < 2:
722
+ if command_required:
723
+ ShortHelpAndExit('FATAL Command expected but none given')
724
+ return None
725
+ command = GetCommandByName(_cmd_argv[1])
726
+ if command is None:
727
+ ShortHelpAndExit("FATAL Command '%s' unknown" % _cmd_argv[1])
728
+ del _cmd_argv[1]
729
+ return command
730
+
731
+
732
+ def _CommandsStart():
733
+ """Main initialization.
734
+
735
+ This initializes flag values, and calls __main__.main(). Only non-flag
736
+ arguments are passed to main(). The return value of main() is used as the
737
+ exit status.
738
+
739
+ """
740
+ app.RegisterAndParseFlagsWithUsage()
741
+ # The following is supposed to return after registering additional commands
742
+ try:
743
+ sys.modules['__main__'].main(GetCommandArgv())
744
+ # If sys.exit was called, return with error code.
745
+ except SystemExit, e:
746
+ sys.exit(e.code)
747
+ except Exception, error:
748
+ traceback.print_exc() # Print a backtrace to stderr.
749
+ ShortHelpAndExit('\nFATAL error in main: %s' % error)
750
+
751
+ if len(GetCommandArgv()) > 1:
752
+ command = GetCommand(command_required=True)
753
+ else:
754
+ command = GetCommandByName('help')
755
+ sys.exit(command.CommandRun(GetCommandArgv()))
756
+
757
+
758
+ def Run():
759
+ """This must be called from __main__ modules main, instead of app.run().
760
+
761
+ app.run will base its actions on its stacktrace.
762
+
763
+ Returns:
764
+ app.run()
765
+ """
766
+ app.parse_flags_with_usage = ParseFlagsWithUsage
767
+ app.really_start = _CommandsStart
768
+ app.usage = _ReplacementAppUsage
769
+ return app.run()
770
+
771
+
772
+ # Always register 'help' command
773
+ AddCmd('help', _CmdHelp)
774
+
775
+
776
+ def _ReplacementAppUsage(shorthelp=0, writeto_stdout=0, detailed_error=None,
777
+ exitcode=None):
778
+ AppcommandsUsage(shorthelp, writeto_stdout, detailed_error, exitcode=exitcode,
779
+ show_cmd=None, show_global_flags=True)
780
+
781
+
782
+ if __name__ == '__main__':
783
+ Run()