Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (C) 2010 Google Inc. All rights reserved. | 1 # Copyright (C) 2010 Google Inc. All rights reserved. |
| 2 # | 2 # |
| 3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
| 4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
| 5 # met: | 5 # met: |
| 6 # | 6 # |
| 7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
| 8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
| 9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
| 10 # copyright notice, this list of conditions and the following disclaimer | 10 # copyright notice, this list of conditions and the following disclaimer |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 | 28 |
| 29 """Wrapper object for the file system / source tree.""" | 29 """Wrapper object for the file system / source tree.""" |
| 30 | 30 |
| 31 import stat | |
| 32 import codecs | 31 import codecs |
| 33 import errno | 32 import errno |
| 34 import exceptions | 33 import exceptions |
| 35 import glob | 34 import glob |
| 36 import hashlib | 35 import hashlib |
| 36 import logging | |
|
qyearsley
2017/05/25 23:32:12
Nit: The general convention in webkitpy is to use
mithro
2017/05/26 04:04:23
Done.
| |
| 37 import os | 37 import os |
| 38 import shutil | 38 import shutil |
| 39 import stat | |
| 39 import sys | 40 import sys |
| 40 import tempfile | 41 import tempfile |
| 41 import time | 42 import time |
| 42 | 43 |
| 43 | 44 |
| 44 class FileSystem(object): | 45 class FileSystem(object): |
| 45 """FileSystem interface for webkitpy. | 46 """FileSystem interface for webkitpy. |
| 46 | 47 |
| 47 Unless otherwise noted, all paths are allowed to be either absolute | 48 Unless otherwise noted, all paths are allowed to be either absolute |
| 48 or relative. | 49 or relative. |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 129 | 130 |
| 130 def isdir(self, path): | 131 def isdir(self, path): |
| 131 return os.path.isdir(path) | 132 return os.path.isdir(path) |
| 132 | 133 |
| 133 def join(self, *comps): | 134 def join(self, *comps): |
| 134 return os.path.join(*comps) | 135 return os.path.join(*comps) |
| 135 | 136 |
| 136 def listdir(self, path): | 137 def listdir(self, path): |
| 137 return os.listdir(path) | 138 return os.listdir(path) |
| 138 | 139 |
| 139 def walk(self, top): | 140 def walk(self, top, topdown=True, onerror=None, followlinks=False): |
| 140 return os.walk(top) | 141 return os.walk(top, topdown=topdown, onerror=onerror, followlinks=follow links) |
| 141 | 142 |
| 142 def mkdtemp(self, **kwargs): | 143 def mkdtemp(self, **kwargs): |
| 143 """Create and return a uniquely named directory. | 144 """Create and return a uniquely named directory. |
| 144 | 145 |
| 145 This is like tempfile.mkdtemp, but if used in a with statement | 146 This is like tempfile.mkdtemp, but if used in a with statement |
| 146 the directory will self-delete at the end of the block (if the | 147 the directory will self-delete at the end of the block (if the |
| 147 directory is empty; non-empty directories raise errors). The | 148 directory is empty; non-empty directories raise errors). The |
| 148 directory can be safely deleted inside the block as well, if so | 149 directory can be safely deleted inside the block as well, if so |
| 149 desired. | 150 desired. |
| 150 | 151 |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 262 while True: | 263 while True: |
| 263 try: | 264 try: |
| 264 osremove(path) | 265 osremove(path) |
| 265 return True | 266 return True |
| 266 except exceptions.WindowsError: | 267 except exceptions.WindowsError: |
| 267 time.sleep(sleep_interval) | 268 time.sleep(sleep_interval) |
| 268 retry_timeout_sec -= sleep_interval | 269 retry_timeout_sec -= sleep_interval |
| 269 if retry_timeout_sec < 0: | 270 if retry_timeout_sec < 0: |
| 270 raise | 271 raise |
| 271 | 272 |
| 272 def rmtree(self, path): | 273 def rmtree(self, path, ignore_errors=True, onerror=None): |
| 273 """Delete the directory rooted at path, whether empty or not.""" | 274 """Delete the directory rooted at path, whether empty or not.""" |
| 274 shutil.rmtree(path, ignore_errors=True) | 275 shutil.rmtree(path, ignore_errors=ignore_errors, onerror=onerror) |
| 276 | |
| 277 def remove_contents(self, dirname): | |
| 278 """Attempt to remove the contents of a directory tree. | |
| 279 Args: | |
| 280 dirname (string): Directory to remove the contents of. | |
| 281 | |
| 282 Returns: | |
| 283 bool: True when the directory is now empty. | |
|
mcgreevy_g
2017/05/26 06:38:01
s/when/if/
mithro
2017/05/29 03:55:58
Done.
| |
| 284 """ | |
| 285 # We try multiple times as on Windows a process which is currently closi ng | |
|
mcgreevy_g
2017/05/26 06:38:01
I'd put a comma after "times" and use "because" in
mithro
2017/05/29 03:55:58
Done.
| |
| 286 # could still have a file open in the directory. | |
| 287 logging.info('Removing contents of %s', dirname) | |
| 288 errors = [] | |
| 289 | |
| 290 def onerror(func, path, exc_info): | |
| 291 errors.append(path) | |
| 292 logging.exception('Failed at %s %s: %r', func, path, exc_info) | |
| 293 | |
| 294 attempts = 0 | |
| 295 while attempts < 5: | |
| 296 del errors[:] | |
| 297 | |
| 298 for name in self.listdir(dirname): | |
| 299 fullname = self.join(dirname, name) | |
| 300 | |
| 301 isdir = True | |
|
mcgreevy_g
2017/05/26 06:38:01
I think it's odd to intialize this to True when yo
mithro
2017/05/29 03:55:57
Acknowledged.
| |
| 302 try: | |
| 303 isdir = self.isdir(fullname) | |
| 304 except os.error: | |
| 305 onerror(self.isdir, fullname, sys.exc_info()) | |
| 306 continue | |
| 307 | |
| 308 if isdir: | |
| 309 try: | |
| 310 self.rmtree(fullname, ignore_errors=False, onerror=onerr or) | |
| 311 except os.error: | |
| 312 onerror(self.rmtree, fullname, sys.exc_info()) | |
| 313 continue | |
| 314 else: | |
| 315 try: | |
| 316 self.remove(fullname) | |
|
mcgreevy_g
2017/05/26 06:38:01
The implementation of remove has a series of retri
mithro
2017/05/29 03:55:57
I added a flag to prevent retry behaviour in the r
mcgreevy_g
2017/05/29 04:17:56
Why three?
mithro
2017/05/29 06:00:30
Arbitrary number.
mcgreevy_g
2017/05/29 07:00:54
I still think setting a deadline would be nicer, b
| |
| 317 except os.error: | |
| 318 onerror(self.remove, fullname, sys.exc_info()) | |
| 319 continue | |
| 320 | |
| 321 if not errors: | |
| 322 break | |
| 323 | |
| 324 attempts += 1 | |
| 325 time.sleep(1) | |
| 326 | |
| 327 # Check the path is gone. | |
| 328 if not self.listdir(dirname): | |
| 329 return True | |
| 330 | |
| 331 logging.warning('Unable to remove %s', dirname) | |
| 332 for dirpath, dirnames, filenames in self.walk(dirname, onerror=onerror, topdown=False): | |
| 333 for fname in filenames: | |
| 334 logging.warning('File %s still in output dir.', self.join(dirpat h, fname)) | |
| 335 for dname in dirnames: | |
| 336 logging.warning('Dir %s still in output dir.', self.join(dirpath , dname)) | |
| 337 | |
| 338 return False | |
| 275 | 339 |
| 276 def copytree(self, source, destination): | 340 def copytree(self, source, destination): |
| 277 shutil.copytree(source, destination) | 341 shutil.copytree(source, destination) |
| 278 | 342 |
| 279 def split(self, path): | 343 def split(self, path): |
| 280 """Return (dirname, basename + '.' + ext)""" | 344 """Return (dirname, basename + '.' + ext)""" |
| 281 return os.path.split(path) | 345 return os.path.split(path) |
| 282 | 346 |
| 283 def splitext(self, path): | 347 def splitext(self, path): |
| 284 """Return (dirname + os.sep + basename, '.' + ext)""" | 348 """Return (dirname + os.sep + basename, '.' + ext)""" |
| 285 return os.path.splitext(path) | 349 return os.path.splitext(path) |
| 286 | 350 |
| 287 def make_executable(self, file_path): | 351 def make_executable(self, file_path): |
| 288 os.chmod(file_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_ IRGRP | stat.S_IXGRP) | 352 os.chmod(file_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_ IRGRP | stat.S_IXGRP) |
| OLD | NEW |