Contents
This page serves as a place to record alternatives, and discuss (in a semi-permanent way) the features such a library might have. A new configuration parsing library could go into the Python standard library, probably in addition to the current ConfigParser module (perhaps with that module being deprecated).
See the "Status of Shootout" section (from about 2006) for a summary of the major period of discussion which lasted on the order of a year.
Goals
Discussion of people's goals for revising ConfigParser have been broken out to the page ConfigParserGoals: what is ConfigParser really for?
Also note that there has been some confusion between "In memory storage of configuration data" and "Simple persistent storage of configuration data". Part of the problem is that almost every configuration storage system (including ConfigParser, optparse, and getopt) comes with its own in-memory API. There should be some uniform means of accessing data from configuration files and command line options parsed via optparse or getopt, including the ability to override options in configuration files with command line options. Ideally, the programmer API should not normally care whether an option was set in the configuration or the command line.
Implementations
Please list interesting implementations of config parsers here.
ConfigObj 4
ConfigObj - A simple to use config file parser
This is now a very powerful config file parser that addresses many of the issues raised here.
As of version 4 it reads and writes sections nested to *any* depth. It uses square brackets round section markers to denote nesting. this means it is compatible with most files written for ConfigParser : ::
key = value
key2 = member1, member2, memebr3
[section name]
key = value
key2 = value2
[[sub-section]]
key = value
key2 = value2Each section is acessed as a dictionary.
It has various features (e.g. list values), and can be initialised from a variety of sources (list of lines, StringIO instance, filename). Because of the straightforward write method it is also very useful for data persistence.
e.g.
config = ConfigObj()
config['key'] = value
config['section'] = {'key': 'value', 'key2': ['val1', 'val2']}
config.filename = filename
config.write()ConfigObj also has a feature I *think* is unique - in the shape of a tightly integrated type checking/conversion system. This is (allegedly) substantially simpler than the type system of ZConfig and *doesn't* involve storing type info in the config file.
The type specification is kept in a separate schema (which has a simple key = checkname(parameters) syntax and also allows for default values. The validation process checks that the config file matches the schema and fills in any default values. It also converts all values that pass into the required type.
This means a ConfigObj is an abstraction of *config data* - not just the config file, at no cost to the *user*. The validation system is simple and extendable.
M. Chermside's candidate
code and test cases. Currently allows files in either str or unicode, with sensible defaults. Allows dictionary or dotted-name access (though dotted-name can fail in some cases). Allows subsections of arbitrary length. For example,
x.y = True
x.y.z = 47
x.y.a = primeWould allow x.y to be viewed as either a value ("True") or a section.
x.y == "True"
but also
x.y['a'] = 'prime'
x.y['z'] = '47'Note that keys and values are always strings or unicode -- no autoconversion to other types. Note that this focuses on storage and API -- reading and writing is left out at the moment, and might reasonably be in a separate module for each format supported.
INITools
INITools is factored into a parser that conforms to the ConfigParser sense of what an INI file is (initools.iniparser), and a couple of implementations based on that. One of those implementations is initools.configparser, which is compatible with the standard library ConfigParser.
It includes several features that can normally be built on to ConfigParser, but enables these through simple class variables per feature. These include features like:
- Percent or dollar substitution (dollar substitution is like what zc.buildout uses)
- Options outside of a section (i.e., leading options before any section is defined)
- A different name for the global section besides DEFAULT
- Case insensitivity without case folding (i.e., preserves case)
- Some unicode handling
- Can be written out preserving comments and ordering
- Allow an extend option, similar to what zc.buildout uses
- You can turn off the behavior where file not found is ignored
- There's a method to get the filename and line number where an option was read from
Because it preserves all the information from the file you could write a view on top of this structure, to present any reasonable API -- the ConfigParser API is awkward, but given a few additions it is at least a reasonably complete description.
Skip's Idea
In my use of INI files I've always been annoyed that I couldn't nest sections to an arbitrary depth and had to resort to baroque XML APIs to accomplish that sort of task. I also figured a structure defined by indentation would be a good way to go, though YAML always seemed too complex. I worked up a little config file parser that reads and writes files like
empty section1:
level1 = new val
section1:
# this is a comment for section1.item1:
item1 = item 1
# this is another comment
subsection:
item2 = item 2
section2:
subsection:
item3 = item 3
very last = 7
Dan Gass'
I've just released a new configuration parser (http://cfgparse.sourceforge.net/). It has many of the features outlined as desireable:
- Simple ini style configuration syntax
- Type checking with error handling and help messages
- Help summary modelled after that in optparse
- Round trip - read, modify, write configuration files with comment retention
- Cooperates with optparse for configuration file options that should be overridden by command line options
- Supports heirarchically organized option settings
- User may store multiple option settings in a arbitrarily deep keyed dictionary.
- Application uses a key list to walk into the dictionary to obtain a setting.
- User controls key list with setting in configuration file.
- Supports adding keys to the list through a command line option or from environment variables.
- Supports allowing user control of configuration files used.
- Environment variables may be used to allow user to specify a default configuration file.
- Command line options to specify configuration file supported.
- Configuration files may include other configuration files where sections are read in parallel.
- Configuration files may be nested heirarchically by including configuration files from within a section or subsection.
- Configuration files may alternatively be written in Python.
- full power and flexibility of Python available for creation of option settings
- allows options settings to be real Python objects
- this feature is NOT enabled by default
- May be extended to support syntax such as XML.
The documentation is complete and is available in HTML and PDF. The home page (http://cfgparse.sourceforge.net/) is the HTML version online. The documentation and the module functionality is quite complete and useful as it is. But I am looking forward to feedback so that it may be improved. The distribution contains the module, help documentation (including source) and a fairly extensive test suite (you'll need Python2.4 to run the tests).
I figure I will make an announcement after any dust settles from this posting. By the way, I apologize for using the same name (cfgparse) as another entry but since this is modelled after optparse, that was the most logical choice.
Enjoy, Dan Gass -- dan.gass@gmail.com
Vinay Sajip's implementation
The config module allows a hierarchical configuration scheme with support for mappings and sequences, cross-references between one part of the configuration and another, the ability to flexibly access real Python objects without full-blown eval(), an include facility, simple expression evaluation and the ability to change, save, cascade and merge configurations. It has been developed on python 2.3 but should work on version 2.2 or greater.
A simple example - with the example configuration file:
messages:
[
{
stream : `sys.stderr`
message: 'Welcome'
name: 'Harry'
}
{
stream : `sys.stdout`
message: 'Welkom'
name: 'Ruud'
}
{
stream : $messages[0].stream
message: 'Bienvenue'
name: Yves
}
]a program to read the configuration would be::
from config import Config
f = file('simple.cfg')
cfg = Config(f)
for m in cfg.messages:
s = '%s, %s' % (m.message, m.name)
try:
print >> m.stream, s
except IOError, e:
print ewhich, when run, would yield the console output::
Welcome, Harry Welkom, Ruud Bienvenue, Yves
One problem I have with this implementation is the configuration file syntax. I respect the need for a syntax to handle dictionaries and lists but why invent yet another language? If one wants a Python like syntax make it the Python syntax. I don't think everyone is on board with a Python syntax for configuration files though. My biggest concern is to have a syntax that supports heirarchies and being able to construct Python objects for configuration settings. I lean toward using Python as the syntax (and even the parser) because of the flexibility offered but if there was another way that makes sense I'm ok with that. -- dan.gass@gmail.com
My reasons for not using Python itself for the syntax and parser are:
- I couldn't see how to just parse the required subset of Python - lists and dicts - while preventing arbitrary code from being executed.
- I wanted to be more forgiving of missing commas.
- I wanted to allow easy cross-referencing, inclusion and evaluation, using a more compact notation, which precludes the use of standard Python.
If someone could show me a way to meet the above desires whilst using Python syntax and parser, I'll gladly revisit the issue. -- VinaySajip
cfgparse
cfgparse - cfgparse is a Python module that provides mechanisms for managing configuration information. It is backward compatible with ConfigParser, in addition to having the following features:
Preserves structure of INI files: Order of sections & options, indentation (to some extent), comments, and blank lines are preserved when data is updated.
More convenient than ConfigParser: Values can be accessed using dotted notation, or using container syntax (cfg[key]).
Backward compatibility: Backward compatible implementations of ConfigParser, RawConfigParser, and SafeConfigParser are included that are API-compatible with the Python standard library. They pass all the unit tests in Python-2.3.4.
- Extensible: It is possible to add other configuration formats, and to convert between different formats (as long as the data models are compatible).
ZConfig
ZConfig - This Python package is a bit larger than some of the others, but provides for schema-based development of configuration structures. The schema language uses XML, but the configuration language is more like Apache's. Sections are typed and completely nestable. The basic implementation does have some limitations that are tedious to work around if you run into them. One that can bite quickly is that names in the configuration language are case-insensitive by default; for versions before 2.3.1 this was terribly difficult to work around without copying lots of code, and even with 2.3.1 it takes more than it should.
tconfpy
OK, I'll Toss My Hat Into The Ring. I just found this discussion tonight for the first time. A fascinating topic and one very much dear to my heart. So much so that, ahem, uh, ... http://www.tundraware.com/Software/tconfpy
configparse
configparse is an extension that is built on top of the command line parsing library optparse. It provides the same interface and is intended to be uses as a drop-in-replacement for optparse. configparse is very limited in its abilities, there's no support for sections, recursiveness or sophisticated value checking etc. Its advantage is that it takes only a few modifications to existing code to add simple support for config files which can be quite handy sometimes.
plistlib
plistlib plistlib is a small module for generating and parsing Mac OS X .plist files. It supports:
- lists
- dictionaries
- arbitrary nesting
- bool, int, float, string and date types
As the default config format on OS X, plist files are already used by hundreds of apps. Though popular on the Mac, the format can be used from any platform or language since it's a subset of XML. Graphical editors are available from Apple as part of its free developer tools, as well as from third party developers
PyOptionTree
PyOptionTree is a hierarchical parameter parser that I wrote with the goal being to allow the user to both specify parameters *and* modify, control, structure, copy, and record them in a efficient and intuitive way. I've been using and refining it for over a year, and it has many of the options that people seem to find desirable, plus a few more, so I thought it'd be worthwhile to post here. If people have any comments, please let me know.
It supports:
- Lists, tuples, and dictionaries (through dict() and list of 2-tuples). Also supports nesting lists and tuples.
- Embedded python code.
- Linking options (even forward linking) using a directory-like syntax.
- A useful set of functions for importing additional files, loading pickles to be the value of objects, string operations, summation, concatenation, etc.
- Allowing for program-defined functions that can be passed to the parser.
- Inheritance and a more object oriented feel through by allowing branches to be copied and modified.
- Saving the tree in the same format. The saving preserves the original order of nodes.
- Allowing the programmer to modify the tree -- even inserting arbitrary pickleable objects -- and save it in the same format.
- Parse and incorporating command line arguments.
Here's a simple example:
Tasks = [exercises/jog, eating/eatcereal, eating/eattoast] # links to the following subtrees
exercises = {
jog = {
action = "jog"
minutes = 30
}
# etc.
}
eating = {
eatcereal = {
action = "eat"
food = "cereal"
}
eattoast = copy(eatcereal) # this creates a copy of the eatcereal subtree
eattoast/food = "toast" # this changed the food option of that copy
}On the programmer's end, each of the braches behaves like a full tree, e.g.:
def runTests(opttreefile):
ot = PyOptionTree(opttreefile)
for t in ot('Tasks'): # According to the above definition,
runTest(t) # t is a subtree (branch).
def runTest(ot):
print 'Current Action: ', ot("name")
