[Python-checkins] cpython (3.3): Close #15415: Factor out temp dir helpers to test.support

nick.coghlan python-checkins at python.org
Sun Jul 28 14:25:49 CEST 2013


http://hg.python.org/cpython/rev/4f0034477ba9
changeset:   84887:4f0034477ba9
branch:      3.3
parent:      84885:0b7ed24f7d33
user:        Nick Coghlan <ncoghlan at gmail.com>
date:        Sun Jul 28 22:11:50 2013 +1000
summary:
  Close #15415: Factor out temp dir helpers to test.support

Patch by Chris Jerdonek

files:
  Doc/library/test.rst             |   38 ++++-
  Lib/test/script_helper.py        |   12 +-
  Lib/test/support/__init__.py     |   80 ++++++++--
  Lib/test/test_cmd_line_script.py |    8 +-
  Lib/test/test_shutil.py          |   10 +-
  Lib/test/test_support.py         |  130 +++++++++++++++++-
  Misc/NEWS                        |    3 +
  7 files changed, 225 insertions(+), 56 deletions(-)


diff --git a/Doc/library/test.rst b/Doc/library/test.rst
--- a/Doc/library/test.rst
+++ b/Doc/library/test.rst
@@ -387,18 +387,40 @@
       self.assertEqual(captured, "hello")
 
 
-.. function:: temp_cwd(name='tempcwd', quiet=False, path=None)
+.. function:: temp_dir(path=None, quiet=False)
+
+   A context manager that creates a temporary directory at *path* and
+   yields the directory.
+
+   If *path* is None, the temporary directory is created using
+   :func:`tempfile.mkdtemp`.  If *quiet* is ``False``, the context manager
+   raises an exception on error.  Otherwise, if *path* is specified and
+   cannot be created, only a warning is issued.
+
+
+.. function:: change_cwd(path, quiet=False)
 
    A context manager that temporarily changes the current working
-   directory (CWD).
+   directory to *path* and yields the directory.
 
-   An existing path may be provided as *path*, in which case this function
-   makes no changes to the file system.
+   If *quiet* is ``False``, the context manager raises an exception
+   on error.  Otherwise, it issues only a warning and keeps the current
+   working directory the same.
 
-   Otherwise, the new CWD is created in the current directory and it's named
-   *name*.  If *quiet* is ``False`` and it's not possible to create or
-   change the CWD, an error is raised.  If it's ``True``, only a warning
-   is raised and the original CWD is used.
+
+.. function:: temp_cwd(name='tempcwd', quiet=False)
+
+   A context manager that temporarily creates a new directory and
+   changes the current working directory (CWD).
+
+   The context manager creates a temporary directory in the current
+   directory with name *name* before temporarily changing the current
+   working directory.  If *name* is None, the temporary directory is
+   created using :func:`tempfile.mkdtemp`.
+
+   If *quiet* is ``False`` and it is not possible to create or change
+   the CWD, an error is raised.  Otherwise, only a warning is raised
+   and the original CWD is used.
 
 
 .. function:: temp_umask(umask)
diff --git a/Lib/test/script_helper.py b/Lib/test/script_helper.py
--- a/Lib/test/script_helper.py
+++ b/Lib/test/script_helper.py
@@ -13,7 +13,7 @@
 import zipfile
 
 from imp import source_from_cache
-from test.support import make_legacy_pyc, strip_python_stderr
+from test.support import make_legacy_pyc, strip_python_stderr, temp_dir
 
 # Executing the interpreter in a subprocess
 def _assert_python(expected_success, *args, **env_vars):
@@ -77,16 +77,6 @@
     subprocess._cleanup()
     return data
 
-# Script creation utilities
- at contextlib.contextmanager
-def temp_dir():
-    dirname = tempfile.mkdtemp()
-    dirname = os.path.realpath(dirname)
-    try:
-        yield dirname
-    finally:
-        shutil.rmtree(dirname)
-
 def make_script(script_dir, script_basename, source):
     script_filename = script_basename+os.extsep+'py'
     script_name = os.path.join(script_dir, script_filename)
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -738,45 +738,85 @@
 SAVEDCWD = os.getcwd()
 
 @contextlib.contextmanager
-def temp_cwd(name='tempcwd', quiet=False, path=None):
+def temp_dir(path=None, quiet=False):
+    """Return a context manager that creates a temporary directory.
+
+    Arguments:
+
+      path: the directory to create temporarily.  If omitted or None,
+        defaults to creating a temporary directory using tempfile.mkdtemp.
+
+      quiet: if False (the default), the context manager raises an exception
+        on error.  Otherwise, if the path is specified and cannot be
+        created, only a warning is issued.
+
     """
-    Context manager that temporarily changes the CWD.
-
-    An existing path may be provided as *path*, in which case this
-    function makes no changes to the file system.
-
-    Otherwise, the new CWD is created in the current directory and it's
-    named *name*. If *quiet* is False (default) and it's not possible to
-    create or change the CWD, an error is raised.  If it's True, only a
-    warning is raised and the original CWD is used.
-    """
-    saved_dir = os.getcwd()
-    is_temporary = False
+    dir_created = False
     if path is None:
-        path = name
+        path = tempfile.mkdtemp()
+        dir_created = True
+        path = os.path.realpath(path)
+    else:
         try:
-            os.mkdir(name)
-            is_temporary = True
+            os.mkdir(path)
+            dir_created = True
         except OSError:
             if not quiet:
                 raise
-            warnings.warn('tests may fail, unable to create temp CWD ' + name,
+            warnings.warn('tests may fail, unable to create temp dir: ' + path,
                           RuntimeWarning, stacklevel=3)
     try:
+        yield path
+    finally:
+        if dir_created:
+            shutil.rmtree(path)
+
+ at contextlib.contextmanager
+def change_cwd(path, quiet=False):
+    """Return a context manager that changes the current working directory.
+
+    Arguments:
+
+      path: the directory to use as the temporary current working directory.
+
+      quiet: if False (the default), the context manager raises an exception
+        on error.  Otherwise, it issues only a warning and keeps the current
+        working directory the same.
+
+    """
+    saved_dir = os.getcwd()
+    try:
         os.chdir(path)
     except OSError:
         if not quiet:
             raise
-        warnings.warn('tests may fail, unable to change the CWD to ' + path,
+        warnings.warn('tests may fail, unable to change CWD to: ' + path,
                       RuntimeWarning, stacklevel=3)
     try:
         yield os.getcwd()
     finally:
         os.chdir(saved_dir)
-        if is_temporary:
-            rmtree(name)
 
 
+ at contextlib.contextmanager
+def temp_cwd(name='tempcwd', quiet=False):
+    """
+    Context manager that temporarily creates and changes the CWD.
+
+    The function temporarily changes the current working directory
+    after creating a temporary directory in the current directory with
+    name *name*.  If *name* is None, the temporary directory is
+    created using tempfile.mkdtemp.
+
+    If *quiet* is False (default) and it is not possible to
+    create or change the CWD, an error is raised.  If *quiet* is True,
+    only a warning is raised and the original CWD is used.
+
+    """
+    with temp_dir(path=name, quiet=quiet) as temp_path:
+        with change_cwd(temp_path, quiet=quiet) as cwd_dir:
+            yield cwd_dir
+
 if hasattr(os, "umask"):
     @contextlib.contextmanager
     def temp_umask(umask):
diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py
--- a/Lib/test/test_cmd_line_script.py
+++ b/Lib/test/test_cmd_line_script.py
@@ -290,7 +290,7 @@
         # Make sure package __init__ modules see "-m" in sys.argv0 while
         # searching for the module to execute
         with temp_dir() as script_dir:
-            with support.temp_cwd(path=script_dir):
+            with support.change_cwd(path=script_dir):
                 pkg_dir = os.path.join(script_dir, 'test_pkg')
                 make_pkg(pkg_dir, "import sys; print('init_argv0==%r' % sys.argv[0])")
                 script_name = _make_test_script(pkg_dir, 'script')
@@ -307,7 +307,7 @@
         # Make sure a "-c" file in the current directory
         # does not alter the value of sys.path[0]
         with temp_dir() as script_dir:
-            with support.temp_cwd(path=script_dir):
+            with support.change_cwd(path=script_dir):
                 with open("-c", "w") as f:
                     f.write("data")
                     rc, out, err = assert_python_ok('-c',
@@ -322,7 +322,7 @@
         # does not alter the value of sys.path[0]
         with temp_dir() as script_dir:
             script_name = _make_test_script(script_dir, 'other')
-            with support.temp_cwd(path=script_dir):
+            with support.change_cwd(path=script_dir):
                 with open("-m", "w") as f:
                     f.write("data")
                     rc, out, err = assert_python_ok('-m', 'other', *example_args)
@@ -335,7 +335,7 @@
         # and results in an error that the return code to the
         # shell is '1'
         with temp_dir() as script_dir:
-            with support.temp_cwd(path=script_dir):
+            with support.change_cwd(path=script_dir):
                 pkg_dir = os.path.join(script_dir, 'test_pkg')
                 make_pkg(pkg_dir)
                 script_name = _make_test_script(pkg_dir, 'other',
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -1307,18 +1307,18 @@
         # that exists, it should be returned.
         base_dir, tail_dir = os.path.split(self.dir)
         relpath = os.path.join(tail_dir, self.file)
-        with support.temp_cwd(path=base_dir):
+        with support.change_cwd(path=base_dir):
             rv = shutil.which(relpath, path=self.temp_dir)
             self.assertEqual(rv, relpath)
         # But it shouldn't be searched in PATH directories (issue #16957).
-        with support.temp_cwd(path=self.dir):
+        with support.change_cwd(path=self.dir):
             rv = shutil.which(relpath, path=base_dir)
             self.assertIsNone(rv)
 
     def test_cwd(self):
         # Issue #16957
         base_dir = os.path.dirname(self.dir)
-        with support.temp_cwd(path=self.dir):
+        with support.change_cwd(path=self.dir):
             rv = shutil.which(self.file, path=base_dir)
             if sys.platform == "win32":
                 # Windows: current directory implicitly on PATH
@@ -1339,7 +1339,7 @@
 
     def test_relative_path(self):
         base_dir, tail_dir = os.path.split(self.dir)
-        with support.temp_cwd(path=base_dir):
+        with support.change_cwd(path=base_dir):
             rv = shutil.which(self.file, path=tail_dir)
             self.assertEqual(rv, os.path.join(tail_dir, self.file))
 
@@ -1364,7 +1364,7 @@
 
     def test_empty_path(self):
         base_dir = os.path.dirname(self.dir)
-        with support.temp_cwd(path=self.dir), \
+        with support.change_cwd(path=self.dir), \
              support.EnvironmentVarGuard() as env:
             env['PATH'] = self.dir
             rv = shutil.which(self.file, path='')
diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py
--- a/Lib/test/test_support.py
+++ b/Lib/test/test_support.py
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 
 import importlib
+import shutil
 import sys
 import os
 import unittest
@@ -88,6 +89,118 @@
         s.listen(1)
         s.close()
 
+    # Tests for temp_dir()
+
+    def test_temp_dir(self):
+        """Test that temp_dir() creates and destroys its directory."""
+        parent_dir = tempfile.mkdtemp()
+        parent_dir = os.path.realpath(parent_dir)
+
+        try:
+            path = os.path.join(parent_dir, 'temp')
+            self.assertFalse(os.path.isdir(path))
+            with support.temp_dir(path) as temp_path:
+                self.assertEqual(temp_path, path)
+                self.assertTrue(os.path.isdir(path))
+            self.assertFalse(os.path.isdir(path))
+        finally:
+            shutil.rmtree(parent_dir)
+
+    def test_temp_dir__path_none(self):
+        """Test passing no path."""
+        with support.temp_dir() as temp_path:
+            self.assertTrue(os.path.isdir(temp_path))
+        self.assertFalse(os.path.isdir(temp_path))
+
+    def test_temp_dir__existing_dir__quiet_default(self):
+        """Test passing a directory that already exists."""
+        def call_temp_dir(path):
+            with support.temp_dir(path) as temp_path:
+                raise Exception("should not get here")
+
+        path = tempfile.mkdtemp()
+        path = os.path.realpath(path)
+        try:
+            self.assertTrue(os.path.isdir(path))
+            self.assertRaises(FileExistsError, call_temp_dir, path)
+            # Make sure temp_dir did not delete the original directory.
+            self.assertTrue(os.path.isdir(path))
+        finally:
+            shutil.rmtree(path)
+
+    def test_temp_dir__existing_dir__quiet_true(self):
+        """Test passing a directory that already exists with quiet=True."""
+        path = tempfile.mkdtemp()
+        path = os.path.realpath(path)
+
+        try:
+            with support.check_warnings() as recorder:
+                with support.temp_dir(path, quiet=True) as temp_path:
+                    self.assertEqual(path, temp_path)
+                warnings = [str(w.message) for w in recorder.warnings]
+            # Make sure temp_dir did not delete the original directory.
+            self.assertTrue(os.path.isdir(path))
+        finally:
+            shutil.rmtree(path)
+
+        expected = ['tests may fail, unable to create temp dir: ' + path]
+        self.assertEqual(warnings, expected)
+
+    # Tests for change_cwd()
+
+    def test_change_cwd(self):
+        original_cwd = os.getcwd()
+
+        with support.temp_dir() as temp_path:
+            with support.change_cwd(temp_path) as new_cwd:
+                self.assertEqual(new_cwd, temp_path)
+                self.assertEqual(os.getcwd(), new_cwd)
+
+        self.assertEqual(os.getcwd(), original_cwd)
+
+    def test_change_cwd__non_existent_dir(self):
+        """Test passing a non-existent directory."""
+        original_cwd = os.getcwd()
+
+        def call_change_cwd(path):
+            with support.change_cwd(path) as new_cwd:
+                raise Exception("should not get here")
+
+        with support.temp_dir() as parent_dir:
+            non_existent_dir = os.path.join(parent_dir, 'does_not_exist')
+            self.assertRaises(FileNotFoundError, call_change_cwd,
+                              non_existent_dir)
+
+        self.assertEqual(os.getcwd(), original_cwd)
+
+    def test_change_cwd__non_existent_dir__quiet_true(self):
+        """Test passing a non-existent directory with quiet=True."""
+        original_cwd = os.getcwd()
+
+        with support.temp_dir() as parent_dir:
+            bad_dir = os.path.join(parent_dir, 'does_not_exist')
+            with support.check_warnings() as recorder:
+                with support.change_cwd(bad_dir, quiet=True) as new_cwd:
+                    self.assertEqual(new_cwd, original_cwd)
+                    self.assertEqual(os.getcwd(), new_cwd)
+                warnings = [str(w.message) for w in recorder.warnings]
+
+        expected = ['tests may fail, unable to change CWD to: ' + bad_dir]
+        self.assertEqual(warnings, expected)
+
+    # Tests for change_cwd()
+
+    def test_change_cwd__chdir_warning(self):
+        """Check the warning message when os.chdir() fails."""
+        path = TESTFN + '_does_not_exist'
+        with support.check_warnings() as recorder:
+            with support.change_cwd(path=path, quiet=True):
+                pass
+            messages = [str(w.message) for w in recorder.warnings]
+        self.assertEqual(messages, ['tests may fail, unable to change CWD to: ' + path])
+
+    # Tests for temp_cwd()
+
     def test_temp_cwd(self):
         here = os.getcwd()
         with support.temp_cwd(name=TESTFN):
@@ -95,14 +208,15 @@
         self.assertFalse(os.path.exists(TESTFN))
         self.assertTrue(os.path.basename(os.getcwd()), here)
 
-    def test_temp_cwd__chdir_warning(self):
-        """Check the warning message when os.chdir() fails."""
-        path = TESTFN + '_does_not_exist'
-        with support.check_warnings() as recorder:
-            with support.temp_cwd(path=path, quiet=True):
-                pass
-            messages = [str(w.message) for w in recorder.warnings]
-        self.assertEqual(messages, ['tests may fail, unable to change the CWD to ' + path])
+
+    def test_temp_cwd__name_none(self):
+        """Test passing None to temp_cwd()."""
+        original_cwd = os.getcwd()
+        with support.temp_cwd(name=None) as new_cwd:
+            self.assertNotEqual(new_cwd, original_cwd)
+            self.assertTrue(os.path.isdir(new_cwd))
+            self.assertEqual(os.getcwd(), new_cwd)
+        self.assertEqual(os.getcwd(), original_cwd)
 
     def test_sortdict(self):
         self.assertEqual(support.sortdict({3:3, 2:2, 1:1}), "{1: 1, 2: 2, 3: 3}")
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -222,6 +222,9 @@
 Tests
 -----
 
+- Issue #15415: Add new temp_dir() and change_cwd() context managers to
+  test.support, and refactor temp_cwd() to use them.  Patch by Chris Jerdonek.
+
 - Issue #15494: test.support is now a package rather than a module (Initial
   patch by Indra Talip)
 

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list