Reading and Writing Configurations

This document describes how to read and write configurations in the ZConfig format.

Reading Configurations

For information on using ZConfig configuration documents in Python, see ZConfig and especially the example at Basic Usage.

For information about configuring the logging framework, see Configuring Logging.

Writing Configurations

Like the ConfigParser format, this format supports key-value pairs arranged in sections. Unlike the ConfigParser format, sections are typed and can be organized hierarchically. Additional files may be included if needed. Schema components not specified in the application schema can be imported from the configuration file. Though both formats are substantially line-oriented, this format is more flexible.

The intent of supporting nested section is to allow setting up the configurations for loosely-associated components in a container. For example, each process running on a host might get its configuration section from that host’s section of a shared configuration file.

The top level of a configuration file consists of a series of inclusions, key-value pairs, and sections.

Comments can be added on lines by themselves. A comment has a # as the first non-space character and extends to the end of the line:

# This is a comment

An inclusion is expressed like this:

%include defaults.conf

The resource to be included can be specified by a relative or absolute URL, resolved relative to the URL of the resource the %include directive is located in.

A key-value pair is expressed like this:

key value

The key may include any non-white characters except for parentheses. The value contains all the characters between the key and the end of the line, with surrounding whitespace removed.

Since comments must be on lines by themselves, the # character can be part of a value:

key value # still part of the value

Sections may be either empty or non-empty. An empty section may be used to provide an alias for another section.

A non-empty section starts with a header, contains configuration data on subsequent lines, and ends with a terminator.

The header for a non-empty section has this form (square brackets denote optional parts):

<section-type [name]>

section-type and name all have the same syntactic constraints as key names.

The terminator looks like this:

</section-type>

The configuration data in a non-empty section consists of a sequence of one or more key-value pairs and sections. For example:

<my-section>
  key-1 value-1
  key-2 value-2

  <another-section>
      key-3 value-3
  </another-section>
</my-section>

(The indentation is used here for clarity, but is not required for syntactic correctness.)

The header for empty sections is similar to that of non-empty sections, but there is no terminator:

<section-type [name] />

Extending the Configuration Schema

As we’ll see in Writing Configuration Schema what can be written in a configuration is controlled by schemas which can be built from components. These components can also be used to extend the set of implementations of objects the application can handle. What this means when writing a configuration is that third-party implementations of application object types can be used wherever those application types are used in the configuration, if there’s a ZConfig component available for that implementation.

The configuration file can use an %import directive to load a named component:

%import Products.Ape

The text to the right of the %import keyword must be the name of a Python package; the ZConfig component provided by that package will be loaded and incorporated into the schema being used to load the configuration file. After the import, section types defined in the component may be used in the configuration.

More detail is needed for this to really make sense.

A schema may define section types which are abstract; these cannot be used directly in a configuration, but multiple concrete section types can be defined which implement the abstract types. Wherever the application allows an abstract type to be used, any concrete type which implements that abstract type can be used in an actual configuration.

The %import directive allows loading schema components which provide alternate concrete section types which implement the abstract types defined by the application. This allows third-party implementations of abstract types to be used in place of or in addition to implementations provided with the application.

Consider an example application which supports logging in the same way Zope 2 does. There are some parameters which configure the general behavior of the logging mechanism, and an arbitrary number of log handlers may be specified to control how the log messages are handled. Several log handlers are provided by the application. Here is an example logging configuration:

<eventlog>
  level verbose

  <logfile>
    path /var/log/myapp/events.log
  </logfile>
</eventlog>

A third-party component may provide a log handler to send high-priority alerts the system administrator’s text pager or SMS-capable phone. All that’s needed is to install the implementation so it can be imported by Python, and modify the configuration:

%import my.pager.loghandler

<eventlog>
  level verbose

  <logfile>
    path /var/log/myapp/events.log
  </logfile>

  <pager>
    number   1-800-555-1234
    message  Something broke!
  </pager>
</eventlog>

Other Examples

Other examples of configuration files can be found at Using The Logging Components.

Textual Substitution in Values

ZConfig provides a limited way to re-use portions of a value using simple string substitution. To use this facility, define named bits of replacement text using the %define directive, and reference these texts from values.

The syntax for %define is:

%define name [value]

The value of name must be a sequence of letters, digits, and underscores, and may not start with a digit; the namespace for these names is separate from the other namespaces used with ZConfig, and is case-insensitive. If value is omitted, it will be the empty string. If given, there must be whitespace between name and value; value will not include any whitespace on either side, just like values from key-value pairs.

Names must be defined before they are used, and may not be re-defined with a different value. All resources being parsed as part of a configuration share a single namespace for defined names.

References to defined names from configuration values use the syntax described for the ZConfig.substitution module. Configuration values which include a $ as part of the actual value will need to use $$ to get a single $ in the result.

The values of defined names are processed in the same way as configuration values, and may contain references to named definitions.

For example, the value for key will evaluate to value:

%define name value
key $name

Substitution in Values from Environment Variables

Values in ZConfig can be substituted from environment variables. It utilizes Pythons os.getenv to fetch the values. Syntax is a $ followed by round brackets (parentheses). In this example the variable key gets a value assigned from the enviroment named ENVKEY:

key $(ENVKEY)

Further details and examples are described in the ZConfig.substitution module.