X

Reload einer Konfigurationsdatei bei Änderung in Python mit watchdog Modul

Um Werte in einem Python-Script einfach ändern zu können, lege ich meist im Scriptverzeichnis eine config.py ab, welche die benötigten Werte und Einstellungen enthält. Ab und an, ist es jedoch notwendig, diese Werte anzupassen. Besonders schön wäre es, wenn das Python-Script diese Änderungen übernimmt, ohne neu gestartet werden zu müssen.

Für ein aktuelles Projekt, sieht die config.py aktuell wie folgt aus:

config_parser = {
    'database': {
        'item': {
            'version': 2
        }
    }
}

In dem eigentlichen Script importiere ich die Settings wie folgt:

import config

class Parser(object):
    def __init__(self):
        self.config = config.config_parser

Um die Überwachung auf Änderungen hinzuzufügen, importiert man ‚Observer‘ und ‚FileSystemEventHandler‘ aus dem ‚watchdog‘-Modul (http://pythonhosted.org/watchdog/). Anschließend erstellt man seinen eigenen FilesystemEventhandler als Sub-Class von FileSystemEventHandler und überschreibt die __init__ und on_modified Methoden, damit man die Möglichkeit hat, eine Methode der Parser-Klasse mit der gewünschten Funktionalität aufzurufen:

class MyFileSystemEventHandler(FileSystemEventHandler):
    def __init__(self, method_to_call):
        self.__method_to_call = method_to_call

    def on_modified(self, event):
        logger.debug('calling method')
        self.__method_to_call()

Anschließend erstellt man in der __init__ Methode der Parser Class eine Instanz des eigenen FileSystemEventHandlers und übergibt die Methode, welche vom Handler bei Änderungen aufgerufen werden soll. Danach instanziert man noch den Observer, welcher die eigene Überwachung des Dateisystems durchführt und man ist eigentlich fertig.

In meinem Fall habe ich noch einen kleinen Tweak hinzugefügt. Bei meiner IDE (PyCharm) werden bei Dateiänderungen im Projektverzeichnis temporär Backup-Kopien und .pyc-Dateien angelegt. Dieses führt natürlich zu einer multiplen Ausführung der gewünschten Methode. Daher habe ich die on_modified Methode noch ein wenig ergänzt:

class MyFileSystemEventHandler(FileSystemEventHandler):
    def on_modified(self, event):
        files_to_react_on = ['config.py']
        if os.path.basename(event.src_path) in files_to_react_on:
            logger.debug('calling method')
            self.__method_to_call()
        else:
            logger.debug('just non interesting files changed')

In einer Liste gebe ich die Dateinamen an, bei deren Änderung der Aufruf durchgeführt werden kann. Hilfreich hierbei ist das Attribut ’src_path‘ des event-Objekts, welches den vollständigen Pfad der geänderten Datei beinhaltet. Über os.path.basename() lassen wir uns von diesem Pfad nur den Dateinamen ausgeben und vergleichen diesen mit unserer Liste.

Hier nochmal das vollständige Script:

import config
import os
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

class MyFileSystemEventHandler(FileSystemEventHandler):
    def __init__(self, method_to_call):
        self.__method_to_call = method_to_call

    def on_modified(self, event):
        files_to_react_on = ['config.py']
        if os.path.basename(event.src_path) in files_to_react_on:
            logger.debug('calling method')
            self.__method_to_call()
        else:
            logger.debug('just non interesting files changed')

class Parser(object):
    def __init__(self):
        self.config = config.config_parser
        filewatcher_event_handler = MyFileSystemEventHandler(self.load_config)
        filewatcher_observer = Observer()
        filewatcher_observer.schedule(filewatcher_event_handler, '.', recursive=False)
        filewatcher_observer.start()

    def load_config(self):
        import config
        reload(config)
        self.config = config.config_parser
        logger.info('loaded config: %s' % (json.dumps(self.config),))

Das ganze gibt es hier noch als Video:

Freue mich auf Eure Kommentare!

Torsten Feld :

This website uses cookies.