]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
#1286: allow using fileinput.FileInput as context manager.
authorGeorg Brandl <georg@python.org>
Sat, 31 Jul 2010 20:08:15 +0000 (20:08 +0000)
committerGeorg Brandl <georg@python.org>
Sat, 31 Jul 2010 20:08:15 +0000 (20:08 +0000)
Doc/library/fileinput.rst
Lib/fileinput.py
Lib/test/test_fileinput.py
Misc/NEWS

index d98a198f5c7d2ac67077a65869a1eaad5207df96..eac324d31838819f79c9537736a761f4bb5c7e3e 100644 (file)
@@ -24,7 +24,7 @@ as the first argument to :func:`.input`.  A single file name is also allowed.
 
 All files are opened in text mode by default, but you can override this by
 specifying the *mode* parameter in the call to :func:`.input` or
-:class:`FileInput()`.  If an I/O error occurs during opening or reading a file,
+:class:`FileInput`.  If an I/O error occurs during opening or reading a file,
 :exc:`IOError` is raised.
 
 If ``sys.stdin`` is used more than once, the second and further use will return
@@ -54,6 +54,16 @@ The following function is the primary interface of this module:
    during iteration.  The parameters to this function will be passed along to the
    constructor of the :class:`FileInput` class.
 
+   The :class:`FileInput` instance can be used as a context manager in the
+   :keyword:`with` statement.  In this example, *input* is closed after the
+   :keyword:`with` statement is exited, even if an exception occurs::
+
+      with fileinput.input(files=('spam.txt', 'eggs.txt')) as input:
+          process(input)
+
+   .. versionchanged:: 3.2
+      Can be used as a context manager.
+
 
 The following functions use the global state created by :func:`fileinput.input`;
 if there is no active state, :exc:`RuntimeError` is raised.
@@ -132,13 +142,23 @@ available for subclassing as well:
    *filename* and *mode*, and returns an accordingly opened file-like object. You
    cannot use *inplace* and *openhook* together.
 
+   A :class:`FileInput` instance can be used as a context manager in the
+   :keyword:`with` statement.  In this example, *input* is closed after the
+   :keyword:`with` statement is exited, even if an exception occurs::
+
+      with FileInput(files=('spam.txt', 'eggs.txt')) as input:
+          process(input)
+
+   .. versionchanged:: 3.2
+      Can be used as a context manager.
+
 
-**Optional in-place filtering:** if the keyword argument ``inplace=1`` is passed
-to :func:`fileinput.input` or to the :class:`FileInput` constructor, the file is
-moved to a backup file and standard output is directed to the input file (if a
-file of the same name as the backup file already exists, it will be replaced
-silently).  This makes it possible to write a filter that rewrites its input
-file in place.  If the *backup* parameter is given (typically as
+**Optional in-place filtering:** if the keyword argument ``inplace=True`` is
+passed to :func:`fileinput.input` or to the :class:`FileInput` constructor, the
+file is moved to a backup file and standard output is directed to the input file
+(if a file of the same name as the backup file already exists, it will be
+replaced silently).  This makes it possible to write a filter that rewrites its
+input file in place.  If the *backup* parameter is given (typically as
 ``backup='.<some extension>'``), it specifies the extension for the backup file,
 and the backup file remains around; by default, the extension is ``'.bak'`` and
 it is deleted when the output file is closed.  In-place filtering is disabled
index 90a600bf11801c7ad2e1e8b58b31827538833486..a25a021eb31d28154b709d6a489cecf12c8937b0 100644 (file)
@@ -238,6 +238,12 @@ class FileInput:
         self.nextfile()
         self._files = ()
 
+    def __enter__(self):
+        return self
+
+    def __exit__(self, type, value, traceback):
+        self.close()
+
     def __iter__(self):
         return self
 
index aae16eadfea6b2a9a5e31c9354d24eb24938c240..f312882b2abbe3fddae76b1bfcc104addb553728 100644 (file)
@@ -231,6 +231,30 @@ class FileInputTests(unittest.TestCase):
 ##         finally:
 ##             remove_tempfiles(t1)
 
+    def test_context_manager(self):
+        try:
+            t1 = writeTmp(1, ["A\nB\nC"])
+            t2 = writeTmp(2, ["D\nE\nF"])
+            with FileInput(files=(t1, t2)) as fi:
+                lines = list(fi)
+            self.assertEqual(lines, ["A\n", "B\n", "C", "D\n", "E\n", "F"])
+            self.assertEqual(fi.filelineno(), 3)
+            self.assertEqual(fi.lineno(), 6)
+            self.assertEqual(fi._files, ())
+        finally:
+            remove_tempfiles(t1, t2)
+
+    def test_close_on_exception(self):
+        try:
+            t1 = writeTmp(1, [""])
+            with FileInput(files=t1) as fi:
+                raise IOError
+        except IOError:
+            self.assertEqual(fi._files, ())
+        finally:
+            remove_tempfiles(t1)
+
+
 def test_main():
     run_unittest(BufferSizesTests, FileInputTests)
 
index 2886e61b20d984feb2139b60e271adf379f6e1fb..768c7df05396a1a6fb708cf96251d2d6cba8d621 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -15,6 +15,8 @@ Core and Builtins
 Library
 -------
 
+- Issue #1286: Allow using fileinput.FileInput as a context manager.
+
 - Add lfu_cache() and lru_cache() decorators to the functools module.