Use shutil.rmtree's 'onerror' error handling parameter to catch and handle th... (issue6015012)

391 views
Skip to first unread message

dmic...@google.com

unread,
Jan 4, 2011, 1:11:58 PM1/4/11
to bradn...@chromium.org, nsyl...@chromium.org, dspr...@chromium.org, chromium...@chromium.org
Reviewers: bradnelson, Nicolas Sylvain, dspringer,

Message:
This supersedes the CL I did that attempted to use os.path.islink, which
didn't
work since os.path.islink always returns False in Python 2.x on Windows:
http://codereview.chromium.org/5975009/
http://codereview.chromium.org/6068003/

(That change apparently didn't do anything, since the svn repo got moved
from
trunk/tools/buildbot/
to
trunk/tools/build/)

Description:
Use shutil.rmtree's 'onerror' error handling parameter to catch and handle
the
error that occurs when it's used on a symlink. Ideally, we would just check
os.path.islink as the code does for Linux/Mac, but os.path.islink doesn't
work
properly in python 2.x on Windows.

BUG= http://code.google.com/p/nativeclient/issues/detail?id=1285
TEST=Tested on manually on the bot as follows:
> mkdir test
> mkdir test\1
> mklink /D test\current test\1
> python -c "import chromium_utils;
chromium_utils.RemoveDirectory('test')"


Please review this at http://codereview.chromium.org/6015012/

SVN Base: svn://svn.chromium.org/chrome/trunk/tools/build/

Affected files:
M scripts/common/chromium_utils.py


Index: scripts/common/chromium_utils.py
===================================================================
--- scripts/common/chromium_utils.py (revision 70419)
+++ scripts/common/chromium_utils.py (working copy)
@@ -266,6 +266,36 @@
else:
remove_with_retry = RemoveWithRetry_non_win

+ # 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.
+ def RmTreeOnError(function, path, excinfo):
+ 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.
@@ -273,7 +303,8 @@
for name in files:
remove_with_retry(os.remove, os.path.join(root, name))
for name in dirs:
- remove_with_retry(shutil.rmtree, os.path.join(root, name))
+ remove_with_retry(lambda p: shutil.rmtree(p, onerror=RmTreeOnError),
+ os.path.join(root, name))

remove_with_retry(os.rmdir, file_path)

nsyl...@google.com

unread,
Jan 4, 2011, 2:06:19 PM1/4/11
to dmic...@google.com, bradn...@chromium.org, nsyl...@chromium.org, dspr...@chromium.org, chromium...@chromium.org

bradn...@google.com

unread,
Jan 4, 2011, 2:06:28 PM1/4/11
to dmic...@google.com, bradn...@chromium.org, nsyl...@chromium.org, dspr...@chromium.org, chromium...@chromium.org
Reply all
Reply to author
Forward
0 new messages