mirror of https://github.com/python/cpython
configparser API cleanup: default values now sensible, slightly incompatible.
Backwards compatible alternative values possible as documented. Done by Łukasz Langa, approved by Raymond and Fred.
This commit is contained in:
parent
ed16bf4aaa
commit
b25a791802
|
@ -230,21 +230,18 @@ may be treated as parts of multiline values or ignored.
|
||||||
|
|
||||||
Configuration files may include comments, prefixed by specific
|
Configuration files may include comments, prefixed by specific
|
||||||
characters (``#`` and ``;`` by default [1]_). Comments may appear on
|
characters (``#`` and ``;`` by default [1]_). Comments may appear on
|
||||||
their own on an otherwise empty line, or may be entered on lines holding
|
their own on an otherwise empty line, possibly indented. [1]_
|
||||||
values or section names. In the latter case, they need to be preceded
|
|
||||||
by a whitespace character to be recognized as a comment. For backwards
|
|
||||||
compatibility, by default only ``;`` starts an inline comment, while
|
|
||||||
``#`` does not [1]_.
|
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
|
|
||||||
[Simple Values]
|
[Simple Values]
|
||||||
key: value
|
key=value
|
||||||
spaces in keys: allowed
|
spaces in keys=allowed
|
||||||
spaces in values: allowed as well
|
spaces in values=allowed as well
|
||||||
you can also use = to delimit keys from values
|
spaces around the delimiter = obviously
|
||||||
|
you can also use : to delimit keys from values
|
||||||
|
|
||||||
[All Values Are Strings]
|
[All Values Are Strings]
|
||||||
values like this: 1000000
|
values like this: 1000000
|
||||||
|
@ -261,12 +258,14 @@ For example:
|
||||||
key_without_value
|
key_without_value
|
||||||
empty string value here =
|
empty string value here =
|
||||||
|
|
||||||
[You can use comments] ; after a useful line
|
[You can use comments]
|
||||||
; in an empty line
|
# like this
|
||||||
after: a_value ; here's another comment
|
; or this
|
||||||
inside: a ;comment
|
|
||||||
multiline ;comment
|
# By default only in an empty line.
|
||||||
value! ;comment
|
# Inline comments can be harmful because they prevent users
|
||||||
|
# from using the delimiting characters as parts of values.
|
||||||
|
# That being said, this can be customized.
|
||||||
|
|
||||||
[Sections Can Be Indented]
|
[Sections Can Be Indented]
|
||||||
can_values_be_as_well = True
|
can_values_be_as_well = True
|
||||||
|
@ -509,7 +508,8 @@ the :meth:`__init__` options:
|
||||||
... skip-external-locking
|
... skip-external-locking
|
||||||
... old_passwords = 1
|
... old_passwords = 1
|
||||||
... skip-bdb
|
... skip-bdb
|
||||||
... skip-innodb # we don't need ACID today
|
... # we don't need ACID today
|
||||||
|
... skip-innodb
|
||||||
... """
|
... """
|
||||||
>>> config = configparser.ConfigParser(allow_no_value=True)
|
>>> config = configparser.ConfigParser(allow_no_value=True)
|
||||||
>>> config.read_string(sample_config)
|
>>> config.read_string(sample_config)
|
||||||
|
@ -536,28 +536,78 @@ the :meth:`__init__` options:
|
||||||
See also the *space_around_delimiters* argument to
|
See also the *space_around_delimiters* argument to
|
||||||
:meth:`ConfigParser.write`.
|
:meth:`ConfigParser.write`.
|
||||||
|
|
||||||
* *comment_prefixes*, default value: ``_COMPATIBLE`` (``'#'`` valid on empty
|
* *comment_prefixes*, default value: ``('#', ';')``
|
||||||
lines, ``';'`` valid also on non-empty lines)
|
|
||||||
|
|
||||||
Comment prefixes are strings that indicate the start of a valid comment
|
* *inline_comment_prefixes*, default value: ``None``
|
||||||
within a config file. The peculiar default value allows for comments starting
|
|
||||||
with ``'#'`` or ``';'`` but only the latter can be used in a non-empty line.
|
Comment prefixes are strings that indicate the start of a valid comment within
|
||||||
This is obviously dictated by backwards compatibiliy. A more predictable
|
a config file. *comment_prefixes* are used only on otherwise empty lines
|
||||||
approach would be to specify prefixes as ``('#', ';')`` which will allow for
|
(optionally indented) whereas *inline_comment_prefixes* can be used after
|
||||||
both prefixes to be used in non-empty lines.
|
every valid value (e.g. section names, options and empty lines as well). By
|
||||||
|
default inline comments are disabled and ``'#'`` and ``';'`` are used as
|
||||||
|
prefixes for whole line comments.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.2
|
||||||
|
In previous versions of :mod:`configparser` behaviour matched
|
||||||
|
``comment_prefixes=('#',';')`` and ``inline_comment_prefixes=(';',)``.
|
||||||
|
|
||||||
Please note that config parsers don't support escaping of comment prefixes so
|
Please note that config parsers don't support escaping of comment prefixes so
|
||||||
leaving characters out of *comment_prefixes* is a way of ensuring they can be
|
using *inline_comment_prefixes* may prevent users from specifying option
|
||||||
used as parts of keys or values.
|
values with characters used as comment prefixes. When in doubt, avoid setting
|
||||||
|
*inline_comment_prefixes*. In any circumstances, the only way of storing
|
||||||
|
comment prefix characters at the beginning of a line in multiline values is to
|
||||||
|
interpolate the prefix, for example::
|
||||||
|
|
||||||
* *strict*, default value: ``False``
|
>>> from configparser import ConfigParser, ExtendedInterpolation
|
||||||
|
>>> parser = ConfigParser(interpolation=ExtendedInterpolation())
|
||||||
|
>>> # the default BasicInterpolation could be used as well
|
||||||
|
>>> parser.read_string("""
|
||||||
|
... [DEFAULT]
|
||||||
|
... hash = #
|
||||||
|
...
|
||||||
|
... [hashes]
|
||||||
|
... shebang =
|
||||||
|
... ${hash}!/usr/bin/env python
|
||||||
|
... ${hash} -*- coding: utf-8 -*-
|
||||||
|
...
|
||||||
|
... extensions =
|
||||||
|
... enabled_extension
|
||||||
|
... another_extension
|
||||||
|
... #disabled_by_comment
|
||||||
|
... yet_another_extension
|
||||||
|
...
|
||||||
|
... interpolation not necessary = if # is not at line start
|
||||||
|
... even in multiline values = line #1
|
||||||
|
... line #2
|
||||||
|
... line #3
|
||||||
|
... """)
|
||||||
|
>>> print(parser['hashes']['shebang'])
|
||||||
|
|
||||||
If set to ``True``, the parser will not allow for any section or option
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
>>> print(parser['hashes']['extensions'])
|
||||||
|
|
||||||
|
enabled_extension
|
||||||
|
another_extension
|
||||||
|
yet_another_extension
|
||||||
|
>>> print(parser['hashes']['interpolation not necessary'])
|
||||||
|
if # is not at line start
|
||||||
|
>>> print(parser['hashes']['even in multiline values'])
|
||||||
|
line #1
|
||||||
|
line #2
|
||||||
|
line #3
|
||||||
|
|
||||||
|
* *strict*, default value: ``True``
|
||||||
|
|
||||||
|
When set to ``True``, the parser will not allow for any section or option
|
||||||
duplicates while reading from a single source (using :meth:`read_file`,
|
duplicates while reading from a single source (using :meth:`read_file`,
|
||||||
:meth:`read_string` or :meth:`read_dict`). The default is ``False`` only
|
:meth:`read_string` or :meth:`read_dict`). It is recommended to use strict
|
||||||
because of backwards compatibility reasons. It is recommended to use strict
|
|
||||||
parsers in new applications.
|
parsers in new applications.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.2
|
||||||
|
In previous versions of :mod:`configparser` behaviour matched
|
||||||
|
``strict=False``.
|
||||||
|
|
||||||
* *empty_lines_in_values*, default value: ``True``
|
* *empty_lines_in_values*, default value: ``True``
|
||||||
|
|
||||||
In config parsers, values can span multiple lines as long as they are
|
In config parsers, values can span multiple lines as long as they are
|
||||||
|
@ -575,7 +625,6 @@ the :meth:`__init__` options:
|
||||||
|
|
||||||
this = is still a part of the multiline value of 'key'
|
this = is still a part of the multiline value of 'key'
|
||||||
|
|
||||||
|
|
||||||
This can be especially problematic for the user to see if she's using a
|
This can be especially problematic for the user to see if she's using a
|
||||||
proportional font to edit the file. That is why when your application does
|
proportional font to edit the file. That is why when your application does
|
||||||
not need values with empty lines, you should consider disallowing them. This
|
not need values with empty lines, you should consider disallowing them. This
|
||||||
|
@ -603,8 +652,7 @@ the :meth:`__init__` options:
|
||||||
interpolation completely, ``ExtendedInterpolation()`` provides a more
|
interpolation completely, ``ExtendedInterpolation()`` provides a more
|
||||||
advanced variant inspired by ``zc.buildout``. More on the subject in the
|
advanced variant inspired by ``zc.buildout``. More on the subject in the
|
||||||
`dedicated documentation section <#interpolation-of-values>`_.
|
`dedicated documentation section <#interpolation-of-values>`_.
|
||||||
|
:class:`RawConfigParser` has a default value of ``None``.
|
||||||
.. note:: :class:`RawConfigParser` is using ``None`` by default.
|
|
||||||
|
|
||||||
|
|
||||||
More advanced customization may be achieved by overriding default values of
|
More advanced customization may be achieved by overriding default values of
|
||||||
|
@ -769,7 +817,7 @@ interpolation if an option used is not defined elsewhere. ::
|
||||||
ConfigParser Objects
|
ConfigParser Objects
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
.. class:: ConfigParser(defaults=None, dict_type=collections.OrderedDict, allow_no_value=False, delimiters=('=', ':'), comment_prefixes=_COMPATIBLE, strict=False, empty_lines_in_values=True, default_section=configparser.DEFAULTSECT, interpolation=BasicInterpolation())
|
.. class:: ConfigParser(defaults=None, dict_type=collections.OrderedDict, allow_no_value=False, delimiters=('=', ':'), comment_prefixes=('#', ';'), inline_comment_prefixes=None, strict=True, empty_lines_in_values=True, default_section=configparser.DEFAULTSECT, interpolation=BasicInterpolation())
|
||||||
|
|
||||||
The main configuration parser. When *defaults* is given, it is initialized
|
The main configuration parser. When *defaults* is given, it is initialized
|
||||||
into the dictionary of intrinsic defaults. When *dict_type* is given, it
|
into the dictionary of intrinsic defaults. When *dict_type* is given, it
|
||||||
|
@ -778,12 +826,15 @@ ConfigParser Objects
|
||||||
|
|
||||||
When *delimiters* is given, it is used as the set of substrings that
|
When *delimiters* is given, it is used as the set of substrings that
|
||||||
divide keys from values. When *comment_prefixes* is given, it will be used
|
divide keys from values. When *comment_prefixes* is given, it will be used
|
||||||
as the set of substrings that prefix comments in a line, both for the whole
|
as the set of substrings that prefix comments in otherwise empty lines.
|
||||||
|
Comments can be indented. When *inline_comment_prefixes* is given, it will be
|
||||||
|
used as the set of substrings that prefix comments in non-empty lines.
|
||||||
|
|
||||||
line and inline comments. For backwards compatibility, the default value for
|
line and inline comments. For backwards compatibility, the default value for
|
||||||
*comment_prefixes* is a special value that indicates that ``;`` and ``#`` can
|
*comment_prefixes* is a special value that indicates that ``;`` and ``#`` can
|
||||||
start whole line comments while only ``;`` can start inline comments.
|
start whole line comments while only ``;`` can start inline comments.
|
||||||
|
|
||||||
When *strict* is ``True`` (default: ``False``), the parser won't allow for
|
When *strict* is ``True`` (the default), the parser won't allow for
|
||||||
any section or option duplicates while reading from a single source (file,
|
any section or option duplicates while reading from a single source (file,
|
||||||
string or dictionary), raising :exc:`DuplicateSectionError` or
|
string or dictionary), raising :exc:`DuplicateSectionError` or
|
||||||
:exc:`DuplicateOptionError`. When *empty_lines_in_values* is ``False``
|
:exc:`DuplicateOptionError`. When *empty_lines_in_values* is ``False``
|
||||||
|
@ -1043,7 +1094,7 @@ ConfigParser Objects
|
||||||
RawConfigParser Objects
|
RawConfigParser Objects
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
.. class:: RawConfigParser(defaults=None, dict_type=collections.OrderedDict, allow_no_value=False, delimiters=('=', ':'), comment_prefixes=_COMPATIBLE, strict=False, empty_lines_in_values=True, default_section=configaparser.DEFAULTSECT, interpolation=None)
|
.. class:: RawConfigParser(defaults=None, dict_type=collections.OrderedDict, allow_no_value=False, delimiters=('=', ':'), comment_prefixes=('#', ';'), inline_comment_prefixes=None, strict=True, empty_lines_in_values=True, default_section=configaparser.DEFAULTSECT, interpolation=None)
|
||||||
|
|
||||||
Legacy variant of the :class:`ConfigParser` with interpolation disabled
|
Legacy variant of the :class:`ConfigParser` with interpolation disabled
|
||||||
by default and unsafe ``add_section`` and ``set`` methods.
|
by default and unsafe ``add_section`` and ``set`` methods.
|
||||||
|
|
|
@ -15,8 +15,9 @@ ConfigParser -- responsible for parsing a list of
|
||||||
methods:
|
methods:
|
||||||
|
|
||||||
__init__(defaults=None, dict_type=_default_dict, allow_no_value=False,
|
__init__(defaults=None, dict_type=_default_dict, allow_no_value=False,
|
||||||
delimiters=('=', ':'), comment_prefixes=_COMPATIBLE,
|
delimiters=('=', ':'), comment_prefixes=('#', ';'),
|
||||||
strict=False, empty_lines_in_values=True):
|
inline_comment_prefixes=None, strict=True,
|
||||||
|
empty_lines_in_values=True):
|
||||||
Create the parser. When `defaults' is given, it is initialized into the
|
Create the parser. When `defaults' is given, it is initialized into the
|
||||||
dictionary or intrinsic defaults. The keys must be strings, the values
|
dictionary or intrinsic defaults. The keys must be strings, the values
|
||||||
must be appropriate for %()s string interpolation.
|
must be appropriate for %()s string interpolation.
|
||||||
|
@ -29,11 +30,15 @@ ConfigParser -- responsible for parsing a list of
|
||||||
that divide keys from values.
|
that divide keys from values.
|
||||||
|
|
||||||
When `comment_prefixes' is given, it will be used as the set of
|
When `comment_prefixes' is given, it will be used as the set of
|
||||||
substrings that prefix comments in a line.
|
substrings that prefix comments in empty lines. Comments can be
|
||||||
|
indented.
|
||||||
|
|
||||||
|
When `inline_comment_prefixes' is given, it will be used as the set of
|
||||||
|
substrings that prefix comments in non-empty lines.
|
||||||
|
|
||||||
When `strict` is True, the parser won't allow for any section or option
|
When `strict` is True, the parser won't allow for any section or option
|
||||||
duplicates while reading from a single source (file, string or
|
duplicates while reading from a single source (file, string or
|
||||||
dictionary). Default is False.
|
dictionary). Default is True.
|
||||||
|
|
||||||
When `empty_lines_in_values' is False (default: True), each empty line
|
When `empty_lines_in_values' is False (default: True), each empty line
|
||||||
marks the end of an option. Otherwise, internal empty lines of
|
marks the end of an option. Otherwise, internal empty lines of
|
||||||
|
@ -340,11 +345,6 @@ class MissingSectionHeaderError(ParsingError):
|
||||||
self.args = (filename, lineno, line)
|
self.args = (filename, lineno, line)
|
||||||
|
|
||||||
|
|
||||||
# Used in parsers to denote selecting a backwards-compatible inline comment
|
|
||||||
# character behavior (; and # are comments at the start of a line, but ; only
|
|
||||||
# inline)
|
|
||||||
_COMPATIBLE = object()
|
|
||||||
|
|
||||||
# Used in parser getters to indicate the default behaviour when a specific
|
# Used in parser getters to indicate the default behaviour when a specific
|
||||||
# option is not found it to raise an exception. Created to enable `None' as
|
# option is not found it to raise an exception. Created to enable `None' as
|
||||||
# a valid fallback value.
|
# a valid fallback value.
|
||||||
|
@ -592,8 +592,8 @@ class RawConfigParser(MutableMapping):
|
||||||
|
|
||||||
def __init__(self, defaults=None, dict_type=_default_dict,
|
def __init__(self, defaults=None, dict_type=_default_dict,
|
||||||
allow_no_value=False, *, delimiters=('=', ':'),
|
allow_no_value=False, *, delimiters=('=', ':'),
|
||||||
comment_prefixes=_COMPATIBLE, strict=False,
|
comment_prefixes=('#', ';'), inline_comment_prefixes=None,
|
||||||
empty_lines_in_values=True,
|
strict=True, empty_lines_in_values=True,
|
||||||
default_section=DEFAULTSECT,
|
default_section=DEFAULTSECT,
|
||||||
interpolation=_UNSET):
|
interpolation=_UNSET):
|
||||||
|
|
||||||
|
@ -616,12 +616,8 @@ class RawConfigParser(MutableMapping):
|
||||||
else:
|
else:
|
||||||
self._optcre = re.compile(self._OPT_TMPL.format(delim=d),
|
self._optcre = re.compile(self._OPT_TMPL.format(delim=d),
|
||||||
re.VERBOSE)
|
re.VERBOSE)
|
||||||
if comment_prefixes is _COMPATIBLE:
|
self._comment_prefixes = tuple(comment_prefixes or ())
|
||||||
self._startonly_comment_prefixes = ('#',)
|
self._inline_comment_prefixes = tuple(inline_comment_prefixes or ())
|
||||||
self._comment_prefixes = (';',)
|
|
||||||
else:
|
|
||||||
self._startonly_comment_prefixes = ()
|
|
||||||
self._comment_prefixes = tuple(comment_prefixes or ())
|
|
||||||
self._strict = strict
|
self._strict = strict
|
||||||
self._allow_no_value = allow_no_value
|
self._allow_no_value = allow_no_value
|
||||||
self._empty_lines_in_values = empty_lines_in_values
|
self._empty_lines_in_values = empty_lines_in_values
|
||||||
|
@ -989,18 +985,18 @@ class RawConfigParser(MutableMapping):
|
||||||
indent_level = 0
|
indent_level = 0
|
||||||
e = None # None, or an exception
|
e = None # None, or an exception
|
||||||
for lineno, line in enumerate(fp, start=1):
|
for lineno, line in enumerate(fp, start=1):
|
||||||
# strip full line comments
|
|
||||||
comment_start = None
|
comment_start = None
|
||||||
for prefix in self._startonly_comment_prefixes:
|
|
||||||
if line.strip().startswith(prefix):
|
|
||||||
comment_start = 0
|
|
||||||
break
|
|
||||||
# strip inline comments
|
# strip inline comments
|
||||||
for prefix in self._comment_prefixes:
|
for prefix in self._inline_comment_prefixes:
|
||||||
index = line.find(prefix)
|
index = line.find(prefix)
|
||||||
if index == 0 or (index > 0 and line[index-1].isspace()):
|
if index == 0 or (index > 0 and line[index-1].isspace()):
|
||||||
comment_start = index
|
comment_start = index
|
||||||
break
|
break
|
||||||
|
# strip full line comments
|
||||||
|
for prefix in self._comment_prefixes:
|
||||||
|
if line.strip().startswith(prefix):
|
||||||
|
comment_start = 0
|
||||||
|
break
|
||||||
value = line[:comment_start].strip()
|
value = line[:comment_start].strip()
|
||||||
if not value:
|
if not value:
|
||||||
if self._empty_lines_in_values:
|
if self._empty_lines_in_values:
|
||||||
|
|
|
@ -29,6 +29,7 @@ class CfgParserTestCaseClass(unittest.TestCase):
|
||||||
allow_no_value = False
|
allow_no_value = False
|
||||||
delimiters = ('=', ':')
|
delimiters = ('=', ':')
|
||||||
comment_prefixes = (';', '#')
|
comment_prefixes = (';', '#')
|
||||||
|
inline_comment_prefixes = (';', '#')
|
||||||
empty_lines_in_values = True
|
empty_lines_in_values = True
|
||||||
dict_type = configparser._default_dict
|
dict_type = configparser._default_dict
|
||||||
strict = False
|
strict = False
|
||||||
|
@ -41,6 +42,7 @@ class CfgParserTestCaseClass(unittest.TestCase):
|
||||||
allow_no_value=self.allow_no_value,
|
allow_no_value=self.allow_no_value,
|
||||||
delimiters=self.delimiters,
|
delimiters=self.delimiters,
|
||||||
comment_prefixes=self.comment_prefixes,
|
comment_prefixes=self.comment_prefixes,
|
||||||
|
inline_comment_prefixes=self.inline_comment_prefixes,
|
||||||
empty_lines_in_values=self.empty_lines_in_values,
|
empty_lines_in_values=self.empty_lines_in_values,
|
||||||
dict_type=self.dict_type,
|
dict_type=self.dict_type,
|
||||||
strict=self.strict,
|
strict=self.strict,
|
||||||
|
@ -812,6 +814,7 @@ class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
|
||||||
class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
|
class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
|
||||||
delimiters = (':=', '$')
|
delimiters = (':=', '$')
|
||||||
comment_prefixes = ('//', '"')
|
comment_prefixes = ('//', '"')
|
||||||
|
inline_comment_prefixes = ('//', '"')
|
||||||
|
|
||||||
class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
|
class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
|
||||||
default_section = 'general'
|
default_section = 'general'
|
||||||
|
@ -888,10 +891,12 @@ class RawConfigParserTestCase(BasicTestCase):
|
||||||
class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
|
class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
|
||||||
delimiters = (':=', '$')
|
delimiters = (':=', '$')
|
||||||
comment_prefixes = ('//', '"')
|
comment_prefixes = ('//', '"')
|
||||||
|
inline_comment_prefixes = ('//', '"')
|
||||||
|
|
||||||
class RawConfigParserTestSambaConf(BasicTestCase):
|
class RawConfigParserTestSambaConf(CfgParserTestCaseClass):
|
||||||
config_class = configparser.RawConfigParser
|
config_class = configparser.RawConfigParser
|
||||||
comment_prefixes = ('#', ';', '//', '----')
|
comment_prefixes = ('#', ';', '----')
|
||||||
|
inline_comment_prefixes = ('//',)
|
||||||
empty_lines_in_values = False
|
empty_lines_in_values = False
|
||||||
|
|
||||||
def test_reading(self):
|
def test_reading(self):
|
||||||
|
@ -1074,7 +1079,8 @@ class SortedTestCase(RawConfigParserTestCase):
|
||||||
|
|
||||||
class CompatibleTestCase(CfgParserTestCaseClass):
|
class CompatibleTestCase(CfgParserTestCaseClass):
|
||||||
config_class = configparser.RawConfigParser
|
config_class = configparser.RawConfigParser
|
||||||
comment_prefixes = configparser._COMPATIBLE
|
comment_prefixes = '#;'
|
||||||
|
inline_comment_prefixes = ';'
|
||||||
|
|
||||||
def test_comment_handling(self):
|
def test_comment_handling(self):
|
||||||
config_string = textwrap.dedent("""\
|
config_string = textwrap.dedent("""\
|
||||||
|
|
Loading…
Reference in New Issue