[Python-checkins] cpython (3.2): Closes #11670: configparser read_file now iterates over f.

lukasz.langa python-checkins at python.org
Wed Apr 27 18:14:35 CEST 2011


http://hg.python.org/cpython/rev/6f937d6369b6
changeset:   69631:6f937d6369b6
branch:      3.2
parent:      69629:0ff8f6105827
user:        Łukasz Langa <lukasz at langa.pl>
date:        Wed Apr 27 18:10:05 2011 +0200
summary:
  Closes #11670: configparser read_file now iterates over f.

files:
  Doc/library/configparser.rst |  29 ++++++++++--
  Lib/configparser.py          |   8 +-
  Lib/test/test_cfgparser.py   |  54 ++++++++++++++++++++++++
  3 files changed, 82 insertions(+), 9 deletions(-)


diff --git a/Doc/library/configparser.rst b/Doc/library/configparser.rst
--- a/Doc/library/configparser.rst
+++ b/Doc/library/configparser.rst
@@ -974,18 +974,37 @@
 
    .. method:: read_file(f, source=None)
 
-      Read and parse configuration data from the file or file-like object in
-      *f* (only the :meth:`readline` method is used).  The file-like object
-      must operate in text mode.  Specifically, it must return strings from
-      :meth:`readline`.
+      Read and parse configuration data from *f* which must be an iterable
+      yielding Unicode strings (for example any file object).
 
       Optional argument *source* specifies the name of the file being read.  If
       not given and *f* has a :attr:`name` attribute, that is used for
       *source*; the default is ``'<???>'``.
 
       .. versionadded:: 3.2
-         Replaces :meth:`readfp`.
+         Replaces :meth:`readfp`. 
+         
+      .. note::
+      
+         Prior to Python 3.2, :meth:`readfp` consumed lines from the file-like
+         argument by calling its :meth:`~file.readline` method. For existing code
+         calling :meth:`readfp` with arguments which don't support iteration,
+         the following generator may be used as a wrapper around the file-like
+         object::
 
+             def readline_generator(f):
+                 line = f.readline()
+                 while line != '':
+                     yield line
+                     line = f.readline()
+
+         Before::
+
+             parser.readfp(f)
+
+         After::
+
+             parser.read_file(readline_generator(f))
 
    .. method:: read_string(string, source='<string>')
 
diff --git a/Lib/configparser.py b/Lib/configparser.py
--- a/Lib/configparser.py
+++ b/Lib/configparser.py
@@ -694,10 +694,10 @@
     def read_file(self, f, source=None):
         """Like read() but the argument must be a file-like object.
 
-        The `f' argument must have a `readline' method.  Optional second
-        argument is the `source' specifying the name of the file being read. If
-        not given, it is taken from f.name. If `f' has no `name' attribute,
-        `<???>' is used.
+        The `f' argument must be iterable, returning one line at a time.
+        Optional second argument is the `source' specifying the name of the
+        file being read. If not given, it is taken from f.name. If `f' has no
+        `name' attribute, `<???>' is used.
         """
         if source is None:
             try:
diff --git a/Lib/test/test_cfgparser.py b/Lib/test/test_cfgparser.py
--- a/Lib/test/test_cfgparser.py
+++ b/Lib/test/test_cfgparser.py
@@ -1235,6 +1235,59 @@
                     del section[default]
         return cf_copy
 
+
+class FakeFile:
+    def __init__(self):
+        file_path = support.findfile("cfgparser.1")
+        with open(file_path) as f:
+            self.lines = f.readlines()
+            self.lines.reverse()
+
+    def readline(self):
+        if len(self.lines):
+            return self.lines.pop()
+        return ''
+
+
+def readline_generator(f):
+    """As advised in Doc/library/configparser.rst."""
+    line = f.readline()
+    while line != '':
+        yield line
+        line = f.readline()
+
+
+class ReadFileTestCase(unittest.TestCase):
+    def test_file(self):
+        file_path = support.findfile("cfgparser.1")
+        parser = configparser.ConfigParser()
+        with open(file_path) as f:
+            parser.read_file(f)
+        self.assertTrue("Foo Bar" in parser)
+        self.assertTrue("foo" in parser["Foo Bar"])
+        self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
+
+    def test_iterable(self):
+        lines = textwrap.dedent("""
+        [Foo Bar]
+        foo=newbar""").strip().split('\n')
+        parser = configparser.ConfigParser()
+        parser.read_file(lines)
+        self.assertTrue("Foo Bar" in parser)
+        self.assertTrue("foo" in parser["Foo Bar"])
+        self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
+
+    def test_readline_generator(self):
+        """Issue #11670."""
+        parser = configparser.ConfigParser()
+        with self.assertRaises(TypeError):
+            parser.read_file(FakeFile())
+        parser.read_file(readline_generator(FakeFile()))
+        self.assertTrue("Foo Bar" in parser)
+        self.assertTrue("foo" in parser["Foo Bar"])
+        self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
+
+
 class CoverageOneHundredTestCase(unittest.TestCase):
     """Covers edge cases in the codebase."""
 
@@ -1338,5 +1391,6 @@
         CompatibleTestCase,
         CopyTestCase,
         ConfigParserTestCaseNonStandardDefaultSection,
+        ReadFileTestCase,
         CoverageOneHundredTestCase,
         )

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


More information about the Python-checkins mailing list