Watchdog gibt WinError123 zurück

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.
Antworten
Phobit
User
Beiträge: 185
Registriert: Freitag 4. Mai 2018, 18:13

Hallo,
ich hab ein Skript im Netz gesehen, mit dem man automatisch Dateien aus einem überwachten Ordner in einen anderen schieben kann. Habs dann etwas modifiziert damits meinen Download Ordner für mich aufräumt/sortiert.

Skript:

Code: Alles auswählen

import os
import platform
import time

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


class MyHandler(FileSystemEventHandler):

    def on_modified(self, event):
        for filename in os.listdir(folder_to_track):
            src = folder_to_track + "/" + filename
            if platform.system() == "Windows":
                win_extension = os.path.splitext(filename)[1]
                if win_extension in music_list:
                    folder_destination = folder_destination + "\\Musik"
                elif win_extension in videos_list:
                    folder_destination = folder_destination + "\\Videos"
                elif win_extension == ".exe":
                    folder_destination = folder_destination + "\\exe-Dateien"
                elif win_extension == ".jar":
                    folder_destination = folder_destination + "\\jar-Dateien"
                elif win_extension == ".zip":
                    folder_destination = folder_destination + "\\zip-Archive"
                else:
                    folder_destination = folder_destination + "\\zzSonstiges"
                new_destination = folder_destination + "\\" + filename
            else:
                new_destination = folder_destination + "/" + filename
            os.rename(src, new_destination)


music_list = [".mp3", ".m4a", ".wav"]
videos_list = [".mp4", ".avi", ".mkv", ".mov", ".mpeg"]

if platform.system() == "Windows":
    with open("paths/windows_path.txt") as file:
        folder_to_track = file.readline()
        folder_destination = file.readline()

else:
    pass
event_handler = MyHandler()
observer = Observer()
observer.schedule(event_handler, folder_to_track, recursive=True)
observer.start()

try:
    while True:
        time.sleep(10)
except KeyboardInterrupt:
    observer.stop()
observer.join()

(Inhalt von windows_path.txt:
C:\\Path\\Zum\\Download
C:\\Path\\Zum\\SortiertenDownload

Das Skript hab ich in meiner Linux Maschine angefertigt bzw halt abgeändert. Habs dann mit nem Stick aufs Windows8.1 gezogen, mir da extra Python3.6.8 installiert, und dann versucht das Skript zu starten, hab dann noch mit pip3 watchdog nachinstalliert, aber jetzt gibt Windows mir den Error aus:

OSError: [WinError123] Die Sintax für den Dateinamen, Verzeichnisnamen oder die Datenträgerbezeichnung ist falsch.

(genauen Error kann ich leider nicht copien, da das Konsolen Fenster sich so schnell öffnet und wieder schließt dass ich nur einen Screenshot machen konnte... gibts sich auch ne andere Möglichkeit aber mir viel grad nix besseres ein)

Woher kommt der error? Und wie kann ich ihn beheben?
Mir egal, ob der Code schön ist oder nicht.
Hauptsache er funkt!
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Du mußt auch das Python-Skript in der Eingabeaufforderung (cmd.exe) ausführen, denn es handelt sich ja um ein Konsolenprogramm.

Soweit ich sehen kann, liest Du aus "paths/windows_path.txt" Pfade und hast noch das Newlinezeichen dran. Warum Linux das nicht stört, oder wie Du da `folder_to_track` ermittelst, zeigst Du ja nicht.

Sonstiges: Konstanten schreibt man komplett groß: MUSIC_EXTENSIONS. Das Ding List zu nennen, ist unnötig einschränkend und wenig aussagekräftig.
›folder_to_track‹ sollte keine globale Variable sein, sondern aus event genommen werden.
Du solltest das soweit abstrahieren, dass man nicht überall die Platform abfragen muß.
Pfade setzt man mit Hilfe von pathlib.Path zusammen, nicht +.

Die Haupfunktionalität des Programms sollte in eine Funktion namens ›main‹ wandern.

Das ganze könnte ungefähr so aussehen (ungetestet):

Code: Alles auswählen

import time
from pathlib import Path

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

FOLDERS_FOR_EXTENSIONS = [
    ([".mp3", ".m4a", ".wav"], "Musik"),
    ([".mp4", ".avi", ".mkv", ".mov", ".mpeg"], "Videos"),
    ([".exe"], "exe-Dateien"),
    ([".jar"], "jar-Dateien"),
    ([".zip"], "zip-Archive"),
]
DEFAULT_FOLDER = "zzSonstiges"

class MyHandler(FileSystemEventHandler):
    def __init__(self, folder_destination):
        super().__init__()
        self.folder_destination = folder_destination

    def on_modified(self, event):
        for filename in Path(event.src_path).iterdir():
            for extensions, destination in FOLDERS_FOR_EXTENSIONS:
                if filename.suffix in extensions:
                    break
            else:
                destination = DEFAULT_FOLDER
            filename.rename(self.folder_destination / destination)

def main():
    with open("paths/windows_path.txt") as paths:
        folder_to_track = paths.readline().strip()
        folder_destination = paths.readline().strip()
    event_handler = MyHandler(folder_destination)
    observer = Observer()
    observer.schedule(event_handler, folder_to_track, recursive=True)
    observer.start()

    try:
        while True:
            time.sleep(1000)
    except KeyboardInterrupt:
        pass
    observer.stop()
    observer.join()

if __name__ == '__main__':
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Phobit: Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert.

Ein ``else``-Zweig in dem nur ein ``pass`` steht ist unsinnig. Und da die beiden Variablen die im ``if`` definiert werden vom Code nach dem ``if``/``else`` verwendet werden, muss der Code der die verwendet auch in den ``if``-Zweig, sonst knallts ja vorhersehbar mit einem `NameError`. Letztlich kann man das ``if`` aber auch umdrehen und gleich nach dem Test abbrechen wenn es nicht Windows ist, denn das funktioniert so ja nur unter Windows. Wobei das eine komische, nicht wirklich nachvollziehbare Einschränkung ist.

Deine Pfadbehandlung ist Mist! Genau wegen Unterschieden bei Systemen klöppelt man sich das nicht selbst mit Zeichenkettenoperationen zusammen, sondern verwendet `pathlib`.

Für die Dateiendungen würden sich `set()`s statt Listen anbieten. Insgesamt sollte man sich da mal eine Datenstruktur überlegen die dann auch die ganzen ``if``/``elif``\s überflüssig macht.

`My` in Namen ist Zeichenverschwendung. Solange es nicht auch `Our`, `Their`, oder ähnliche Präfixe gibt, sagt My einfach nichts sinnvolles aus.

Funktionen und Methoden sollten alles was sie ausser Konstanten benötigen als Argument(e) übergeben bekommen. Die `on_modified()`-Methode kann nicht einfach so magisch auf die beiden Ordner die aus der Datei ausgelesen wurden zugreifen, die muss man dem Handler-Objekt beim erstellen also als Argumente übergeben und an das Objekt binden.

Also das ganze mal aufgeräumt und weitestgehend plattformunabhängig. Der Dateiname von wo die beiden Ordner bezogen werden wird über den Systemnamen bestimmt, aber ansonsten sollte der Code unabhängig davon sein. Und natürlich ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import os
from pathlib import Path
import platform
import time

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


def _map_extension_to_foldername(data):
    result = dict()
    for extensions, foldername in data:
        for extension in extensions:
            result[extension] = foldername
    return result


EXTENSION_TO_FOLDERNAME = _map_extension_to_foldername(
    [
        ([".mp3", ".m4a", ".wav"], "Musik"),
        ([".mp4", ".avi", ".mkv", ".mov", ".mpeg"], "Videos"),
        ([".exe"], "exe-Dateien"),
        ([".jar"], "jar-Dateien"),
        ([".zip"], "zip-Archive"),
    ]
)

OTHER_FILES_FOLDERNAME = "zzSonstiges"


class EventHandler(FileSystemEventHandler):
    def __init__(self, tracked_folder, destination_folder):
        FileSystemEventHandler.__init__(self)
        self.tracked_folder = tracked_folder
        self.destination_folder = destination_folder

    def on_modified(self, event):
        for path in self.tracked_folder.iterdir():
            extension = path.suffix.lower()
            foldername = EXTENSION_TO_FOLDERNAME.get(
                extension, OTHER_FILES_FOLDERNAME
            )
            (self.tracked_folder / path).rename(
                self.destination_folder / foldername / path.name
            )


def main():
    pathsfile_path = Path("paths") / f"{platform.system().lower()}_path.txt"
    with pathsfile_path.open() as lines:
        tracked_folder = Path(next(lines).strip())
        destination_folder = Path(next(lines).strip())

    event_handler = EventHandler(tracked_folder, destination_folder)
    observer = Observer()
    observer.schedule(event_handler, tracked_folder, recursive=True)
    observer.start()
    try:
        while True:
            time.sleep(10)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()


if __name__ == "__main__":
    main()
Ein bisschen problematisch könnte das `rename()` sein, denn das funktioniert sowohl unter Linux als auch unter Windows nur innerhalb des gleichen Dateisystems. Um auch Dateisystemgrenzen überwinden zu können braucht man Funktionen aus dem `shutil`-Modul die in dem Fall dann tatsächlich die Daten kopieren und die alte Datei dann löschen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Phobit
User
Beiträge: 185
Registriert: Freitag 4. Mai 2018, 18:13

So, konnte den Code jetzt endlich mal testen.
Unter Linux läuft auch der von Sirius einwandfrei, aber auf Windows erhalte ich jetzt folgenden Error:

Exception in thread Thread-1:
Traceback (most recent call last):
File "C:\Users\Phillip\AppData\Local\Programs\Python\Python36\lib\threading.py", line 916, in _bootstrap_inner
self.run()
File "C:\Users\Phillip\AppData\Local\Temp\AFT_Test_Forum.py\venv\lib\site-packages\watchdog\observers\api.py", line 199, in run
self.dispatch_events(self.event_queue, self.timeout)
File "C:\Users\Phillip\AppData\Local\Temp\AFT_Test_Forum.py\venv\lib\site-packages\watchdog\observers\api.py", line 368, in dispatch_events
handler.dispatch(event)
File "C:\Users\Phillip\AppData\Local\Temp\AFT_Test_Forum.py\venv\lib\site-packages\watchdog\events.py", line 330, in dispatch
_method_map[event_type](event)
File "I:/FileTransferProtocol/AFT_Test_Forum.py", line 24, in on_modified
for filename in Path(event.src_path).iterdir():
File "C:\Users\Phillip\AppData\Local\Programs\Python\Python36\lib\pathlib.py", line 1081, in iterdir
for name in self._accessor.listdir(self):
File "C:\Users\Phillip\AppData\Local\Programs\Python\Python36\lib\pathlib.py", line 387, in wrapped
return strfunc(str(pathobj), *args)
NotADirectoryError: [WinError 267] Der Verzeichnisname ist ungültig: 'C:\\Users\\Phillip\\Downloads\\TestDatei.txt'

Der Error scheint aus der Codezeile

Code: Alles auswählen

for filename in Path(event.src_path).iterdir():
zu kommen. Da ich mich mit pathlib jetzt nicht soo gut auskenne, habe ich keine Ahnung was da falsch läuft.


Standart Code ist der obige von Sirius.
Mir egal, ob der Code schön ist oder nicht.
Hauptsache er funkt!
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

`event.src_path` scheint unter Windows wohl der konkrete Dateiname zu sein, wie die Fehlermeldung ja deutlich sagt. Das solltest Du prüfen, oder gleich das gesamte Verzeichnis durchsuchen, wie in __blackjack__s Code.
Phobit
User
Beiträge: 185
Registriert: Freitag 4. Mai 2018, 18:13

So, ich habe jetzt rausgefunden (ok, das hätte sich auch aus dem error rauslesenlassen können) dass Watchdog bei event.src_path immer mit dem doppelten \\ arbeitet, was ja auch ok ist, aber windows dann natürlich den Pfad nimmer kennt. Habe also jetzt einfach mal

Code: Alles auswählen

source_path = event.src_path.replace("\\\\", "\\")
for filename in Path(source_path).iterdir():
gemacht, jetzt gibt es keinen Error mehr, aber dafür macht das Programm einfach nix mehr. Läuft unendlich ohne error oder ähnliches weiter, verschiebt aber auch keine Dateien... Woran könnte das liegen? Funktioniert das Watchdogs Modul einfach unter Windows ned richtig? Kann sowas sein?
Mir egal, ob der Code schön ist oder nicht.
Hauptsache er funkt!
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Phobit: Das ist Unsinn. Da ist nur jeweils *ein* \ der natürlich als "\\" in der `repr()`-Darstellung angezeigt wird. Die Fehlermeldung ist, wie Sirius3 ja bereits geschrieben hat, dass es sich nicht um ein Verzeichnis, sondern um eine *Datei* handelt. Und den Pfad zu einer Datei kann man sehr offensichtlich nicht dazu Verwenden um über den Verzeichnisinhalt zu iterieren.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Phobit
User
Beiträge: 185
Registriert: Freitag 4. Mai 2018, 18:13

Hab das Programm jetzt soweit geschrieben, dass es eigentlich funktioneiren würde. Blos jetzt tritt folgendes ein: Erst wenn ich eine Datei in den Überwachten Ordner schiebe, sie wieder rausscheieb, und dann nochmal reinschiebe wird sie automatisch einsortiert. Ich hab keine Ahnung woran das liegt, ich habe jetzt eingefügt, dass der Code selbst am Anfang wenn er gestartet wird eine Datei einmal reinschiebt, rausschiebt und wieder reinschiebt, aber das funktioniert auch nicht... Hier mal der vorerst "fertige" code:

Code: Alles auswählen

import time
from pathlib import Path

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

FOLDERS_FOR_EXTENSIONS = [
    ([".mp3", ".m4a", ".wav"], "Musik"),
    ([".mp4", ".avi", ".mkv", ".mov", ".mpeg", ".MOV"], "Videos"),
    ([".txt", ".doc", ".docx", ".pdf"], "Text-Dateien"),
    ([".exe"], "exe-Dateien"),
    ([".jar"], "jar-Dateien"),
    ([".zip", ".7z", ".tar", ".gz", ".rar"], "Archive"),
    ([".msi"], "Installer-Dateien"),
    ([".png", ".jpg", ".jpeg"], "Bild-Dateien"),
    ([".iso", ".img"], "Images")
]
DEFAULT_FOLDER = "zzSonstiges"


class MyHandler(FileSystemEventHandler):
    def __init__(self, folder_destination):
        super().__init__()
        self.folder_destination = folder_destination

    def on_modified(self, event):
        path = str(Path(event.src_path)).rpartition("\\")
        path = path[0]
        for filename in Path(path).iterdir():
            for extensions, destination in FOLDERS_FOR_EXTENSIONS:
                if filename.suffix in extensions:
                    break
            else:
                destination = DEFAULT_FOLDER
            file_path = Path(str(list(str(filename).rpartition("/"))[-1]))
            file_path = str(file_path).rpartition("\\")
            file_path = Path(file_path[-1])
            filename.rename(Path(self.folder_destination) / Path(destination) / file_path)


def main():
    with open("I:\\FileTransferProtocol\\neu\\paths\\windows_path.txt") as paths:
        folder_to_track = paths.readline().strip()
        folder_destination = paths.readline().strip()
    event_handler = MyHandler(folder_destination)
    observer = Observer()
    observer.schedule(event_handler, folder_to_track, recursive=True)
    observer.start()

    try:
        while True:
            time.sleep(10)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()


if __name__ == '__main__':
    main()
Mir egal, ob der Code schön ist oder nicht.
Hauptsache er funkt!
Antworten