Tue, 23 Sep 2025 00:00:00 +0200
programmieren, python, script

Default-Konfiguration finden

Um einem Python-Skript die nötigen Parameter mitzugeben, verlasse ich mich gern auf zwei Module. Mit configparser kann ich in Python Konfigurationsdateien einlesen oder schreiben. Mit argparse definiere ich eine Kommandozeilenoption, die angibt, wo die Konfigurationsdatei liegt.

Oft will ich aber keine Konfigurationsdatei angeben, wenn ich mein Skript starte. Zwar könnte ich ein Shell-Skript oder einen Alias schreiben, welches den Aufruf des Skripts mit den benötigten Parametern versorgt.

Dann wiederum müsste ich dieses Skript oder den Alias ändern, wenn ich einmal eine andere Konfigurationsdatei verwenden will. Besser wäre es, wenn das Skript automatisch an vordefinierten Stellen nach seiner Konfigurationsdatei suchen würde, wenn ich keine vorgebe.

Mit der XDG Base Directory Specification gibt es eine Konvention, wo die vordefinierten Stellen unter anderem für Konfigurationsdateien liegen sollten. Und mit xdg_base_dirs ein Python-Modul, welches diese Stellen entsprechend der obigen Konvention findet. Damit kann mein Skript die Konfiguration an diesen Stellen suchen.

Das könnte in etwa so aussehen:

#!/usr/bin/env python3

import argparse
import configparser
import sys

from os.path import ( isfile, join as path_join )
from xdg_base_dirs import ( xdg_config_dirs, xdg_config_home )

def configure(args):
    cfn_default ='config.ini'
    cf_name = None
    if args.config is not None:
        cf_name = args.config
    else:
        if isfile('.{}'.format(cfn_default)):
            cf_name = '.{}'.format(cfn_default)
        elif isfile(path_join(str(xdg_config_home()), cfn_default)):
            cf_name = path_join(str(xdg_config_home()), cfn_default)
        else:
            for p in xdg_config_dirs():
                if isfile(path_join(str(p), cfn_default)):
                    cf_name = path_join(str(p), cfn_default)
                    break

    if cf_name is not None:
        conf = configparser.ConfigParser()
        print("Reading config from '{}'".format(cf_name), file=sys.stderr)
        conf.read(cf_name)
    else:
        print('No config file given and none found', file=sys.stderr)
        sys.exit(1)

    return conf

def parse_args():
    """CLI argument definitions and parsing"""

    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--config', '-c',
        help='name of config file',
    )

    return parser.parse_args()

if '__main__' == __name__:
    args = parse_args()
    conf = configure(args)
    conf.write(sys.stdout)
    sys.exit()

Das Beispielskript sucht in der folgenden Reihenfolge nach seiner Konfigurationsdatei:

1. Die Datei, deren Name mit der --config Option angegeben wurde, ohne weiter zu suchen.

2. Eine Datei namens .config.ini im aktuellen Verzeichnis (man beachte den vorangestellten Punkt).

3. Eine Datei namens config.ini in $XDG_CONFIG_HOME.

4. Die erste gefundene Datei namens config.ini in einem Verzeichnis aus $XDG_CONFIG_DIRS.

Wird keine Option --config verwendet, nimmt das Skript die erste gefundene Konfigurationsdatei oder bricht mit Fehlermeldung ab, wenn es keine finden konnte.