Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(55)

Side by Side Diff: recipe_modules/shutil/resources/rmtree.py

Issue 2146523003: shutil recipe_module: port chromium_utils rmtree implementation. (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/recipes-py@master
Patch Set: nit Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « recipe_modules/shutil/resources/__init__.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright 2016 The LUCI Authors. All rights reserved.
2 # Use of this source code is governed under the Apache License, Version 2.0
3 # that can be found in the LICENSE file.
4
5 # Copy of RemoveDirectory from scripts/common/chromium_utils.
6 # See also http://crbug.com/584783.
7 # The only difference is that now the method will properly report failures.
8 # The recipe can choose to ignore them explicitely with ok_ret='any'.
9
10 import argparse
11 import os
12 import shutil
13 import subprocess
14 import sys
15 import time
16
17
18 def RemoveDirectory(file_path):
19 """Recursively removes a directory, even if it's marked read-only.
20
21 Remove the directory located at *path, if it exists.
22
23 shutil.rmtree() doesn't work on Windows if any of the files or directories
24 are read-only, which svn repositories and some .svn files are. We need to
25 be able to force the files to be writable (i.e., deletable) as we traverse
26 the tree.
27
28 Even with all this, Windows still sometimes fails to delete a file, citing
29 a permission error (maybe something to do with antivirus scans or disk
30 indexing). The best suggestion any of the user forums had was to wait a
31 bit and try again, so we do that too. It's hand-waving, but sometimes it
32 works. :/
33 """
34 if not os.path.exists(file_path):
35 return 0
36
37 if sys.platform == 'win32':
38 # Give up and use cmd.exe's rd command.
39 file_path = os.path.normcase(file_path)
40 for _ in xrange(3):
41 print 'RemoveDirectory running %s' % (' '.join(
42 ['cmd.exe', '/c', 'rd', '/q', '/s', file_path]))
43 if not subprocess.call(['cmd.exe', '/c', 'rd', '/q', '/s', file_path]):
44 return 0
45 print ' Failed'
46 time.sleep(3)
47 return 1
48
49 def RemoveWithRetry_non_win(rmfunc, path):
50 if os.path.islink(path):
51 return os.remove(path)
52 else:
53 return rmfunc(path)
54
55 remove_with_retry = RemoveWithRetry_non_win
56
57 def RmTreeOnError(function, path, excinfo):
58 r"""This works around a problem whereby python 2.x on Windows has no ability
59 to check for symbolic links. os.path.islink always returns False. But
60 shutil.rmtree will fail if invoked on a symbolic link whose target was
61 deleted before the link. E.g., reproduce like this:
62 > mkdir test
63 > mkdir test\1
64 > mklink /D test\current test\1
65 > python -c "import chromium_utils; chromium_utils.RemoveDirectory('test')"
66 To avoid this issue, we pass this error-handling function to rmtree. If
67 we see the exact sort of failure, we ignore it. All other failures we re-
68 raise.
69 """
70
71 exception_type = excinfo[0]
72 exception_value = excinfo[1]
73 # If shutil.rmtree encounters a symbolic link on Windows, os.listdir will
74 # fail with a WindowsError exception with an ENOENT errno (i.e., file not
75 # found). We'll ignore that error. Note that WindowsError is not defined
76 # for non-Windows platforms, so we use OSError (of which it is a subclass)
77 # to avoid lint complaints about an undefined global on non-Windows
78 # platforms.
79 if (function is os.listdir) and issubclass(exception_type, OSError):
80 if exception_value.errno == errno.ENOENT:
81 # File does not exist, and we're trying to delete, so we can ignore the
82 # failure.
83 print 'WARNING: Failed to list %s during rmtree. Ignoring.\n' % path
84 else:
85 raise
86 else:
87 raise
88
89 for root, dirs, files in os.walk(file_path, topdown=False):
90 # For POSIX: making the directory writable guarantees removability.
91 # Windows will ignore the non-read-only bits in the chmod value.
92 os.chmod(root, 0770)
93 for name in files:
94 remove_with_retry(os.remove, os.path.join(root, name))
95 for name in dirs:
96 remove_with_retry(lambda p: shutil.rmtree(p, onerror=RmTreeOnError),
97 os.path.join(root, name))
98
99 remove_with_retry(os.rmdir, file_path)
100 return 0
101
102
103 def main(args):
104 parser = argparse.ArgumentParser()
105 parser.add_argument('path', help='Absolute path to remove')
106 options = parser.parse_args(args)
107 return RemoveDirectory(options.path)
108
109
110 if __name__ == '__main__':
111 sys.exit(main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « recipe_modules/shutil/resources/__init__.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698