| 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 |