From 6f5c5cb75b2fddf31cbb311d2a5508c54f6e21f6 Mon Sep 17 00:00:00 2001 From: Brian Curtin Date: Mon, 13 Aug 2012 17:05:57 -0500 Subject: [PATCH] Fix #15496. Add directory removal helpers to make Windows tests more reliable. Patch by Jeremy Kloth --- Lib/test/support.py | 68 +++++++++++++++++++++++++++++++++++++++++++-- Misc/NEWS | 3 ++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/Lib/test/support.py b/Lib/test/support.py index 162805d3982..d88562e907d 100644 --- a/Lib/test/support.py +++ b/Lib/test/support.py @@ -200,17 +200,81 @@ def unload(name): except KeyError: pass +if sys.platform.startswith("win"): + def _waitfor(func, pathname, waitall=False): + # Peform the operation + func(pathname) + # Now setup the wait loop + if waitall: + dirname = pathname + else: + dirname, name = os.path.split(pathname) + dirname = dirname or '.' + # Check for `pathname` to be removed from the filesystem. + # The exponential backoff of the timeout amounts to a total + # of ~1 second after which the deletion is probably an error + # anyway. + # Testing on a i7@4.3GHz shows that usually only 1 iteration is + # required when contention occurs. + timeout = 0.001 + while timeout < 1.0: + # Note we are only testing for the existance of the file(s) in + # the contents of the directory regardless of any security or + # access rights. If we have made it this far, we have sufficient + # permissions to do that much using Python's equivalent of the + # Windows API FindFirstFile. + # Other Windows APIs can fail or give incorrect results when + # dealing with files that are pending deletion. + L = os.listdir(dirname) + if not (L if waitall else name in L): + return + # Increase the timeout and try again + time.sleep(timeout) + timeout *= 2 + warnings.warn('tests may fail, delete still pending for ' + pathname, + RuntimeWarning, stacklevel=4) + + def _unlink(filename): + _waitfor(os.unlink, filename) + + def _rmdir(dirname): + _waitfor(os.rmdir, dirname) + + def _rmtree(path): + def _rmtree_inner(path): + for name in os.listdir(path): + fullname = os.path.join(path, name) + if os.path.isdir(fullname): + _waitfor(_rmtree_inner, fullname, waitall=True) + os.rmdir(fullname) + else: + os.unlink(fullname) + _waitfor(_rmtree_inner, path, waitall=True) + _waitfor(os.rmdir, path) +else: + _unlink = os.unlink + _rmdir = os.rmdir + _rmtree = shutil.rmtree + def unlink(filename): try: - os.unlink(filename) + _unlink(filename) except OSError as error: # The filename need not exist. if error.errno not in (errno.ENOENT, errno.ENOTDIR): raise +def rmdir(dirname): + try: + _rmdir(dirname) + except OSError as error: + # The directory need not exist. + if error.errno != errno.ENOENT: + raise + def rmtree(path): try: - shutil.rmtree(path) + _rmtree(path) except OSError as error: # Unix returns ENOENT, Windows returns ESRCH. if error.errno not in (errno.ENOENT, errno.ESRCH): diff --git a/Misc/NEWS b/Misc/NEWS index e0559c55c82..30e6b967864 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -413,6 +413,9 @@ Extension Modules Tests ----- +- Issue #15496: Add directory removal helpers for tests on Windows. + Patch by Jeremy Kloth. + - Issue #15467: Move helpers for __sizeof__ tests into test_support. Patch by Serhiy Storchaka.