gcloud 0.0.2 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +2 -3
- data/CHANGELOG +4 -0
- data/LICENSE +674 -0
- data/Manifest +111 -0
- data/README.md +4 -3
- data/bin/gcutil +53 -0
- data/gcloud.gemspec +4 -3
- data/packages/gcutil-1.7.1/CHANGELOG +197 -0
- data/packages/gcutil-1.7.1/LICENSE +202 -0
- data/packages/gcutil-1.7.1/VERSION +1 -0
- data/packages/gcutil-1.7.1/gcutil +53 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/LICENSE +23 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/__init__.py +1 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/discovery.py +743 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/errors.py +123 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/ext/__init__.py +0 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/http.py +1443 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/mimeparse.py +172 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/model.py +385 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/schema.py +303 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/__init__.py +1 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/anyjson.py +32 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/appengine.py +528 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.py +1139 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/clientsecrets.py +105 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/crypt.py +244 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/django_orm.py +124 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/file.py +107 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/locked_file.py +343 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/multistore_file.py +379 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/tools.py +174 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/uritemplate/__init__.py +147 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/LICENSE +202 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/__init__.py +3 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/__init__.py +3 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/app.py +356 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/appcommands.py +783 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/basetest.py +1260 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/datelib.py +421 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/debug.py +60 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/file_util.py +181 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/resources.py +67 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/run_script_module.py +217 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/setup_command.py +159 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/shellutil.py +49 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/stopwatch.py +204 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/__init__.py +0 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auth_helper.py +140 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auth_helper_test.py +149 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auto_auth.py +130 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auto_auth_test.py +75 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/basic_cmds.py +128 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/basic_cmds_test.py +111 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/command_base.py +1808 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/command_base_test.py +1651 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/compute/v1beta13.json +2851 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/compute/v1beta14.json +3361 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/disk_cmds.py +342 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/disk_cmds_test.py +474 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/firewall_cmds.py +344 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/firewall_cmds_test.py +231 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/flags_cache.py +274 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/gcutil +89 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/gcutil_logging.py +69 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/image_cmds.py +262 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/image_cmds_test.py +172 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/instance_cmds.py +1506 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/instance_cmds_test.py +1904 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/kernel_cmds.py +91 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/kernel_cmds_test.py +56 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/machine_type_cmds.py +106 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/machine_type_cmds_test.py +59 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata.py +96 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata_lib.py +357 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata_test.py +84 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/mock_api.py +420 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/mock_metadata.py +58 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/move_cmds.py +824 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/move_cmds_test.py +307 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/network_cmds.py +178 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/network_cmds_test.py +133 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/operation_cmds.py +181 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/operation_cmds_test.py +196 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/path_initializer.py +38 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/project_cmds.py +173 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/project_cmds_test.py +111 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/scopes.py +61 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/scopes_test.py +50 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/snapshot_cmds.py +276 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/snapshot_cmds_test.py +260 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/ssh_keys.py +266 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/ssh_keys_test.py +128 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/table_formatter.py +563 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/thread_pool.py +188 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/thread_pool_test.py +88 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/utils.py +208 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/utils_test.py +193 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version.py +17 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version_checker.py +246 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version_checker_test.py +271 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/zone_cmds.py +151 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/zone_cmds_test.py +60 -0
- data/packages/gcutil-1.7.1/lib/httplib2/LICENSE +21 -0
- data/packages/gcutil-1.7.1/lib/httplib2/httplib2/__init__.py +1630 -0
- data/packages/gcutil-1.7.1/lib/httplib2/httplib2/cacerts.txt +714 -0
- data/packages/gcutil-1.7.1/lib/httplib2/httplib2/iri2uri.py +110 -0
- data/packages/gcutil-1.7.1/lib/httplib2/httplib2/socks.py +438 -0
- data/packages/gcutil-1.7.1/lib/iso8601/LICENSE +20 -0
- data/packages/gcutil-1.7.1/lib/iso8601/iso8601/__init__.py +1 -0
- data/packages/gcutil-1.7.1/lib/iso8601/iso8601/iso8601.py +102 -0
- data/packages/gcutil-1.7.1/lib/iso8601/iso8601/test_iso8601.py +111 -0
- data/packages/gcutil-1.7.1/lib/python_gflags/AUTHORS +2 -0
- data/packages/gcutil-1.7.1/lib/python_gflags/LICENSE +28 -0
- data/packages/gcutil-1.7.1/lib/python_gflags/gflags.py +2862 -0
- data/packages/gcutil-1.7.1/lib/python_gflags/gflags2man.py +544 -0
- data/packages/gcutil-1.7.1/lib/python_gflags/gflags_validators.py +187 -0
- metadata +118 -5
- metadata.gz.sig +0 -0
@@ -0,0 +1,181 @@
|
|
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
|
+
"""Simple file system utilities."""
|
17
|
+
|
18
|
+
__author__ = ('elaforge@google.com (Evan LaForge)',
|
19
|
+
'matthewb@google.com (Matthew Blecker)')
|
20
|
+
|
21
|
+
import errno
|
22
|
+
import os
|
23
|
+
import pwd
|
24
|
+
import shutil
|
25
|
+
import stat
|
26
|
+
import tempfile
|
27
|
+
|
28
|
+
|
29
|
+
class PasswdError(Exception):
|
30
|
+
"""Exception class for errors loading a password from a file."""
|
31
|
+
|
32
|
+
|
33
|
+
def ListDirPath(dir_name):
|
34
|
+
"""Like os.listdir with prepended dir_name, which is often more convenient."""
|
35
|
+
return [os.path.join(dir_name, fn) for fn in os.listdir(dir_name)]
|
36
|
+
|
37
|
+
|
38
|
+
def Read(filename):
|
39
|
+
"""Read entire contents of file with name 'filename'."""
|
40
|
+
fp = open(filename)
|
41
|
+
try:
|
42
|
+
return fp.read()
|
43
|
+
finally:
|
44
|
+
fp.close()
|
45
|
+
|
46
|
+
|
47
|
+
def Write(filename, contents, overwrite_existing=True, mode=0666):
|
48
|
+
"""Create a file 'filename' with 'contents', with the mode given in 'mode'.
|
49
|
+
|
50
|
+
The 'mode' is modified by the umask, as in open(2). If
|
51
|
+
'overwrite_existing' is False, the file will be opened in O_EXCL mode.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
filename: str; the name of the file
|
55
|
+
contents: str; the data to write to the file
|
56
|
+
overwrite_existing: bool; whether or not to allow the write if the file
|
57
|
+
already exists
|
58
|
+
mode: int; permissions with which to create the file (default is 0666 octal)
|
59
|
+
"""
|
60
|
+
flags = os.O_WRONLY | os.O_TRUNC | os.O_CREAT
|
61
|
+
if not overwrite_existing:
|
62
|
+
flags |= os.O_EXCL
|
63
|
+
fd = os.open(filename, flags, mode)
|
64
|
+
try:
|
65
|
+
os.write(fd, contents)
|
66
|
+
finally:
|
67
|
+
os.close(fd)
|
68
|
+
|
69
|
+
|
70
|
+
def AtomicWrite(filename, contents, mode=0666):
|
71
|
+
"""Create a file 'filename' with 'contents' atomically.
|
72
|
+
|
73
|
+
As in Write, 'mode' is modified by the umask. This creates and moves
|
74
|
+
a temporary file, and errors doing the above will be propagated normally,
|
75
|
+
though it will try to clean up the temporary file in that case.
|
76
|
+
|
77
|
+
This is very similar to the prodlib function with the same name.
|
78
|
+
|
79
|
+
Args:
|
80
|
+
filename: str; the name of the file
|
81
|
+
contents: str; the data to write to the file
|
82
|
+
mode: int; permissions with which to create the file (default is 0666 octal)
|
83
|
+
"""
|
84
|
+
(fd, tmp_filename) = tempfile.mkstemp(dir=os.path.dirname(filename))
|
85
|
+
try:
|
86
|
+
os.write(fd, contents)
|
87
|
+
finally:
|
88
|
+
os.close(fd)
|
89
|
+
try:
|
90
|
+
os.chmod(tmp_filename, mode)
|
91
|
+
os.rename(tmp_filename, filename)
|
92
|
+
except OSError, exc:
|
93
|
+
try:
|
94
|
+
os.remove(tmp_filename)
|
95
|
+
except OSError, e:
|
96
|
+
exc = OSError('%s. Additional errors cleaning up: %s' % (exc, e))
|
97
|
+
raise exc
|
98
|
+
|
99
|
+
|
100
|
+
def MkDirs(directory, force_mode=None):
|
101
|
+
"""Makes a directory including its parent directories.
|
102
|
+
|
103
|
+
This function is equivalent to os.makedirs() but it avoids a race
|
104
|
+
condition that os.makedirs() has. The race is between os.mkdir() and
|
105
|
+
os.path.exists() which fail with errors when run in parallel.
|
106
|
+
|
107
|
+
Args:
|
108
|
+
directory: str; the directory to make
|
109
|
+
force_mode: optional octal, chmod dir to get rid of umask interaction
|
110
|
+
Raises:
|
111
|
+
Whatever os.mkdir() raises when it fails for any reason EXCLUDING
|
112
|
+
"dir already exists". If a directory already exists, it does not
|
113
|
+
raise anything. This behaviour is different than os.makedirs()
|
114
|
+
"""
|
115
|
+
name = os.path.normpath(directory)
|
116
|
+
dirs = name.split(os.path.sep)
|
117
|
+
for i in range(0, len(dirs)):
|
118
|
+
path = os.path.sep.join(dirs[:i+1])
|
119
|
+
try:
|
120
|
+
if path:
|
121
|
+
os.mkdir(path)
|
122
|
+
# only chmod if we created
|
123
|
+
if force_mode is not None:
|
124
|
+
os.chmod(path, force_mode)
|
125
|
+
except OSError, exc:
|
126
|
+
if not (exc.errno == errno.EEXIST and os.path.isdir(path)):
|
127
|
+
raise
|
128
|
+
|
129
|
+
|
130
|
+
def RmDirs(dir_name):
|
131
|
+
"""Removes dir_name and every non-empty directory in dir_name.
|
132
|
+
|
133
|
+
Unlike os.removedirs and shutil.rmtree, this function doesn't raise an error
|
134
|
+
if the directory does not exist.
|
135
|
+
|
136
|
+
Args:
|
137
|
+
dir_name: Directory to be removed.
|
138
|
+
"""
|
139
|
+
try:
|
140
|
+
shutil.rmtree(dir_name)
|
141
|
+
except OSError, err:
|
142
|
+
if err.errno != errno.ENOENT:
|
143
|
+
raise
|
144
|
+
|
145
|
+
try:
|
146
|
+
parent_directory = os.path.dirname(dir_name)
|
147
|
+
while parent_directory:
|
148
|
+
try:
|
149
|
+
os.rmdir(parent_directory)
|
150
|
+
except OSError, err:
|
151
|
+
if err.errno != errno.ENOENT:
|
152
|
+
raise
|
153
|
+
|
154
|
+
parent_directory = os.path.dirname(parent_directory)
|
155
|
+
except OSError, err:
|
156
|
+
if err.errno not in (errno.EACCES, errno.ENOTEMPTY):
|
157
|
+
raise
|
158
|
+
|
159
|
+
|
160
|
+
def HomeDir(user=None):
|
161
|
+
"""Find the home directory of a user.
|
162
|
+
|
163
|
+
Args:
|
164
|
+
user: int, str, or None - the uid or login of the user to query for,
|
165
|
+
or None (the default) to query for the current process' effective user
|
166
|
+
|
167
|
+
Returns:
|
168
|
+
str - the user's home directory
|
169
|
+
|
170
|
+
Raises:
|
171
|
+
TypeError: if user is not int, str, or None.
|
172
|
+
"""
|
173
|
+
if user is None:
|
174
|
+
pw_struct = pwd.getpwuid(os.geteuid())
|
175
|
+
elif isinstance(user, int):
|
176
|
+
pw_struct = pwd.getpwuid(user)
|
177
|
+
elif isinstance(user, str):
|
178
|
+
pw_struct = pwd.getpwnam(user)
|
179
|
+
else:
|
180
|
+
raise TypeError('user must be None or an instance of int or str')
|
181
|
+
return pw_struct.pw_dir
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# Copyright 2010 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
|
+
"""Wrapper around setuptools' pkg_resources with more Google-like names.
|
17
|
+
|
18
|
+
This module is not very useful on its own, but many Google open-source projects
|
19
|
+
are used to a different naming scheme, and this module makes the transition
|
20
|
+
easier.
|
21
|
+
"""
|
22
|
+
|
23
|
+
__author__ = 'dborowitz@google.com (Dave Borowitz)'
|
24
|
+
|
25
|
+
import atexit
|
26
|
+
|
27
|
+
import pkg_resources
|
28
|
+
|
29
|
+
|
30
|
+
def _Call(func, name):
|
31
|
+
"""Call a pkg_resources function.
|
32
|
+
|
33
|
+
Args:
|
34
|
+
func: A function from pkg_resources that takes the arguments
|
35
|
+
(package_or_requirement, resource_name); for more info,
|
36
|
+
see http://peak.telecommunity.com/DevCenter/PkgResources
|
37
|
+
name: A name of the form 'module.name:path/to/resource'; this should
|
38
|
+
generally be built from __name__ in the calling module.
|
39
|
+
|
40
|
+
Returns:
|
41
|
+
The result of calling the function on the split resource name.
|
42
|
+
"""
|
43
|
+
pkg_name, resource_name = name.split(':', 1)
|
44
|
+
return func(pkg_name, resource_name)
|
45
|
+
|
46
|
+
|
47
|
+
def GetResource(name):
|
48
|
+
"""Get a resource as a string; see _Call."""
|
49
|
+
return _Call(pkg_resources.resource_string, name)
|
50
|
+
|
51
|
+
|
52
|
+
def GetResourceAsFile(name):
|
53
|
+
"""Get a resource as a file-like object; see _Call."""
|
54
|
+
return _Call(pkg_resources.resource_stream, name)
|
55
|
+
|
56
|
+
|
57
|
+
_extracted_files = False
|
58
|
+
|
59
|
+
|
60
|
+
def GetResourceFilename(name):
|
61
|
+
"""Get a filename for a resource; see _Call."""
|
62
|
+
global _extracted_files # pylint: disable-msg=W0603
|
63
|
+
if not _extracted_files:
|
64
|
+
atexit.register(pkg_resources.cleanup_resources)
|
65
|
+
_extracted_files = True
|
66
|
+
|
67
|
+
return _Call(pkg_resources.resource_filename, name)
|
@@ -0,0 +1,217 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# Copyright 2010 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
|
+
"""Script for running Google-style applications.
|
17
|
+
|
18
|
+
Unlike normal scripts run through setuptools console_script entry points,
|
19
|
+
Google-style applications must be run as top-level scripts.
|
20
|
+
|
21
|
+
Given an already-imported module, users can use the RunScriptModule function to
|
22
|
+
set up the appropriate executable environment to spawn a new Python process to
|
23
|
+
run the module as a script.
|
24
|
+
|
25
|
+
To use this technique in your project, first create a module called e.g.
|
26
|
+
stubs.py with contents like:
|
27
|
+
|
28
|
+
from google.apputils import run_script_module
|
29
|
+
|
30
|
+
def RunMyScript():
|
31
|
+
import my.script
|
32
|
+
run_script_module.RunScriptModule(my.script)
|
33
|
+
|
34
|
+
def RunMyOtherScript():
|
35
|
+
import my.other_script
|
36
|
+
run_script_module.RunScriptModule(my.other_script)
|
37
|
+
|
38
|
+
Then, set up entry points in your setup.py that point to the functions in your
|
39
|
+
stubs module:
|
40
|
+
|
41
|
+
setup(
|
42
|
+
...
|
43
|
+
entry_points = {
|
44
|
+
'console_scripts': [
|
45
|
+
'my_script = my.stubs:RunMyScript',
|
46
|
+
'my_other_script = my.stubs.RunMyOtherScript',
|
47
|
+
],
|
48
|
+
},
|
49
|
+
)
|
50
|
+
|
51
|
+
When your project is installed, setuptools will generate minimal wrapper scripts
|
52
|
+
to call your stub functions, which in turn execv your script modules. That's it!
|
53
|
+
"""
|
54
|
+
|
55
|
+
__author__ = 'dborowitz@google.com (Dave Borowitz)'
|
56
|
+
|
57
|
+
import os
|
58
|
+
import re
|
59
|
+
import sys
|
60
|
+
|
61
|
+
|
62
|
+
def FindEnv(progname):
|
63
|
+
"""Find the program in the system path.
|
64
|
+
|
65
|
+
Args:
|
66
|
+
progname: The name of the program.
|
67
|
+
|
68
|
+
Returns:
|
69
|
+
The full pathname of the program.
|
70
|
+
|
71
|
+
Raises:
|
72
|
+
AssertionError: if the program was not found.
|
73
|
+
"""
|
74
|
+
for path in os.environ['PATH'].split(':'):
|
75
|
+
fullname = os.path.join(path, progname)
|
76
|
+
if os.access(fullname, os.X_OK):
|
77
|
+
return fullname
|
78
|
+
raise AssertionError(
|
79
|
+
"Could not find an executable named '%s' in the system path" % progname)
|
80
|
+
|
81
|
+
|
82
|
+
def GetPdbArgs(python):
|
83
|
+
"""Try to get the path to pdb.py and return it in a list.
|
84
|
+
|
85
|
+
Args:
|
86
|
+
python: The full path to a Python executable.
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
A list of strings. If a relevant pdb.py was found, this will be
|
90
|
+
['/path/to/pdb.py']; if not, return ['-m', 'pdb'] and hope for the best.
|
91
|
+
(This latter technique will fail for Python 2.2.)
|
92
|
+
"""
|
93
|
+
# Usually, python is /usr/bin/pythonxx and pdb is /usr/lib/pythonxx/pdb.py
|
94
|
+
components = python.split('/')
|
95
|
+
if len(components) >= 2:
|
96
|
+
pdb_path = '/'.join(components[0:-2] + ['lib'] +
|
97
|
+
components[-1:] + ['pdb.py'])
|
98
|
+
if os.access(pdb_path, os.R_OK):
|
99
|
+
return [pdb_path]
|
100
|
+
|
101
|
+
# No pdb module found in the python path, default to -m pdb
|
102
|
+
return ['-m', 'pdb']
|
103
|
+
|
104
|
+
|
105
|
+
def StripDelimiters(s, beg, end):
|
106
|
+
if s[0] == beg:
|
107
|
+
assert s[-1] == end
|
108
|
+
return (s[1:-1], True)
|
109
|
+
else:
|
110
|
+
return (s, False)
|
111
|
+
|
112
|
+
|
113
|
+
def StripQuotes(s):
|
114
|
+
(s, stripped) = StripDelimiters(s, '"', '"')
|
115
|
+
if not stripped:
|
116
|
+
(s, stripped) = StripDelimiters(s, "'", "'")
|
117
|
+
return s
|
118
|
+
|
119
|
+
|
120
|
+
def PrintOurUsage():
|
121
|
+
"""Print usage for the stub script."""
|
122
|
+
print 'Stub script %s (auto-generated). Options:' % sys.argv[0]
|
123
|
+
print ('--helpstub '
|
124
|
+
'Show help for stub script.')
|
125
|
+
print ('--debug_binary '
|
126
|
+
'Run python under debugger specified by --debugger.')
|
127
|
+
print ('--debugger=<debugger> '
|
128
|
+
"Debugger for --debug_binary. Default: 'gdb --args'.")
|
129
|
+
print ('--debug_script '
|
130
|
+
'Run wrapped script with python debugger module (pdb).')
|
131
|
+
print ('--show_command_and_exit '
|
132
|
+
'Print command which would be executed and exit.')
|
133
|
+
print ('These options must appear first in the command line, all others will '
|
134
|
+
'be passed to the wrapped script.')
|
135
|
+
|
136
|
+
|
137
|
+
def RunScriptModule(module):
|
138
|
+
"""Run a module as a script.
|
139
|
+
|
140
|
+
Locates the module's file and runs it in the current interpreter, or
|
141
|
+
optionally a debugger.
|
142
|
+
|
143
|
+
Args:
|
144
|
+
module: The module object to run.
|
145
|
+
"""
|
146
|
+
args = sys.argv[1:]
|
147
|
+
|
148
|
+
debug_binary = False
|
149
|
+
debugger = 'gdb --args'
|
150
|
+
debug_script = False
|
151
|
+
show_command_and_exit = False
|
152
|
+
|
153
|
+
while args:
|
154
|
+
if args[0] == '--helpstub':
|
155
|
+
PrintOurUsage()
|
156
|
+
sys.exit(0)
|
157
|
+
if args[0] == '--debug_binary':
|
158
|
+
debug_binary = True
|
159
|
+
args = args[1:]
|
160
|
+
continue
|
161
|
+
if args[0] == '--debug_script':
|
162
|
+
debug_script = True
|
163
|
+
args = args[1:]
|
164
|
+
continue
|
165
|
+
if args[0] == '--show_command_and_exit':
|
166
|
+
show_command_and_exit = True
|
167
|
+
args = args[1:]
|
168
|
+
continue
|
169
|
+
matchobj = re.match('--debugger=(.+)', args[0])
|
170
|
+
if matchobj is not None:
|
171
|
+
debugger = StripQuotes(matchobj.group(1))
|
172
|
+
args = args[1:]
|
173
|
+
continue
|
174
|
+
break
|
175
|
+
|
176
|
+
# Now look for my main python source file
|
177
|
+
# TODO(dborowitz): This will fail if the module was zipimported, which means
|
178
|
+
# no egg depending on this script runner can be zip_safe.
|
179
|
+
main_filename = module.__file__
|
180
|
+
assert os.path.exists(main_filename), ('Cannot exec() %r: file not found.' %
|
181
|
+
main_filename)
|
182
|
+
assert os.access(main_filename, os.R_OK), ('Cannot exec() %r: file not'
|
183
|
+
' readable.' % main_filename)
|
184
|
+
|
185
|
+
args = [main_filename] + args
|
186
|
+
|
187
|
+
if debug_binary:
|
188
|
+
debugger_args = debugger.split()
|
189
|
+
program = debugger_args[0]
|
190
|
+
# If pathname is not absolute, determine full path using PATH
|
191
|
+
if not os.path.isabs(program):
|
192
|
+
program = FindEnv(program)
|
193
|
+
python_path = sys.executable
|
194
|
+
command_vec = [python_path]
|
195
|
+
if debug_script:
|
196
|
+
command_vec.extend(GetPdbArgs(python_path))
|
197
|
+
args = [program] + debugger_args[1:] + command_vec + args
|
198
|
+
|
199
|
+
elif debug_script:
|
200
|
+
args = [sys.executable] + GetPdbArgs(program) + args
|
201
|
+
|
202
|
+
else:
|
203
|
+
program = sys.executable
|
204
|
+
args = [sys.executable] + args
|
205
|
+
|
206
|
+
if show_command_and_exit:
|
207
|
+
print 'program: "%s"' % program
|
208
|
+
print 'args:', args
|
209
|
+
sys.exit(0)
|
210
|
+
|
211
|
+
try:
|
212
|
+
sys.stdout.flush()
|
213
|
+
os.execv(program, args)
|
214
|
+
except EnvironmentError, e:
|
215
|
+
if not getattr(e, 'filename', None):
|
216
|
+
e.filename = program # Add info to error message
|
217
|
+
raise
|