Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit daab1c8

Browse files
committed
Closes #11670: configparser read_file now iterates over f.
1 parent 944d16c commit daab1c8

3 files changed

Lines changed: 82 additions & 9 deletions

File tree

Doc/library/configparser.rst

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -974,18 +974,37 @@ ConfigParser Objects
974974

975975
.. method:: read_file(f, source=None)
976976

977-
Read and parse configuration data from the file or file-like object in
978-
*f* (only the :meth:`readline` method is used). The file-like object
979-
must operate in text mode. Specifically, it must return strings from
980-
:meth:`readline`.
977+
Read and parse configuration data from *f* which must be an iterable
978+
yielding Unicode strings (for example any file object).
981979

982980
Optional argument *source* specifies the name of the file being read. If
983981
not given and *f* has a :attr:`name` attribute, that is used for
984982
*source*; the default is ``'<???>'``.
985983

986984
.. versionadded:: 3.2
987-
Replaces :meth:`readfp`.
985+
Replaces :meth:`readfp`.
986+
987+
.. note::
988+
989+
Prior to Python 3.2, :meth:`readfp` consumed lines from the file-like
990+
argument by calling its :meth:`~file.readline` method. For existing code
991+
calling :meth:`readfp` with arguments which don't support iteration,
992+
the following generator may be used as a wrapper around the file-like
993+
object::
988994

995+
def readline_generator(f):
996+
line = f.readline()
997+
while line != '':
998+
yield line
999+
line = f.readline()
1000+
1001+
Before::
1002+
1003+
parser.readfp(f)
1004+
1005+
After::
1006+
1007+
parser.read_file(readline_generator(f))
9891008

9901009
.. method:: read_string(string, source='<string>')
9911010

Lib/configparser.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -694,10 +694,10 @@ def read(self, filenames, encoding=None):
694694
def read_file(self, f, source=None):
695695
"""Like read() but the argument must be a file-like object.
696696
697-
The `f' argument must have a `readline' method. Optional second
698-
argument is the `source' specifying the name of the file being read. If
699-
not given, it is taken from f.name. If `f' has no `name' attribute,
700-
`<???>' is used.
697+
The `f' argument must be iterable, returning one line at a time.
698+
Optional second argument is the `source' specifying the name of the
699+
file being read. If not given, it is taken from f.name. If `f' has no
700+
`name' attribute, `<???>' is used.
701701
"""
702702
if source is None:
703703
try:

Lib/test/test_cfgparser.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,59 @@ def fromstring(self, string, defaults=None):
12351235
del section[default]
12361236
return cf_copy
12371237

1238+
1239+
class FakeFile:
1240+
def __init__(self):
1241+
file_path = support.findfile("cfgparser.1")
1242+
with open(file_path) as f:
1243+
self.lines = f.readlines()
1244+
self.lines.reverse()
1245+
1246+
def readline(self):
1247+
if len(self.lines):
1248+
return self.lines.pop()
1249+
return ''
1250+
1251+
1252+
def readline_generator(f):
1253+
"""As advised in Doc/library/configparser.rst."""
1254+
line = f.readline()
1255+
while line != '':
1256+
yield line
1257+
line = f.readline()
1258+
1259+
1260+
class ReadFileTestCase(unittest.TestCase):
1261+
def test_file(self):
1262+
file_path = support.findfile("cfgparser.1")
1263+
parser = configparser.ConfigParser()
1264+
with open(file_path) as f:
1265+
parser.read_file(f)
1266+
self.assertTrue("Foo Bar" in parser)
1267+
self.assertTrue("foo" in parser["Foo Bar"])
1268+
self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1269+
1270+
def test_iterable(self):
1271+
lines = textwrap.dedent("""
1272+
[Foo Bar]
1273+
foo=newbar""").strip().split('\n')
1274+
parser = configparser.ConfigParser()
1275+
parser.read_file(lines)
1276+
self.assertTrue("Foo Bar" in parser)
1277+
self.assertTrue("foo" in parser["Foo Bar"])
1278+
self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1279+
1280+
def test_readline_generator(self):
1281+
"""Issue #11670."""
1282+
parser = configparser.ConfigParser()
1283+
with self.assertRaises(TypeError):
1284+
parser.read_file(FakeFile())
1285+
parser.read_file(readline_generator(FakeFile()))
1286+
self.assertTrue("Foo Bar" in parser)
1287+
self.assertTrue("foo" in parser["Foo Bar"])
1288+
self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
1289+
1290+
12381291
class CoverageOneHundredTestCase(unittest.TestCase):
12391292
"""Covers edge cases in the codebase."""
12401293

@@ -1338,5 +1391,6 @@ def test_main():
13381391
CompatibleTestCase,
13391392
CopyTestCase,
13401393
ConfigParserTestCaseNonStandardDefaultSection,
1394+
ReadFileTestCase,
13411395
CoverageOneHundredTestCase,
13421396
)

0 commit comments

Comments
 (0)