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

Ezio Melotti ezio.melotti at gmail.com
Thu Apr 28 00:46:06 CEST 2011


Hi,

On 27/04/2011 19.14, lukasz.langa wrote:
> 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).

I wouldn't use 'any' here. File objects opened in binary mode will yield 
byte strings.

>
>         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,

This note could be a versionchanged and be less verbose (e.g. the 
before/after could be replaced by something like "and use it as 
parser.read_file(readline_generator(f))").

>   :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 != '':

The "!= ''" can probably go.

> +                     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 != '':

Ditto.

> +        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"])

These should be 'assertIn'.

> +        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"])

Ditto.

> +        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"])

Ditto.

> +        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,
>           )

Best Regards,
Ezio Melotti


More information about the Python-checkins mailing list