Overview

The library provides a customized configparser.ConfigParser object, adding just one small feature, which seems to have originated from zend config (A web framework).

It processes special ZSEP string in sections (default: ' : '). So that the section [aa : bb] means [aa], but now [aa] has a parent section [bb].

It is useful in omitting duplicate options in related or similar sections.

(Only for 'dot separator', reverse ordering is also supported, e.g. [bb.aa]. See below)

Installation

It is a single file Python module, with no other library dependency.

Python 3.6 and above are supported.:

pip install zconfigparser

Note

It is mainly developed for my other script.

And it is meant to be an user-level script helper.

Usage

## myconfig.ini
[aa : bb]
x=aaa
[bb]
x=bbb
y=bbb

>>> from zconfigparser import ZConfigParser
>>> config = ZConfigParser()
>>> config.read('myconfig.ini')
>>> config.get('aa : bb', 'x')
aaa
>>> config.get('aa', 'x')
aaa
>>> config.get('aa', 'y')
bbb
  • [aa] doesn’t have y option, so the option in [bb] is looked-up instead (if any).
  • In .get, both section names [aa] and [aa : bb] can be used. They are called short [section] name and long [section] name respectively.
  • the original Configparser values are preserved in item accesses e.g. config['aa : bb']['x']. (So only long name can be used there).

Specification

Changed Function

.get(section, key, *, raw=False, vars=None[, fallback])
Explained above. It overrides Configparser’s .get. Signature is the same.

Added Functions

.has_zsection(section)

Return True or False, according to the existence of a section, as ZConfigParser sees it. Accept long names and short names.

So using above example:

>>> config.has_zsection('aa : bb')
True
>>> config.has_zsection('aa')
True

Note Configparser’s .has_section is kept as is.

>>> config.has_section('aa : bb')
True
>>> config.has_section('aa')
False
.has_zoption(section, option)

Return True or False, according to the existence of a option, as ZConfigParser sees it. Accept long names and short names.

Note Configparser’s .has_option is kept as is.

>>> config.has_zoption('aa : bb', 'x')
True
>>> config.has_option('aa : bb', 'y')
False
.zsections()

Return a set containing all short section names in config. Accept long names and short names.

It also does error checks config-wide. See below

Added Argument

ZSEP
Default separator word is ' : ', exactly one space before and after ':'. To change this, use the argument.
config = ZConfigParser(ZSEP='->')   # separator is '->'.

Lookup Order:

Lookup order is depth-first.

## myconfig.ini
[aa : bb : cc]
[bb : dd]
[cc : ee]
x=ccc
[dd]
x=ddd
[ee]
x=eee

>>> config.get('aa', 'x')
ddd                     ## order: aa -> bb -> dd -> cc -> ee

dot separator:

Some examples are found using a ‘dot’ for separator, reversing inheritance order. And indeed it seems natural in this case.

So although a little confusing, it is also implemented here. Note it is a special case only when ZSEP='.'.

[aa : bb : cc] ('cc' is grandparent)
   -->  -->

[aa.bb.cc]('aa' is grandparent)
  <-- <--

Errors:

Errors are rather rigid. Before looking into whether or not there are actual conflicts in options, it just checks sections structure, and judges accordingly. It is regardless of Configparser setting of 'strict'.

  • Parent section lookup failure raises zconfigparser.NoZSectionError. (When there is [aa : bb], there must be [bb] or [bb : xx] etc.)
  • Blank parent section also raises zconfigparser.NoZSectionError. ([aa : ] etc.)
  • More than two same leftmost section names raise zconfigparser.DuplicateZKeyError. (cf. any two combination of [aa], [aa : bb], [aa : cc])
  • Circular lookup raises zconfigParser.RecursiveZkeyError ([aa : bb], [bb : cc], [cc : aa])

Note that ZConfigParser does not automatically check Parent section lookup failure. Configparser can read multiple config files or strings, so deciding an appropriate time for validation is rather difficult.

  • .get detects Parent section lookup failure only for parsed sections.
  • .zsections checks it for all sections, because it parses all sections. So, it can be used for manual config-wide validation.

And it raises zconfigparser.NoZOptionError, when nonexistent option is looked-up and default or fallback is not provided.

Thanks to

Mr. Kazzer’s nestedconfigparser. The idea of overriding ._unify_values is from his code. I think this is a very clean approach.