From 6770f8a4877eba56dd9978caf197795dbc3d3ffb Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 20 Nov 2016 16:16:06 +0200 Subject: [PATCH] Issue #28666: Now test.support.rmtree is able to remove unwritable or unreadable directories. --- Lib/test/support/__init__.py | 32 +++++++++++++++++++++++++++++++- Lib/test/test_support.py | 33 +++++++++++++++++++++++---------- Misc/NEWS | 3 +++ 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 09095db265a..1d658513acd 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -357,7 +357,37 @@ if sys.platform.startswith("win"): else: _unlink = os.unlink _rmdir = os.rmdir - _rmtree = shutil.rmtree + + def _rmtree(path): + try: + shutil.rmtree(path) + return + except OSError: + pass + + def force_run(path, func, *args): + try: + return func(*args) + except OSError as err: + if verbose >= 2: + print('%s: %s' % (err.__class__.__name__, err)) + print('re-run %s%r' % (func.__name__, args)) + os.chmod(path, stat.S_IRWXU) + return func(*args) + def _rmtree_inner(path): + for name in force_run(path, os.listdir, path): + fullname = os.path.join(path, name) + try: + mode = os.lstat(fullname).st_mode + except OSError: + mode = 0 + if stat.S_ISDIR(mode): + _rmtree_inner(fullname) + force_run(path, os.rmdir, fullname) + else: + force_run(path, os.unlink, fullname) + _rmtree_inner(path) + os.rmdir(path) def unlink(filename): try: diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 2c004174148..30f9f10deb7 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -1,5 +1,6 @@ import importlib import shutil +import stat import sys import os import unittest @@ -9,14 +10,9 @@ import errno from test import support TESTFN = support.TESTFN -TESTDIRN = os.path.basename(tempfile.mkdtemp(dir='.')) class TestSupport(unittest.TestCase): - def setUp(self): - support.unlink(TESTFN) - support.rmtree(TESTDIRN) - tearDown = setUp def test_import_module(self): support.import_module("ftplib") @@ -48,11 +44,28 @@ class TestSupport(unittest.TestCase): support.unlink(TESTFN) def test_rmtree(self): - os.mkdir(TESTDIRN) - os.mkdir(os.path.join(TESTDIRN, TESTDIRN)) - support.rmtree(TESTDIRN) - self.assertFalse(os.path.exists(TESTDIRN)) - support.rmtree(TESTDIRN) + dirpath = support.TESTFN + 'd' + subdirpath = os.path.join(dirpath, 'subdir') + os.mkdir(dirpath) + os.mkdir(subdirpath) + support.rmtree(dirpath) + self.assertFalse(os.path.exists(dirpath)) + with support.swap_attr(support, 'verbose', 0): + support.rmtree(dirpath) + + os.mkdir(dirpath) + os.mkdir(subdirpath) + os.chmod(dirpath, stat.S_IRUSR|stat.S_IXUSR) + with support.swap_attr(support, 'verbose', 0): + support.rmtree(dirpath) + self.assertFalse(os.path.exists(dirpath)) + + os.mkdir(dirpath) + os.mkdir(subdirpath) + os.chmod(dirpath, 0) + with support.swap_attr(support, 'verbose', 0): + support.rmtree(dirpath) + self.assertFalse(os.path.exists(dirpath)) def test_forget(self): mod_filename = TESTFN + '.py' diff --git a/Misc/NEWS b/Misc/NEWS index f8144b341d3..0e5d5f1cd78 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -511,6 +511,9 @@ Documentation Tests ----- +- Issue #28666: Now test.support.rmtree is able to remove unwritable or + unreadable directories. + - Issue #23839: Various caches now are cleared before running every test file. - Issue #28409: regrtest: fix the parser of command line arguments.