Representation, persistence, state, and usability

When looking at a configuration file, we're looking at a human-friendly version of an object state. Often, we'll provide the state of more than one object. When we edit a configuration file, we're changing the persistent state of an object that will get reloaded when the application is started (or restarted). We have two common ways of looking at a configuration file:

  • A mapping or a group of mappings from parameter names to configuration values. Note that even when there are nested mappings, the structure is essentially keys and values.
  • A serialized object that has complex attributes and properties with the configuration values. The distinguishing feature is the possibility of properties, methods, and derived values in addition to the user-supplied values.

Both of these views are equivalent; the mapping view relies on a built-in dictionary or namespace object. The serialized object will be a more complex Python object, which has been created from an external, human editable representation of the object. The advantage of a dictionary is the simplicity of putting a few parameters into a simple structure. The advantage of a serialized object is its ability to track the number of complex relationships.

For a flat dictionary or namespace to work, the parameter names must be chosen carefully. Part of designing the configuration is to design useful keys, which is something we looked at in Chapter 11, Storing and Retrieving Objects via Shelve, and Chapter 12, Storing and Retrieving Objects via SQLite. A mapping requires unique names so that other parts of the application can refer to it properly.

When we try to reduce a configuration file to a single mapping, we often discover that there are groups of related parameters. This leads to namespaces within the overall collection of names. Let's consider a web application that uses other web services; we might have two parallel groups of parameters: service_one_host_name and service_one_port_number, as well as service_two_host_name and service_two_port_number. These could be four separate names, or we could use a more complex structure to combine the names into two related groups; for example, by perhaps creating a configuration data structure such as {"service_one": {"host_name": "example.com", "port_number": 8080}, etc.}

There is a blurry space between using simple mappings and using more complex serialized Python objects. Some of the modules we'll look at in this chapter use complex nested dictionaries and namespace objects. The variety of alternative solutions suggests there is no single best way to organize the configuration parameters.

It's helpful to look at the logging configuration for examples of how it can be very challenging to configure a complex system. The relationships between Python logging object-loggers, formatters, filters, and handlers must all be bound together to create a logger usable by the application. If any pieces are missing, the logger will not produce output. Section 16.8 of the Standard Library Reference describes two different syntaxes for logging configuration files. We'll look at logging in Chapter 16, The Logging and Warning Modules.

In some cases, it may be simpler to use Python code directly as the configuration file. In this case, the configuration is a Python module, and the details are brought in using a simple import statement. If a configuration file's syntax adds too much complexity, then it may not be of any real value.

Once we've decided on the overall data structure, there are two common design patterns for the scope of that structure.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset