Issue 10499: Modular interpolation in configparser
This commit is contained in:
parent
ecace28ef4
commit
b6a6f5f886
|
@ -17,11 +17,10 @@
|
|||
single: ini file
|
||||
single: Windows ini file
|
||||
|
||||
This module provides the classes :class:`RawConfigParser` and
|
||||
:class:`SafeConfigParser`. They implement a basic configuration
|
||||
language which provides a structure similar to what's found in Microsoft
|
||||
Windows INI files. You can use this to write Python programs which can be
|
||||
customized by end users easily.
|
||||
This module provides the :class:`SafeConfigParser` class which implements
|
||||
a basic configuration language which provides a structure similar to what's
|
||||
found in Microsoft Windows INI files. You can use this to write Python
|
||||
programs which can be customized by end users easily.
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -34,6 +33,10 @@ customized by end users easily.
|
|||
Support for a creating Unix shell-like mini-languages which can be used
|
||||
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
|
||||
-----------
|
||||
|
@ -43,17 +46,17 @@ Let's take a very basic configuration file that looks like this:
|
|||
.. code-block:: ini
|
||||
|
||||
[DEFAULT]
|
||||
ServerAliveInterval = 45
|
||||
Compression = yes
|
||||
CompressionLevel = 9
|
||||
ForwardX11 = yes
|
||||
ServerAliveInterval = 45
|
||||
Compression = yes
|
||||
CompressionLevel = 9
|
||||
ForwardX11 = yes
|
||||
|
||||
[bitbucket.org]
|
||||
User = hg
|
||||
User = hg
|
||||
|
||||
[topsecret.server.com]
|
||||
Port = 50022
|
||||
ForwardX11 = no
|
||||
Port = 50022
|
||||
ForwardX11 = no
|
||||
|
||||
The structure of INI files is described `in the following section
|
||||
<#supported-ini-file-structure>`_. Essentially, the file
|
||||
|
@ -64,7 +67,7 @@ creating the above configuration file programatically.
|
|||
.. doctest::
|
||||
|
||||
>>> import configparser
|
||||
>>> config = configparser.RawConfigParser()
|
||||
>>> config = configparser.SafeConfigParser()
|
||||
>>> config['DEFAULT'] = {'ServerAliveInterval': '45',
|
||||
... 'Compression': 'yes',
|
||||
... 'CompressionLevel': '9'}
|
||||
|
@ -89,7 +92,7 @@ back and explore the data it holds.
|
|||
.. doctest::
|
||||
|
||||
>>> import configparser
|
||||
>>> config = configparser.RawConfigParser()
|
||||
>>> config = configparser.SafeConfigParser()
|
||||
>>> config.sections()
|
||||
[]
|
||||
>>> config.read('example.ini')
|
||||
|
@ -233,23 +236,26 @@ by a whitespace character to be recognized as a comment. For backwards
|
|||
compatibility, by default only ``;`` starts an inline comment, while
|
||||
``#`` 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:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[Paths]
|
||||
home_dir: /Users
|
||||
my_dir: %(home_dir)s/lumberjack
|
||||
my_pictures: %(my_dir)s/Pictures
|
||||
[Simple Values]
|
||||
key: value
|
||||
spaces in keys: allowed
|
||||
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]
|
||||
chorus: I'm a lumberjack, and I'm okay
|
||||
I sleep all night and I work all day
|
||||
I sleep all night and I work all day
|
||||
|
||||
[No Values]
|
||||
key_without_value
|
||||
|
@ -262,28 +268,92 @@ For example:
|
|||
multiline ;comment
|
||||
value! ;comment
|
||||
|
||||
[Sections Can Be Indented]
|
||||
can_values_be_as_well = True
|
||||
does_that_mean_anything_special = False
|
||||
purpose = formatting for readability
|
||||
multiline_values = are
|
||||
handled just fine as
|
||||
long as they are indented
|
||||
deeper than the first line
|
||||
of a value
|
||||
# Did I mention we can indent comments, too?
|
||||
[Sections Can Be Indented]
|
||||
can_values_be_as_well = True
|
||||
does_that_mean_anything_special = False
|
||||
purpose = formatting for readability
|
||||
multiline_values = are
|
||||
handled just fine as
|
||||
long as they are indented
|
||||
deeper than the first line
|
||||
of a value
|
||||
# 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
|
||||
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.
|
||||
Interpolation of values
|
||||
-----------------------
|
||||
|
||||
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
|
||||
-----------------------
|
||||
|
@ -350,9 +420,9 @@ the :meth:`__init__` options:
|
|||
* *defaults*, default value: ``None``
|
||||
|
||||
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
|
||||
configuration files that don't specify values which are the same as the
|
||||
documented default.
|
||||
put in the ``DEFAULT`` section. This makes for an elegant way to support
|
||||
concise configuration files that don't specify values which are the same as
|
||||
the documented default.
|
||||
|
||||
Hint: if you want to specify default values for a specific section, use
|
||||
:meth:`read_dict` before you read the actual file.
|
||||
|
@ -374,7 +444,7 @@ the :meth:`__init__` options:
|
|||
|
||||
.. doctest::
|
||||
|
||||
>>> parser = configparser.RawConfigParser()
|
||||
>>> parser = configparser.SafeConfigParser()
|
||||
>>> parser.read_dict({'section1': {'key1': 'value1',
|
||||
... 'key2': 'value2',
|
||||
... 'key3': 'value3'},
|
||||
|
@ -395,7 +465,7 @@ the :meth:`__init__` options:
|
|||
.. doctest::
|
||||
|
||||
>>> from collections import OrderedDict
|
||||
>>> parser = configparser.RawConfigParser()
|
||||
>>> parser = configparser.SafeConfigParser()
|
||||
>>> parser.read_dict(
|
||||
... OrderedDict((
|
||||
... ('s1',
|
||||
|
@ -441,7 +511,7 @@ the :meth:`__init__` options:
|
|||
... skip-bdb
|
||||
... 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)
|
||||
|
||||
>>> # 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.
|
||||
|
||||
See also the *space_around_delimiters* argument to
|
||||
:meth:`RawConfigParser.write`.
|
||||
:meth:`SafeConfigParser.write`.
|
||||
|
||||
* *comment_prefixes*, default value: ``_COMPATIBLE`` (``'#'`` valid on empty
|
||||
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
|
||||
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
|
||||
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::
|
||||
|
||||
>>> custom = configparser.RawConfigParser()
|
||||
>>> custom = configparser.SafeConfigParser()
|
||||
>>> custom['section1'] = {'funky': 'nope'}
|
||||
>>> custom['section1'].getboolean('funky')
|
||||
Traceback (most recent call last):
|
||||
|
@ -557,7 +652,7 @@ may be overriden by subclasses or by attribute assignment.
|
|||
... [Section2]
|
||||
... AnotherKey = Value
|
||||
... """
|
||||
>>> typical = configparser.RawConfigParser()
|
||||
>>> typical = configparser.SafeConfigParser()
|
||||
>>> typical.read_string(config)
|
||||
>>> list(typical['Section1'].keys())
|
||||
['key']
|
||||
|
@ -623,8 +718,7 @@ An example of reading the configuration file again::
|
|||
if config.getboolean('Section1', 'bool'):
|
||||
print(config.get('Section1', 'foo'))
|
||||
|
||||
To get interpolation, use :class:`SafeConfigParser` or, if
|
||||
you absolutely have to, a :class:`ConfigParser`::
|
||||
To get interpolation, use :class:`SafeConfigParser`::
|
||||
|
||||
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!"
|
||||
|
||||
|
||||
.. _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
|
||||
will be used to create the dictionary objects for the list of sections, for
|
||||
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,
|
||||
internal empty lines of a multiline option are kept as part of the value.
|
||||
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
|
||||
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.
|
||||
*allow_no_value*, *delimiters*, *comment_prefixes*, *strict*,
|
||||
*empty_lines_in_values*, *default_section* and *interpolation* were
|
||||
added.
|
||||
|
||||
|
||||
.. method:: defaults()
|
||||
|
@ -717,22 +828,21 @@ RawConfigParser Objects
|
|||
|
||||
.. method:: sections()
|
||||
|
||||
Return a list of the sections available; ``DEFAULT`` is not included in
|
||||
the list.
|
||||
Return a list of the sections available; the *default section* is not
|
||||
included in the list.
|
||||
|
||||
|
||||
.. method:: add_section(section)
|
||||
|
||||
Add a section named *section* to the instance. If a section by the given
|
||||
name already exists, :exc:`DuplicateSectionError` is raised. If the name
|
||||
``DEFAULT`` (or any of it's case-insensitive variants) is passed,
|
||||
:exc:`ValueError` is raised.
|
||||
name already exists, :exc:`DuplicateSectionError` is raised. If the
|
||||
*default section* name is passed, :exc:`ValueError` is raised.
|
||||
|
||||
|
||||
.. method:: has_section(section)
|
||||
|
||||
Indicates whether the named section is present in the configuration. The
|
||||
``DEFAULT`` section is not acknowledged.
|
||||
Indicates whether the named *section* is present in the configuration.
|
||||
The *default section* is not acknowledged.
|
||||
|
||||
|
||||
.. method:: options(section)
|
||||
|
@ -742,7 +852,7 @@ RawConfigParser Objects
|
|||
|
||||
.. 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`.
|
||||
|
||||
|
||||
|
@ -750,19 +860,20 @@ RawConfigParser Objects
|
|||
|
||||
Attempt to read and parse a list of filenames, returning a list of
|
||||
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
|
||||
opened, that file will be ignored. This is designed so that you can
|
||||
specify a list of potential configuration file locations (for example, the
|
||||
current directory, the user's home directory, and some system-wide
|
||||
directory), and all existing configuration files in the list will be read.
|
||||
If none of the named files exist, the :class:`ConfigParser` instance will
|
||||
contain an empty dataset. An application which requires initial values to
|
||||
be loaded from a file should load the required file or files using
|
||||
:meth:`read_file` before calling :meth:`read` for any optional files::
|
||||
is treated as a single filename. If a file named in *filenames* cannot
|
||||
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 current directory, the user's home directory, and some system-wide
|
||||
directory), and all existing configuration files in the list will be
|
||||
read. If none of the named files exist, the :class:`ConfigParser`
|
||||
instance will contain an empty dataset. An application which requires
|
||||
initial values to be loaded from a file should load the required file or
|
||||
files using :meth:`read_file` before calling :meth:`read` for any
|
||||
optional files::
|
||||
|
||||
import configparser, os
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config = configparser.SafeConfigParser()
|
||||
config.read_file(open('defaults.cfg'))
|
||||
config.read(['site.cfg', os.path.expanduser('~/.myapp.cfg')],
|
||||
encoding='cp1250')
|
||||
|
@ -810,154 +921,6 @@ RawConfigParser Objects
|
|||
|
||||
.. 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])
|
||||
|
||||
|
@ -1010,40 +973,6 @@ The :class:`ConfigParser` class extends some methods of the
|
|||
: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)
|
||||
|
||||
If the given section exists, set the given option to the specified value;
|
||||
|
@ -1051,6 +980,112 @@ SafeConfigParser Objects
|
|||
: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
|
||||
----------
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ 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::
|
||||
|
|
|
@ -4,23 +4,13 @@ A configuration file consists of sections, lead by a "[section]" header,
|
|||
and followed by "name: value" entries, with continuations and such in
|
||||
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
|
||||
ConfigParser constructor as a dictionary.
|
||||
SafeConfigParser constructor as a dictionary.
|
||||
|
||||
class:
|
||||
|
||||
ConfigParser -- responsible for parsing a list of
|
||||
configuration files, and managing the parsed database.
|
||||
SafeConfigParser -- responsible for parsing a list of
|
||||
configuration files, and managing the parsed database.
|
||||
|
||||
methods:
|
||||
|
||||
|
@ -316,7 +306,7 @@ class ParsingError(Error):
|
|||
def filename(self):
|
||||
"""Deprecated, use `source'."""
|
||||
warnings.warn(
|
||||
"This 'filename' attribute will be removed in future versions. "
|
||||
"The 'filename' attribute will be removed in future versions. "
|
||||
"Use 'source' instead.",
|
||||
DeprecationWarning, stacklevel=2
|
||||
)
|
||||
|
@ -362,6 +352,204 @@ _COMPATIBLE = 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):
|
||||
"""ConfigParser that does not do interpolation."""
|
||||
|
||||
|
@ -388,7 +576,8 @@ class RawConfigParser(MutableMapping):
|
|||
# space/tab
|
||||
(?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
|
||||
SECTCRE = re.compile(_SECT_TMPL, re.VERBOSE)
|
||||
# Compiled regular expression for matching options with typical separators
|
||||
|
@ -406,7 +595,15 @@ class RawConfigParser(MutableMapping):
|
|||
allow_no_value=False, *, delimiters=('=', ':'),
|
||||
comment_prefixes=_COMPATIBLE, strict=False,
|
||||
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._sections = self._dict()
|
||||
self._defaults = self._dict()
|
||||
|
@ -435,7 +632,11 @@ class RawConfigParser(MutableMapping):
|
|||
self._strict = strict
|
||||
self._allow_no_value = allow_no_value
|
||||
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):
|
||||
return self._defaults
|
||||
|
@ -451,7 +652,7 @@ class RawConfigParser(MutableMapping):
|
|||
Raise DuplicateSectionError if a section by the specified name
|
||||
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)
|
||||
|
||||
if section in self._sections:
|
||||
|
@ -555,7 +756,7 @@ class RawConfigParser(MutableMapping):
|
|||
)
|
||||
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.
|
||||
|
||||
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
|
||||
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:
|
||||
d = self._unify_values(section, vars)
|
||||
|
@ -574,61 +780,90 @@ class RawConfigParser(MutableMapping):
|
|||
return fallback
|
||||
option = self.optionxform(option)
|
||||
try:
|
||||
return d[option]
|
||||
value = d[option]
|
||||
except KeyError:
|
||||
if fallback is _UNSET:
|
||||
raise NoOptionError(option, section)
|
||||
else:
|
||||
return fallback
|
||||
|
||||
def items(self, section):
|
||||
try:
|
||||
d2 = self._sections[section]
|
||||
except KeyError:
|
||||
if section != self._default_section:
|
||||
raise NoSectionError(section)
|
||||
d2 = self._dict()
|
||||
d = self._defaults.copy()
|
||||
d.update(d2)
|
||||
return d.items()
|
||||
if raw or value is None:
|
||||
return value
|
||||
else:
|
||||
return self._interpolation.before_get(self, section, option, value,
|
||||
d)
|
||||
|
||||
def _get(self, section, conv, 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:
|
||||
return self._get(section, int, option, vars=vars)
|
||||
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, *, vars=None, fallback=_UNSET):
|
||||
def getfloat(self, section, option, *, raw=False, vars=None,
|
||||
fallback=_UNSET):
|
||||
try:
|
||||
return self._get(section, float, option, vars=vars)
|
||||
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, *, vars=None, fallback=_UNSET):
|
||||
def getboolean(self, section, option, *, raw=False, vars=None,
|
||||
fallback=_UNSET):
|
||||
try:
|
||||
return self._get(section, self._convert_to_boolean, option,
|
||||
vars=vars)
|
||||
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._interpolation.before_get(self, section,
|
||||
option, d[option],
|
||||
d))
|
||||
for option in options]
|
||||
|
||||
def optionxform(self, optionstr):
|
||||
return optionstr.lower()
|
||||
|
||||
def has_option(self, section, option):
|
||||
"""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)
|
||||
return option in self._defaults
|
||||
elif section not in self._sections:
|
||||
|
@ -640,7 +875,10 @@ class RawConfigParser(MutableMapping):
|
|||
|
||||
def set(self, section, option, value=None):
|
||||
"""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
|
||||
else:
|
||||
try:
|
||||
|
@ -660,7 +898,7 @@ class RawConfigParser(MutableMapping):
|
|||
else:
|
||||
d = self._delimiters[0]
|
||||
if self._defaults:
|
||||
self._write_section(fp, self._default_section,
|
||||
self._write_section(fp, self.default_section,
|
||||
self._defaults.items(), d)
|
||||
for section in self._sections:
|
||||
self._write_section(fp, section,
|
||||
|
@ -670,6 +908,8 @@ class RawConfigParser(MutableMapping):
|
|||
"""Write a single section to the specified `fp'."""
|
||||
fp.write("[{}]\n".format(section_name))
|
||||
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:
|
||||
value = delimiter + str(value).replace('\n', '\n\t')
|
||||
else:
|
||||
|
@ -679,7 +919,7 @@ class RawConfigParser(MutableMapping):
|
|||
|
||||
def remove_option(self, section, option):
|
||||
"""Remove an option."""
|
||||
if not section or section == self._default_section:
|
||||
if not section or section == self.default_section:
|
||||
sectdict = self._defaults
|
||||
else:
|
||||
try:
|
||||
|
@ -701,7 +941,7 @@ class RawConfigParser(MutableMapping):
|
|||
return existed
|
||||
|
||||
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)
|
||||
return self._proxies[key]
|
||||
|
||||
|
@ -715,21 +955,21 @@ class RawConfigParser(MutableMapping):
|
|||
self.read_dict({key: value})
|
||||
|
||||
def __delitem__(self, key):
|
||||
if key == self._default_section:
|
||||
if key == self.default_section:
|
||||
raise ValueError("Cannot remove the default section.")
|
||||
if not self.has_section(key):
|
||||
raise KeyError(key)
|
||||
self.remove_section(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):
|
||||
return len(self._sections) + 1 # the default section
|
||||
|
||||
def __iter__(self):
|
||||
# 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):
|
||||
"""Parse a sectioned configuration file.
|
||||
|
@ -801,7 +1041,7 @@ class RawConfigParser(MutableMapping):
|
|||
lineno)
|
||||
cursect = self._sections[sectname]
|
||||
elements_added.add(sectname)
|
||||
elif sectname == self._default_section:
|
||||
elif sectname == self.default_section:
|
||||
cursect = self._defaults
|
||||
else:
|
||||
cursect = self._dict()
|
||||
|
@ -836,7 +1076,7 @@ class RawConfigParser(MutableMapping):
|
|||
cursect[optname] = [optval]
|
||||
else:
|
||||
# valueless option handling
|
||||
cursect[optname] = optval
|
||||
cursect[optname] = None
|
||||
else:
|
||||
# a non-fatal parsing error occurred. set up the
|
||||
# exception but keep going. the exception will be
|
||||
|
@ -849,12 +1089,16 @@ class RawConfigParser(MutableMapping):
|
|||
self._join_multiline_values()
|
||||
|
||||
def _join_multiline_values(self):
|
||||
all_sections = itertools.chain((self._defaults,),
|
||||
self._sections.values())
|
||||
for options in all_sections:
|
||||
defaults = self.default_section, self._defaults
|
||||
all_sections = itertools.chain((defaults,),
|
||||
self._sections.items())
|
||||
for section, options in all_sections:
|
||||
for name, val in options.items():
|
||||
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):
|
||||
if not exc:
|
||||
|
@ -871,7 +1115,7 @@ class RawConfigParser(MutableMapping):
|
|||
try:
|
||||
d.update(self._sections[section])
|
||||
except KeyError:
|
||||
if section != self._default_section:
|
||||
if section != self.default_section:
|
||||
raise NoSectionError(section)
|
||||
# Update with the entry specific variables
|
||||
if vars:
|
||||
|
@ -906,197 +1150,31 @@ class RawConfigParser(MutableMapping):
|
|||
raise TypeError("option values must be strings")
|
||||
|
||||
|
||||
|
||||
class ConfigParser(RawConfigParser):
|
||||
"""ConfigParser implementing interpolation."""
|
||||
|
||||
def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET):
|
||||
"""Get an option value for a given section.
|
||||
_DEFAULT_INTERPOLATION = BrokenInterpolation()
|
||||
|
||||
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.
|
||||
|
||||
All % interpolations are expanded in the return values, unless the
|
||||
optional argument `raw' is true. Values for interpolation keys are
|
||||
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)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if self.__class__ is ConfigParser:
|
||||
warnings.warn(
|
||||
"The ConfigParser class will be removed in future versions."
|
||||
" Use SafeConfigParser instead.",
|
||||
DeprecationWarning, stacklevel=2
|
||||
)
|
||||
|
||||
|
||||
class SafeConfigParser(ConfigParser):
|
||||
"""ConfigParser implementing sane interpolation."""
|
||||
|
||||
def _interpolate(self, section, option, rawval, vars):
|
||||
# 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,))
|
||||
_DEFAULT_INTERPOLATION = BasicInterpolation()
|
||||
|
||||
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)
|
||||
# check for bad percent signs
|
||||
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)
|
||||
super().set(section, option, value)
|
||||
|
||||
|
||||
class SectionProxy(MutableMapping):
|
||||
|
|
|
@ -4,6 +4,7 @@ import io
|
|||
import os
|
||||
import unittest
|
||||
import textwrap
|
||||
import warnings
|
||||
|
||||
from test import support
|
||||
|
||||
|
@ -32,6 +33,7 @@ class CfgParserTestCaseClass(unittest.TestCase):
|
|||
dict_type = configparser._default_dict
|
||||
strict = False
|
||||
default_section = configparser.DEFAULTSECT
|
||||
interpolation = configparser._UNSET
|
||||
|
||||
def newconfig(self, defaults=None):
|
||||
arguments = dict(
|
||||
|
@ -43,8 +45,12 @@ class CfgParserTestCaseClass(unittest.TestCase):
|
|||
dict_type=self.dict_type,
|
||||
strict=self.strict,
|
||||
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):
|
||||
cf = self.newconfig(defaults)
|
||||
|
@ -847,6 +853,70 @@ class SafeConfigParserTestCase(ConfigParserTestCase):
|
|||
cf = self.newconfig()
|
||||
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):
|
||||
delimiters = (':=', '$')
|
||||
comment_prefixes = ('//', '"')
|
||||
|
@ -910,7 +980,9 @@ class Issue7005TestCase(unittest.TestCase):
|
|||
|
||||
def prepare(self, config_class):
|
||||
# This is the default, but that's the point.
|
||||
cp = config_class(allow_no_value=False)
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", category=DeprecationWarning)
|
||||
cp = config_class(allow_no_value=False)
|
||||
cp.add_section("section")
|
||||
cp.set("section", "option", None)
|
||||
sio = io.StringIO()
|
||||
|
@ -978,6 +1050,7 @@ def test_main():
|
|||
RawConfigParserTestCaseNonStandardDelimiters,
|
||||
RawConfigParserTestSambaConf,
|
||||
SafeConfigParserTestCase,
|
||||
SafeConfigParserTestCaseExtendedInterpolation,
|
||||
SafeConfigParserTestCaseNonStandardDelimiters,
|
||||
SafeConfigParserTestCaseNoValue,
|
||||
SafeConfigParserTestCaseTrickyFile,
|
||||
|
|
|
@ -149,6 +149,14 @@ Library
|
|||
- Issue #10467: Fix BytesIO.readinto() after seeking into a position after the
|
||||
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 #5412: configparser supports mapping protocol access.
|
||||
|
|
Loading…
Reference in New Issue