]> granicus.if.org Git - python/commitdiff
Backport relevant part of issue 2021 fix (r60695): Support with statement properly...
authorNick Coghlan <ncoghlan@gmail.com>
Mon, 11 Feb 2008 12:53:42 +0000 (12:53 +0000)
committerNick Coghlan <ncoghlan@gmail.com>
Mon, 11 Feb 2008 12:53:42 +0000 (12:53 +0000)
Lib/tempfile.py
Lib/test/test_tempfile.py
Misc/NEWS

index 2e8cd6d7d52c60510915f07a75c12b1bb51731bf..045e2304243866f3bfa8248ee7f2fadfbaaf276c 100644 (file)
@@ -378,17 +378,25 @@ class _TemporaryFileWrapper:
         self.close_called = False
 
     def __getattr__(self, name):
+        # Attribute lookups are delegated to the underlying file
+        # and cached for non-numeric results
+        # (i.e. methods are cached, closed and friends are not)
         file = self.__dict__['file']
         a = getattr(file, name)
         if type(a) != type(0):
             setattr(self, name, a)
         return a
 
+    # The underlying __enter__ method returns the wrong object
+    # (self.file) so override it to return the wrapper
+    def __enter__(self):
+        self.file.__enter__()
+        return self
+
     # NT provides delete-on-close as a primitive, so we don't need
     # the wrapper to do anything special.  We still use it so that
     # file.name is useful (i.e. not "(fdopen)") with NamedTemporaryFile.
     if _os.name != 'nt':
-
         # Cache the unlinker so we don't get spurious errors at
         # shutdown when the module-level "os" is None'd out.  Note
         # that this must be referenced as self.unlink, because the
@@ -405,6 +413,14 @@ class _TemporaryFileWrapper:
         def __del__(self):
             self.close()
 
+        # Need to trap __exit__ as well to ensure the file gets
+        # deleted when used in a with statement
+        def __exit__(self, exc, value, tb):
+            result = self.file.__exit__(exc, value, tb)
+            self.close()
+            return result
+
+
 def NamedTemporaryFile(mode='w+b', bufsize=-1, suffix="",
                        prefix=template, dir=None):
     """Create and return a temporary file.
index 2047a6364ef72d5c9d7238c533bff1f24ef5222a..cd7ff4e1b547a556cdf56445770cf6fe0379f5e4 100644 (file)
@@ -1,5 +1,5 @@
 # tempfile.py unit tests.
-
+from __future__ import with_statement
 import tempfile
 import os
 import sys
@@ -298,7 +298,7 @@ class test__mkstemp_inner(TC):
         # On Windows a spawn* /path/ with embedded spaces shouldn't be quoted,
         # but an arg with embedded spaces should be decorated with double
         # quotes on each end
-        if sys.platform in ('win32'):
+        if sys.platform in ('win32',):
             decorated = '"%s"' % sys.executable
             tester = '"%s"' % tester
         else:
@@ -601,7 +601,6 @@ class test_NamedTemporaryFile(TC):
 
     def test_multiple_close(self):
         # A NamedTemporaryFile can be closed many times without error
-
         f = tempfile.NamedTemporaryFile()
         f.write('abc\n')
         f.close()
@@ -611,6 +610,16 @@ class test_NamedTemporaryFile(TC):
         except:
             self.failOnException("close")
 
+    def test_context_manager(self):
+        # A NamedTemporaryFile can be used as a context manager
+        with tempfile.NamedTemporaryFile() as f:
+            self.failUnless(os.path.exists(f.name))
+        self.failIf(os.path.exists(f.name))
+        def use_closed():
+            with f:
+                pass
+        self.failUnlessRaises(ValueError, use_closed)
+
     # How to test the mode and bufsize parameters?
 
 test_classes.append(test_NamedTemporaryFile)
index cf73bcae07bf674536932f0d946b1e330ef21cfd..e9473dd749a426eb2f78b6d0a6b1c7cccb8fbf74 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -83,6 +83,9 @@ Core and builtins
 Library
 -------
 
+- #2021: Allow tempfile.NamedTemporaryFile to be used in with statements
+  by correctly supporting the context management protocol.
+
 - Fixed _ctypes.COMError so that it must be called with exactly three
   arguments, instances now have the hresult, text, and details
   instance variables.