Index: Lib/test/__init__.py =================================================================== --- Lib/test/__init__.py (revision 78033) +++ Lib/test/__init__.py (working copy) @@ -1 +1,12 @@ # Dummy file to make this directory a package. +import os + +# If the package path is not absolute, next imports will fail +# because 'regrtest' changes current working directory to a +# temporary directory before importing test modules. +if globals().get('__path__') is not None: + __path__ = [os.path.abspath(p) for p in __path__] + +# Some tests will fail if __file__ is not absolute +if globals().get('__file__') is not None: + __file__ = os.path.abspath(__file__) Index: Lib/test/regrtest.py =================================================================== --- Lib/test/regrtest.py (revision 78033) +++ Lib/test/regrtest.py (working copy) @@ -160,6 +160,7 @@ import warnings import unittest import imp +import tempfile # Ignore ImportWarnings that only occur in the source tree, @@ -201,6 +202,18 @@ 'xpickle') +# Define a writable temp dir that will be used as cwd while running +# the tests. The name of the dir includes the pid to allow parallel +# testing (see the -j option). +TESTCWD = "{}_python_{}".format(test_support.TESTFN, os.getpid()) +TESTCWD = os.path.abspath(os.path.join(tempfile.gettempdir(), TESTCWD)) + +# Create a context manager that temporary sets TESTCWD as current working +# directory. This context manager is used to run the tests, and provides +# a writable cwd. The original cwd is saved in test_support.SAVEDCWD. +temporary_cwd = test_support.temp_cwd(TESTCWD, quiet=True) + + def usage(code, msg=''): print __doc__ if msg: print msg @@ -328,7 +341,8 @@ elif o == '--slaveargs': args, kwargs = json.loads(a) try: - result = runtest(*args, **kwargs) + with temporary_cwd: + result = runtest(*args, **kwargs) except BaseException, e: result = INTERRUPTED, e.__class__.__name__ print # Force a newline (just in case) @@ -365,8 +379,7 @@ found_garbage = [] if single: - from tempfile import gettempdir - filename = os.path.join(gettempdir(), 'pynexttest') + filename = os.path.join(tempfile.gettempdir(), 'pynexttest') try: fp = open(filename, 'r') next_test = fp.read().strip() @@ -514,7 +527,7 @@ for worker in workers: worker.join() else: - for test in tests: + def runtest_wrapper(test): if not quiet: print test sys.stdout.flush() @@ -534,7 +547,7 @@ except KeyboardInterrupt: # print a newline separate from the ^C print - break + return False except: raise if findleaks: @@ -550,6 +563,11 @@ for module in sys.modules.keys(): if module not in save_modules and module.startswith("test."): test_support.unload(module) + return True + with temporary_cwd: + for test in tests: + if not runtest_wrapper(test): + break # The lists won't be sorted if running with -r good.sort() @@ -595,7 +613,7 @@ if verbose2 and bad: print "Re-running failed tests in verbose mode" - for test in bad: + def runtest_wrapper(test): print "Re-running test %r in verbose mode" % test sys.stdout.flush() try: @@ -605,9 +623,14 @@ except KeyboardInterrupt: # print a newline separate from the ^C print - break + return False except: raise + return True + with temporary_cwd: + for test in bad: + if not runtest_wrapper(test): + break if single: if next_single_test: Index: Lib/test/test_support.py =================================================================== --- Lib/test/test_support.py (revision 78033) +++ Lib/test/test_support.py (working copy) @@ -22,6 +22,7 @@ "get_original_stdout", "unload", "unlink", "rmtree", "forget", "is_resource_enabled", "requires", "find_unused_port", "bind_port", "fcmp", "have_unicode", "is_jython", "TESTFN", "HOST", "FUZZ", + "SAVEDCWD", "temp_cwd", "findfile", "sortdict", "check_syntax_error", "open_urlresource", "check_warnings", "CleanImport", "EnvironmentVarGuard", "captured_output", @@ -379,27 +380,42 @@ 'Unicode filename tests may not be effective' \ % TESTFN_UNICODE_UNENCODEABLE -# Disambiguate TESTFN for parallel testing, while letting it remain a valid -# module name. -TESTFN = "{0}_{1}_tmp".format(TESTFN, os.getpid()) -# Make sure we can write to TESTFN, try in /tmp if we can't -fp = None -try: - fp = open(TESTFN, 'w+') -except IOError: - TMP_TESTFN = os.path.join('/tmp', TESTFN) - try: - fp = open(TMP_TESTFN, 'w+') - TESTFN = TMP_TESTFN - del TMP_TESTFN - except IOError: - print ('WARNING: tests will fail, unable to write to: %s or %s' % - (TESTFN, TMP_TESTFN)) -if fp is not None: - fp.close() - unlink(TESTFN) -del fp +# Save the startup directory. +SAVEDCWD = os.getcwd() + +@contextlib.contextmanager +def temp_cwd(name='tempcwd', quiet=False): + """Create a temporary directory. Set it as current directory. + + Context manager that creates a temporary directory and set it as CWD. + The new CWD is created in the current directory with name 'name'. + The CWD is restored on __exit__ and the directory is purged. + + Optional arguments: + - 'name' basename of the temporary CWD (it should not exist) + - 'quiet' (default: False) If True, it raises a warning instead of + an error if it fails to create or change the CWD. + In that case, the original CWD is used. + """ + saved_dir = os.getcwd() + is_temporary = False + try: + os.mkdir(name) + os.chdir(name) + is_temporary = True + except EnvironmentError: + if not quiet: + raise + warnings.warn('tests may fail, unable to switch to ' + name, + RuntimeWarning, stacklevel=3) + try: + yield os.getcwd() + finally: + os.chdir(saved_dir) + if is_temporary: + rmtree(name) + def findfile(file, here=__file__): """Try to find a file on sys.path and the working directory. If it is not Index: Lib/test/test_subprocess.py =================================================================== --- Lib/test/test_subprocess.py (revision 78033) +++ Lib/test/test_subprocess.py (working copy) @@ -32,12 +32,17 @@ # doesn't crash on some buildbots (Alphas in particular). if hasattr(test_support, "reap_children"): test_support.reap_children() + # This test should be run from the original working directory + self._previous_cwd = os.getcwd() + os.chdir(test_support.SAVEDCWD) def tearDown(self): # Try to minimize the number of children we have so this test # doesn't crash on some buildbots (Alphas in particular). if hasattr(test_support, "reap_children"): test_support.reap_children() + # Restore the working directory + os.chdir(self._previous_cwd) def mkstemp(self): """wrapper for mkstemp, calling mktemp if mkstemp is not available"""