bpo-33504: Migrate configparser from OrderedDict to dict. (#6819)

With 3.7+, dictionary are ordered by design.  Configparser still uses
collections.OrderedDict, which is unnecessary.  This updates the module
to use the standard dict implementation by default, and changes the
docs and tests to match.
This commit is contained in:
John Reese 2018-06-05 16:31:33 -07:00 committed by Łukasz Langa
parent 5f3d04fa4e
commit 3a5b0d8988
5 changed files with 15 additions and 43 deletions

View File

@ -445,20 +445,19 @@ the :meth:`__init__` options:
Hint: if you want to specify default values for a specific section, use
:meth:`read_dict` before you read the actual file.
* *dict_type*, default value: :class:`collections.OrderedDict`
* *dict_type*, default value: :class:`dict`
This option has a major impact on how the mapping protocol will behave and how
the written configuration files look. With the default ordered
dictionary, every section is stored in the order they were added to the
parser. Same goes for options within sections.
the written configuration files look. With the standard dictionary, every
section is stored in the order they were added to the parser. Same goes for
options within sections.
An alternative dictionary type can be used for example to sort sections and
options on write-back. You can also use a regular dictionary for performance
reasons.
options on write-back.
Please note: there are ways to add a set of key-value pairs in a single
operation. When you use a regular dictionary in those operations, the order
of the keys may be random. For example:
of the keys will be ordered. For example:
.. doctest::
@ -474,40 +473,9 @@ the :meth:`__init__` options:
... 'baz': 'z'}
... })
>>> parser.sections() # doctest: +SKIP
['section3', 'section2', 'section1']
>>> [option for option in parser['section3']] # doctest: +SKIP
['baz', 'foo', 'bar']
In these operations you need to use an ordered dictionary as well:
.. doctest::
>>> from collections import OrderedDict
>>> parser = configparser.ConfigParser()
>>> parser.read_dict(
... OrderedDict((
... ('s1',
... OrderedDict((
... ('1', '2'),
... ('3', '4'),
... ('5', '6'),
... ))
... ),
... ('s2',
... OrderedDict((
... ('a', 'b'),
... ('c', 'd'),
... ('e', 'f'),
... ))
... ),
... ))
... )
>>> parser.sections() # doctest: +SKIP
['s1', 's2']
>>> [option for option in parser['s1']] # doctest: +SKIP
['1', '3', '5']
>>> [option for option in parser['s2'].values()] # doctest: +SKIP
['b', 'd', 'f']
['section1', 'section2', 'section3']
>>> [option for option in parser['section3']] # doctest: +SKIP
['foo', 'bar', 'baz']
* *allow_no_value*, default value: ``False``

View File

@ -139,7 +139,7 @@ ConfigParser -- responsible for parsing a list of
"""
from collections.abc import MutableMapping
from collections import OrderedDict as _default_dict, ChainMap as _ChainMap
from collections import ChainMap as _ChainMap
import functools
import io
import itertools
@ -157,6 +157,7 @@ __all__ = ["NoSectionError", "DuplicateOptionError", "DuplicateSectionError",
"LegacyInterpolation", "SectionProxy", "ConverterMapping",
"DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"]
_default_dict = dict
DEFAULTSECT = "DEFAULT"
MAX_INTERPOLATION_DEPTH = 10

View File

@ -1109,7 +1109,7 @@ class RawConfigParserTestCase(BasicTestCase, unittest.TestCase):
self.assertEqual(cf.get(123, 'this is sick'), True)
if cf._dict is configparser._default_dict:
# would not work for SortedDict; only checking for the most common
# default dictionary (OrderedDict)
# default dictionary (dict)
cf.optionxform = lambda x: x
cf.set('non-string', 1, 1)
self.assertEqual(cf.get('non-string', 1), 1)

View File

@ -1315,6 +1315,7 @@ Marc Recht
John Redford
Terry J. Reedy
Gareth Rees
John Reese
Steve Reeves
Lennart Regebro
John Regehr

View File

@ -0,0 +1,2 @@
Switch the default dictionary implementation for :mod:`configparser` from
:class:`collections.OrderedDict` to the standard :class:`dict` type.