Issue 10499: Modular interpolation in configparser
This commit is contained in:
parent
ecace28ef4
commit
b6a6f5f886
|
@ -17,11 +17,10 @@
|
||||||
single: ini file
|
single: ini file
|
||||||
single: Windows ini file
|
single: Windows ini file
|
||||||
|
|
||||||
This module provides the classes :class:`RawConfigParser` and
|
This module provides the :class:`SafeConfigParser` class which implements
|
||||||
:class:`SafeConfigParser`. They implement a basic configuration
|
a basic configuration language which provides a structure similar to what's
|
||||||
language which provides a structure similar to what's found in Microsoft
|
found in Microsoft Windows INI files. You can use this to write Python
|
||||||
Windows INI files. You can use this to write Python programs which can be
|
programs which can be customized by end users easily.
|
||||||
customized by end users easily.
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
@ -34,6 +33,10 @@ customized by end users easily.
|
||||||
Support for a creating Unix shell-like mini-languages which can be used
|
Support for a creating Unix shell-like mini-languages which can be used
|
||||||
as an alternate format for application configuration files.
|
as an alternate format for application configuration files.
|
||||||
|
|
||||||
|
Module :mod:`json`
|
||||||
|
The json module implements a subset of JavaScript syntax which can also
|
||||||
|
be used for this purpose.
|
||||||
|
|
||||||
|
|
||||||
Quick Start
|
Quick Start
|
||||||
-----------
|
-----------
|
||||||
|
@ -64,7 +67,7 @@ creating the above configuration file programatically.
|
||||||
.. doctest::
|
.. doctest::
|
||||||
|
|
||||||
>>> import configparser
|
>>> import configparser
|
||||||
>>> config = configparser.RawConfigParser()
|
>>> config = configparser.SafeConfigParser()
|
||||||
>>> config['DEFAULT'] = {'ServerAliveInterval': '45',
|
>>> config['DEFAULT'] = {'ServerAliveInterval': '45',
|
||||||
... 'Compression': 'yes',
|
... 'Compression': 'yes',
|
||||||
... 'CompressionLevel': '9'}
|
... 'CompressionLevel': '9'}
|
||||||
|
@ -89,7 +92,7 @@ back and explore the data it holds.
|
||||||
.. doctest::
|
.. doctest::
|
||||||
|
|
||||||
>>> import configparser
|
>>> import configparser
|
||||||
>>> config = configparser.RawConfigParser()
|
>>> config = configparser.SafeConfigParser()
|
||||||
>>> config.sections()
|
>>> config.sections()
|
||||||
[]
|
[]
|
||||||
>>> config.read('example.ini')
|
>>> config.read('example.ini')
|
||||||
|
@ -233,19 +236,22 @@ by a whitespace character to be recognized as a comment. For backwards
|
||||||
compatibility, by default only ``;`` starts an inline comment, while
|
compatibility, by default only ``;`` starts an inline comment, while
|
||||||
``#`` does not [1]_.
|
``#`` does not [1]_.
|
||||||
|
|
||||||
On top of the core functionality, :class:`SafeConfigParser` supports
|
|
||||||
interpolation. This means values can contain format strings which refer to
|
|
||||||
other values in the same section, or values in a special ``DEFAULT`` section
|
|
||||||
[1]_. Additional defaults can be provided on initialization.
|
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
|
|
||||||
[Paths]
|
[Simple Values]
|
||||||
home_dir: /Users
|
key: value
|
||||||
my_dir: %(home_dir)s/lumberjack
|
spaces in keys: allowed
|
||||||
my_pictures: %(my_dir)s/Pictures
|
spaces in values: allowed as well
|
||||||
|
you can also use = to delimit keys from values
|
||||||
|
|
||||||
|
[All Values Are Strings]
|
||||||
|
values like this: 1000000
|
||||||
|
or this: 3.14159265359
|
||||||
|
are they treated as numbers? : no
|
||||||
|
integers, floats and booleans are held as: strings
|
||||||
|
can use the API to get converted values directly: true
|
||||||
|
|
||||||
[Multiline Values]
|
[Multiline Values]
|
||||||
chorus: I'm a lumberjack, and I'm okay
|
chorus: I'm a lumberjack, and I'm okay
|
||||||
|
@ -273,17 +279,81 @@ For example:
|
||||||
of a value
|
of a value
|
||||||
# Did I mention we can indent comments, too?
|
# Did I mention we can indent comments, too?
|
||||||
|
|
||||||
In the example above, :class:`SafeConfigParser` would resolve ``%(home_dir)s``
|
|
||||||
to the value of ``home_dir`` (``/Users`` in this case). ``%(my_dir)s`` in
|
|
||||||
effect would resolve to ``/Users/lumberjack``. All interpolations are done on
|
|
||||||
demand so keys used in the chain of references do not have to be specified in
|
|
||||||
any specific order in the configuration file.
|
|
||||||
|
|
||||||
:class:`RawConfigParser` would simply return ``%(my_dir)s/Pictures`` as the
|
Interpolation of values
|
||||||
value of ``my_pictures`` and ``%(home_dir)s/lumberjack`` as the value of
|
-----------------------
|
||||||
``my_dir``. Other features presented in the example are handled in the same
|
|
||||||
manner by both parsers.
|
|
||||||
|
|
||||||
|
On top of the core functionality, :class:`SafeConfigParser` supports
|
||||||
|
interpolation. This means values can be preprocessed before returning them
|
||||||
|
from ``get()`` calls.
|
||||||
|
|
||||||
|
.. class:: BasicInterpolation()
|
||||||
|
|
||||||
|
The default implementation used by :class:`SafeConfigParser`. It enables
|
||||||
|
values to contain format strings which refer to other values in the same
|
||||||
|
section, or values in the special default section [1]_. Additional default
|
||||||
|
values can be provided on initialization.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[Paths]
|
||||||
|
home_dir: /Users
|
||||||
|
my_dir: %(home_dir)s/lumberjack
|
||||||
|
my_pictures: %(my_dir)s/Pictures
|
||||||
|
|
||||||
|
|
||||||
|
In the example above, :class:`SafeConfigParser` with *interpolation* set to
|
||||||
|
``BasicInterpolation()`` would resolve ``%(home_dir)s`` to the value of
|
||||||
|
``home_dir`` (``/Users`` in this case). ``%(my_dir)s`` in effect would
|
||||||
|
resolve to ``/Users/lumberjack``. All interpolations are done on demand so
|
||||||
|
keys used in the chain of references do not have to be specified in any
|
||||||
|
specific order in the configuration file.
|
||||||
|
|
||||||
|
With ``interpolation`` set to ``None``, the parser would simply return
|
||||||
|
``%(my_dir)s/Pictures`` as the value of ``my_pictures`` and
|
||||||
|
``%(home_dir)s/lumberjack`` as the value of ``my_dir``.
|
||||||
|
|
||||||
|
.. class:: ExtendedInterpolation()
|
||||||
|
|
||||||
|
An alternative handler for interpolation which implements a more advanced
|
||||||
|
syntax, used for instance in ``zc.buildout``. Extended interpolation is
|
||||||
|
using ``${section:option}`` to denote a value from a foreign section.
|
||||||
|
Interpolation can span multiple levels. For convenience, if the ``section:``
|
||||||
|
part is omitted, interpolation defaults to the current section (and possibly
|
||||||
|
the default values from the special section).
|
||||||
|
|
||||||
|
For example, the configuration specified above with basic interpolation,
|
||||||
|
would look like this with extended interpolation:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[Paths]
|
||||||
|
home_dir: /Users
|
||||||
|
my_dir: ${home_dir}/lumberjack
|
||||||
|
my_pictures: ${my_dir}/Pictures
|
||||||
|
|
||||||
|
Values from other sections can be fetched as well:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[Common]
|
||||||
|
home_dir: /Users
|
||||||
|
library_dir: /Library
|
||||||
|
system_dir: /System
|
||||||
|
macports_dir: /opt/local
|
||||||
|
|
||||||
|
[Frameworks]
|
||||||
|
Python: 3.2
|
||||||
|
path: ${Common:system_dir}/Library/Frameworks/
|
||||||
|
|
||||||
|
[Arthur]
|
||||||
|
nickname: Two Sheds
|
||||||
|
last_name: Jackson
|
||||||
|
my_dir: ${Common:home_dir}/twosheds
|
||||||
|
my_pictures: ${my_dir}/Pictures
|
||||||
|
python_dir: ${Frameworks:path}/Python/Versions/${Frameworks:Python}
|
||||||
|
|
||||||
Mapping Protocol Access
|
Mapping Protocol Access
|
||||||
-----------------------
|
-----------------------
|
||||||
|
@ -350,9 +420,9 @@ the :meth:`__init__` options:
|
||||||
* *defaults*, default value: ``None``
|
* *defaults*, default value: ``None``
|
||||||
|
|
||||||
This option accepts a dictionary of key-value pairs which will be initially
|
This option accepts a dictionary of key-value pairs which will be initially
|
||||||
put in the ``DEFAULTSECT``. This makes for an elegant way to support concise
|
put in the ``DEFAULT`` section. This makes for an elegant way to support
|
||||||
configuration files that don't specify values which are the same as the
|
concise configuration files that don't specify values which are the same as
|
||||||
documented default.
|
the documented default.
|
||||||
|
|
||||||
Hint: if you want to specify default values for a specific section, use
|
Hint: if you want to specify default values for a specific section, use
|
||||||
:meth:`read_dict` before you read the actual file.
|
:meth:`read_dict` before you read the actual file.
|
||||||
|
@ -374,7 +444,7 @@ the :meth:`__init__` options:
|
||||||
|
|
||||||
.. doctest::
|
.. doctest::
|
||||||
|
|
||||||
>>> parser = configparser.RawConfigParser()
|
>>> parser = configparser.SafeConfigParser()
|
||||||
>>> parser.read_dict({'section1': {'key1': 'value1',
|
>>> parser.read_dict({'section1': {'key1': 'value1',
|
||||||
... 'key2': 'value2',
|
... 'key2': 'value2',
|
||||||
... 'key3': 'value3'},
|
... 'key3': 'value3'},
|
||||||
|
@ -395,7 +465,7 @@ the :meth:`__init__` options:
|
||||||
.. doctest::
|
.. doctest::
|
||||||
|
|
||||||
>>> from collections import OrderedDict
|
>>> from collections import OrderedDict
|
||||||
>>> parser = configparser.RawConfigParser()
|
>>> parser = configparser.SafeConfigParser()
|
||||||
>>> parser.read_dict(
|
>>> parser.read_dict(
|
||||||
... OrderedDict((
|
... OrderedDict((
|
||||||
... ('s1',
|
... ('s1',
|
||||||
|
@ -441,7 +511,7 @@ the :meth:`__init__` options:
|
||||||
... skip-bdb
|
... skip-bdb
|
||||||
... skip-innodb # we don't need ACID today
|
... skip-innodb # we don't need ACID today
|
||||||
... """
|
... """
|
||||||
>>> config = configparser.RawConfigParser(allow_no_value=True)
|
>>> config = configparser.SafeConfigParser(allow_no_value=True)
|
||||||
>>> config.read_string(sample_config)
|
>>> config.read_string(sample_config)
|
||||||
|
|
||||||
>>> # Settings with values are treated as before:
|
>>> # Settings with values are treated as before:
|
||||||
|
@ -464,7 +534,7 @@ the :meth:`__init__` options:
|
||||||
This means values (but not keys) can contain the delimiters.
|
This means values (but not keys) can contain the delimiters.
|
||||||
|
|
||||||
See also the *space_around_delimiters* argument to
|
See also the *space_around_delimiters* argument to
|
||||||
:meth:`RawConfigParser.write`.
|
:meth:`SafeConfigParser.write`.
|
||||||
|
|
||||||
* *comment_prefixes*, default value: ``_COMPATIBLE`` (``'#'`` valid on empty
|
* *comment_prefixes*, default value: ``_COMPATIBLE`` (``'#'`` valid on empty
|
||||||
lines, ``';'`` valid also on non-empty lines)
|
lines, ``';'`` valid also on non-empty lines)
|
||||||
|
@ -512,6 +582,31 @@ the :meth:`__init__` options:
|
||||||
will make empty lines split keys every time. In the example above, it would
|
will make empty lines split keys every time. In the example above, it would
|
||||||
produce two keys, ``key`` and ``this``.
|
produce two keys, ``key`` and ``this``.
|
||||||
|
|
||||||
|
* *default_section*, default value: ``configparser.DEFAULTSECT`` (that is:
|
||||||
|
``"DEFAULT"``)
|
||||||
|
|
||||||
|
The convention of allowing a special section of default values for other
|
||||||
|
sections or interpolation purposes is a powerful concept of this library,
|
||||||
|
letting users create complex declarative configurations. This section is
|
||||||
|
normally called ``"DEFAULT"`` but this can be customized to point to any
|
||||||
|
other valid section name. Some typical values include: ``"general"`` or
|
||||||
|
``"common"``. The name provided is used for recognizing default sections when
|
||||||
|
reading from any source and is used when writing configuration back to
|
||||||
|
a file. Its current value can be retrieved using the
|
||||||
|
``parser_instance.default_section`` attribute and may be modified at runtime
|
||||||
|
(i.e. to convert files from one format to another).
|
||||||
|
|
||||||
|
* *interpolation*, default value: ``configparser.BasicInterpolation``
|
||||||
|
|
||||||
|
Interpolation behaviour may be customized by providing a custom handler
|
||||||
|
through the *interpolation* argument. ``None`` can be used to turn off
|
||||||
|
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 and
|
||||||
|
:class:`ConfigParser` is using ``configparser.BrokenInterpolation``.
|
||||||
|
|
||||||
|
|
||||||
More advanced customization may be achieved by overriding default values of
|
More advanced customization may be achieved by overriding default values of
|
||||||
these parser attributes. The defaults are defined on the classes, so they
|
these parser attributes. The defaults are defined on the classes, so they
|
||||||
|
@ -527,7 +622,7 @@ may be overriden by subclasses or by attribute assignment.
|
||||||
|
|
||||||
.. doctest::
|
.. doctest::
|
||||||
|
|
||||||
>>> custom = configparser.RawConfigParser()
|
>>> custom = configparser.SafeConfigParser()
|
||||||
>>> custom['section1'] = {'funky': 'nope'}
|
>>> custom['section1'] = {'funky': 'nope'}
|
||||||
>>> custom['section1'].getboolean('funky')
|
>>> custom['section1'].getboolean('funky')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
|
@ -557,7 +652,7 @@ may be overriden by subclasses or by attribute assignment.
|
||||||
... [Section2]
|
... [Section2]
|
||||||
... AnotherKey = Value
|
... AnotherKey = Value
|
||||||
... """
|
... """
|
||||||
>>> typical = configparser.RawConfigParser()
|
>>> typical = configparser.SafeConfigParser()
|
||||||
>>> typical.read_string(config)
|
>>> typical.read_string(config)
|
||||||
>>> list(typical['Section1'].keys())
|
>>> list(typical['Section1'].keys())
|
||||||
['key']
|
['key']
|
||||||
|
@ -623,8 +718,7 @@ An example of reading the configuration file again::
|
||||||
if config.getboolean('Section1', 'bool'):
|
if config.getboolean('Section1', 'bool'):
|
||||||
print(config.get('Section1', 'foo'))
|
print(config.get('Section1', 'foo'))
|
||||||
|
|
||||||
To get interpolation, use :class:`SafeConfigParser` or, if
|
To get interpolation, use :class:`SafeConfigParser`::
|
||||||
you absolutely have to, a :class:`ConfigParser`::
|
|
||||||
|
|
||||||
import configparser
|
import configparser
|
||||||
|
|
||||||
|
@ -672,14 +766,14 @@ used in interpolation if an option used is not defined elsewhere. ::
|
||||||
print(config.get('Section1', 'foo')) # -> "Life is hard!"
|
print(config.get('Section1', 'foo')) # -> "Life is hard!"
|
||||||
|
|
||||||
|
|
||||||
.. _rawconfigparser-objects:
|
.. _safeconfigparser-objects:
|
||||||
|
|
||||||
RawConfigParser Objects
|
SafeConfigParser Objects
|
||||||
-----------------------
|
------------------------
|
||||||
|
|
||||||
.. class:: RawConfigParser(defaults=None, dict_type=collections.OrderedDict, allow_no_value=False, delimiters=('=', ':'), comment_prefixes=_COMPATIBLE, strict=False, empty_lines_in_values=True)
|
.. class:: SafeConfigParser(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())
|
||||||
|
|
||||||
The basic 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
|
||||||
will be used to create the dictionary objects for the list of sections, for
|
will be used to create the dictionary objects for the list of sections, for
|
||||||
the options within a section, and for the default values.
|
the options within a section, and for the default values.
|
||||||
|
@ -698,16 +792,33 @@ RawConfigParser Objects
|
||||||
(default: ``True``), each empty line marks the end of an option. Otherwise,
|
(default: ``True``), each empty line marks the end of an option. Otherwise,
|
||||||
internal empty lines of a multiline option are kept as part of the value.
|
internal empty lines of a multiline option are kept as part of the value.
|
||||||
When *allow_no_value* is ``True`` (default: ``False``), options without
|
When *allow_no_value* is ``True`` (default: ``False``), options without
|
||||||
values are accepted; the value presented for these is ``None``.
|
values are accepted; the value held for these is ``None`` and they are
|
||||||
|
serialized without the trailing delimiter.
|
||||||
|
|
||||||
This class does not support the magical interpolation behavior.
|
When *default_section* is given, it specifies the name for the special
|
||||||
|
section holding default values for other sections and interpolation purposes
|
||||||
|
(normally named ``"DEFAULT"``). This value can be retrieved and changed on
|
||||||
|
runtime using the ``default_section`` instance attribute.
|
||||||
|
|
||||||
|
Interpolation behaviour may be customized by providing a custom handler
|
||||||
|
through the *interpolation* argument. ``None`` can be used to turn off
|
||||||
|
interpolation completely, ``ExtendedInterpolation()`` provides a more
|
||||||
|
advanced variant inspired by ``zc.buildout``. More on the subject in the
|
||||||
|
`dedicated documentation section <#interpolation-of-values>`_.
|
||||||
|
|
||||||
|
All option names used in interpolation will be passed through the
|
||||||
|
:meth:`optionxform` method just like any other option name reference. For
|
||||||
|
example, using the default implementation of :meth:`optionxform` (which
|
||||||
|
converts option names to lower case), the values ``foo %(bar)s`` and ``foo
|
||||||
|
%(BAR)s`` are equivalent.
|
||||||
|
|
||||||
.. versionchanged:: 3.1
|
.. versionchanged:: 3.1
|
||||||
The default *dict_type* is :class:`collections.OrderedDict`.
|
The default *dict_type* is :class:`collections.OrderedDict`.
|
||||||
|
|
||||||
.. versionchanged:: 3.2
|
.. versionchanged:: 3.2
|
||||||
*allow_no_value*, *delimiters*, *comment_prefixes*, *strict* and
|
*allow_no_value*, *delimiters*, *comment_prefixes*, *strict*,
|
||||||
*empty_lines_in_values* were added.
|
*empty_lines_in_values*, *default_section* and *interpolation* were
|
||||||
|
added.
|
||||||
|
|
||||||
|
|
||||||
.. method:: defaults()
|
.. method:: defaults()
|
||||||
|
@ -717,22 +828,21 @@ RawConfigParser Objects
|
||||||
|
|
||||||
.. method:: sections()
|
.. method:: sections()
|
||||||
|
|
||||||
Return a list of the sections available; ``DEFAULT`` is not included in
|
Return a list of the sections available; the *default section* is not
|
||||||
the list.
|
included in the list.
|
||||||
|
|
||||||
|
|
||||||
.. method:: add_section(section)
|
.. method:: add_section(section)
|
||||||
|
|
||||||
Add a section named *section* to the instance. If a section by the given
|
Add a section named *section* to the instance. If a section by the given
|
||||||
name already exists, :exc:`DuplicateSectionError` is raised. If the name
|
name already exists, :exc:`DuplicateSectionError` is raised. If the
|
||||||
``DEFAULT`` (or any of it's case-insensitive variants) is passed,
|
*default section* name is passed, :exc:`ValueError` is raised.
|
||||||
:exc:`ValueError` is raised.
|
|
||||||
|
|
||||||
|
|
||||||
.. method:: has_section(section)
|
.. method:: has_section(section)
|
||||||
|
|
||||||
Indicates whether the named section is present in the configuration. The
|
Indicates whether the named *section* is present in the configuration.
|
||||||
``DEFAULT`` section is not acknowledged.
|
The *default section* is not acknowledged.
|
||||||
|
|
||||||
|
|
||||||
.. method:: options(section)
|
.. method:: options(section)
|
||||||
|
@ -742,7 +852,7 @@ RawConfigParser Objects
|
||||||
|
|
||||||
.. method:: has_option(section, option)
|
.. method:: has_option(section, option)
|
||||||
|
|
||||||
If the given section exists, and contains the given option, return
|
If the given *section* exists, and contains the given *option*, return
|
||||||
:const:`True`; otherwise return :const:`False`.
|
:const:`True`; otherwise return :const:`False`.
|
||||||
|
|
||||||
|
|
||||||
|
@ -750,19 +860,20 @@ RawConfigParser Objects
|
||||||
|
|
||||||
Attempt to read and parse a list of filenames, returning a list of
|
Attempt to read and parse a list of filenames, returning a list of
|
||||||
filenames which were successfully parsed. If *filenames* is a string, it
|
filenames which were successfully parsed. If *filenames* is a string, it
|
||||||
is treated as a single filename. If a file named in *filenames* cannot be
|
is treated as a single filename. If a file named in *filenames* cannot
|
||||||
opened, that file will be ignored. This is designed so that you can
|
be opened, that file will be ignored. This is designed so that you can
|
||||||
specify a list of potential configuration file locations (for example, the
|
specify a list of potential configuration file locations (for example,
|
||||||
current directory, the user's home directory, and some system-wide
|
the current directory, the user's home directory, and some system-wide
|
||||||
directory), and all existing configuration files in the list will be read.
|
directory), and all existing configuration files in the list will be
|
||||||
If none of the named files exist, the :class:`ConfigParser` instance will
|
read. If none of the named files exist, the :class:`ConfigParser`
|
||||||
contain an empty dataset. An application which requires initial values to
|
instance will contain an empty dataset. An application which requires
|
||||||
be loaded from a file should load the required file or files using
|
initial values to be loaded from a file should load the required file or
|
||||||
:meth:`read_file` before calling :meth:`read` for any optional files::
|
files using :meth:`read_file` before calling :meth:`read` for any
|
||||||
|
optional files::
|
||||||
|
|
||||||
import configparser, os
|
import configparser, os
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
config = configparser.SafeConfigParser()
|
||||||
config.read_file(open('defaults.cfg'))
|
config.read_file(open('defaults.cfg'))
|
||||||
config.read(['site.cfg', os.path.expanduser('~/.myapp.cfg')],
|
config.read(['site.cfg', os.path.expanduser('~/.myapp.cfg')],
|
||||||
encoding='cp1250')
|
encoding='cp1250')
|
||||||
|
@ -810,154 +921,6 @@ RawConfigParser Objects
|
||||||
|
|
||||||
.. versionadded:: 3.2
|
.. versionadded:: 3.2
|
||||||
|
|
||||||
.. method:: get(section, option, [vars, fallback])
|
|
||||||
|
|
||||||
Get an *option* value for the named *section*. If *vars* is provided, it
|
|
||||||
must be a dictionary. The *option* is looked up in *vars* (if provided),
|
|
||||||
*section*, and in *DEFAULTSECT* in that order. If the key is not found
|
|
||||||
and *fallback* is provided, it is used as a fallback value. ``None`` can
|
|
||||||
be provided as a *fallback* value.
|
|
||||||
|
|
||||||
.. versionchanged:: 3.2
|
|
||||||
Arguments *vars* and *fallback* are keyword only to protect users from
|
|
||||||
trying to use the third argument as the *fallback* fallback (especially
|
|
||||||
when using the mapping protocol).
|
|
||||||
|
|
||||||
|
|
||||||
.. method:: getint(section, option, [vars, fallback])
|
|
||||||
|
|
||||||
A convenience method which coerces the *option* in the specified *section*
|
|
||||||
to an integer. See :meth:`get` for explanation of *vars* and *fallback*.
|
|
||||||
|
|
||||||
|
|
||||||
.. method:: getfloat(section, option, [vars, fallback])
|
|
||||||
|
|
||||||
A convenience method which coerces the *option* in the specified *section*
|
|
||||||
to a floating point number. See :meth:`get` for explanation of *vars* and
|
|
||||||
*fallback*.
|
|
||||||
|
|
||||||
|
|
||||||
.. method:: getboolean(section, option, [vars, fallback])
|
|
||||||
|
|
||||||
A convenience method which coerces the *option* in the specified *section*
|
|
||||||
to a Boolean value. Note that the accepted values for the option are
|
|
||||||
``"1"``, ``"yes"``, ``"true"``, and ``"on"``, which cause this method to
|
|
||||||
return ``True``, and ``"0"``, ``"no"``, ``"false"``, and ``"off"``, which
|
|
||||||
cause it to return ``False``. These string values are checked in a
|
|
||||||
case-insensitive manner. Any other value will cause it to raise
|
|
||||||
:exc:`ValueError`. See :meth:`get` for explanation of *vars* and
|
|
||||||
*fallback*.
|
|
||||||
|
|
||||||
|
|
||||||
.. method:: items(section)
|
|
||||||
|
|
||||||
Return a list of *name*, *value* pairs for each option in the given
|
|
||||||
*section*.
|
|
||||||
|
|
||||||
|
|
||||||
.. method:: set(section, option, value)
|
|
||||||
|
|
||||||
If the given section exists, set the given option to the specified value;
|
|
||||||
otherwise raise :exc:`NoSectionError`. While it is possible to use
|
|
||||||
:class:`RawConfigParser` (or :class:`ConfigParser` with *raw* parameters
|
|
||||||
set to true) for *internal* storage of non-string values, full
|
|
||||||
functionality (including interpolation and output to files) can only be
|
|
||||||
achieved using string values.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
This method lets users assign non-string values to keys internally.
|
|
||||||
This behaviour is unsupported and will cause errors when attempting to
|
|
||||||
write to a file or get it in non-raw mode. **Use the mapping protocol
|
|
||||||
API** which does not allow such assignments to take place.
|
|
||||||
|
|
||||||
|
|
||||||
.. method:: write(fileobject, space_around_delimiters=True)
|
|
||||||
|
|
||||||
Write a representation of the configuration to the specified :term:`file
|
|
||||||
object`, which must be opened in text mode (accepting strings). This
|
|
||||||
representation can be parsed by a future :meth:`read` call. If
|
|
||||||
*space_around_delimiters* is true, delimiters between
|
|
||||||
keys and values are surrounded by spaces.
|
|
||||||
|
|
||||||
|
|
||||||
.. method:: remove_option(section, option)
|
|
||||||
|
|
||||||
Remove the specified *option* from the specified *section*. If the
|
|
||||||
section does not exist, raise :exc:`NoSectionError`. If the option
|
|
||||||
existed to be removed, return :const:`True`; otherwise return
|
|
||||||
:const:`False`.
|
|
||||||
|
|
||||||
|
|
||||||
.. method:: remove_section(section)
|
|
||||||
|
|
||||||
Remove the specified *section* from the configuration. If the section in
|
|
||||||
fact existed, return ``True``. Otherwise return ``False``.
|
|
||||||
|
|
||||||
|
|
||||||
.. method:: optionxform(option)
|
|
||||||
|
|
||||||
Transforms the option name *option* as found in an input file or as passed
|
|
||||||
in by client code to the form that should be used in the internal
|
|
||||||
structures. The default implementation returns a lower-case version of
|
|
||||||
*option*; subclasses may override this or client code can set an attribute
|
|
||||||
of this name on instances to affect this behavior.
|
|
||||||
|
|
||||||
You don't need to subclass the parser to use this method, you can also
|
|
||||||
set it on an instance, to a function that takes a string argument and
|
|
||||||
returns a string. Setting it to ``str``, for example, would make option
|
|
||||||
names case sensitive::
|
|
||||||
|
|
||||||
cfgparser = ConfigParser()
|
|
||||||
cfgparser.optionxform = str
|
|
||||||
|
|
||||||
Note that when reading configuration files, whitespace around the option
|
|
||||||
names is stripped before :meth:`optionxform` is called.
|
|
||||||
|
|
||||||
|
|
||||||
.. method:: readfp(fp, filename=None)
|
|
||||||
|
|
||||||
.. deprecated:: 3.2
|
|
||||||
Use :meth:`read_file` instead.
|
|
||||||
|
|
||||||
|
|
||||||
.. _configparser-objects:
|
|
||||||
|
|
||||||
ConfigParser Objects
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
Whenever you can, consider using :class:`SafeConfigParser` which adds
|
|
||||||
validation and escaping for the interpolation.
|
|
||||||
|
|
||||||
The :class:`ConfigParser` class extends some methods of the
|
|
||||||
:class:`RawConfigParser` interface, adding some optional arguments.
|
|
||||||
|
|
||||||
.. class:: ConfigParser(defaults=None, dict_type=collections.OrderedDict, allow_no_value=False, delimiters=('=', ':'), comment_prefixes=_COMPATIBLE, strict=False, empty_lines_in_values=True)
|
|
||||||
|
|
||||||
Derived class of :class:`RawConfigParser` that implements the magical
|
|
||||||
interpolation feature and adds optional arguments to the :meth:`get` and
|
|
||||||
:meth:`items` methods.
|
|
||||||
|
|
||||||
:class:`SafeConfigParser` is generally recommended over this class if you
|
|
||||||
need interpolation.
|
|
||||||
|
|
||||||
The values in *defaults* must be appropriate for the ``%()s`` string
|
|
||||||
interpolation.
|
|
||||||
|
|
||||||
All option names used in interpolation will be passed through the
|
|
||||||
:meth:`optionxform` method just like any other option name reference. For
|
|
||||||
example, using the default implementation of :meth:`optionxform` (which
|
|
||||||
converts option names to lower case), the values ``foo %(bar)s`` and ``foo
|
|
||||||
%(BAR)s`` are equivalent.
|
|
||||||
|
|
||||||
.. versionchanged:: 3.1
|
|
||||||
The default *dict_type* is :class:`collections.OrderedDict`.
|
|
||||||
|
|
||||||
.. versionchanged:: 3.2
|
|
||||||
*allow_no_value*, *delimiters*, *comment_prefixes*,
|
|
||||||
*strict* and *empty_lines_in_values* were added.
|
|
||||||
|
|
||||||
|
|
||||||
.. method:: get(section, option, raw=False, [vars, fallback])
|
.. method:: get(section, option, raw=False, [vars, fallback])
|
||||||
|
|
||||||
|
@ -1010,40 +973,6 @@ The :class:`ConfigParser` class extends some methods of the
|
||||||
:meth:`get` method.
|
:meth:`get` method.
|
||||||
|
|
||||||
|
|
||||||
.. data:: MAX_INTERPOLATION_DEPTH
|
|
||||||
|
|
||||||
The maximum depth for recursive interpolation for :meth:`get` when the *raw*
|
|
||||||
parameter is false. This is relevant only for the :class:`ConfigParser` class.
|
|
||||||
|
|
||||||
|
|
||||||
.. _safeconfigparser-objects:
|
|
||||||
|
|
||||||
SafeConfigParser Objects
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
.. class:: SafeConfigParser(defaults=None, dict_type=collections.OrderedDict, allow_no_value=False, delimiters=('=', ':'), comment_prefixes=_COMPATIBLE, strict=False, empty_lines_in_values=True)
|
|
||||||
|
|
||||||
Derived class of :class:`ConfigParser` that implements a variant of the
|
|
||||||
magical interpolation feature. This implementation is more predictable as
|
|
||||||
it validates the interpolation syntax used within a configuration file.
|
|
||||||
This class also enables escaping the interpolation character (a key can have
|
|
||||||
``%`` as part of the value by specifying ``%%`` in the file).
|
|
||||||
|
|
||||||
Applications that don't require interpolation should use
|
|
||||||
:class:`RawConfigParser`, otherwise :class:`SafeConfigParser` is the best
|
|
||||||
option.
|
|
||||||
|
|
||||||
.. versionchanged:: 3.1
|
|
||||||
The default *dict_type* is :class:`collections.OrderedDict`.
|
|
||||||
|
|
||||||
.. versionchanged:: 3.2
|
|
||||||
*allow_no_value*, *delimiters*, *comment_prefixes*, *strict* and
|
|
||||||
*empty_lines_in_values* were added.
|
|
||||||
|
|
||||||
|
|
||||||
The :class:`SafeConfigParser` class implements the same extended interface
|
|
||||||
as :class:`ConfigParser`, with the following addition:
|
|
||||||
|
|
||||||
.. method:: set(section, option, value)
|
.. method:: set(section, option, value)
|
||||||
|
|
||||||
If the given section exists, set the given option to the specified value;
|
If the given section exists, set the given option to the specified value;
|
||||||
|
@ -1051,6 +980,112 @@ SafeConfigParser Objects
|
||||||
:exc:`TypeError` is raised.
|
:exc:`TypeError` is raised.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: write(fileobject, space_around_delimiters=True)
|
||||||
|
|
||||||
|
Write a representation of the configuration to the specified :term:`file
|
||||||
|
object`, which must be opened in text mode (accepting strings). This
|
||||||
|
representation can be parsed by a future :meth:`read` call. If
|
||||||
|
*space_around_delimiters* is true, delimiters between
|
||||||
|
keys and values are surrounded by spaces.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: remove_option(section, option)
|
||||||
|
|
||||||
|
Remove the specified *option* from the specified *section*. If the
|
||||||
|
section does not exist, raise :exc:`NoSectionError`. If the option
|
||||||
|
existed to be removed, return :const:`True`; otherwise return
|
||||||
|
:const:`False`.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: remove_section(section)
|
||||||
|
|
||||||
|
Remove the specified *section* from the configuration. If the section in
|
||||||
|
fact existed, return ``True``. Otherwise return ``False``.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: optionxform(option)
|
||||||
|
|
||||||
|
Transforms the option name *option* as found in an input file or as passed
|
||||||
|
in by client code to the form that should be used in the internal
|
||||||
|
structures. The default implementation returns a lower-case version of
|
||||||
|
*option*; subclasses may override this or client code can set an attribute
|
||||||
|
of this name on instances to affect this behavior.
|
||||||
|
|
||||||
|
You don't need to subclass the parser to use this method, you can also
|
||||||
|
set it on an instance, to a function that takes a string argument and
|
||||||
|
returns a string. Setting it to ``str``, for example, would make option
|
||||||
|
names case sensitive::
|
||||||
|
|
||||||
|
cfgparser = ConfigParser()
|
||||||
|
cfgparser.optionxform = str
|
||||||
|
|
||||||
|
Note that when reading configuration files, whitespace around the option
|
||||||
|
names is stripped before :meth:`optionxform` is called.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: readfp(fp, filename=None)
|
||||||
|
|
||||||
|
.. deprecated:: 3.2
|
||||||
|
Use :meth:`read_file` instead.
|
||||||
|
|
||||||
|
|
||||||
|
.. data:: MAX_INTERPOLATION_DEPTH
|
||||||
|
|
||||||
|
The maximum depth for recursive interpolation for :meth:`get` when the *raw*
|
||||||
|
parameter is false. This is relevant only when the default *interpolation*
|
||||||
|
is used.
|
||||||
|
|
||||||
|
|
||||||
|
.. _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)
|
||||||
|
|
||||||
|
Legacy variant of the :class:`SafeConfigParser` with interpolation disabled
|
||||||
|
by default and an unsafe ``set`` method.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Consider using :class:`SafeConfigParser` instead which checks types of
|
||||||
|
the values to be stored internally. If you don't want interpolation, you
|
||||||
|
can use ``SafeConfigParser(interpolation=None)``.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: set(section, option, value)
|
||||||
|
|
||||||
|
If the given section exists, set the given option to the specified value;
|
||||||
|
otherwise raise :exc:`NoSectionError`. While it is possible to use
|
||||||
|
:class:`RawConfigParser` (or :class:`ConfigParser` with *raw* parameters
|
||||||
|
set to true) for *internal* storage of non-string values, full
|
||||||
|
functionality (including interpolation and output to files) can only be
|
||||||
|
achieved using string values.
|
||||||
|
|
||||||
|
This method lets users assign non-string values to keys internally. This
|
||||||
|
behaviour is unsupported and will cause errors when attempting to write
|
||||||
|
to a file or get it in non-raw mode. **Use the mapping protocol API**
|
||||||
|
which does not allow such assignments to take place.
|
||||||
|
|
||||||
|
|
||||||
|
.. _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=BrokenInterpolation())
|
||||||
|
|
||||||
|
.. deprecated:: 3.2
|
||||||
|
Whenever you can, consider using :class:`SafeConfigParser`. The
|
||||||
|
:class:`ConfigParser` provides the same functionality but its
|
||||||
|
implementation is less predictable. It does not validate the
|
||||||
|
interpolation syntax used within a configuration file. It also does not
|
||||||
|
enable escaping the interpolation character (when using
|
||||||
|
:class:`SafeConfigParser`, a key can have ``%`` as part of the value by
|
||||||
|
specifying ``%%`` in the file). On top of that, this class doesn't ensure
|
||||||
|
whether values passed to the parser object are strings which may lead to
|
||||||
|
inconsistent internal state.
|
||||||
|
|
||||||
|
|
||||||
Exceptions
|
Exceptions
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ File Formats
|
||||||
************
|
************
|
||||||
|
|
||||||
The modules described in this chapter parse various miscellaneous file formats
|
The modules described in this chapter parse various miscellaneous file formats
|
||||||
that aren't markup languages or are related to e-mail.
|
that aren't markup languages and are not related to e-mail.
|
||||||
|
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
|
|
@ -4,22 +4,12 @@ A configuration file consists of sections, lead by a "[section]" header,
|
||||||
and followed by "name: value" entries, with continuations and such in
|
and followed by "name: value" entries, with continuations and such in
|
||||||
the style of RFC 822.
|
the style of RFC 822.
|
||||||
|
|
||||||
The option values can contain format strings which refer to other values in
|
|
||||||
the same section, or values in a special [DEFAULT] section.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
something: %(dir)s/whatever
|
|
||||||
|
|
||||||
would resolve the "%(dir)s" to the value of dir. All reference
|
|
||||||
expansions are done late, on demand.
|
|
||||||
|
|
||||||
Intrinsic defaults can be specified by passing them into the
|
Intrinsic defaults can be specified by passing them into the
|
||||||
ConfigParser constructor as a dictionary.
|
SafeConfigParser constructor as a dictionary.
|
||||||
|
|
||||||
class:
|
class:
|
||||||
|
|
||||||
ConfigParser -- responsible for parsing a list of
|
SafeConfigParser -- responsible for parsing a list of
|
||||||
configuration files, and managing the parsed database.
|
configuration files, and managing the parsed database.
|
||||||
|
|
||||||
methods:
|
methods:
|
||||||
|
@ -316,7 +306,7 @@ class ParsingError(Error):
|
||||||
def filename(self):
|
def filename(self):
|
||||||
"""Deprecated, use `source'."""
|
"""Deprecated, use `source'."""
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
"This 'filename' attribute will be removed in future versions. "
|
"The 'filename' attribute will be removed in future versions. "
|
||||||
"Use 'source' instead.",
|
"Use 'source' instead.",
|
||||||
DeprecationWarning, stacklevel=2
|
DeprecationWarning, stacklevel=2
|
||||||
)
|
)
|
||||||
|
@ -362,6 +352,204 @@ _COMPATIBLE = object()
|
||||||
_UNSET = object()
|
_UNSET = object()
|
||||||
|
|
||||||
|
|
||||||
|
class Interpolation:
|
||||||
|
"""Dummy interpolation that passes the value through with no changes."""
|
||||||
|
|
||||||
|
def before_get(self, parser, section, option, value, defaults):
|
||||||
|
return value
|
||||||
|
|
||||||
|
def before_set(self, parser, section, option, value):
|
||||||
|
return value
|
||||||
|
|
||||||
|
def before_read(self, parser, section, option, value):
|
||||||
|
return value
|
||||||
|
|
||||||
|
def before_write(self, parser, section, option, value):
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class BasicInterpolation(Interpolation):
|
||||||
|
"""Interpolation as implemented in the classic SafeConfigParser.
|
||||||
|
|
||||||
|
The option values can contain format strings which refer to other values in
|
||||||
|
the same section, or values in the special default section.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
something: %(dir)s/whatever
|
||||||
|
|
||||||
|
would resolve the "%(dir)s" to the value of dir. All reference
|
||||||
|
expansions are done late, on demand. If a user needs to use a bare % in
|
||||||
|
a configuration file, she can escape it by writing %%. Other other % usage
|
||||||
|
is considered a user error and raises `InterpolationSyntaxError'."""
|
||||||
|
|
||||||
|
_KEYCRE = re.compile(r"%\(([^)]+)\)s")
|
||||||
|
|
||||||
|
def before_get(self, parser, section, option, value, defaults):
|
||||||
|
L = []
|
||||||
|
self._interpolate_some(parser, option, L, value, section, defaults, 1)
|
||||||
|
return ''.join(L)
|
||||||
|
|
||||||
|
def before_set(self, parser, section, option, value):
|
||||||
|
tmp_value = value.replace('%%', '') # escaped percent signs
|
||||||
|
tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax
|
||||||
|
if '%' in tmp_value:
|
||||||
|
raise ValueError("invalid interpolation syntax in %r at "
|
||||||
|
"position %d" % (value, tmp_value.find('%')))
|
||||||
|
return value
|
||||||
|
|
||||||
|
def _interpolate_some(self, parser, option, accum, rest, section, map,
|
||||||
|
depth):
|
||||||
|
if depth > MAX_INTERPOLATION_DEPTH:
|
||||||
|
raise InterpolationDepthError(option, section, rest)
|
||||||
|
while rest:
|
||||||
|
p = rest.find("%")
|
||||||
|
if p < 0:
|
||||||
|
accum.append(rest)
|
||||||
|
return
|
||||||
|
if p > 0:
|
||||||
|
accum.append(rest[:p])
|
||||||
|
rest = rest[p:]
|
||||||
|
# p is no longer used
|
||||||
|
c = rest[1:2]
|
||||||
|
if c == "%":
|
||||||
|
accum.append("%")
|
||||||
|
rest = rest[2:]
|
||||||
|
elif c == "(":
|
||||||
|
m = self._KEYCRE.match(rest)
|
||||||
|
if m is None:
|
||||||
|
raise InterpolationSyntaxError(option, section,
|
||||||
|
"bad interpolation variable reference %r" % rest)
|
||||||
|
var = parser.optionxform(m.group(1))
|
||||||
|
rest = rest[m.end():]
|
||||||
|
try:
|
||||||
|
v = map[var]
|
||||||
|
except KeyError:
|
||||||
|
raise InterpolationMissingOptionError(
|
||||||
|
option, section, rest, var)
|
||||||
|
if "%" in v:
|
||||||
|
self._interpolate_some(parser, option, accum, v,
|
||||||
|
section, map, depth + 1)
|
||||||
|
else:
|
||||||
|
accum.append(v)
|
||||||
|
else:
|
||||||
|
raise InterpolationSyntaxError(
|
||||||
|
option, section,
|
||||||
|
"'%%' must be followed by '%%' or '(', "
|
||||||
|
"found: %r" % (rest,))
|
||||||
|
|
||||||
|
|
||||||
|
class ExtendedInterpolation(Interpolation):
|
||||||
|
"""Advanced variant of interpolation, supports the syntax used by
|
||||||
|
`zc.buildout'. Enables interpolation between sections."""
|
||||||
|
|
||||||
|
_KEYCRE = re.compile(r"\$\{([^}]+)\}")
|
||||||
|
|
||||||
|
def before_get(self, parser, section, option, value, defaults):
|
||||||
|
L = []
|
||||||
|
self._interpolate_some(parser, option, L, value, section, defaults, 1)
|
||||||
|
return ''.join(L)
|
||||||
|
|
||||||
|
def before_set(self, parser, section, option, value):
|
||||||
|
tmp_value = value.replace('$$', '') # escaped dollar signs
|
||||||
|
tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax
|
||||||
|
if '$' in tmp_value:
|
||||||
|
raise ValueError("invalid interpolation syntax in %r at "
|
||||||
|
"position %d" % (value, tmp_value.find('%')))
|
||||||
|
return value
|
||||||
|
|
||||||
|
def _interpolate_some(self, parser, option, accum, rest, section, map,
|
||||||
|
depth):
|
||||||
|
if depth > MAX_INTERPOLATION_DEPTH:
|
||||||
|
raise InterpolationDepthError(option, section, rest)
|
||||||
|
while rest:
|
||||||
|
p = rest.find("$")
|
||||||
|
if p < 0:
|
||||||
|
accum.append(rest)
|
||||||
|
return
|
||||||
|
if p > 0:
|
||||||
|
accum.append(rest[:p])
|
||||||
|
rest = rest[p:]
|
||||||
|
# p is no longer used
|
||||||
|
c = rest[1:2]
|
||||||
|
if c == "$":
|
||||||
|
accum.append("$")
|
||||||
|
rest = rest[2:]
|
||||||
|
elif c == "{":
|
||||||
|
m = self._KEYCRE.match(rest)
|
||||||
|
if m is None:
|
||||||
|
raise InterpolationSyntaxError(option, section,
|
||||||
|
"bad interpolation variable reference %r" % rest)
|
||||||
|
path = parser.optionxform(m.group(1)).split(':')
|
||||||
|
rest = rest[m.end():]
|
||||||
|
sect = section
|
||||||
|
opt = option
|
||||||
|
try:
|
||||||
|
if len(path) == 1:
|
||||||
|
opt = path[0]
|
||||||
|
v = map[opt]
|
||||||
|
elif len(path) == 2:
|
||||||
|
sect = path[0]
|
||||||
|
opt = path[1]
|
||||||
|
v = parser.get(sect, opt, raw=True)
|
||||||
|
else:
|
||||||
|
raise InterpolationSyntaxError(
|
||||||
|
option, section,
|
||||||
|
"More than one ':' found: %r" % (rest,))
|
||||||
|
except KeyError:
|
||||||
|
raise InterpolationMissingOptionError(
|
||||||
|
option, section, rest, var)
|
||||||
|
if "$" in v:
|
||||||
|
self._interpolate_some(parser, opt, accum, v, sect,
|
||||||
|
dict(parser.items(sect, raw=True)),
|
||||||
|
depth + 1)
|
||||||
|
else:
|
||||||
|
accum.append(v)
|
||||||
|
else:
|
||||||
|
raise InterpolationSyntaxError(
|
||||||
|
option, section,
|
||||||
|
"'$' must be followed by '$' or '{', "
|
||||||
|
"found: %r" % (rest,))
|
||||||
|
|
||||||
|
|
||||||
|
class BrokenInterpolation(Interpolation):
|
||||||
|
"""Deprecated interpolation as implemented in the classic ConfigParser.
|
||||||
|
Use BasicInterpolation or ExtendedInterpolation instead."""
|
||||||
|
|
||||||
|
_KEYCRE = re.compile(r"%\(([^)]*)\)s|.")
|
||||||
|
|
||||||
|
def before_get(self, parser, section, option, value, vars):
|
||||||
|
rawval = value
|
||||||
|
depth = MAX_INTERPOLATION_DEPTH
|
||||||
|
while depth: # Loop through this until it's done
|
||||||
|
depth -= 1
|
||||||
|
if value and "%(" in value:
|
||||||
|
replace = functools.partial(self._interpolation_replace,
|
||||||
|
parser=parser)
|
||||||
|
value = self._KEYCRE.sub(replace, value)
|
||||||
|
try:
|
||||||
|
value = value % vars
|
||||||
|
except KeyError as e:
|
||||||
|
raise InterpolationMissingOptionError(
|
||||||
|
option, section, rawval, e.args[0])
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
if value and "%(" in value:
|
||||||
|
raise InterpolationDepthError(option, section, rawval)
|
||||||
|
return value
|
||||||
|
|
||||||
|
def before_set(self, parser, section, option, value):
|
||||||
|
return value
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _interpolation_replace(match, parser):
|
||||||
|
s = match.group(1)
|
||||||
|
if s is None:
|
||||||
|
return match.group()
|
||||||
|
else:
|
||||||
|
return "%%(%s)s" % parser.optionxform(s)
|
||||||
|
|
||||||
|
|
||||||
class RawConfigParser(MutableMapping):
|
class RawConfigParser(MutableMapping):
|
||||||
"""ConfigParser that does not do interpolation."""
|
"""ConfigParser that does not do interpolation."""
|
||||||
|
|
||||||
|
@ -388,7 +576,8 @@ class RawConfigParser(MutableMapping):
|
||||||
# space/tab
|
# space/tab
|
||||||
(?P<value>.*))?$ # everything up to eol
|
(?P<value>.*))?$ # everything up to eol
|
||||||
"""
|
"""
|
||||||
|
# Interpolation algorithm to be used if the user does not specify another
|
||||||
|
_DEFAULT_INTERPOLATION = Interpolation()
|
||||||
# Compiled regular expression for matching sections
|
# Compiled regular expression for matching sections
|
||||||
SECTCRE = re.compile(_SECT_TMPL, re.VERBOSE)
|
SECTCRE = re.compile(_SECT_TMPL, re.VERBOSE)
|
||||||
# Compiled regular expression for matching options with typical separators
|
# Compiled regular expression for matching options with typical separators
|
||||||
|
@ -406,7 +595,15 @@ class RawConfigParser(MutableMapping):
|
||||||
allow_no_value=False, *, delimiters=('=', ':'),
|
allow_no_value=False, *, delimiters=('=', ':'),
|
||||||
comment_prefixes=_COMPATIBLE, strict=False,
|
comment_prefixes=_COMPATIBLE, strict=False,
|
||||||
empty_lines_in_values=True,
|
empty_lines_in_values=True,
|
||||||
default_section=DEFAULTSECT):
|
default_section=DEFAULTSECT,
|
||||||
|
interpolation=_UNSET):
|
||||||
|
|
||||||
|
if self.__class__ is RawConfigParser:
|
||||||
|
warnings.warn(
|
||||||
|
"The RawConfigParser class will be removed in future versions."
|
||||||
|
" Use 'SafeConfigParser(interpolation=None)' instead.",
|
||||||
|
DeprecationWarning, stacklevel=2
|
||||||
|
)
|
||||||
self._dict = dict_type
|
self._dict = dict_type
|
||||||
self._sections = self._dict()
|
self._sections = self._dict()
|
||||||
self._defaults = self._dict()
|
self._defaults = self._dict()
|
||||||
|
@ -435,7 +632,11 @@ class RawConfigParser(MutableMapping):
|
||||||
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
|
||||||
self._default_section=default_section
|
if interpolation is _UNSET:
|
||||||
|
self._interpolation = self._DEFAULT_INTERPOLATION
|
||||||
|
else:
|
||||||
|
self._interpolation = interpolation
|
||||||
|
self.default_section=default_section
|
||||||
|
|
||||||
def defaults(self):
|
def defaults(self):
|
||||||
return self._defaults
|
return self._defaults
|
||||||
|
@ -451,7 +652,7 @@ class RawConfigParser(MutableMapping):
|
||||||
Raise DuplicateSectionError if a section by the specified name
|
Raise DuplicateSectionError if a section by the specified name
|
||||||
already exists. Raise ValueError if name is DEFAULT.
|
already exists. Raise ValueError if name is DEFAULT.
|
||||||
"""
|
"""
|
||||||
if section == self._default_section:
|
if section == self.default_section:
|
||||||
raise ValueError('Invalid section name: %s' % section)
|
raise ValueError('Invalid section name: %s' % section)
|
||||||
|
|
||||||
if section in self._sections:
|
if section in self._sections:
|
||||||
|
@ -555,7 +756,7 @@ class RawConfigParser(MutableMapping):
|
||||||
)
|
)
|
||||||
self.read_file(fp, source=filename)
|
self.read_file(fp, source=filename)
|
||||||
|
|
||||||
def get(self, section, option, *, vars=None, fallback=_UNSET):
|
def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET):
|
||||||
"""Get an option value for a given section.
|
"""Get an option value for a given section.
|
||||||
|
|
||||||
If `vars' is provided, it must be a dictionary. The option is looked up
|
If `vars' is provided, it must be a dictionary. The option is looked up
|
||||||
|
@ -563,7 +764,12 @@ class RawConfigParser(MutableMapping):
|
||||||
If the key is not found and `fallback' is provided, it is used as
|
If the key is not found and `fallback' is provided, it is used as
|
||||||
a fallback value. `None' can be provided as a `fallback' value.
|
a fallback value. `None' can be provided as a `fallback' value.
|
||||||
|
|
||||||
Arguments `vars' and `fallback' are keyword only.
|
If interpolation is enabled and the optional argument `raw' is False,
|
||||||
|
all interpolations are expanded in the return values.
|
||||||
|
|
||||||
|
Arguments `raw', `vars', and `fallback' are keyword only.
|
||||||
|
|
||||||
|
The section DEFAULT is special.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
d = self._unify_values(section, vars)
|
d = self._unify_values(section, vars)
|
||||||
|
@ -574,61 +780,90 @@ class RawConfigParser(MutableMapping):
|
||||||
return fallback
|
return fallback
|
||||||
option = self.optionxform(option)
|
option = self.optionxform(option)
|
||||||
try:
|
try:
|
||||||
return d[option]
|
value = d[option]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
if fallback is _UNSET:
|
if fallback is _UNSET:
|
||||||
raise NoOptionError(option, section)
|
raise NoOptionError(option, section)
|
||||||
else:
|
else:
|
||||||
return fallback
|
return fallback
|
||||||
|
|
||||||
def items(self, section):
|
if raw or value is None:
|
||||||
try:
|
return value
|
||||||
d2 = self._sections[section]
|
else:
|
||||||
except KeyError:
|
return self._interpolation.before_get(self, section, option, value,
|
||||||
if section != self._default_section:
|
d)
|
||||||
raise NoSectionError(section)
|
|
||||||
d2 = self._dict()
|
|
||||||
d = self._defaults.copy()
|
|
||||||
d.update(d2)
|
|
||||||
return d.items()
|
|
||||||
|
|
||||||
def _get(self, section, conv, option, **kwargs):
|
def _get(self, section, conv, option, **kwargs):
|
||||||
return conv(self.get(section, option, **kwargs))
|
return conv(self.get(section, option, **kwargs))
|
||||||
|
|
||||||
def getint(self, section, option, *, vars=None, fallback=_UNSET):
|
def getint(self, section, option, *, raw=False, vars=None,
|
||||||
|
fallback=_UNSET):
|
||||||
try:
|
try:
|
||||||
return self._get(section, int, option, vars=vars)
|
return self._get(section, int, option, raw=raw, vars=vars)
|
||||||
except (NoSectionError, NoOptionError):
|
except (NoSectionError, NoOptionError):
|
||||||
if fallback is _UNSET:
|
if fallback is _UNSET:
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
return fallback
|
return fallback
|
||||||
|
|
||||||
def getfloat(self, section, option, *, vars=None, fallback=_UNSET):
|
def getfloat(self, section, option, *, raw=False, vars=None,
|
||||||
|
fallback=_UNSET):
|
||||||
try:
|
try:
|
||||||
return self._get(section, float, option, vars=vars)
|
return self._get(section, float, option, raw=raw, vars=vars)
|
||||||
except (NoSectionError, NoOptionError):
|
except (NoSectionError, NoOptionError):
|
||||||
if fallback is _UNSET:
|
if fallback is _UNSET:
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
return fallback
|
return fallback
|
||||||
|
|
||||||
def getboolean(self, section, option, *, vars=None, fallback=_UNSET):
|
def getboolean(self, section, option, *, raw=False, vars=None,
|
||||||
|
fallback=_UNSET):
|
||||||
try:
|
try:
|
||||||
return self._get(section, self._convert_to_boolean, option,
|
return self._get(section, self._convert_to_boolean, option,
|
||||||
vars=vars)
|
raw=raw, vars=vars)
|
||||||
except (NoSectionError, NoOptionError):
|
except (NoSectionError, NoOptionError):
|
||||||
if fallback is _UNSET:
|
if fallback is _UNSET:
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
return fallback
|
return fallback
|
||||||
|
|
||||||
|
def items(self, section, raw=False, vars=None):
|
||||||
|
"""Return a list of (name, value) tuples for each option in a section.
|
||||||
|
|
||||||
|
All % interpolations are expanded in the return values, based on the
|
||||||
|
defaults passed into the constructor, unless the optional argument
|
||||||
|
`raw' is true. Additional substitutions may be provided using the
|
||||||
|
`vars' argument, which must be a dictionary whose contents overrides
|
||||||
|
any pre-existing defaults.
|
||||||
|
|
||||||
|
The section DEFAULT is special.
|
||||||
|
"""
|
||||||
|
d = self._defaults.copy()
|
||||||
|
try:
|
||||||
|
d.update(self._sections[section])
|
||||||
|
except KeyError:
|
||||||
|
if section != self.default_section:
|
||||||
|
raise NoSectionError(section)
|
||||||
|
# Update with the entry specific variables
|
||||||
|
if vars:
|
||||||
|
for key, value in vars.items():
|
||||||
|
d[self.optionxform(key)] = value
|
||||||
|
options = list(d.keys())
|
||||||
|
if raw:
|
||||||
|
return [(option, d[option])
|
||||||
|
for option in options]
|
||||||
|
else:
|
||||||
|
return [(option, self._interpolation.before_get(self, section,
|
||||||
|
option, d[option],
|
||||||
|
d))
|
||||||
|
for option in options]
|
||||||
|
|
||||||
def optionxform(self, optionstr):
|
def optionxform(self, optionstr):
|
||||||
return optionstr.lower()
|
return optionstr.lower()
|
||||||
|
|
||||||
def has_option(self, section, option):
|
def has_option(self, section, option):
|
||||||
"""Check for the existence of a given option in a given section."""
|
"""Check for the existence of a given option in a given section."""
|
||||||
if not section or section == self._default_section:
|
if not section or section == self.default_section:
|
||||||
option = self.optionxform(option)
|
option = self.optionxform(option)
|
||||||
return option in self._defaults
|
return option in self._defaults
|
||||||
elif section not in self._sections:
|
elif section not in self._sections:
|
||||||
|
@ -640,7 +875,10 @@ class RawConfigParser(MutableMapping):
|
||||||
|
|
||||||
def set(self, section, option, value=None):
|
def set(self, section, option, value=None):
|
||||||
"""Set an option."""
|
"""Set an option."""
|
||||||
if not section or section == self._default_section:
|
if value:
|
||||||
|
value = self._interpolation.before_set(self, section, option,
|
||||||
|
value)
|
||||||
|
if not section or section == self.default_section:
|
||||||
sectdict = self._defaults
|
sectdict = self._defaults
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
@ -660,7 +898,7 @@ class RawConfigParser(MutableMapping):
|
||||||
else:
|
else:
|
||||||
d = self._delimiters[0]
|
d = self._delimiters[0]
|
||||||
if self._defaults:
|
if self._defaults:
|
||||||
self._write_section(fp, self._default_section,
|
self._write_section(fp, self.default_section,
|
||||||
self._defaults.items(), d)
|
self._defaults.items(), d)
|
||||||
for section in self._sections:
|
for section in self._sections:
|
||||||
self._write_section(fp, section,
|
self._write_section(fp, section,
|
||||||
|
@ -670,6 +908,8 @@ class RawConfigParser(MutableMapping):
|
||||||
"""Write a single section to the specified `fp'."""
|
"""Write a single section to the specified `fp'."""
|
||||||
fp.write("[{}]\n".format(section_name))
|
fp.write("[{}]\n".format(section_name))
|
||||||
for key, value in section_items:
|
for key, value in section_items:
|
||||||
|
value = self._interpolation.before_write(self, section_name, key,
|
||||||
|
value)
|
||||||
if value is not None or not self._allow_no_value:
|
if value is not None or not self._allow_no_value:
|
||||||
value = delimiter + str(value).replace('\n', '\n\t')
|
value = delimiter + str(value).replace('\n', '\n\t')
|
||||||
else:
|
else:
|
||||||
|
@ -679,7 +919,7 @@ class RawConfigParser(MutableMapping):
|
||||||
|
|
||||||
def remove_option(self, section, option):
|
def remove_option(self, section, option):
|
||||||
"""Remove an option."""
|
"""Remove an option."""
|
||||||
if not section or section == self._default_section:
|
if not section or section == self.default_section:
|
||||||
sectdict = self._defaults
|
sectdict = self._defaults
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
@ -701,7 +941,7 @@ class RawConfigParser(MutableMapping):
|
||||||
return existed
|
return existed
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
if key != self._default_section and not self.has_section(key):
|
if key != self.default_section and not self.has_section(key):
|
||||||
raise KeyError(key)
|
raise KeyError(key)
|
||||||
return self._proxies[key]
|
return self._proxies[key]
|
||||||
|
|
||||||
|
@ -715,21 +955,21 @@ class RawConfigParser(MutableMapping):
|
||||||
self.read_dict({key: value})
|
self.read_dict({key: value})
|
||||||
|
|
||||||
def __delitem__(self, key):
|
def __delitem__(self, key):
|
||||||
if key == self._default_section:
|
if key == self.default_section:
|
||||||
raise ValueError("Cannot remove the default section.")
|
raise ValueError("Cannot remove the default section.")
|
||||||
if not self.has_section(key):
|
if not self.has_section(key):
|
||||||
raise KeyError(key)
|
raise KeyError(key)
|
||||||
self.remove_section(key)
|
self.remove_section(key)
|
||||||
|
|
||||||
def __contains__(self, key):
|
def __contains__(self, key):
|
||||||
return key == self._default_section or self.has_section(key)
|
return key == self.default_section or self.has_section(key)
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self._sections) + 1 # the default section
|
return len(self._sections) + 1 # the default section
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
# XXX does it break when underlying container state changed?
|
# XXX does it break when underlying container state changed?
|
||||||
return itertools.chain((self._default_section,), self._sections.keys())
|
return itertools.chain((self.default_section,), self._sections.keys())
|
||||||
|
|
||||||
def _read(self, fp, fpname):
|
def _read(self, fp, fpname):
|
||||||
"""Parse a sectioned configuration file.
|
"""Parse a sectioned configuration file.
|
||||||
|
@ -801,7 +1041,7 @@ class RawConfigParser(MutableMapping):
|
||||||
lineno)
|
lineno)
|
||||||
cursect = self._sections[sectname]
|
cursect = self._sections[sectname]
|
||||||
elements_added.add(sectname)
|
elements_added.add(sectname)
|
||||||
elif sectname == self._default_section:
|
elif sectname == self.default_section:
|
||||||
cursect = self._defaults
|
cursect = self._defaults
|
||||||
else:
|
else:
|
||||||
cursect = self._dict()
|
cursect = self._dict()
|
||||||
|
@ -836,7 +1076,7 @@ class RawConfigParser(MutableMapping):
|
||||||
cursect[optname] = [optval]
|
cursect[optname] = [optval]
|
||||||
else:
|
else:
|
||||||
# valueless option handling
|
# valueless option handling
|
||||||
cursect[optname] = optval
|
cursect[optname] = None
|
||||||
else:
|
else:
|
||||||
# a non-fatal parsing error occurred. set up the
|
# a non-fatal parsing error occurred. set up the
|
||||||
# exception but keep going. the exception will be
|
# exception but keep going. the exception will be
|
||||||
|
@ -849,12 +1089,16 @@ class RawConfigParser(MutableMapping):
|
||||||
self._join_multiline_values()
|
self._join_multiline_values()
|
||||||
|
|
||||||
def _join_multiline_values(self):
|
def _join_multiline_values(self):
|
||||||
all_sections = itertools.chain((self._defaults,),
|
defaults = self.default_section, self._defaults
|
||||||
self._sections.values())
|
all_sections = itertools.chain((defaults,),
|
||||||
for options in all_sections:
|
self._sections.items())
|
||||||
|
for section, options in all_sections:
|
||||||
for name, val in options.items():
|
for name, val in options.items():
|
||||||
if isinstance(val, list):
|
if isinstance(val, list):
|
||||||
options[name] = '\n'.join(val).rstrip()
|
val = '\n'.join(val).rstrip()
|
||||||
|
options[name] = self._interpolation.before_read(self,
|
||||||
|
section,
|
||||||
|
name, val)
|
||||||
|
|
||||||
def _handle_error(self, exc, fpname, lineno, line):
|
def _handle_error(self, exc, fpname, lineno, line):
|
||||||
if not exc:
|
if not exc:
|
||||||
|
@ -871,7 +1115,7 @@ class RawConfigParser(MutableMapping):
|
||||||
try:
|
try:
|
||||||
d.update(self._sections[section])
|
d.update(self._sections[section])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
if section != self._default_section:
|
if section != self.default_section:
|
||||||
raise NoSectionError(section)
|
raise NoSectionError(section)
|
||||||
# Update with the entry specific variables
|
# Update with the entry specific variables
|
||||||
if vars:
|
if vars:
|
||||||
|
@ -906,197 +1150,31 @@ class RawConfigParser(MutableMapping):
|
||||||
raise TypeError("option values must be strings")
|
raise TypeError("option values must be strings")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigParser(RawConfigParser):
|
class ConfigParser(RawConfigParser):
|
||||||
"""ConfigParser implementing interpolation."""
|
"""ConfigParser implementing interpolation."""
|
||||||
|
|
||||||
def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET):
|
_DEFAULT_INTERPOLATION = BrokenInterpolation()
|
||||||
"""Get an option value for a given section.
|
|
||||||
|
|
||||||
If `vars' is provided, it must be a dictionary. The option is looked up
|
def __init__(self, *args, **kwargs):
|
||||||
in `vars' (if provided), `section', and in `DEFAULTSECT' in that order.
|
super().__init__(*args, **kwargs)
|
||||||
If the key is not found and `fallback' is provided, it is used as
|
if self.__class__ is ConfigParser:
|
||||||
a fallback value. `None' can be provided as a `fallback' value.
|
warnings.warn(
|
||||||
|
"The ConfigParser class will be removed in future versions."
|
||||||
All % interpolations are expanded in the return values, unless the
|
" Use SafeConfigParser instead.",
|
||||||
optional argument `raw' is true. Values for interpolation keys are
|
DeprecationWarning, stacklevel=2
|
||||||
looked up in the same manner as the option.
|
)
|
||||||
|
|
||||||
Arguments `raw', `vars', and `fallback' are keyword only.
|
|
||||||
|
|
||||||
The section DEFAULT is special.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
d = self._unify_values(section, vars)
|
|
||||||
except NoSectionError:
|
|
||||||
if fallback is _UNSET:
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
return fallback
|
|
||||||
option = self.optionxform(option)
|
|
||||||
try:
|
|
||||||
value = d[option]
|
|
||||||
except KeyError:
|
|
||||||
if fallback is _UNSET:
|
|
||||||
raise NoOptionError(option, section)
|
|
||||||
else:
|
|
||||||
return fallback
|
|
||||||
|
|
||||||
if raw or value is None:
|
|
||||||
return value
|
|
||||||
else:
|
|
||||||
return self._interpolate(section, option, value, d)
|
|
||||||
|
|
||||||
def getint(self, section, option, *, raw=False, vars=None,
|
|
||||||
fallback=_UNSET):
|
|
||||||
try:
|
|
||||||
return self._get(section, int, option, raw=raw, vars=vars)
|
|
||||||
except (NoSectionError, NoOptionError):
|
|
||||||
if fallback is _UNSET:
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
return fallback
|
|
||||||
|
|
||||||
def getfloat(self, section, option, *, raw=False, vars=None,
|
|
||||||
fallback=_UNSET):
|
|
||||||
try:
|
|
||||||
return self._get(section, float, option, raw=raw, vars=vars)
|
|
||||||
except (NoSectionError, NoOptionError):
|
|
||||||
if fallback is _UNSET:
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
return fallback
|
|
||||||
|
|
||||||
def getboolean(self, section, option, *, raw=False, vars=None,
|
|
||||||
fallback=_UNSET):
|
|
||||||
try:
|
|
||||||
return self._get(section, self._convert_to_boolean, option,
|
|
||||||
raw=raw, vars=vars)
|
|
||||||
except (NoSectionError, NoOptionError):
|
|
||||||
if fallback is _UNSET:
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
return fallback
|
|
||||||
|
|
||||||
def items(self, section, raw=False, vars=None):
|
|
||||||
"""Return a list of (name, value) tuples for each option in a section.
|
|
||||||
|
|
||||||
All % interpolations are expanded in the return values, based on the
|
|
||||||
defaults passed into the constructor, unless the optional argument
|
|
||||||
`raw' is true. Additional substitutions may be provided using the
|
|
||||||
`vars' argument, which must be a dictionary whose contents overrides
|
|
||||||
any pre-existing defaults.
|
|
||||||
|
|
||||||
The section DEFAULT is special.
|
|
||||||
"""
|
|
||||||
d = self._defaults.copy()
|
|
||||||
try:
|
|
||||||
d.update(self._sections[section])
|
|
||||||
except KeyError:
|
|
||||||
if section != self._default_section:
|
|
||||||
raise NoSectionError(section)
|
|
||||||
# Update with the entry specific variables
|
|
||||||
if vars:
|
|
||||||
for key, value in vars.items():
|
|
||||||
d[self.optionxform(key)] = value
|
|
||||||
options = list(d.keys())
|
|
||||||
if raw:
|
|
||||||
return [(option, d[option])
|
|
||||||
for option in options]
|
|
||||||
else:
|
|
||||||
return [(option, self._interpolate(section, option, d[option], d))
|
|
||||||
for option in options]
|
|
||||||
|
|
||||||
def _interpolate(self, section, option, rawval, vars):
|
|
||||||
# do the string interpolation
|
|
||||||
value = rawval
|
|
||||||
depth = MAX_INTERPOLATION_DEPTH
|
|
||||||
while depth: # Loop through this until it's done
|
|
||||||
depth -= 1
|
|
||||||
if value and "%(" in value:
|
|
||||||
value = self._KEYCRE.sub(self._interpolation_replace, value)
|
|
||||||
try:
|
|
||||||
value = value % vars
|
|
||||||
except KeyError as e:
|
|
||||||
raise InterpolationMissingOptionError(
|
|
||||||
option, section, rawval, e.args[0])
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
if value and "%(" in value:
|
|
||||||
raise InterpolationDepthError(option, section, rawval)
|
|
||||||
return value
|
|
||||||
|
|
||||||
_KEYCRE = re.compile(r"%\(([^)]*)\)s|.")
|
|
||||||
|
|
||||||
def _interpolation_replace(self, match):
|
|
||||||
s = match.group(1)
|
|
||||||
if s is None:
|
|
||||||
return match.group()
|
|
||||||
else:
|
|
||||||
return "%%(%s)s" % self.optionxform(s)
|
|
||||||
|
|
||||||
|
|
||||||
class SafeConfigParser(ConfigParser):
|
class SafeConfigParser(ConfigParser):
|
||||||
"""ConfigParser implementing sane interpolation."""
|
"""ConfigParser implementing sane interpolation."""
|
||||||
|
|
||||||
def _interpolate(self, section, option, rawval, vars):
|
_DEFAULT_INTERPOLATION = BasicInterpolation()
|
||||||
# do the string interpolation
|
|
||||||
L = []
|
|
||||||
self._interpolate_some(option, L, rawval, section, vars, 1)
|
|
||||||
return ''.join(L)
|
|
||||||
|
|
||||||
_interpvar_re = re.compile(r"%\(([^)]+)\)s")
|
|
||||||
|
|
||||||
def _interpolate_some(self, option, accum, rest, section, map, depth):
|
|
||||||
if depth > MAX_INTERPOLATION_DEPTH:
|
|
||||||
raise InterpolationDepthError(option, section, rest)
|
|
||||||
while rest:
|
|
||||||
p = rest.find("%")
|
|
||||||
if p < 0:
|
|
||||||
accum.append(rest)
|
|
||||||
return
|
|
||||||
if p > 0:
|
|
||||||
accum.append(rest[:p])
|
|
||||||
rest = rest[p:]
|
|
||||||
# p is no longer used
|
|
||||||
c = rest[1:2]
|
|
||||||
if c == "%":
|
|
||||||
accum.append("%")
|
|
||||||
rest = rest[2:]
|
|
||||||
elif c == "(":
|
|
||||||
m = self._interpvar_re.match(rest)
|
|
||||||
if m is None:
|
|
||||||
raise InterpolationSyntaxError(option, section,
|
|
||||||
"bad interpolation variable reference %r" % rest)
|
|
||||||
var = self.optionxform(m.group(1))
|
|
||||||
rest = rest[m.end():]
|
|
||||||
try:
|
|
||||||
v = map[var]
|
|
||||||
except KeyError:
|
|
||||||
raise InterpolationMissingOptionError(
|
|
||||||
option, section, rest, var)
|
|
||||||
if "%" in v:
|
|
||||||
self._interpolate_some(option, accum, v,
|
|
||||||
section, map, depth + 1)
|
|
||||||
else:
|
|
||||||
accum.append(v)
|
|
||||||
else:
|
|
||||||
raise InterpolationSyntaxError(
|
|
||||||
option, section,
|
|
||||||
"'%%' must be followed by '%%' or '(', "
|
|
||||||
"found: %r" % (rest,))
|
|
||||||
|
|
||||||
def set(self, section, option, value=None):
|
def set(self, section, option, value=None):
|
||||||
"""Set an option. Extend ConfigParser.set: check for string values."""
|
"""Set an option. Extends RawConfigParser.set by validating type and
|
||||||
|
interpolation syntax on the value."""
|
||||||
self._validate_value_type(value)
|
self._validate_value_type(value)
|
||||||
# check for bad percent signs
|
super().set(section, option, value)
|
||||||
if value:
|
|
||||||
tmp_value = value.replace('%%', '') # escaped percent signs
|
|
||||||
tmp_value = self._interpvar_re.sub('', tmp_value) # valid syntax
|
|
||||||
if '%' in tmp_value:
|
|
||||||
raise ValueError("invalid interpolation syntax in %r at "
|
|
||||||
"position %d" % (value, tmp_value.find('%')))
|
|
||||||
ConfigParser.set(self, section, option, value)
|
|
||||||
|
|
||||||
|
|
||||||
class SectionProxy(MutableMapping):
|
class SectionProxy(MutableMapping):
|
||||||
|
|
|
@ -4,6 +4,7 @@ import io
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
import textwrap
|
import textwrap
|
||||||
|
import warnings
|
||||||
|
|
||||||
from test import support
|
from test import support
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ class CfgParserTestCaseClass(unittest.TestCase):
|
||||||
dict_type = configparser._default_dict
|
dict_type = configparser._default_dict
|
||||||
strict = False
|
strict = False
|
||||||
default_section = configparser.DEFAULTSECT
|
default_section = configparser.DEFAULTSECT
|
||||||
|
interpolation = configparser._UNSET
|
||||||
|
|
||||||
def newconfig(self, defaults=None):
|
def newconfig(self, defaults=None):
|
||||||
arguments = dict(
|
arguments = dict(
|
||||||
|
@ -43,8 +45,12 @@ class CfgParserTestCaseClass(unittest.TestCase):
|
||||||
dict_type=self.dict_type,
|
dict_type=self.dict_type,
|
||||||
strict=self.strict,
|
strict=self.strict,
|
||||||
default_section=self.default_section,
|
default_section=self.default_section,
|
||||||
|
interpolation=self.interpolation,
|
||||||
)
|
)
|
||||||
return self.config_class(**arguments)
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore", category=DeprecationWarning)
|
||||||
|
instance = self.config_class(**arguments)
|
||||||
|
return instance
|
||||||
|
|
||||||
def fromstring(self, string, defaults=None):
|
def fromstring(self, string, defaults=None):
|
||||||
cf = self.newconfig(defaults)
|
cf = self.newconfig(defaults)
|
||||||
|
@ -847,6 +853,70 @@ class SafeConfigParserTestCase(ConfigParserTestCase):
|
||||||
cf = self.newconfig()
|
cf = self.newconfig()
|
||||||
self.assertRaises(ValueError, cf.add_section, self.default_section)
|
self.assertRaises(ValueError, cf.add_section, self.default_section)
|
||||||
|
|
||||||
|
class SafeConfigParserTestCaseExtendedInterpolation(BasicTestCase):
|
||||||
|
config_class = configparser.SafeConfigParser
|
||||||
|
interpolation = configparser.ExtendedInterpolation()
|
||||||
|
default_section = 'common'
|
||||||
|
|
||||||
|
def test_extended_interpolation(self):
|
||||||
|
cf = self.fromstring(textwrap.dedent("""
|
||||||
|
[common]
|
||||||
|
favourite Beatle = Paul
|
||||||
|
favourite color = green
|
||||||
|
|
||||||
|
[tom]
|
||||||
|
favourite band = ${favourite color} day
|
||||||
|
favourite pope = John ${favourite Beatle} II
|
||||||
|
sequel = ${favourite pope}I
|
||||||
|
|
||||||
|
[ambv]
|
||||||
|
favourite Beatle = George
|
||||||
|
son of Edward VII = ${favourite Beatle} V
|
||||||
|
son of George V = ${son of Edward VII}I
|
||||||
|
|
||||||
|
[stanley]
|
||||||
|
favourite Beatle = ${ambv:favourite Beatle}
|
||||||
|
favourite pope = ${tom:favourite pope}
|
||||||
|
favourite color = black
|
||||||
|
favourite state of mind = paranoid
|
||||||
|
favourite movie = soylent ${common:favourite color}
|
||||||
|
favourite song = ${favourite color} sabbath - ${favourite state of mind}
|
||||||
|
""").strip())
|
||||||
|
|
||||||
|
eq = self.assertEqual
|
||||||
|
eq(cf['common']['favourite Beatle'], 'Paul')
|
||||||
|
eq(cf['common']['favourite color'], 'green')
|
||||||
|
eq(cf['tom']['favourite Beatle'], 'Paul')
|
||||||
|
eq(cf['tom']['favourite color'], 'green')
|
||||||
|
eq(cf['tom']['favourite band'], 'green day')
|
||||||
|
eq(cf['tom']['favourite pope'], 'John Paul II')
|
||||||
|
eq(cf['tom']['sequel'], 'John Paul III')
|
||||||
|
eq(cf['ambv']['favourite Beatle'], 'George')
|
||||||
|
eq(cf['ambv']['favourite color'], 'green')
|
||||||
|
eq(cf['ambv']['son of Edward VII'], 'George V')
|
||||||
|
eq(cf['ambv']['son of George V'], 'George VI')
|
||||||
|
eq(cf['stanley']['favourite Beatle'], 'George')
|
||||||
|
eq(cf['stanley']['favourite color'], 'black')
|
||||||
|
eq(cf['stanley']['favourite state of mind'], 'paranoid')
|
||||||
|
eq(cf['stanley']['favourite movie'], 'soylent green')
|
||||||
|
eq(cf['stanley']['favourite pope'], 'John Paul II')
|
||||||
|
eq(cf['stanley']['favourite song'],
|
||||||
|
'black sabbath - paranoid')
|
||||||
|
|
||||||
|
def test_endless_loop(self):
|
||||||
|
cf = self.fromstring(textwrap.dedent("""
|
||||||
|
[one for you]
|
||||||
|
ping = ${one for me:pong}
|
||||||
|
|
||||||
|
[one for me]
|
||||||
|
pong = ${one for you:ping}
|
||||||
|
""").strip())
|
||||||
|
|
||||||
|
with self.assertRaises(configparser.InterpolationDepthError):
|
||||||
|
cf['one for you']['ping']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SafeConfigParserTestCaseNonStandardDelimiters(SafeConfigParserTestCase):
|
class SafeConfigParserTestCaseNonStandardDelimiters(SafeConfigParserTestCase):
|
||||||
delimiters = (':=', '$')
|
delimiters = (':=', '$')
|
||||||
comment_prefixes = ('//', '"')
|
comment_prefixes = ('//', '"')
|
||||||
|
@ -910,6 +980,8 @@ class Issue7005TestCase(unittest.TestCase):
|
||||||
|
|
||||||
def prepare(self, config_class):
|
def prepare(self, config_class):
|
||||||
# This is the default, but that's the point.
|
# This is the default, but that's the point.
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore", category=DeprecationWarning)
|
||||||
cp = config_class(allow_no_value=False)
|
cp = config_class(allow_no_value=False)
|
||||||
cp.add_section("section")
|
cp.add_section("section")
|
||||||
cp.set("section", "option", None)
|
cp.set("section", "option", None)
|
||||||
|
@ -978,6 +1050,7 @@ def test_main():
|
||||||
RawConfigParserTestCaseNonStandardDelimiters,
|
RawConfigParserTestCaseNonStandardDelimiters,
|
||||||
RawConfigParserTestSambaConf,
|
RawConfigParserTestSambaConf,
|
||||||
SafeConfigParserTestCase,
|
SafeConfigParserTestCase,
|
||||||
|
SafeConfigParserTestCaseExtendedInterpolation,
|
||||||
SafeConfigParserTestCaseNonStandardDelimiters,
|
SafeConfigParserTestCaseNonStandardDelimiters,
|
||||||
SafeConfigParserTestCaseNoValue,
|
SafeConfigParserTestCaseNoValue,
|
||||||
SafeConfigParserTestCaseTrickyFile,
|
SafeConfigParserTestCaseTrickyFile,
|
||||||
|
|
|
@ -149,6 +149,14 @@ Library
|
||||||
- Issue #10467: Fix BytesIO.readinto() after seeking into a position after the
|
- Issue #10467: Fix BytesIO.readinto() after seeking into a position after the
|
||||||
end of the file.
|
end of the file.
|
||||||
|
|
||||||
|
- configparser: the ConfigParser class has been deprecated in favor of
|
||||||
|
SafeConfigParser. Usage of RawConfigParser is now discouraged for new
|
||||||
|
projects in favor of SafeConfigParser(interpolation=None).
|
||||||
|
|
||||||
|
- Issue #10499: configparser supports pluggable interpolation handlers. New
|
||||||
|
interpolation handler added (ExtendedInterpolation) which supports the syntax
|
||||||
|
used by zc.buildout (e.g. interpolation between sections).
|
||||||
|
|
||||||
- Issue #1682942: configparser supports alternative option/value delimiters.
|
- Issue #1682942: configparser supports alternative option/value delimiters.
|
||||||
|
|
||||||
- Issue #5412: configparser supports mapping protocol access.
|
- Issue #5412: configparser supports mapping protocol access.
|
||||||
|
|
Loading…
Reference in New Issue