Closes #11670: configparser read_file now iterates over f.

This commit is contained in:
Łukasz Langa 2011-04-27 18:10:05 +02:00
parent 944d16c6c4
commit daab1c8092
3 changed files with 82 additions and 9 deletions

View File

@ -974,10 +974,8 @@ ConfigParser Objects
.. 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
@ -986,6 +984,27 @@ ConfigParser Objects
.. versionadded:: 3.2
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>')

View File

@ -694,10 +694,10 @@ class RawConfigParser(MutableMapping):
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:

View File

@ -1235,6 +1235,59 @@ class CopyTestCase(BasicTestCase):
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 @@ def test_main():
CompatibleTestCase,
CopyTestCase,
ConfigParserTestCaseNonStandardDefaultSection,
ReadFileTestCase,
CoverageOneHundredTestCase,
)