The CFG configuration format

The CFG configuration format

The CFG Configuration Format

The CFG configuration format is a text format for configuration files which is similar to, and a superset of, the JSON format. It dates from before its first announcement in 2008 and has the following aims:

  • Allow a hierarchical configuration scheme with support for key-value mappings and lists.
  • Support cross-references between one part of the configuration and another.
  • Provide a string interpolation facility to easily build up configuration values from other configuration values.
  • Provide the ability to compose configurations (using include and merge facilities).
  • Provide the ability to access real application objects safely, where supported by the platform.
  • Be completely declarative.

It overcomes a number of drawbacks of JSON when used as a configuration format:

  • JSON is more verbose than necessary.
  • JSON doesn’t allow comments.
  • JSON doesn’t provide first-class support for dates and multi-line strings.
  • JSON doesn’t allow trailing commas in lists and mappings.
  • JSON doesn’t provide easy cross-referencing, interpolation, or composition.

More details of the design motivation are to be found in the section entitled What, another new configuration format?

A page where you can try CFG interactively is available at the section entitled Interactive Playground for CFG. (It uses the JavaScript implementation of the CFG API.)

Reference implementations are available for Python, the JVM (using Kotlin), .NET, Go, Rust, D, JavaScript, Ruby, Elixir, Nim and Dart and are licenced using the 3-clause BSD licence.

There are minimal examples of programs/projects that use CFG here.

A Simple Example

The simplest scenario is, of course, “Hello, world”. Let’s look at a very simple configuration file simple.cfg where a message is configured:

simple.cfg
# The configured message (this is a comment)
message: 'Hello, world!'

A Python program to read this configuration would be:

from config import Config

cfg = Config('simple.cfg')
print(cfg['message'])  # will print 'Hello, world!'

A configuration is, at the top level, a list of key-value pairs. Each value, as we’ll see later, can be a list or a key-value mapping, and these can be nested without any arbitrary limits.

Nested Values

With the following configuration

langs.cfg
nested: {
  hello: {
    en: 'Hello'
    fr: 'Bonjour'
    de: 'Hallo'
    es: 'Hola'
  }
  goodbye: {
    en: 'Goodbye'
    fr: 'Au revoir'
    de: 'Auf Wiedersehen'
    es: 'Adiós'
  }
}

You can access the nested values in a single step. Again, using Python, we have:

from config import Config

cfg = Config('langs.cfg')
print(cfg['nested.hello.es'])  # will print 'Hola'
print(cfg['nested.goodbye.fr'])  # will print 'Au revoir'

A Larger Example

A larger example of a CFG configuration is below. It shows a few more features of the format.

# You can have comments anywhere in a configuration.
{
  # You can have standard JSON-like key-value mapping.
  "writer": "Oscar Fingal O'Flahertie Wills Wilde",
  # But also use single-quotes for keys and values.
  'a dimension': 'length: 5"',
  # You can use identifiers for the keys.
  string_value: 'a string value',
  integer_value: 3,
  float_value = 2.71828,         # you can use = instead of : as a key-value separator
  boolean_value: true,           # these values are just like in JSON
  opposite_boolean_value: false,
  null_value: null
  list_value: [
    123,
    4.5  # note the absence of a comma - a newline acts as a separator, too.
    [
      1,
      'A',
      2,
      'b',  # note the trailing comma - doesn't cause errors
    ]
  ]  # a comma isn't needed here.
  nested_mapping: {
    integer_as_hex: 0x123
    float_value: .14159,  # note the trailing comma - doesn't cause errors
  } # no comma needed here either.
  # You can use escape sequences ...
  snowman_escaped: '\u2603'
  # or not, and use e.g. utf-8 encoding.
  snowman_unescaped: '☃'
  # You can refer to code points outside the Basic Multilingual Plane
  face_with_tears_of_joy: '\U0001F602'
  unescaped_face_with_tears_of_joy: '😂'
  # Include sub-configurations.
  logging: @'logging.cfg',
  # Refer to other values in this configuration.
  refer_1: ${string_value},                  # -> 'a string value'
  refer_2: ${list_value[1]},                 # -> 4.5
  refer_3: ${nested_mapping.float_value},    # -> 0.14159
  # Special values are implementation-dependent. On Python, for example:
  s_val_1: `sys:stderr`,                     # -> module attribute sys.stderr
  s_val_2: `$LANG|en_GB.UTF-8`               # -> environment var with default
  s_val_3: `2019-03-28T23:27:04.314159`      # -> date/time value

  # Expressions.
  # N.B. backslash immediately followed by newline is seen as a continuation:
  pi_approx: ${integer_value} + \
             ${nested_mapping.float_value}   # -> 3.14159
  sept_et_demi: ${integer_value} + \
                ${list_value[1]}             # -> 7.5
}

Next Steps

If you are interested in knowing more, you might want to look at the History, the Features, A simplified grammar for CFG or the Application Programming Interfaces.