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
|
||||
characters (``#`` and ``;`` by default [1]_). Comments may appear on
|
||||
their own on an otherwise empty line, or may be entered on lines holding
|
||||
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]_.
|
||||
their own on an otherwise empty line, possibly indented. [1]_
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[Simple Values]
|
||||
key: value
|
||||
spaces in keys: allowed
|
||||
spaces in values: allowed as well
|
||||
you can also use = to delimit keys from values
|
||||
key=value
|
||||
spaces in keys=allowed
|
||||
spaces in values=allowed as well
|
||||
spaces around the delimiter = obviously
|
||||
you can also use : to delimit keys from values
|
||||
|
||||
[All Values Are Strings]
|
||||
values like this: 1000000
|
||||
|
@ -261,12 +258,14 @@ For example:
|
|||
key_without_value
|
||||
empty string value here =
|
||||
|
||||
[You can use comments] ; after a useful line
|
||||
; in an empty line
|
||||
after: a_value ; here's another comment
|
||||
inside: a ;comment
|
||||
multiline ;comment
|
||||
value! ;comment
|
||||
[You can use comments]
|
||||
# like this
|
||||
; or this
|
||||
|
||||
# By default only in an empty line.
|
||||
# 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]
|
||||
can_values_be_as_well = True
|
||||
|
@ -509,7 +508,8 @@ the :meth:`__init__` options:
|
|||
... skip-external-locking
|
||||
... old_passwords = 1
|
||||
... 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.read_string(sample_config)
|
||||
|
@ -536,28 +536,78 @@ the :meth:`__init__` options:
|
|||
See also the *space_around_delimiters* argument to
|
||||
:meth:`ConfigParser.write`.
|
||||
|
||||
* *comment_prefixes*, default value: ``_COMPATIBLE`` (``'#'`` valid on empty
|
||||
lines, ``';'`` valid also on non-empty lines)
|
||||
* *comment_prefixes*, default value: ``('#', ';')``
|
||||
|
||||
Comment prefixes are strings that indicate the start of a valid comment
|
||||
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.
|
||||
This is obviously dictated by backwards compatibiliy. A more predictable
|
||||
approach would be to specify prefixes as ``('#', ';')`` which will allow for
|
||||
both prefixes to be used in non-empty lines.
|
||||
* *inline_comment_prefixes*, default value: ``None``
|
||||
|
||||
Comment prefixes are strings that indicate the start of a valid comment within
|
||||
a config file. *comment_prefixes* are used only on otherwise empty lines
|
||||
(optionally indented) whereas *inline_comment_prefixes* can be used after
|
||||
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
|
||||
leaving characters out of *comment_prefixes* is a way of ensuring they can be
|
||||
used as parts of keys or values.
|
||||
using *inline_comment_prefixes* may prevent users from specifying option
|
||||
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`,
|
||||
:meth:`read_string` or :meth:`read_dict`). The default is ``False`` only
|
||||
because of backwards compatibility reasons. It is recommended to use strict
|
||||
:meth:`read_string` or :meth:`read_dict`). It is recommended to use strict
|
||||
parsers in new applications.
|
||||
|
||||
.. versionchanged:: 3.2
|
||||
In previous versions of :mod:`configparser` behaviour matched
|
||||
``strict=False``.
|
||||
|
||||
* *empty_lines_in_values*, default value: ``True``
|
||||
|
||||
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 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
|
||||
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
|
||||
advanced variant inspired by ``zc.buildout``. More on the subject in the
|
||||
`dedicated documentation section <#interpolation-of-values>`_.
|
||||
|
||||
.. note:: :class:`RawConfigParser` is using ``None`` by default.
|
||||
:class:`RawConfigParser` has a default value of ``None``.
|
||||
|
||||
|
||||
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
|
||||
--------------------
|
||||
|
||||
.. 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
|
||||
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
|
||||
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
|
||||
*comment_prefixes* is a special value that indicates that ``;`` and ``#`` can
|
||||
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,
|
||||
string or dictionary), raising :exc:`DuplicateSectionError` or
|
||||
:exc:`DuplicateOptionError`. When *empty_lines_in_values* is ``False``
|
||||
|
@ -1043,7 +1094,7 @@ ConfigParser 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
|
||||
by default and unsafe ``add_section`` and ``set`` methods.
|
||||
|
|
|
@ -15,8 +15,9 @@ ConfigParser -- responsible for parsing a list of
|
|||
methods:
|
||||
|
||||
__init__(defaults=None, dict_type=_default_dict, allow_no_value=False,
|
||||
delimiters=('=', ':'), comment_prefixes=_COMPATIBLE,
|
||||
strict=False, empty_lines_in_values=True):
|
||||
delimiters=('=', ':'), comment_prefixes=('#', ';'),
|
||||
inline_comment_prefixes=None, strict=True,
|
||||
empty_lines_in_values=True):
|
||||
Create the parser. When `defaults' is given, it is initialized into the
|
||||
dictionary or intrinsic defaults. The keys must be strings, the values
|
||||
must be appropriate for %()s string interpolation.
|
||||
|
@ -29,11 +30,15 @@ ConfigParser -- responsible for parsing a list of
|
|||
that divide keys from values.
|
||||
|
||||
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
|
||||
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
|
||||
marks the end of an option. Otherwise, internal empty lines of
|
||||
|
@ -340,11 +345,6 @@ class MissingSectionHeaderError(ParsingError):
|
|||
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
|
||||
# option is not found it to raise an exception. Created to enable `None' as
|
||||
# a valid fallback value.
|
||||
|
@ -592,8 +592,8 @@ class RawConfigParser(MutableMapping):
|
|||
|
||||
def __init__(self, defaults=None, dict_type=_default_dict,
|
||||
allow_no_value=False, *, delimiters=('=', ':'),
|
||||
comment_prefixes=_COMPATIBLE, strict=False,
|
||||
empty_lines_in_values=True,
|
||||
comment_prefixes=('#', ';'), inline_comment_prefixes=None,
|
||||
strict=True, empty_lines_in_values=True,
|
||||
default_section=DEFAULTSECT,
|
||||
interpolation=_UNSET):
|
||||
|
||||
|
@ -616,12 +616,8 @@ class RawConfigParser(MutableMapping):
|
|||
else:
|
||||
self._optcre = re.compile(self._OPT_TMPL.format(delim=d),
|
||||
re.VERBOSE)
|
||||
if comment_prefixes is _COMPATIBLE:
|
||||
self._startonly_comment_prefixes = ('#',)
|
||||
self._comment_prefixes = (';',)
|
||||
else:
|
||||
self._startonly_comment_prefixes = ()
|
||||
self._comment_prefixes = tuple(comment_prefixes or ())
|
||||
self._comment_prefixes = tuple(comment_prefixes or ())
|
||||
self._inline_comment_prefixes = tuple(inline_comment_prefixes or ())
|
||||
self._strict = strict
|
||||
self._allow_no_value = allow_no_value
|
||||
self._empty_lines_in_values = empty_lines_in_values
|
||||
|
@ -989,18 +985,18 @@ class RawConfigParser(MutableMapping):
|
|||
indent_level = 0
|
||||
e = None # None, or an exception
|
||||
for lineno, line in enumerate(fp, start=1):
|
||||
# strip full line comments
|
||||
comment_start = None
|
||||
for prefix in self._startonly_comment_prefixes:
|
||||
if line.strip().startswith(prefix):
|
||||
comment_start = 0
|
||||
break
|
||||
# strip inline comments
|
||||
for prefix in self._comment_prefixes:
|
||||
for prefix in self._inline_comment_prefixes:
|
||||
index = line.find(prefix)
|
||||
if index == 0 or (index > 0 and line[index-1].isspace()):
|
||||
comment_start = index
|
||||
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()
|
||||
if not value:
|
||||
if self._empty_lines_in_values:
|
||||
|
|
|
@ -29,6 +29,7 @@ class CfgParserTestCaseClass(unittest.TestCase):
|
|||
allow_no_value = False
|
||||
delimiters = ('=', ':')
|
||||
comment_prefixes = (';', '#')
|
||||
inline_comment_prefixes = (';', '#')
|
||||
empty_lines_in_values = True
|
||||
dict_type = configparser._default_dict
|
||||
strict = False
|
||||
|
@ -41,6 +42,7 @@ class CfgParserTestCaseClass(unittest.TestCase):
|
|||
allow_no_value=self.allow_no_value,
|
||||
delimiters=self.delimiters,
|
||||
comment_prefixes=self.comment_prefixes,
|
||||
inline_comment_prefixes=self.inline_comment_prefixes,
|
||||
empty_lines_in_values=self.empty_lines_in_values,
|
||||
dict_type=self.dict_type,
|
||||
strict=self.strict,
|
||||
|
@ -812,6 +814,7 @@ class ConfigParserTestCaseLegacyInterpolation(ConfigParserTestCase):
|
|||
class ConfigParserTestCaseNonStandardDelimiters(ConfigParserTestCase):
|
||||
delimiters = (':=', '$')
|
||||
comment_prefixes = ('//', '"')
|
||||
inline_comment_prefixes = ('//', '"')
|
||||
|
||||
class ConfigParserTestCaseNonStandardDefaultSection(ConfigParserTestCase):
|
||||
default_section = 'general'
|
||||
|
@ -888,10 +891,12 @@ class RawConfigParserTestCase(BasicTestCase):
|
|||
class RawConfigParserTestCaseNonStandardDelimiters(RawConfigParserTestCase):
|
||||
delimiters = (':=', '$')
|
||||
comment_prefixes = ('//', '"')
|
||||
inline_comment_prefixes = ('//', '"')
|
||||
|
||||
class RawConfigParserTestSambaConf(BasicTestCase):
|
||||
class RawConfigParserTestSambaConf(CfgParserTestCaseClass):
|
||||
config_class = configparser.RawConfigParser
|
||||
comment_prefixes = ('#', ';', '//', '----')
|
||||
comment_prefixes = ('#', ';', '----')
|
||||
inline_comment_prefixes = ('//',)
|
||||
empty_lines_in_values = False
|
||||
|
||||
def test_reading(self):
|
||||
|
@ -1074,7 +1079,8 @@ class SortedTestCase(RawConfigParserTestCase):
|
|||
|
||||
class CompatibleTestCase(CfgParserTestCaseClass):
|
||||
config_class = configparser.RawConfigParser
|
||||
comment_prefixes = configparser._COMPATIBLE
|
||||
comment_prefixes = '#;'
|
||||
inline_comment_prefixes = ';'
|
||||
|
||||
def test_comment_handling(self):
|
||||
config_string = textwrap.dedent("""\
|
||||
|
|
Loading…
Reference in New Issue