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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « recipe_modules/shutil/resources/__init__.py ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: recipe_modules/shutil/resources/rmtree.py
diff --git a/recipe_modules/shutil/resources/rmtree.py b/recipe_modules/shutil/resources/rmtree.py
new file mode 100644
index 0000000000000000000000000000000000000000..25ef212b6d33ea6dfaae1f537cad6f49910bb040
--- /dev/null
+++ b/recipe_modules/shutil/resources/rmtree.py
@@ -0,0 +1,111 @@
+# Copyright 2016 The LUCI Authors. All rights reserved.
+# Use of this source code is governed under the Apache License, Version 2.0
+# that can be found in the LICENSE file.
+
+# Copy of RemoveDirectory from scripts/common/chromium_utils.
+# See also http://crbug.com/584783.
+# The only difference is that now the method will properly report failures.
+# The recipe can choose to ignore them explicitely with ok_ret='any'.
+
+import argparse
+import os
+import shutil
+import subprocess
+import sys
+import time
+
+
+def RemoveDirectory(file_path):
+ """Recursively removes a directory, even if it's marked read-only.
+
+ Remove the directory located at *path, if it exists.
+
+ shutil.rmtree() doesn't work on Windows if any of the files or directories
+ are read-only, which svn repositories and some .svn files are. We need to
+ be able to force the files to be writable (i.e., deletable) as we traverse
+ the tree.
+
+ Even with all this, Windows still sometimes fails to delete a file, citing
+ a permission error (maybe something to do with antivirus scans or disk
+ indexing). The best suggestion any of the user forums had was to wait a
+ bit and try again, so we do that too. It's hand-waving, but sometimes it
+ works. :/
+ """
+ if not os.path.exists(file_path):
+ return 0
+
+ if sys.platform == 'win32':
+ # Give up and use cmd.exe's rd command.
+ file_path = os.path.normcase(file_path)
+ for _ in xrange(3):
+ print 'RemoveDirectory running %s' % (' '.join(
+ ['cmd.exe', '/c', 'rd', '/q', '/s', file_path]))
+ if not subprocess.call(['cmd.exe', '/c', 'rd', '/q', '/s', file_path]):
+ return 0
+ print ' Failed'
+ time.sleep(3)
+ return 1
+
+ def RemoveWithRetry_non_win(rmfunc, path):
+ if os.path.islink(path):
+ return os.remove(path)
+ else:
+ return rmfunc(path)
+
+ remove_with_retry = RemoveWithRetry_non_win
+
+ def RmTreeOnError(function, path, excinfo):
+ r"""This works around a problem whereby python 2.x on Windows has no ability
+ to check for symbolic links. os.path.islink always returns False. But
+ shutil.rmtree will fail if invoked on a symbolic link whose target was
+ deleted before the link. E.g., reproduce like this:
+ > mkdir test
+ > mkdir test\1
+ > mklink /D test\current test\1
+ > python -c "import chromium_utils; chromium_utils.RemoveDirectory('test')"
+ To avoid this issue, we pass this error-handling function to rmtree. If
+ we see the exact sort of failure, we ignore it. All other failures we re-
+ raise.
+ """
+
+ exception_type = excinfo[0]
+ exception_value = excinfo[1]
+ # If shutil.rmtree encounters a symbolic link on Windows, os.listdir will
+ # fail with a WindowsError exception with an ENOENT errno (i.e., file not
+ # found). We'll ignore that error. Note that WindowsError is not defined
+ # for non-Windows platforms, so we use OSError (of which it is a subclass)
+ # to avoid lint complaints about an undefined global on non-Windows
+ # platforms.
+ if (function is os.listdir) and issubclass(exception_type, OSError):
+ if exception_value.errno == errno.ENOENT:
+ # File does not exist, and we're trying to delete, so we can ignore the
+ # failure.
+ print 'WARNING: Failed to list %s during rmtree. Ignoring.\n' % path
+ else:
+ raise
+ else:
+ raise
+
+ for root, dirs, files in os.walk(file_path, topdown=False):
+ # For POSIX: making the directory writable guarantees removability.
+ # Windows will ignore the non-read-only bits in the chmod value.
+ os.chmod(root, 0770)
+ for name in files:
+ remove_with_retry(os.remove, os.path.join(root, name))
+ for name in dirs:
+ remove_with_retry(lambda p: shutil.rmtree(p, onerror=RmTreeOnError),
+ os.path.join(root, name))
+
+ remove_with_retry(os.rmdir, file_path)
+ return 0
+
+
+def main(args):
+ parser = argparse.ArgumentParser()
+ parser.add_argument('path', help='Absolute path to remove')
+ options = parser.parse_args(args)
+ return RemoveDirectory(options.path)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
« 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