[Python-checkins] bpo-45173: Keep configparser deprecations until Python 3.12 (GH-30952)

gpshead webhook-mailer at python.org
Wed Feb 2 11:59:48 EST 2022


https://github.com/python/cpython/commit/e8659b47dece5a272111c0af5e340c364a9f807b
commit: e8659b47dece5a272111c0af5e340c364a9f807b
branch: main
author: Hugo van Kemenade <hugovk at users.noreply.github.com>
committer: gpshead <greg at krypto.org>
date: 2022-02-02T08:59:39-08:00
summary:

bpo-45173: Keep configparser deprecations until Python 3.12 (GH-30952)

* Revert "bpo-45173 Remove configparser deprecations"

This reverts commit df2284bc416dcccba1125b12af4499c45baabe4f.

* bpo-45173: Note these configparser deprecations will be removed in 3.12

files:
A Misc/NEWS.d/next/Library/2022-01-27-11-16-59.bpo-45173.wreRF2.rst
M Doc/library/configparser.rst
M Doc/whatsnew/3.11.rst
M Lib/configparser.py
M Lib/test/test_configparser.py

diff --git a/Doc/library/configparser.rst b/Doc/library/configparser.rst
index d31452edb974f..1ebda53ecda0f 100644
--- a/Doc/library/configparser.rst
+++ b/Doc/library/configparser.rst
@@ -1201,6 +1201,28 @@ ConfigParser Objects
       names is stripped before :meth:`optionxform` is called.
 
 
+   .. method:: readfp(fp, filename=None)
+
+      .. deprecated:: 3.2
+         Use :meth:`read_file` instead.
+
+      .. versionchanged:: 3.2
+         :meth:`readfp` now iterates on *fp* instead of calling ``fp.readline()``.
+
+      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(fp):
+             line = fp.readline()
+             while line:
+                 yield line
+                 line = fp.readline()
+
+      Instead of ``parser.readfp(fp)`` use
+      ``parser.read_file(readline_generator(fp))``.
+
+
 .. data:: MAX_INTERPOLATION_DEPTH
 
    The maximum depth for recursive interpolation for :meth:`get` when the *raw*
@@ -1338,9 +1360,6 @@ Exceptions
       The ``filename`` attribute and :meth:`__init__` argument were renamed to
       ``source`` for consistency.
 
-   .. versionchanged:: 3.11
-      The deprecated ``filename`` attribute was removed.
-
 
 .. rubric:: Footnotes
 
diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index 3458ad63c9df8..33f39e5775269 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -458,6 +458,16 @@ Deprecated
   as deprecated, its docstring is now corrected).
   (Contributed by Hugo van Kemenade in :issue:`45837`.)
 
+* The following have been deprecated in :mod:`configparser` since Python 3.2.
+  Their deprecation warnings have now been updated to note they will removed in
+  Python 3.12:
+
+  * the :class:`configparser.SafeConfigParser` class
+  * the :attr:`configparser.ParsingError.filename` property
+  * the :meth:`configparser.ParsingError.readfp` method
+
+  (Contributed by Hugo van Kemenade in :issue:`45173`.)
+
 Removed
 =======
 
@@ -502,13 +512,6 @@ Removed
   the ``l*gettext()`` functions.
   (Contributed by Dong-hee Na and Serhiy Storchaka in :issue:`44235`.)
 
-* Removed from the :mod:`configparser` module:
-  the :class:`SafeConfigParser` class,
-  the :attr:`filename` property of the :class:`ParsingError` class,
-  the :meth:`readfp` method of the :class:`ConfigParser` class,
-  deprecated since Python 3.2.
-  (Contributed by Hugo van Kemenade in :issue:`45173`.)
-
 * The :func:`@asyncio.coroutine <asyncio.coroutine>` :term:`decorator` enabling
   legacy generator-based coroutines to be compatible with async/await code.
   The function has been deprecated since Python 3.8 and the removal was
diff --git a/Lib/configparser.py b/Lib/configparser.py
index c10309acf1ab4..3470624e63f61 100644
--- a/Lib/configparser.py
+++ b/Lib/configparser.py
@@ -146,12 +146,13 @@
 import os
 import re
 import sys
+import warnings
 
 __all__ = ["NoSectionError", "DuplicateOptionError", "DuplicateSectionError",
            "NoOptionError", "InterpolationError", "InterpolationDepthError",
            "InterpolationMissingOptionError", "InterpolationSyntaxError",
            "ParsingError", "MissingSectionHeaderError",
-           "ConfigParser", "RawConfigParser",
+           "ConfigParser", "SafeConfigParser", "RawConfigParser",
            "Interpolation", "BasicInterpolation",  "ExtendedInterpolation",
            "LegacyInterpolation", "SectionProxy", "ConverterMapping",
            "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"]
@@ -311,6 +312,26 @@ def __init__(self, source=None, filename=None):
         self.errors = []
         self.args = (source, )
 
+    @property
+    def filename(self):
+        """Deprecated, use `source'."""
+        warnings.warn(
+            "The 'filename' attribute will be removed in Python 3.12. "
+            "Use 'source' instead.",
+            DeprecationWarning, stacklevel=2
+        )
+        return self.source
+
+    @filename.setter
+    def filename(self, value):
+        """Deprecated, user `source'."""
+        warnings.warn(
+            "The 'filename' attribute will be removed in Python 3.12. "
+            "Use 'source' instead.",
+            DeprecationWarning, stacklevel=2
+        )
+        self.source = value
+
     def append(self, lineno, line):
         self.errors.append((lineno, line))
         self.message += '\n\t[line %2d]: %s' % (lineno, line)
@@ -733,6 +754,15 @@ def read_dict(self, dictionary, source='<dict>'):
                 elements_added.add((section, key))
                 self.set(section, key, value)
 
+    def readfp(self, fp, filename=None):
+        """Deprecated, use read_file instead."""
+        warnings.warn(
+            "This method will be removed in Python 3.12. "
+            "Use 'parser.read_file()' instead.",
+            DeprecationWarning, stacklevel=2
+        )
+        self.read_file(fp, source=filename)
+
     def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET):
         """Get an option value for a given section.
 
@@ -1195,6 +1225,19 @@ def _read_defaults(self, defaults):
             self._interpolation = hold_interpolation
 
 
+class SafeConfigParser(ConfigParser):
+    """ConfigParser alias for backwards compatibility purposes."""
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        warnings.warn(
+            "The SafeConfigParser class has been renamed to ConfigParser "
+            "in Python 3.2. This alias will be removed in Python 3.12."
+            " Use ConfigParser directly instead.",
+            DeprecationWarning, stacklevel=2
+        )
+
+
 class SectionProxy(MutableMapping):
     """A proxy for a single section from a parser."""
 
diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py
index cedf505122d13..e9b03e6c62ef1 100644
--- a/Lib/test/test_configparser.py
+++ b/Lib/test/test_configparser.py
@@ -1612,6 +1612,13 @@ def test_parsing_error(self):
                                             "and `source'. Use `source'.")
         error = configparser.ParsingError(filename='source')
         self.assertEqual(error.source, 'source')
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter("always", DeprecationWarning)
+            self.assertEqual(error.filename, 'source')
+            error.filename = 'filename'
+            self.assertEqual(error.source, 'filename')
+        for warning in w:
+            self.assertTrue(warning.category is DeprecationWarning)
 
     def test_interpolation_validation(self):
         parser = configparser.ConfigParser()
@@ -1630,6 +1637,27 @@ def test_interpolation_validation(self):
         self.assertEqual(str(cm.exception), "bad interpolation variable "
                                             "reference '%(()'")
 
+    def test_readfp_deprecation(self):
+        sio = io.StringIO("""
+        [section]
+        option = value
+        """)
+        parser = configparser.ConfigParser()
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter("always", DeprecationWarning)
+            parser.readfp(sio, filename='StringIO')
+        for warning in w:
+            self.assertTrue(warning.category is DeprecationWarning)
+        self.assertEqual(len(parser), 2)
+        self.assertEqual(parser['section']['option'], 'value')
+
+    def test_safeconfigparser_deprecation(self):
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter("always", DeprecationWarning)
+            parser = configparser.SafeConfigParser()
+        for warning in w:
+            self.assertTrue(warning.category is DeprecationWarning)
+
     def test_sectionproxy_repr(self):
         parser = configparser.ConfigParser()
         parser.read_string("""
diff --git a/Misc/NEWS.d/next/Library/2022-01-27-11-16-59.bpo-45173.wreRF2.rst b/Misc/NEWS.d/next/Library/2022-01-27-11-16-59.bpo-45173.wreRF2.rst
new file mode 100644
index 0000000000000..ee5a88f621498
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-01-27-11-16-59.bpo-45173.wreRF2.rst
@@ -0,0 +1 @@
+Note the configparser deprecations will be removed in Python 3.12.



More information about the Python-checkins mailing list