Implementing a configuration hierarchy

We often have several choices as to where a configuration file should be placed. There are several common locations, and we can use any combination of choices to create a kind of inheritance hierarchy for the parameters:

  • The Python installation directory: We can find the installed location for a module using the __file__ attribute of the module. From here, we can use a Path object to locate a configuration file:
>>> import this 
>>> from pathlib import Path
>>> Path(this.__file__)
PosixPath('/Users/slott/miniconda3/envs/mastering/lib/python3.7/this.py')
  • The system application installation directory: This is often based on an owning username. In some cases, we can simply create a special user ID to own the application itself. This lets us use ~theapp/ as a configuration location. We can use Path("~theapp").expanduser() to track down the configuration defaults. In other cases, the application's code may live in the /opt or /var directories.
  • A system-wide configuration directory: This is often present in /etc. Note that this can be transformed into C:etc on Windows.
  • The current user's home directory: We generally use Path.home() to identify the user's home directory.
  • The current working directory: We generally use Path.cwd() to identify the current working directory.
  • A file named in the command-line parameters: This is an explicitly named file and no further processing should be done to the name.

An application can integrate configuration options from all of these sources. Any installation default values should be considered the most generic and least user-specific; these defaults can be overridden by more specific values.

This can lead to a list of files like the following code:

from pathlib import Path

config_locations = (
Path(__file__),
# Path("~thisapp").expanduser(), requires special username
Path("/opt") / "someapp",
Path("/etc") / "someapp",
Path.home(),
Path.cwd(),
)
candidates = (dir / "someapp.config"
for dir in config_locations)
config_paths = [path for path in candidates if path.exists()]

Here, the config_locations variable is a tuple of alternative paths where a configuration file might be located. The candidates generator will create paths that include a base path with a common base name, someapp.config. A final list object, config_paths, is built for those paths that actually exist. The idea is to provide the most generic names first, and the most user-specific names last.

Once we have this list of configuration filenames, we can append any filename supplied through the command-line arguments to the end of the list with the following code:

config_paths.append(command_line_option) 

This gives us a list of locations to place a user-updated configuration file as well as the configuration default values.

Let's take a look at how to store the configuration in INI files.

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

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