gavo.utils.fancyconfig module

A wrapper around configparser that defines syntax and types within the configuration options.

This tries to do for configuration processing what optparse did for command line option processing: A declarative way of handling the main chores.

The idea is that, in a client program, you say something like:

from pftf.fancyconfig import (Configuration, Section, ConfigError,
        ...(items you want)...)

_config = Config(
        Section(...
                XYConfigItem(...)
        ),
        Section(...
        ...
        )
)

get = _config.get
set = _config.set

if __name__=="__main__":
        print fancyconfig.makeTxtDocs(_config)
else:
        try:
                fancyconfig.readConfiguration(_config, None,
                        os.path.join(dataDir, "config"))
        except ConfigError, msg:
                import sys
                sys.stderr.write("%s: %s\n"%(
                        sys.argv[0], unicode(msg)))
                sys.exit(0)

and be done with most of it.

For examples of how this is used, see pftf (http://www.tfiu.de/pftf) or DaCHS (http://soft.g-vo.org/dachs)

exception gavo.utils.fancyconfig.BadConfigValue[source]

Bases: ConfigError

is raised by getConfiguration when there is a syntax error or the like in a value.

The error message gives a hint at the reason of the error and is intended for human consumption.

class gavo.utils.fancyconfig.BooleanConfigItem(name, default=None, description='Undocumented')[source]

Bases: ConfigItem

A config item that contains a boolean and can be parsed from many fancy representations.

default = 'False'
falseLiterals = {'0', 'disabled', 'f', 'false', 'no', 'off'}
trueLiterals = {'1', 'enabled', 'on', 't', 'true', 'yes'}
typedesc = 'boolean'
class gavo.utils.fancyconfig.BytestringConfigItem(name, default=None, description='Undocumented')[source]

Bases: ConfigItem

A config item containing byte strings. No characters outside of ASCII are allowed.

default = ''
typedesc = 'ASCII string'
exception gavo.utils.fancyconfig.ConfigError[source]

Bases: Exception

is the base class of the user visible exceptions from this module.

fileName = '<internal>'
class gavo.utils.fancyconfig.ConfigItem(name, default=None, description='Undocumented')[source]

Bases: object

A description of a configuration item including methods to parse and unparse them.

This class is an abstract base class for options with real syntax (_parse and _unparse methods).

ConfigItems have a section and a name (as in configparser), a value (that defaults to default), an origin (which is “default”, if the value has not been changed and otherwise can be freely used by clients), and a description. The origin is important for distinguishing what to save.

You need to define the _parse and _unparse methods when deriving subclasses. The _parse methods must take a string and return anything or raise ParseErrors (with a sensible description of the problem) if there is a problem with the input; they must not raise other exceptions when passed a string (but may do anything when passed something else). _unparse methods must not raise exceptions, take a value as returned by parse (nothing else must be passed in) and return a string that _parse would parse into this value.

Thus, the set method only takes strings as values. To set parsed values, assign to the value attribute directly. However, _unparse methods are not required to cope with any crazy stuff you enter in this way, and thus you suddenly may receive all kinds of funny exceptions when serializing a Configuration.

Inheriting classes need to specify a class attribute default that kicks in when no default has been specified during construction. These must be strings parseable by _parse.

Finally, you should provide a typedesc class attribute, a description of the type intended for human consumption. See the documentation functions below to get an idea how the would be shown.

getAsString()[source]
set(value, origin='user')[source]
typedesc = 'unspecified value'
class gavo.utils.fancyconfig.Configuration(*sections)[source]

Bases: object

A collection of config Sections and provides an interface to access them and their items.

You construct it with the Sections you want and then use the get method to access their content. You can either use get(section, name) or just get(name), which implies the defaultSection section defined at the top (right now, “general”).

To read configuration items, use addFromFp. addFromFp should only raise subclasses of ConfigError.

You can also set individual items using set.

The class follows the default behaviour of configparser in that section and item names are lowercased.

Note that direct access to sections is not forbidden, but you have to keep case mangling of keys into account when doing so.

addFromFp(fp, origin='user', fName='<internal>')[source]

adds the config items in the file fp to self.

get(arg1, arg2=None, default=<class 'gavo.utils.fancyconfig._Undefined'>)[source]
getUserConfig()[source]

returns a configparser containing the user set config items.

getitem(arg1, arg2=None)[source]

returns the item described by section, name or just name.

saveUserConfig(destName)[source]

writes the config items changed by the user to destName.

set(arg1, arg2, arg3=None, origin='user')[source]

sets a configuration item to a value.

arg1 can be a section, in which case arg2 is a key and arg3 is a value; alternatively, if arg3 is not given, arg1 is a key in the defaultSection, and arg2 is the value.

All arguments are strings that must be parseable by the referenced item’s _parse method.

Origin is a tag you can use to, e.g., determine what to save.

class gavo.utils.fancyconfig.DefaultSection(documentation, *items)[source]

Bases: Section

is the default section, named by defaultSection above.

The only difference to Section is that you leave out the name.

class gavo.utils.fancyconfig.DictConfigItem(name, default=None, description='Undocumented')[source]

Bases: ListConfigItem

A config item that contains a concise representation of a string-string mapping.

The literal format is {<key>:<value>,}, where whitespace is ignored between tokens and the last comma may be omitted.

No commas and colons are allowed within keys and values. To lift this, I’d probably go for backslash escaping.

>>> ci = DictConfigItem("foo"); ci.value
{}
>>> ci.set("ab:cd, foo:Fuß"); ci.value
{'ab': 'cd', 'foo': 'Fuß'}
>>> ci.getAsString();ci.set(ci.getAsString()); ci.value
'ab:cd, foo:Fuß, '
{'ab': 'cd', 'foo': 'Fuß'}
>>> ci.set("ab:cd, rubbish")
Traceback (most recent call last):
        ...
fancyconfig.ParseError: 'rubbish' is not a valid mapping literal element
default = ''
typedesc = 'mapping'
class gavo.utils.fancyconfig.EnumeratedConfigItem(name, default=None, description='Undocumented', options=[])[source]

Bases: StringConfigItem

A ConfigItem taking string values out of a set of possible strings.

Use the keyword argument options to pass in the possible strings. The first item becomes the default unless you give a default. You must give a non-empty list of strings as options.

typedesc = 'value from a defined set'
class gavo.utils.fancyconfig.ExpandedPathConfigItem(name, default=None, description='Undocumented')[source]

Bases: StringConfigItem

A configuration item in that returns its value expandusered.

class gavo.utils.fancyconfig.FloatConfigItem(name, default=None, description='Undocumented')[source]

Bases: ConfigItem

A config item containing a float.

It supports a Null value through the special None literal.

>>> ci = FloatConfigItem("foo"); print(ci.value)
None
>>> ci = FloatConfigItem("foo", default="23"); ci.value
23.0
>>> ci.set("42.25"); ci.value
42.25
>>> ci.getAsString()
'42.25'
default = 'None'
typedesc = 'floating point value'
class gavo.utils.fancyconfig.IntConfigItem(name, default=None, description='Undocumented')[source]

Bases: ConfigItem

A config item containing an integer.

It supports a Null value through the special None literal.

>>> ci = IntConfigItem("foo"); print(ci.value)
None
>>> ci = IntConfigItem("foo", default="23"); ci.value
23
>>> ci.set("42"); ci.value
42
>>> ci.getAsString()
'42'
default = 'None'
typedesc = 'integer'
class gavo.utils.fancyconfig.IntListConfigItem(name, default=None, description='Undocumented')[source]

Bases: ListConfigItem

A ConfigItem containing a comma separated list of ints.

Literal handling is analoguos to ListConfigItem.

>>> ci = IntListConfigItem("foo"); ci.value, ci.getAsString()
([], '')
>>> ci.set("3,2, 1"); ci.value, ci.getAsString()
([3, 2, 1], '3, 2, 1, ')
>>> ci.set(ci.getAsString()); ci.value
[3, 2, 1]
>>> ci.set("1, 2, 3, rubbish")
Traceback (most recent call last):
        ...
fancyconfig.ParseError: Non-integer in integer list
default = ''
typedesc = 'list of integers'
class gavo.utils.fancyconfig.IntSetConfigItem(name, default=None, description='Undocumented')[source]

Bases: IntListConfigItem

A set-valued IntListConfigItem for fast existence lookups.

typedesc = 'set of integers'
class gavo.utils.fancyconfig.ListConfigItem(name, default=None, description='Undocumented')[source]

Bases: StringConfigItem

A ConfigItem containing a list of strings, comma separated.

The values are space-normalized. Trailing whitespace-only items are discarded, so “” is an empty list, “,” is a list containing one empty string.

There is currently no way to embed commas in the values. If that should become necessary, I’d probably go for backslash escaping.

>>> ci = ListConfigItem("foo"); ci.value, ci.getAsString()
([], '')
>>> ci.set(ci.getAsString());ci.value
[]
>>> ci.set("3, 2, 1, Zündung"); ci.value, ci.getAsString()
(['3', '2', '1', 'Zündung'], '3, 2, 1, Zündung, ')
>>> ci.set(",");ci.value
['']
>>> ci.set(ci.getAsString());ci.value
['']
default = ''
typedesc = 'list of strings'
class gavo.utils.fancyconfig.MagicSection(name, documentation='Undocumented', itemFactory=<class 'gavo.utils.fancyconfig.StringConfigItem'>, defaults=[])[source]

Bases: Section

A section that creates new keys on the fly.

Use this a dictionary-like thing when successive edits are necessary or the DictConfigItem becomes too unwieldy.

A MagicSection is constructed with the section name, an item factory, which has to be a subclass of ConfigItem (you may want to write a special constructor to provide documentation, etc.), and defaults as a sequence of pairs of keys and values. And there should be documentation, too, of course.

set(name, value, origin='user')[source]

set the value of the configuration item name.

value must always be a string, regardless of the item’s actual type.

exception gavo.utils.fancyconfig.NoConfigItem[source]

Bases: ConfigError

is raised by Configuration if a non-existing configuration item is set or requested.

exception gavo.utils.fancyconfig.ParseError[source]

Bases: ConfigError

is raised by ConfigItem’s parse methods if there is a problem with the input.

These should only escape to users of this module unless they call ConfigItem.set themselves (which they shouldn’t).

class gavo.utils.fancyconfig.PathConfigItem(name, default=None, description='Undocumented')[source]

Bases: StringConfigItem

A ConfigItem for a unix shell-type path.

The individual items are separated by colons, ~ is replaced by the current value of $HOME (or “/”, if unset), and $<key> substitutions are supported, with key having to point to a key in the defaultSection.

To embed a real $ sign, double it.

This is parented ConfigItem, i.e., it needs a Configuration parent before its value can be accessed.

typedesc = 'shell-type path'
property value
class gavo.utils.fancyconfig.PathRelativeConfigItem(name, default=None, description='Undocumented')[source]

Bases: StringConfigItem

A configuration item interpreted relative to a path given in the general section.

Basically, this is a replacement for configparser’s %(x)s interpolation. In addition, we expand ~ in front of a value to the current value of $HOME.

To enable general-type interpolation, override the baseKey class Attribute.

baseKey = None
property value
class gavo.utils.fancyconfig.Section(name, documentation, *items)[source]

Bases: object

A section within the configuration.

It is constructed with a name, a documentation, and the configuration items.

They double as proxies between the configuration and their items via the setParent method.

get(name)[source]

returns the value of the configuration item name.

If it does not exist, a NoConfigItem exception will be raised.

getitem(name)[source]
set(name, value, origin='user')[source]

set the value of the configuration item name.

value must always be a string, regardless of the item’s actual type.

setParent(parent)[source]
class gavo.utils.fancyconfig.SetConfigItem(name, default=None, description='Undocumented')[source]

Bases: ListConfigItem

A set-valued ListConfigItem for quick existence lookups.

typedesc = 'set of strings'
class gavo.utils.fancyconfig.StringConfigItem(name, default=None, description='Undocumented')[source]

Bases: ConfigItem

A config item containing unicode strings.

The serialization of the config file is supposed to be utf-8.

The special value None is used as a Null value literal.

Tests are below.

default = ''
typedesc = 'string'
exception gavo.utils.fancyconfig.SyntaxError[source]

Bases: ConfigError

is raised when the input file syntax is bad (i.e., on configparser.ParsingErrors)

gavo.utils.fancyconfig.load_tests(loader, tests, ignore)[source]
gavo.utils.fancyconfig.makeTxtDocs(config, underlineChar='.')[source]
gavo.utils.fancyconfig.readConfiguration(config, systemFName, userFName)[source]

fills the Configuration config with values from the the two locations.

File names that are none or point to non-existing locations are ignored.