OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
| 6 """Utility script for emulating common unix commands.""" |
| 7 |
| 8 from __future__ import print_function |
| 9 |
6 import fnmatch | 10 import fnmatch |
7 import glob | 11 import glob |
8 import optparse | 12 import argparse |
9 import os | 13 import os |
10 import posixpath | 14 import posixpath |
11 import shutil | 15 import shutil |
12 import stat | 16 import stat |
13 import sys | 17 import sys |
14 import time | 18 import time |
15 import zipfile | 19 import zipfile |
16 | 20 |
17 if sys.version_info < (2, 7, 0): | 21 if sys.version_info < (2, 7, 0): |
18 sys.stderr.write("python 2.7 or later is required run this script\n") | 22 sys.stderr.write("python 2.7 or later is required run this script\n") |
19 sys.exit(1) | 23 sys.exit(1) |
20 | 24 |
21 | 25 |
22 def IncludeFiles(filters, files): | 26 def IncludeFiles(filters, files): |
23 """Filter files based on inclusion lists | 27 """Filter files based on inclusion lists |
24 | 28 |
25 Return a list of files which match and of the Unix shell-style wildcards | 29 Return a list of files which match and of the Unix shell-style wildcards |
26 provided, or return all the files if no filter is provided.""" | 30 provided, or return all the files if no filter is provided. |
| 31 """ |
27 if not filters: | 32 if not filters: |
28 return files | 33 return files |
29 match = set() | 34 match = set() |
30 for file_filter in filters: | 35 for file_filter in filters: |
31 match |= set(fnmatch.filter(files, file_filter)) | 36 match |= set(fnmatch.filter(files, file_filter)) |
32 return [name for name in files if name in match] | 37 return [name for name in files if name in match] |
33 | 38 |
34 | 39 |
35 def ExcludeFiles(filters, files): | 40 def ExcludeFiles(filters, files): |
36 """Filter files based on exclusions lists | 41 """Filter files based on exclusions lists |
37 | 42 |
38 Return a list of files which do not match any of the Unix shell-style | 43 Return a list of files which do not match any of the Unix shell-style |
39 wildcards provided, or return all the files if no filter is provided.""" | 44 wildcards provided, or return all the files if no filter is provided. |
| 45 """ |
40 if not filters: | 46 if not filters: |
41 return files | 47 return files |
42 match = set() | 48 match = set() |
43 for file_filter in filters: | 49 for file_filter in filters: |
44 excludes = set(fnmatch.filter(files, file_filter)) | 50 excludes = set(fnmatch.filter(files, file_filter)) |
45 match |= excludes | 51 match |= excludes |
46 return [name for name in files if name not in match] | 52 return [name for name in files if name not in match] |
47 | 53 |
48 | 54 |
49 def CopyPath(options, src, dst): | 55 def CopyPath(options, src, dst): |
50 """CopyPath from src to dst | 56 """CopyPath from src to dst |
51 | 57 |
52 Copy a fully specified src to a fully specified dst. If src and dst are | 58 Copy a fully specified src to a fully specified dst. If src and dst are |
53 both files, the dst file is removed first to prevent error. If and include | 59 both files, the dst file is removed first to prevent error. If and include |
54 or exclude list are provided, the destination is first matched against that | 60 or exclude list are provided, the destination is first matched against that |
55 filter.""" | 61 filter. |
| 62 """ |
56 if options.includes: | 63 if options.includes: |
57 if not IncludeFiles(options.includes, [src]): | 64 if not IncludeFiles(options.includes, [src]): |
58 return | 65 return |
59 | 66 |
60 if options.excludes: | 67 if options.excludes: |
61 if not ExcludeFiles(options.excludes, [src]): | 68 if not ExcludeFiles(options.excludes, [src]): |
62 return | 69 return |
63 | 70 |
64 if options.verbose: | 71 if options.verbose: |
65 print 'cp %s %s' % (src, dst) | 72 print('cp %s %s' % (src, dst)) |
66 | 73 |
67 # If the source is a single file, copy it individually | 74 # If the source is a single file, copy it individually |
68 if os.path.isfile(src): | 75 if os.path.isfile(src): |
69 # We can not copy over a directory with a file. | 76 # We can not copy over a directory with a file. |
70 if os.path.exists(dst): | 77 if os.path.exists(dst): |
71 if not os.path.isfile(dst): | 78 if not os.path.isfile(dst): |
72 msg = "cp: cannot overwrite non-file '%s' with file." % dst | 79 msg = "cp: cannot overwrite non-file '%s' with file." % dst |
73 raise OSError(msg) | 80 raise OSError(msg) |
74 # If the destination exists as a file, remove it before copying to avoid | 81 # If the destination exists as a file, remove it before copying to avoid |
75 # 'readonly' issues. | 82 # 'readonly' issues. |
76 os.remove(dst) | 83 os.remove(dst) |
77 | 84 |
78 # Now copy to the non-existent fully qualified target | 85 # Now copy to the non-existent fully qualified target |
79 shutil.copy(src, dst) | 86 shutil.copy(src, dst) |
80 return | 87 return |
81 | 88 |
82 # Otherwise it's a directory, ignore it unless allowed | 89 # Otherwise it's a directory, ignore it unless allowed |
83 if os.path.isdir(src): | 90 if os.path.isdir(src): |
84 if not options.recursive: | 91 if not options.recursive: |
85 print "cp: omitting directory '%s'" % src | 92 print("cp: omitting directory '%s'" % src) |
86 return | 93 return |
87 | 94 |
88 # We can not copy over a file with a directory. | 95 # We can not copy over a file with a directory. |
89 if os.path.exists(dst): | 96 if os.path.exists(dst): |
90 if not os.path.isdir(dst): | 97 if not os.path.isdir(dst): |
91 msg = "cp: cannot overwrite non-directory '%s' with directory." % dst | 98 msg = "cp: cannot overwrite non-directory '%s' with directory." % dst |
92 raise OSError(msg) | 99 raise OSError(msg) |
93 else: | 100 else: |
94 # if it didn't exist, create the directory | 101 # if it didn't exist, create the directory |
95 os.makedirs(dst) | 102 os.makedirs(dst) |
96 | 103 |
97 # Now copy all members | 104 # Now copy all members |
98 for filename in os.listdir(src): | 105 for filename in os.listdir(src): |
99 srcfile = os.path.join(src, filename) | 106 srcfile = os.path.join(src, filename) |
100 dstfile = os.path.join(dst, filename) | 107 dstfile = os.path.join(dst, filename) |
101 CopyPath(options, srcfile, dstfile) | 108 CopyPath(options, srcfile, dstfile) |
102 return | 109 return |
103 | 110 |
104 | 111 |
105 def Copy(args): | 112 def Copy(args): |
106 """A Unix cp style copy. | 113 """A Unix cp style copy. |
107 | 114 |
108 Copies multiple sources to a single destination using the normal cp | 115 Copies multiple sources to a single destination using the normal cp |
109 semantics. In addition, it support inclusion and exclusion filters which | 116 semantics. In addition, it support inclusion and exclusion filters which |
110 allows the copy to skip certain types of files.""" | 117 allows the copy to skip certain types of files. |
111 parser = optparse.OptionParser(usage='usage: cp [Options] sources... dest') | 118 """ |
112 parser.add_option( | 119 parser = argparse.ArgumentParser(usage='cp [Options] sources... dest', |
| 120 description=Copy.__doc__) |
| 121 parser.add_argument( |
113 '-R', '-r', '--recursive', dest='recursive', action='store_true', | 122 '-R', '-r', '--recursive', dest='recursive', action='store_true', |
114 default=False, | 123 default=False, |
115 help='copy directories recursively.') | 124 help='copy directories recursively.') |
116 parser.add_option( | 125 parser.add_argument( |
117 '-v', '--verbose', dest='verbose', action='store_true', | 126 '-v', '--verbose', dest='verbose', action='store_true', |
118 default=False, | 127 default=False, |
119 help='verbose output.') | 128 help='verbose output.') |
120 parser.add_option( | 129 parser.add_argument( |
121 '--include', dest='includes', action='append', default=[], | 130 '--include', dest='includes', action='append', default=[], |
122 help='include files matching this expression.') | 131 help='include files matching this expression.') |
123 parser.add_option( | 132 parser.add_argument( |
124 '--exclude', dest='excludes', action='append', default=[], | 133 '--exclude', dest='excludes', action='append', default=[], |
125 help='exclude files matching this expression.') | 134 help='exclude files matching this expression.') |
126 options, files = parser.parse_args(args) | 135 parser.add_argument('srcs', nargs='+', help='files to copy') |
127 if len(files) < 2: | 136 parser.add_argument('dest', help='destination') |
128 parser.error('ERROR: expecting SOURCE(s) and DEST.') | |
129 | 137 |
130 srcs = files[:-1] | 138 options = parser.parse_args(args) |
131 dst = files[-1] | |
132 | 139 |
133 src_list = [] | 140 src_list = [] |
134 for src in srcs: | 141 for src in options.srcs: |
135 files = glob.glob(src) | 142 files = glob.glob(src) |
136 if not files: | 143 if not files: |
137 raise OSError('cp: no such file or directory: ' + src) | 144 raise OSError('cp: no such file or directory: ' + src) |
138 if files: | 145 if files: |
139 src_list.extend(files) | 146 src_list.extend(files) |
140 | 147 |
141 for src in src_list: | 148 for src in src_list: |
142 # If the destination is a directory, then append the basename of the src | 149 # If the destination is a directory, then append the basename of the src |
143 # to the destination. | 150 # to the destination. |
144 if os.path.isdir(dst): | 151 if os.path.isdir(options.dest): |
145 CopyPath(options, src, os.path.join(dst, os.path.basename(src))) | 152 CopyPath(options, src, os.path.join(options.dest, os.path.basename(src))) |
146 else: | 153 else: |
147 CopyPath(options, src, dst) | 154 CopyPath(options, src, options.dest) |
148 | 155 |
149 | 156 |
150 def Mkdir(args): | 157 def Mkdir(args): |
151 """A Unix style mkdir""" | 158 """A Unix style mkdir.""" |
152 parser = optparse.OptionParser(usage='usage: mkdir [Options] DIRECTORY...') | 159 parser = argparse.ArgumentParser(usage='mkdir [Options] DIRECTORY...', |
153 parser.add_option( | 160 description=Mkdir.__doc__) |
| 161 parser.add_argument( |
154 '-p', '--parents', dest='parents', action='store_true', | 162 '-p', '--parents', dest='parents', action='store_true', |
155 default=False, | 163 default=False, |
156 help='ignore existing parents, create parents as needed.') | 164 help='ignore existing parents, create parents as needed.') |
157 parser.add_option( | 165 parser.add_argument( |
158 '-v', '--verbose', dest='verbose', action='store_true', | 166 '-v', '--verbose', dest='verbose', action='store_true', |
159 default=False, | 167 default=False, |
160 help='verbose output.') | 168 help='verbose output.') |
| 169 parser.add_argument('dirs', nargs='+', help='directory(s) to create') |
161 | 170 |
162 options, dsts = parser.parse_args(args) | 171 options = parser.parse_args(args) |
163 if len(dsts) < 1: | |
164 parser.error('ERROR: expecting DIRECTORY...') | |
165 | 172 |
166 for dst in dsts: | 173 for dst in options.dirs: |
167 if options.verbose: | 174 if options.verbose: |
168 print 'mkdir ' + dst | 175 print('mkdir %s' % dst) |
169 try: | 176 try: |
170 os.makedirs(dst) | 177 os.makedirs(dst) |
171 except OSError: | 178 except OSError: |
172 if os.path.isdir(dst): | 179 if os.path.isdir(dst): |
173 if options.parents: | 180 if options.parents: |
174 continue | 181 continue |
175 raise OSError('mkdir: Already exists: ' + dst) | 182 raise OSError('mkdir: Already exists: ' + dst) |
176 else: | 183 else: |
177 raise OSError('mkdir: Failed to create: ' + dst) | 184 raise OSError('mkdir: Failed to create: ' + dst) |
178 return 0 | 185 return 0 |
179 | 186 |
180 | 187 |
181 def MovePath(options, src, dst): | 188 def MovePath(options, src, dst): |
182 """MovePath from src to dst | 189 """MovePath from src to dst. |
183 | 190 |
184 Moves the src to the dst much like the Unix style mv command, except it | 191 Moves the src to the dst much like the Unix style mv command, except it |
185 only handles one source at a time. Because of possible temporary failures | 192 only handles one source at a time. Because of possible temporary failures |
186 do to locks (such as anti-virus software on Windows), the function will retry | 193 do to locks (such as anti-virus software on Windows), the function will retry |
187 up to five times.""" | 194 up to five times. |
| 195 """ |
188 # if the destination is not an existing directory, then overwrite it | 196 # if the destination is not an existing directory, then overwrite it |
189 if os.path.isdir(dst): | 197 if os.path.isdir(dst): |
190 dst = os.path.join(dst, os.path.basename(src)) | 198 dst = os.path.join(dst, os.path.basename(src)) |
191 | 199 |
192 # If the destination exists, the remove it | 200 # If the destination exists, the remove it |
193 if os.path.exists(dst): | 201 if os.path.exists(dst): |
194 if options.force: | 202 if options.force: |
195 Remove(['-vfr', dst]) | 203 Remove(['-vfr', dst]) |
196 if os.path.exists(dst): | 204 if os.path.exists(dst): |
197 raise OSError('mv: FAILED TO REMOVE ' + dst) | 205 raise OSError('mv: FAILED TO REMOVE ' + dst) |
198 else: | 206 else: |
199 raise OSError('mv: already exists ' + dst) | 207 raise OSError('mv: already exists ' + dst) |
200 for _ in range(5): | 208 for _ in range(5): |
201 try: | 209 try: |
202 os.rename(src, dst) | 210 os.rename(src, dst) |
203 return | 211 break |
204 except OSError as error: | 212 except OSError as error: |
205 print 'Failed on %s with %s, retrying' % (src, error) | 213 print('Failed on %s with %s, retrying' % (src, error)) |
206 time.sleep(5) | 214 time.sleep(5) |
207 print 'Gave up.' | 215 else: |
208 raise OSError('mv: ' + error) | 216 print('Gave up.') |
| 217 raise OSError('mv: ' + error) |
209 | 218 |
210 | 219 |
211 def Move(args): | 220 def Move(args): |
212 parser = optparse.OptionParser(usage='usage: mv [Options] sources... dest') | 221 """A Unix style mv.""" |
213 parser.add_option( | 222 |
| 223 parser = argparse.ArgumentParser(usage='mv [Options] sources... dest', |
| 224 description=Move.__doc__) |
| 225 parser.add_argument( |
214 '-v', '--verbose', dest='verbose', action='store_true', | 226 '-v', '--verbose', dest='verbose', action='store_true', |
215 default=False, | 227 default=False, |
216 help='verbose output.') | 228 help='verbose output.') |
217 parser.add_option( | 229 parser.add_argument( |
218 '-f', '--force', dest='force', action='store_true', | 230 '-f', '--force', dest='force', action='store_true', |
219 default=False, | 231 default=False, |
220 help='force, do not error it files already exist.') | 232 help='force, do not error it files already exist.') |
221 options, files = parser.parse_args(args) | 233 parser.add_argument('srcs', nargs='+') |
222 if len(files) < 2: | 234 parser.add_argument('dest') |
223 parser.error('ERROR: expecting SOURCE... and DEST.') | |
224 | 235 |
225 srcs = files[:-1] | 236 options = parser.parse_args(args) |
226 dst = files[-1] | |
227 | 237 |
228 if options.verbose: | 238 if options.verbose: |
229 print 'mv %s %s' % (' '.join(srcs), dst) | 239 print('mv %s %s' % (' '.join(options.srcs), options.dest)) |
230 | 240 |
231 for src in srcs: | 241 for src in options.srcs: |
232 MovePath(options, src, dst) | 242 MovePath(options, src, options.dest) |
233 return 0 | 243 return 0 |
234 | 244 |
235 | 245 |
236 def Remove(args): | 246 def Remove(args): |
237 """A Unix style rm. | 247 """A Unix style rm. |
238 | 248 |
239 Removes the list of paths. Because of possible temporary failures do to locks | 249 Removes the list of paths. Because of possible temporary failures do to locks |
240 (such as anti-virus software on Windows), the function will retry up to five | 250 (such as anti-virus software on Windows), the function will retry up to five |
241 times.""" | 251 times. |
242 parser = optparse.OptionParser(usage='usage: rm [Options] PATHS...') | 252 """ |
243 parser.add_option( | 253 parser = argparse.ArgumentParser(usage='rm [Options] PATHS...', |
| 254 description=Remove.__doc__) |
| 255 parser.add_argument( |
244 '-R', '-r', '--recursive', dest='recursive', action='store_true', | 256 '-R', '-r', '--recursive', dest='recursive', action='store_true', |
245 default=False, | 257 default=False, |
246 help='remove directories recursively.') | 258 help='remove directories recursively.') |
247 parser.add_option( | 259 parser.add_argument( |
248 '-v', '--verbose', dest='verbose', action='store_true', | 260 '-v', '--verbose', dest='verbose', action='store_true', |
249 default=False, | 261 default=False, |
250 help='verbose output.') | 262 help='verbose output.') |
251 parser.add_option( | 263 parser.add_argument( |
252 '-f', '--force', dest='force', action='store_true', | 264 '-f', '--force', dest='force', action='store_true', |
253 default=False, | 265 default=False, |
254 help='force, do not error it files does not exist.') | 266 help='force, do not error it files does not exist.') |
255 options, files = parser.parse_args(args) | 267 parser.add_argument('files', nargs='+') |
256 if len(files) < 1: | 268 options = parser.parse_args(args) |
257 parser.error('ERROR: expecting FILE...') | |
258 | 269 |
259 try: | 270 try: |
260 for pattern in files: | 271 for pattern in options.files: |
261 dst_files = glob.glob(pattern) | 272 dst_files = glob.glob(pattern) |
262 if not dst_files: | 273 if not dst_files: |
263 # Ignore non existing files when using force | 274 # Ignore non existing files when using force |
264 if options.force: | 275 if options.force: |
265 continue | 276 continue |
266 raise OSError('rm: no such file or directory: ' + pattern) | 277 raise OSError('rm: no such file or directory: ' + pattern) |
267 | 278 |
268 for dst in dst_files: | 279 for dst in dst_files: |
269 if options.verbose: | 280 if options.verbose: |
270 print 'rm ' + dst | 281 print('rm ' + dst) |
271 | 282 |
272 if os.path.isfile(dst) or os.path.islink(dst): | 283 if os.path.isfile(dst) or os.path.islink(dst): |
273 for i in range(5): | 284 for _ in range(5): |
274 try: | 285 try: |
275 # Check every time, since it may have been deleted after the | 286 # Check every time, since it may have been deleted after the |
276 # previous failed attempt. | 287 # previous failed attempt. |
277 if os.path.isfile(dst) or os.path.islink(dst): | 288 if os.path.isfile(dst) or os.path.islink(dst): |
278 os.remove(dst) | 289 os.remove(dst) |
279 break | 290 break |
280 except OSError as error: | 291 except OSError as error: |
281 if i == 5: | 292 print('Failed remove with %s, retrying' % error) |
282 print 'Gave up.' | |
283 raise OSError('rm: ' + str(error)) | |
284 print 'Failed remove with %s, retrying' % error | |
285 time.sleep(5) | 293 time.sleep(5) |
| 294 else: |
| 295 print('Gave up.') |
| 296 raise OSError('rm: ' + str(error)) |
286 | 297 |
287 if options.recursive: | 298 if options.recursive: |
288 for i in range(5): | 299 for _ in range(5): |
289 try: | 300 try: |
290 if os.path.isdir(dst): | 301 if os.path.isdir(dst): |
291 shutil.rmtree(dst) | 302 shutil.rmtree(dst) |
292 break | 303 break |
293 except OSError as error: | 304 except OSError as error: |
294 if i == 5: | 305 print('Failed rmtree with %s, retrying' % error) |
295 print 'Gave up.' | |
296 raise OSError('rm: ' + str(error)) | |
297 print 'Failed rmtree with %s, retrying' % error | |
298 time.sleep(5) | 306 time.sleep(5) |
| 307 else: |
| 308 print('Gave up.') |
| 309 raise OSError('rm: ' + str(error)) |
299 | 310 |
300 except OSError as error: | 311 except OSError as error: |
301 print error | 312 print(error) |
| 313 |
302 return 0 | 314 return 0 |
303 | 315 |
304 | 316 |
305 def MakeZipPath(os_path, isdir, iswindows): | 317 def MakeZipPath(os_path, isdir, iswindows): |
306 """Changes a path into zipfile format. | 318 """Changes a path into zipfile format. |
307 | 319 |
308 # doctest doesn't seem to honor r'' strings, so the backslashes need to be | 320 # doctest doesn't seem to honor r'' strings, so the backslashes need to be |
309 # escaped. | 321 # escaped. |
310 >>> MakeZipPath(r'C:\\users\\foobar\\blah', False, True) | 322 >>> MakeZipPath(r'C:\\users\\foobar\\blah', False, True) |
311 'users/foobar/blah' | 323 'users/foobar/blah' |
(...skipping 23 matching lines...) Expand all Loading... |
335 return zip_path | 347 return zip_path |
336 | 348 |
337 | 349 |
338 def OSMakeZipPath(os_path): | 350 def OSMakeZipPath(os_path): |
339 return MakeZipPath(os_path, os.path.isdir(os_path), sys.platform == 'win32') | 351 return MakeZipPath(os_path, os.path.isdir(os_path), sys.platform == 'win32') |
340 | 352 |
341 | 353 |
342 def Zip(args): | 354 def Zip(args): |
343 """A Unix style zip. | 355 """A Unix style zip. |
344 | 356 |
345 Compresses the listed files.""" | 357 Compresses the listed files. |
346 parser = optparse.OptionParser(usage='usage: zip [Options] zipfile list') | 358 """ |
347 parser.add_option( | 359 parser = argparse.ArgumentParser(description=Zip.__doc__) |
| 360 parser.add_argument( |
348 '-r', dest='recursive', action='store_true', | 361 '-r', dest='recursive', action='store_true', |
349 default=False, | 362 default=False, |
350 help='recurse into directories') | 363 help='recurse into directories') |
351 parser.add_option( | 364 parser.add_argument( |
352 '-q', dest='quiet', action='store_true', | 365 '-q', dest='quiet', action='store_true', |
353 default=False, | 366 default=False, |
354 help='quiet operation') | 367 help='quiet operation') |
355 options, files = parser.parse_args(args) | 368 parser.add_argument('zipfile') |
356 if len(files) < 2: | 369 parser.add_argument('filenames', nargs='+') |
357 parser.error('ERROR: expecting ZIPFILE and LIST.') | 370 options = parser.parse_args(args) |
358 | |
359 dest_zip = files[0] | |
360 src_args = files[1:] | |
361 | 371 |
362 src_files = [] | 372 src_files = [] |
363 for src_arg in src_args: | 373 for filename in options.filenames: |
364 globbed_src_args = glob.glob(src_arg) | 374 globbed_src_args = glob.glob(filename) |
365 if not globbed_src_args: | 375 if not globbed_src_args: |
366 if not options.quiet: | 376 if not options.quiet: |
367 print 'zip warning: name not matched: %s' % (src_arg,) | 377 print('zip warning: name not matched: %s' % filename) |
368 | 378 |
369 for src_file in globbed_src_args: | 379 for src_file in globbed_src_args: |
370 src_file = os.path.normpath(src_file) | 380 src_file = os.path.normpath(src_file) |
371 src_files.append(src_file) | 381 src_files.append(src_file) |
372 if options.recursive and os.path.isdir(src_file): | 382 if options.recursive and os.path.isdir(src_file): |
373 for root, dirs, files in os.walk(src_file): | 383 for root, dirs, files in os.walk(src_file): |
374 for dirname in dirs: | 384 for dirname in dirs: |
375 src_files.append(os.path.join(root, dirname)) | 385 src_files.append(os.path.join(root, dirname)) |
376 for filename in files: | 386 for filename in files: |
377 src_files.append(os.path.join(root, filename)) | 387 src_files.append(os.path.join(root, filename)) |
378 | 388 |
379 zip_stream = None | |
380 # zip_data represents a list of the data to be written or appended to the | 389 # zip_data represents a list of the data to be written or appended to the |
381 # zip_stream. It is a list of tuples: | 390 # zip_stream. It is a list of tuples: |
382 # (OS file path, zip path/zip file info, and file data) | 391 # (OS file path, zip path/zip file info, and file data) |
383 # In all cases one of the |os path| or the |file data| will be None. | 392 # In all cases one of the |os path| or the |file data| will be None. |
384 # |os path| is None when there is no OS file to write to the archive (i.e. | 393 # |os path| is None when there is no OS file to write to the archive (i.e. |
385 # the file data already existed in the archive). |file data| is None when the | 394 # the file data already existed in the archive). |file data| is None when the |
386 # file is new (never existed in the archive) or being updated. | 395 # file is new (never existed in the archive) or being updated. |
387 zip_data = [] | 396 zip_data = [] |
388 new_files_to_add = [OSMakeZipPath(src_file) for src_file in src_files] | 397 new_files_to_add = [OSMakeZipPath(src_file) for src_file in src_files] |
389 zip_path_to_os_path_dict = dict((new_files_to_add[i], src_files[i]) | 398 zip_path_to_os_path_dict = dict((new_files_to_add[i], src_files[i]) |
390 for i in range(len(src_files))) | 399 for i in range(len(src_files))) |
391 write_mode = 'a' | 400 write_mode = 'a' |
392 try: | 401 if os.path.exists(options.zipfile): |
393 zip_stream = zipfile.ZipFile(dest_zip, 'r') | 402 with zipfile.ZipFile(options.zipfile, 'r') as zip_stream: |
394 files_to_update = set(new_files_to_add).intersection( | 403 try: |
395 set(zip_stream.namelist())) | 404 files_to_update = set(new_files_to_add).intersection( |
396 if files_to_update: | 405 set(zip_stream.namelist())) |
397 # As far as I can tell, there is no way to update a zip entry using | 406 if files_to_update: |
398 # zipfile; the best you can do is rewrite the archive. | 407 # As far as I can tell, there is no way to update a zip entry using |
399 # Iterate through the zipfile to maintain file order. | 408 # zipfile; the best you can do is rewrite the archive. |
400 write_mode = 'w' | 409 # Iterate through the zipfile to maintain file order. |
401 for zip_path in zip_stream.namelist(): | 410 write_mode = 'w' |
402 if zip_path in files_to_update: | 411 for zip_path in zip_stream.namelist(): |
403 os_path = zip_path_to_os_path_dict[zip_path] | 412 if zip_path in files_to_update: |
404 zip_data.append((os_path, zip_path, None)) | 413 os_path = zip_path_to_os_path_dict[zip_path] |
405 new_files_to_add.remove(zip_path) | 414 zip_data.append((os_path, zip_path, None)) |
406 else: | 415 new_files_to_add.remove(zip_path) |
407 file_bytes = zip_stream.read(zip_path) | 416 else: |
408 file_info = zip_stream.getinfo(zip_path) | 417 file_bytes = zip_stream.read(zip_path) |
409 zip_data.append((None, file_info, file_bytes)) | 418 file_info = zip_stream.getinfo(zip_path) |
410 except IOError: | 419 zip_data.append((None, file_info, file_bytes)) |
411 pass | 420 except IOError: |
412 finally: | 421 pass |
413 if zip_stream: | |
414 zip_stream.close() | |
415 | 422 |
416 for zip_path in new_files_to_add: | 423 for zip_path in new_files_to_add: |
417 zip_data.append((zip_path_to_os_path_dict[zip_path], zip_path, None)) | 424 zip_data.append((zip_path_to_os_path_dict[zip_path], zip_path, None)) |
418 | 425 |
419 if not zip_data: | 426 if not zip_data: |
420 print 'zip error: Nothing to do! (%s)' % (dest_zip,) | 427 print('zip error: Nothing to do! (%s)' % options.zipfile) |
421 return 1 | 428 return 1 |
422 | 429 |
423 try: | 430 with zipfile.ZipFile(options.zipfile, write_mode, |
424 zip_stream = zipfile.ZipFile(dest_zip, write_mode, zipfile.ZIP_DEFLATED) | 431 zipfile.ZIP_DEFLATED) as zip_stream: |
425 for os_path, file_info_or_zip_path, file_bytes in zip_data: | 432 for os_path, file_info_or_zip_path, file_bytes in zip_data: |
426 if isinstance(file_info_or_zip_path, zipfile.ZipInfo): | 433 if isinstance(file_info_or_zip_path, zipfile.ZipInfo): |
427 zip_path = file_info_or_zip_path.filename | 434 zip_path = file_info_or_zip_path.filename |
428 else: | 435 else: |
429 zip_path = file_info_or_zip_path | 436 zip_path = file_info_or_zip_path |
430 | 437 |
431 if os_path: | 438 if os_path: |
432 st = os.stat(os_path) | 439 st = os.stat(os_path) |
433 if stat.S_ISDIR(st.st_mode): | 440 if stat.S_ISDIR(st.st_mode): |
434 # Python 2.6 on the buildbots doesn't support writing directories to | 441 # Python 2.6 on the buildbots doesn't support writing directories to |
(...skipping 16 matching lines...) Expand all Loading... |
451 zip_stream.writestr(file_info_or_zip_path, file_bytes) | 458 zip_stream.writestr(file_info_or_zip_path, file_bytes) |
452 | 459 |
453 if not options.quiet: | 460 if not options.quiet: |
454 if zip_path in new_files_to_add: | 461 if zip_path in new_files_to_add: |
455 operation = 'adding' | 462 operation = 'adding' |
456 else: | 463 else: |
457 operation = 'updating' | 464 operation = 'updating' |
458 zip_info = zip_stream.getinfo(zip_path) | 465 zip_info = zip_stream.getinfo(zip_path) |
459 if (zip_info.compress_type == zipfile.ZIP_STORED or | 466 if (zip_info.compress_type == zipfile.ZIP_STORED or |
460 zip_info.file_size == 0): | 467 zip_info.file_size == 0): |
461 print ' %s: %s (stored 0%%)' % (operation, zip_path) | 468 print(' %s: %s (stored 0%%)' % (operation, zip_path)) |
462 elif zip_info.compress_type == zipfile.ZIP_DEFLATED: | 469 elif zip_info.compress_type == zipfile.ZIP_DEFLATED: |
463 print ' %s: %s (deflated %d%%)' % (operation, zip_path, | 470 print(' %s: %s (deflated %d%%)' % (operation, zip_path, |
464 100 - zip_info.compress_size * 100 / zip_info.file_size) | 471 100 - zip_info.compress_size * 100 / zip_info.file_size)) |
465 finally: | |
466 zip_stream.close() | |
467 | 472 |
468 return 0 | 473 return 0 |
469 | 474 |
470 | 475 |
471 def FindExeInPath(filename): | 476 def FindExeInPath(filename): |
472 env_path = os.environ.get('PATH', '') | 477 env_path = os.environ.get('PATH', '') |
473 paths = env_path.split(os.pathsep) | 478 paths = env_path.split(os.pathsep) |
474 | 479 |
475 def IsExecutableFile(path): | 480 def IsExecutableFile(path): |
476 return os.path.isfile(path) and os.access(path, os.X_OK) | 481 return os.path.isfile(path) and os.access(path, os.X_OK) |
(...skipping 10 matching lines...) Expand all Loading... |
487 | 492 |
488 def Which(args): | 493 def Which(args): |
489 """A Unix style which. | 494 """A Unix style which. |
490 | 495 |
491 Looks for all arguments in the PATH environment variable, and prints their | 496 Looks for all arguments in the PATH environment variable, and prints their |
492 path if they are executable files. | 497 path if they are executable files. |
493 | 498 |
494 Note: If you pass an argument with a path to which, it will just test if it | 499 Note: If you pass an argument with a path to which, it will just test if it |
495 is executable, not if it is in the path. | 500 is executable, not if it is in the path. |
496 """ | 501 """ |
497 parser = optparse.OptionParser(usage='usage: which args...') | 502 parser = argparse.ArgumentParser(description=Which.__doc__) |
498 _, files = parser.parse_args(args) | 503 parser.add_argument('files', nargs='+') |
499 if not files: | 504 options = parser.parse_args(args) |
500 return 0 | |
501 | 505 |
502 retval = 0 | 506 retval = 0 |
503 for filename in files: | 507 for filename in options.files: |
504 fullname = FindExeInPath(filename) | 508 fullname = FindExeInPath(filename) |
505 if fullname: | 509 if fullname: |
506 print fullname | 510 print(fullname) |
507 else: | 511 else: |
508 retval = 1 | 512 retval = 1 |
509 | 513 |
510 return retval | 514 return retval |
511 | 515 |
512 | 516 |
513 FuncMap = { | 517 FuncMap = { |
514 'cp': Copy, | 518 'cp': Copy, |
515 'mkdir': Mkdir, | 519 'mkdir': Mkdir, |
516 'mv': Move, | 520 'mv': Move, |
517 'rm': Remove, | 521 'rm': Remove, |
518 'zip': Zip, | 522 'zip': Zip, |
519 'which': Which, | 523 'which': Which, |
520 } | 524 } |
521 | 525 |
522 | 526 |
523 def main(args): | 527 def main(args): |
524 if not args: | 528 if not args: |
525 print 'No command specified' | 529 print('No command specified') |
526 print 'Available commands: %s' % ' '.join(FuncMap) | 530 print('Available commands: %s' % ' '.join(FuncMap)) |
527 return 1 | 531 return 1 |
528 func_name = args[0] | 532 func_name = args[0] |
529 func = FuncMap.get(func_name) | 533 func = FuncMap.get(func_name) |
530 if not func: | 534 if not func: |
531 print 'Do not recognize command: %s' % func_name | 535 print('Do not recognize command: %s' % func_name) |
532 print 'Available commands: %s' % ' '.join(FuncMap) | 536 print('Available commands: %s' % ' '.join(FuncMap)) |
533 return 1 | 537 return 1 |
534 try: | 538 try: |
535 return func(args[1:]) | 539 return func(args[1:]) |
536 except KeyboardInterrupt: | 540 except KeyboardInterrupt: |
537 print '%s: interrupted' % func_name | 541 print('%s: interrupted' % func_name) |
538 return 1 | 542 return 1 |
539 | 543 |
540 if __name__ == '__main__': | 544 if __name__ == '__main__': |
541 sys.exit(main(sys.argv[1:])) | 545 sys.exit(main(sys.argv[1:])) |
OLD | NEW |