Index: Lib/test/regrtest.py =================================================================== --- Lib/test/regrtest.py (revision 77580) +++ Lib/test/regrtest.py (working copy) @@ -199,6 +199,10 @@ 'decimal', 'compiler', 'subprocess', 'urlfetch', 'gui', 'xpickle') +# Context manager which creates a temporary directory, unique and writable. +# It changes the current directory to the temporary directory. +temporary_cwd = test_support.temp_cwd(test_support.TESTCWD) + def usage(code, msg=''): print __doc__ @@ -327,7 +331,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) @@ -513,42 +518,43 @@ for worker in workers: worker.join() else: - for test in tests: - if not quiet: - print test - sys.stdout.flush() - if trace: - # If we're tracing code coverage, then we don't exit with status - # if on a false return value from main. - tracer.runctx('runtest(test, verbose, quiet, testdir)', - globals=globals(), locals=vars()) - else: - try: - result = runtest(test, verbose, quiet, - testdir, huntrleaks) - accumulate_result(test, result) - if verbose3 and result[0] == FAILED: - print "Re-running test %r in verbose mode" % test - runtest(test, True, quiet, testdir, huntrleaks) - except KeyboardInterrupt: - # print a newline separate from the ^C - print - break - except: - raise - if findleaks: - gc.collect() - if gc.garbage: - print "Warning: test created", len(gc.garbage), - print "uncollectable object(s)." - # move the uncollectable objects somewhere so we don't see - # them again - found_garbage.extend(gc.garbage) - del gc.garbage[:] - # Unload the newly imported modules (best effort finalization) - for module in sys.modules.keys(): - if module not in save_modules and module.startswith("test."): - test_support.unload(module) + with temporary_cwd: + for test in tests: + if not quiet: + print test + sys.stdout.flush() + if trace: + # If we're tracing code coverage, then we don't exit with status + # if on a false return value from main. + tracer.runctx('runtest(test, verbose, quiet, testdir)', + globals=globals(), locals=vars()) + else: + try: + result = runtest(test, verbose, quiet, + testdir, huntrleaks) + accumulate_result(test, result) + if verbose3 and result[0] == FAILED: + print "Re-running test %r in verbose mode" % test + runtest(test, True, quiet, testdir, huntrleaks) + except KeyboardInterrupt: + # print a newline separate from the ^C + print + break + except: + raise + if findleaks: + gc.collect() + if gc.garbage: + print "Warning: test created", len(gc.garbage), + print "uncollectable object(s)." + # move the uncollectable objects somewhere so we don't see + # them again + found_garbage.extend(gc.garbage) + del gc.garbage[:] + # Unload the newly imported modules (best effort finalization) + for module in sys.modules.keys(): + if module not in save_modules and module.startswith("test."): + test_support.unload(module) # The lists won't be sorted if running with -r good.sort() @@ -594,19 +600,20 @@ if verbose2 and bad: print "Re-running failed tests in verbose mode" - for test in bad: - print "Re-running test %r in verbose mode" % test - sys.stdout.flush() - try: - test_support.verbose = True - ok = runtest(test, True, quiet, testdir, - huntrleaks) - except KeyboardInterrupt: - # print a newline separate from the ^C - print - break - except: - raise + with temporary_cwd: + for test in bad: + print "Re-running test %r in verbose mode" % test + sys.stdout.flush() + try: + test_support.verbose = True + ok = runtest(test, True, quiet, testdir, + huntrleaks) + except KeyboardInterrupt: + # print a newline separate from the ^C + print + break + except: + raise if single: if next_single_test: Index: Lib/test/test_subprocess.py =================================================================== --- Lib/test/test_subprocess.py (revision 77580) +++ 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.save_cwd = os.getcwd() + os.chdir(test_support.SAVECWD) 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.save_cwd) def mkstemp(self): """wrapper for mkstemp, calling mktemp if mkstemp is not available""" Index: Lib/test/test_support.py =================================================================== --- Lib/test/test_support.py (revision 77580) +++ Lib/test/test_support.py (working copy) @@ -24,6 +24,7 @@ "fcmp", "have_unicode", "is_jython", "TESTFN", "HOST", "FUZZ", "findfile", "verify", "vereq", "sortdict", "check_syntax_error", "open_urlresource", "check_warnings", "CleanImport", + "SAVECWD", "TESTCWD", "temp_cwd", "EnvironmentVarGuard", "captured_output", "captured_stdout", "TransientResource", "transient_internet", "run_with_locale", "set_memlimit", "bigmemtest", "bigaddrspacetest", @@ -379,28 +380,44 @@ '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()) +# Save the startup directory, for future reference. +SAVECWD = os.getcwd() -# Make sure we can write to TESTFN, try in /tmp if we can't +# Disambiguate TESTCWD for parallel testing +TESTCWD = "{0}_{1}_tmp".format(TESTFN, os.getpid()) + +# Make sure we can write to TESTCWD, try in /tmp if we can't fp = None try: - fp = open(TESTFN, 'w+') + fp = open(TESTCWD, 'w+') except IOError: - TMP_TESTFN = os.path.join('/tmp', TESTFN) + TMP_TESTCWD = os.path.join('/tmp', TESTCWD) try: - fp = open(TMP_TESTFN, 'w+') - TESTFN = TMP_TESTFN - del TMP_TESTFN + fp = open(TMP_TESTCWD, 'w+') + TESTCWD = TMP_TESTCWD + del TMP_TESTCWD except IOError: print ('WARNING: tests will fail, unable to write to: %s or %s' % - (TESTFN, TMP_TESTFN)) + (TESTCWD, TMP_TESTCWD)) if fp is not None: fp.close() - unlink(TESTFN) + unlink(TESTCWD) del fp +TESTCWD = os.path.abspath(TESTCWD) + +@contextlib.contextmanager +def temp_cwd(name='tempcwd'): + """Create a temporary directory. Set it as current directory.""" + saved_dir = os.getcwd() + os.mkdir(name) + try: + os.chdir(name) + yield name + finally: + os.chdir(saved_dir) + rmtree(name) + def findfile(file, here=__file__): """Try to find a file on sys.path and the working directory. If it is not found the argument passed to the function is returned (this does not