watchdog reagiert nicht mehr

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Spaghetticoder
User
Beiträge: 13
Registriert: Dienstag 7. April 2020, 18:05

Hallo von einem Neuling,

ich verzweifle an einem eigentlich banalemThema: ein lokales Verzeichnis soll per watchdog auf geänderte Dateien überwacht werden und ggf. eine Funktion aufgerufen werden. Das Ganze funktioniert auch einige Minuten gut - dann reagiert watchdog aber nicht mehr auf Dateiändeurngen. Habe zuvor massiv gegoogelt jedoch keine Lösung gefunden. Das Problem tritt immer auf - jedoch nach verschiedenen Zeitpunkten...

Kann mir hier jemand helfen?


Grüße,
Andy

---


import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from datetime import datetime, timedelta
import ftplib
import os
from dateutil import parser
from datetime import datetime


um_sourcepath = "c:/"
um_ftpuser = "user"
um_ftpserver = "ftp.testdomain.de"
um_ftppassword = "password"
um_ftpdirectory = "test"



class MyHandler(FileSystemEventHandler):
def __init__(self):
self.last_modified = datetime.now()

def on_modified(self, event):
if datetime.now() - self.last_modified < timedelta(seconds=1):
return
else:
self.last_modified = datetime.now()
print(f'Event type: {event.event_type} path : {event.src_path}')
anftpsenden(filename, filepath) #diese Funktion soll ggf. ausgeführt werden, Parameter werden extra generiert


def anftpsenden(filename,filepath):

with ftplib.FTP(um_ftpserver) as ftp:
try:
ftp.login(um_ftpuser, um_ftppassword)
ftp.cwd(um_ftpdirectory)

with open(filepath, 'rb') as fp:
res = ftp.storbinary("STOR " + filename, fp)
if not res.startswith('226 Transfer complete'):
print('\a\a\aUpload failed')
return(False)
else:
print("\a>>> OK: Datei wurde erfolgreich übertragen.")
return(True)
ftp.quit()
except ftplib.all_errors as e:
print('\a\a\aFTP error: #', e)
return(False)



if __name__ == "__main__":

event_handler = MyHandler()
observer = Observer()
observer.schedule(event_handler, path=um_sourcepath, recursive=False)
observer.start()

try:
while True:
time.sleep(10)
print("Bin noch wach...")
except KeyboardInterrupt:
observer.stop()
observer.join(timeout=None)
Spaghetticoder
User
Beiträge: 13
Registriert: Dienstag 7. April 2020, 18:05

Hallo,

eben herausgefunden: das Programm oben funktioniert - WENN ich die FTP-Übertragung bzw. weitere Unterfunktionen weglasse...

Von daher bemühe ich mich weiterhin selbst...

Grüße
Benutzeravatar
__blackjack__
User
Beiträge: 14019
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Spaghetticoder: Anmerkungen zum Quelltext:

`os` und `parser` aus `dateutil` werden importiert, aber nicht verwendet.

Namen sollten keine kryptischen Abkürzungen oder Prä-/Suffixe enthalten. Was soll das `um_` bei den Konstanten bedeuten? Ein `my` hat vor Namen auch nichts zu suchen wenn es da nicht auch ein `our` oder `their` gibt, wovon das `my` abgrenzt.

Konstanten werden KOMPLETT_GROSS geschrieben um sie deutlich als solche erkennen zu können.

LeerzeichenzwischenWortensindinderRegelsehrgutfürdieLesbarkeit. Also bitte `an_ftp_senden()` und nicht `anftpsenden()`.

Der Code im ``if __name__ …`` gehört auch in eine Funktion, sonst hat man globale Variablen und die Gefahr das man sie aus versehen verwendet.

Das ``observer.stop()`` gehört in einen ``finally``-Zweig, damit es auch bei anderen Ausnahmen als `KeyboardInterrupt` ausgeführt wird.

``return`` ist keine Funktion, sollte also auch nicht so aussehen als wäre das ein Funktionsaufruf.

Bei `MyHandler` würde ich die `__init__()` der Basisklasse aufrufen.

Die Argumente `filename` und `filepath` bei `anftpsenden()` senden sind sich zu ähnlich als dass daraus ersichtlich wird welches davon die lokale Datei und welches die auf dem FTP-Server beschreiben soll. Das erst das Ziel und dann die Quelle kommt, ist IMHO auch etwas unerwartet.

`fp` ist kein guter Name für ein Dateiobjekt. Thema kryptische Abkürzungen. Was soll das denn überhaupt bedeuten? `file` sind nur zwei Zeichen mehr und man muss nicht mehr rätselraten.

`res` ist auch nicht toll. Da könnte man denken es hiesse `result`, aber es ist eine `response`.

`ftp.quit()` muss nicht aufgerufen werden — dafür wird ja die ``with``-Anweisung verwendet.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import ftplib
import time
from datetime import datetime, timedelta

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

SOURCE_PATH = "c:/"
FTP_USER = "user"
FTP_SERVER = "ftp.testdomain.de"
FTP_PASSWORD = "password"
FTP_DIRECTORY = "test"


def an_ftp_senden(local_filename, remote_filename):
    with ftplib.FTP(FTP_SERVER) as ftp:
        try:
            ftp.login(FTP_USER, FTP_PASSWORD)
            ftp.cwd(FTP_DIRECTORY)
            with open(local_filename, "rb") as file:
                response = ftp.storbinary("STOR " + remote_filename, file)
                if not response.startswith("226 Transfer complete"):
                    print("\a\a\aUpload failed")
                    return False
                else:
                    print("\a>>> OK: Datei wurde erfolgreich übertragen.")
                    return True
        except ftplib.all_errors as error:
            print("\a\a\aFTP error: #", error)
            return False


class FtpUploadHandler(FileSystemEventHandler):
    def __init__(self):
        FileSystemEventHandler.__init__(self)
        self.last_modified = datetime.now()

    def on_modified(self, event):
        if datetime.now() - self.last_modified < timedelta(seconds=1):
            return
        
        self.last_modified = datetime.now()
        print(f"Event type: {event.event_type}  path : {event.src_path}")
        #
        # FIXME Diese Funktion soll ggf. ausgeführt werden, Parameter werden
        # extra generiert
        #
        an_ftp_senden(local_filename, remote_filename)


def main():
    observer = Observer()
    observer.schedule(FtpUploadHandler(), path=SOURCE_PATH, recursive=False)
    observer.start()
    try:
        while True:
            time.sleep(10)
            print("Bin noch wach...")
    except KeyboardInterrupt:
        pass  # User pressed Ctrl+C.
    finally:
        observer.stop()
    observer.join()


if __name__ == "__main__":
    main()
Das `anftpsenden()` Ausnahmen in einen Wahrheitswert als Rückgabewert umwandelt statt Ausnahmen zu verwenden ist unschön. Ebenso dass dort `print()`-Ausgaben für den Benutzer drin stehen. Da würde man eher Logging verwenden.

Problematisch wird sein, dass die `on_modified()` solange nicht aufgerufen werden kann wie der FTP-Upload läuft. Und danach können dann die Ereignisse abgearbeitet werden die während des hochladens auftraten. Falls die Sache mit dem `last_modified` *das* rausfiltern sollte: Tut es nicht.

Der Test bezieht sich auch gar nicht auf eine bestimmte Datei sondern auf *irgendeine*. Wenn sich mehr als eine Datei in kurzer Zeit ändert, hätte man also auch ein Problem wenn das hochladen nicht die Ereignisabarbeitung blockieren würde.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Spaghetticoder
User
Beiträge: 13
Registriert: Dienstag 7. April 2020, 18:05

Hallo Blackjack,

Danke für Deine Ausführliche Antwort. Ich werde mir Deine Stil-Empfehlungen zu Herzen nehmen - der Nickname von mir wurde wohl nicht zu unrecht gewählt...

Ich teste aktuell Deinen Code. Das Problem hängt wohl eher mit der FTP-Übertragung / Watchdog Kombi zusammen: ohne FTP funktioniert alles einwandfrei.

Zum Hintergrund: das echte Programm ist deutlich umfangreicher - ich wollte die für mein Thema unwichtigen Teile hier aber nicht komplett reinstellen.

Ich melde mich wieder.


Ciao - und bleib gesund
Benutzeravatar
__blackjack__
User
Beiträge: 14019
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Spaghetticoder: Also um Deinen Nickname zu rechtfertigen müsstest Du wohl erst eines der beiden `goto`-Module verwenden:

https://pypi.org/project/goto-statement/
http://entrian.com/goto/

😉
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Spaghetticoder
User
Beiträge: 13
Registriert: Dienstag 7. April 2020, 18:05

Hallo,

ah - das gab's doch auch in den 80ern in BASIC. Aber ich versuche das weiterhin zu umgehen :-).

Frage: Ich habe das gesamte Programm betrachtet, aber die Problemursache leider nicht finden können.

Unguten Programmierstil mal aussen vor: Das Programm läuft zunächst wie gewünscht, aber nach "einiger Zeit" springt der Event jedoch nicht mehr an, wenn eine Datei im Quellverzeichnis modifiziert oder neu erstellt wurde. Woran könnte das denn überhaupt noch liegen?

Grüße
Benutzeravatar
__blackjack__
User
Beiträge: 14019
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Spaghetticoder: Wie sieht denn der Code jetzt aus?

``goto`` gibt's auch in C heute noch und wird in modernem, idiomatischem C sogar eingesetzt. Es ist nicht so schlecht wie sein Ruf. 🙂
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Spaghetticoder
User
Beiträge: 13
Registriert: Dienstag 7. April 2020, 18:05

Hallo,

leider reagiert der neue Code auch nicht mehr, wenn er längere Zeit läuft...
Bin hier wirklich am verzweifeln: ist der "watchdog" schon zuverlässig, wenn er dauerhaft aktiv ist?

Grüße & frohe Ostern
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nimm das ftp raus & ersetz es mit einem einfachen print in der Ereignis-Behandlung. Wenn das dauerhaft läuft, kennst du die Ursache, und wir sehen weiter.
Spaghetticoder
User
Beiträge: 13
Registriert: Dienstag 7. April 2020, 18:05

Hallo,

"irgendwie" verabschiedet sich der watchdog bei mir immer wieder.

Die main-Schleife läuft generell bis zum User-Abbruch: passt.
Aber der Event wird eben nicht mehr ausgelöst, wenn das Programm einige Zeit läuft. Für ca. 3 Minuten funktioniert das Programm tadellos.

Sind hier evtl. watchdog-Probleme bekannt? Ich konnte keine Ursache für eigen Programmfehler finden - was aber keinerlei Selbstüberschätzung darstellen soll :-)

Versucht habe ich u.a.:
1. ftp Routinen entfernt
2. time.sleep(...) auf 1 Sekunde, 10 Sekunden, 120 Sekunden gestellt
3. Logging - zeigt keine Auffälligkeiten

System:
Windows 10 Pro
Python 3.7

Leider alles ohne Erfolg. Bin ehrlich gesagt ratlos. Das "Problem" an sich ist ja eigentlich banal...

Ein weiterer Versuch wäre evtl. den observer zu stoppen (wie?) und danach alle paar Minuten wieder zu starten (in der main-Schleife).
Oder: darf man denn den observer mehrfach per "observer.start()" anschubsen um einen Abbruch der Abfragen zu verhindern? Das könnte man ja ebenfalls alle paar Minuten (nach dem time.sleep()) machen...

Kann mir jemand weiterhelfen?


Und: Frohe Ostern!
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn du dir sicher bist, dass du das richtig getestet hast, was ich vorschlug (also FTP weg), dann bliebe mE nur noch der Kontakt mit dem Autor. Unter Linux/mac haette ich noch ein paar Tricks auf der Tasche, aber wie man tracing mit Windows macht, ist mir nicht so aus der Lameng raus klar.
Spaghetticoder
User
Beiträge: 13
Registriert: Dienstag 7. April 2020, 18:05

Hallo nochmal hierzu,

könnte einmal gefallenshalber folgendes Programm über den Zeitraum von ca. 2 Stunden testen?

Bei mir (2 PCs, Win 10 Pro, Win 10 Server) steigt selbst dieses Miniprogramm nach einer längerenZeitspanne aus und reagiert dann nicht mehr auf modifizierte/neue/... Dateien im Suchverzeichnis... Wenn das bei anderen Systemem ebenfalls der Fall wäre, deutet das auf einen watchdog-Fehler hin, oder?

Schöne Grüße und ggf. Dank vorab.

----



import sys
import time
import logging
from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler

if __name__ == "__main__":
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S')
path = "C:/testverzeichnis"
event_handler = LoggingEventHandler()
observer = Observer()
observer.schedule(event_handler, path, recursive=True)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
Benutzeravatar
__blackjack__
User
Beiträge: 14019
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Spaghetticoder: Läuft bei mir (Linux) seit ca. 13 Uhr und reagiert immer noch auf Änderungen.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Spaghetticoder
User
Beiträge: 13
Registriert: Dienstag 7. April 2020, 18:05

Guten Morgen blackjack,

vielen Dank für's testen!

Dann liegt das an "irgendwas" bei uns im Haus. Hat jemand eine Idee (Virenscanner, Win10, ...)? Sind hier Probleme bekannt? Ich konnte auf der Website von watchdog nichts finden...


Ciao
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich hab’s mit Win 10 laufen gehabt. Für mehrere Stunden, und ohne Probleme.
Spaghetticoder
User
Beiträge: 13
Registriert: Dienstag 7. April 2020, 18:05

Hm - setzt ihr Win10 innerhalb einer Domäne ein?

Danke auf jeden Fall für die Feedbacks - ich bin zum einen erleichtert, dass mein Spaghetticode ok ist. Und ich kann nun eine virtuelle Maschine (Win7Pro oder Win10 ohne Domäne...) einsetzen, welche das Programm ausführt. Das Problem ist also über einen Umweg lösbar!

Herzliche Grüße und bleibt gesund!
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich habe das nur auf meinem privaten WIN10 home edition getestet.
Spaghetticoder
User
Beiträge: 13
Registriert: Dienstag 7. April 2020, 18:05

Hallo,

leider poppt mein Problem erneut auf. Es klemmt leider immer wieder...

Ich würde das Programm nun doch "irgendwie" laufen lassen wollen. Hier 2 Fragen:

Frage 1: kann/darf man innerhalb der main Schleife den Observer per time.sleep(10) und observer.stop() / observer.start() denn einfach regelmäßig neu starten? Oder gibt das evtl. Folgeprobleme?

Frage 2: ich will ausschließlich modifizierte und neu geschrieben PDFs auf den FTP Server hochladen: Schreiben denn die Adobe Programme (Acrobat Reader / Acrobat Pro) vor dem eigentlichen File evtl. eine temporäre Datei und benennen diese am Ende in den eigentlichen Dateinamen um? Ich erhalte regelmäßig "modified" Meldungen mit Dateinamen die gar nicht aus meinem Workflow stammen. Wäre dem so hätte ich ein neues Problem.


Schöne Grüße vom Spaghetticoder
Spaghetticoder
User
Beiträge: 13
Registriert: Dienstag 7. April 2020, 18:05

... noch eine Idee bzw. Frage Nr 3: wäre es möglich die "on_modified" Events um einige Sekunden verzögert auszulösen? Damit könnte man ggf. dem Problem der Frage 2 (Temp Dateien) entgegenwirken, oder?

Ciao,
Spaghetticoder
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

Natürlich kannst Du 2 Sekunden warten, aber was passiert, wenn es sich um eine sehr große PDF-Datei handelt, oder der Virenscanner gerade denkt, er müßte den Rechner lahmlegen, oder ...
Besser ist es, nur Dateien zu bearbeiten, die einem bestimmten Muster entsprechen, und so die Temporären Dateien ignorieren.
Antworten