Python - Code

Probleme bei der Installation?
Strunz_1975
User
Beiträge: 36
Registriert: Freitag 13. Dezember 2019, 17:14

Hallo,

ich habe folgendes "Python-Script":

Code: Alles auswählen

#!/usr/bin/env python3
import argparse
#from pathlib import Path
import os.path
import shutil
import sys


class FileReconstructor():

    def __init__(self, unpacked_dir, restore_dir):
        self.unpacked_path = Path(unpacked_dir).resolve()
        self.restore_path = Path(restore_dir).resolve()

    def reconstruct_files(self):
        for leaf_dir in self.walk_unpacked_leaf_dirs():
            target_path = self.target_path(leaf_dir)
            target_path.parent.mkdir(parents=True, exist_ok=True)
            with target_path.open('wb') as target_file:
                self.copy_file_parts_to(target_file, leaf_dir)

    def copy_file_parts_to(self, target_file, leaf_dir):
        file_parts = sorted(leaf_dir.iterdir(), key=lambda x: int(x.name))
        for file_part in file_parts:
            with file_part.open('rb') as source_file:
                shutil.copyfileobj(source_file, target_file)

    def walk_unpacked_leaf_dirs(self):
        """
        based on the assumption that all leaf files are named as numbers
        """
        seen_dirs = set()
        for path in self.unpacked_path.rglob('*'):
            if path.is_file():
                if path.parent not in seen_dirs:
                    seen_dirs.add(path.parent)
                    yield path.parent

    def target_path(self, leaf_dir_path):
        return self.restore_path / leaf_dir_path.relative_to(self.unpacked_path)


def parse_args(argv):
    parser = argparse.ArgumentParser()
    parser.add_argument(
        'unpacked_dir',
        help='The directory with the unpacked tar files',
    )
    parser.add_argument(
        'restore_dir',
        help='The directory to restore files into',
    )
    return parser.parse_args(argv)


def main(argv):
    args = parse_args(argv)
    reconstuctor = FileReconstructor(args.unpacked_dir, args.restore_dir)
    return reconstuctor.reconstruct_files()


if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))
    
 
Jetzt bringt er mir folgende Aussage:

unpacked_dir restore_dir
db.py: error: too few arguments

Was muss ich tun?

Mfg
Srunz_1975
Benutzeravatar
sparrow
User
Beiträge: 4183
Registriert: Freitag 17. April 2009, 10:28

Bitte zeig doch den gesamten Traceback und die komplette Fehlermeldung, damit man weiß, wo man im Code suchen soll.
Strunz_1975
User
Beiträge: 36
Registriert: Freitag 13. Dezember 2019, 17:14

def __init__(self, unpacked_dir, restore_dir):

python db.py
usage: db.py [-h] unpacked_dir restore_dir
db.py: error: too few arguments
Benutzeravatar
sparrow
User
Beiträge: 4183
Registriert: Freitag 17. April 2009, 10:28

Das Programm erwartet 2 Argumente beim Aufruf, wie ein Aufruf mit dem Argument -h zeigt.
In deinem Fall also

Code: Alles auswählen

python3 db.py Pfad\zum\unpacked\directory Pfad\zum\Herstellverzeichnis
Funktioniert aber auch dann nicht. Bricht dann mit einem NameError ab, weil Path in Zeile 12 nicht definiert ist. Die import-Zeile ist in deinem Code auskommentiert.

Edit: Nach __blackjack__s Post den Aufruf von python auf python3 geändert.
Zuletzt geändert von sparrow am Freitag 13. Dezember 2019, 17:51, insgesamt 3-mal geändert.
Benutzeravatar
__blackjack__
User
Beiträge: 13063
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Srunz_1975: Da fehlen halt die beiden Argumente beim Aufruf des Programms die das zum arbeiten braucht. Genau das sagt es ja. Du kannst es auch mal mit der Option -h aufrufen, was es ja auch sagt um die Hilfe auszugeben. Auch wenn man die natürlich am im Quelltext sehen/lesen kann.

Und Du solltest das nicht mit ``python`` aufrufen sondern mit ``python3`` wie die erste Zeile ja nahelegt. Am besten machst Du es ausführbar und führst es dann direkt aus, ohne selbst da noch ``python`` vor schreiben zu müssen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Strunz_1975
User
Beiträge: 36
Registriert: Freitag 13. Dezember 2019, 17:14

Wenn ich folgendes eingebe;

python3 db.py \home\werner\Downloads\ \home\werner\
Kommt folgendes Zeichen:
>
Benutzeravatar
sparrow
User
Beiträge: 4183
Registriert: Freitag 17. April 2009, 10:28

ist das ein unixoides System? Linux, Unix, MacOS? Wenn ja ist der Pfadtrenner ein / und kein \.
Strunz_1975
User
Beiträge: 36
Registriert: Freitag 13. Dezember 2019, 17:14

Ja, es ist Linux
Strunz_1975
User
Beiträge: 36
Registriert: Freitag 13. Dezember 2019, 17:14

Wenn ich jetzt folgendes eingebe:

python3 db.py /home/werner/Downloads/ /home/werner/

Kommt folgende Fehlermeldung:

Traceback (most recent call last):
File "db.py", line 63, in <module>
sys.exit(main(sys.argv[1:]))
File "db.py", line 58, in main
reconstuctor = FileReconstructor(args.unpacked_dir, args.restore_dir)
File "db.py", line 12, in __init__
self.unpacked_path = Path(unpacked_dir).resolve()
NameError: name 'Path' is not defined
Benutzeravatar
__blackjack__
User
Beiträge: 13063
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Srunz_1975: Ergänzend zu sparrow's Anmerkung zu `Path`: Warum ist das auskommentiert und warum wird ``os.path`` importiert aber nirgends im Programm verwendet‽

Die übergabe von `sys.argv` minus dem ersten Element kann man sich sparen weil man der `ArgumentParser.parse()` auch einfach gar nichts übergeben kann.

`sys.exit()` ist sinnfrei weil `FileReconstructor.reconstruct_files()` überhaupt keinen Rückgabewert hat.

Mindestens `copy_file_parts_to()` ist keine Methode sondern einfach nur eine Funktion die in die Klasse gesteckt wurde. Ich würde aber die ganze Klasse in Frage stellen.

Mal ohne die unnütze Klasse (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import argparse
import shutil
from pathlib import Path


def walk_unpacked_leaf_dirs(unpacked_path):
    """
    Based on the assumption that all leaf files are named as numbers.
    """
    seen_dirs = set()
    for path in unpacked_path.rglob("*"):
        if path.is_file():
            if path.parent not in seen_dirs:
                seen_dirs.add(path.parent)
                yield path.parent


def copy_file_parts_to(target_file, leaf_dir_path):
    file_parts = sorted(leaf_dir_path.iterdir(), key=lambda x: int(x.name))
    for file_part in file_parts:
        with file_part.open("rb") as source_file:
            shutil.copyfileobj(source_file, target_file)


def reconstruct_files(unpacked_path, restore_path):
    for leaf_dir_path in walk_unpacked_leaf_dirs(unpacked_path):
        target_path = restore_path / leaf_dir_path.relative_to(unpacked_path)
        target_path.parent.mkdir(parents=True, exist_ok=True)
        with target_path.open("wb") as target_file:
            copy_file_parts_to(target_file, leaf_dir_path)


def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "unpacked_dir", help="The directory with the unpacked tar files"
    )
    parser.add_argument(
        "restore_dir", help="The directory to restore files into"
    )
    return parser.parse_args()


def main():
    args = parse_args()
    reconstruct_files(
        Path(args.unpacked_dir).resolve(), Path(args.restore_dir).resolve()
    )


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Strunz_1975
User
Beiträge: 36
Registriert: Freitag 13. Dezember 2019, 17:14

Danke für eure schnelle HIlfe:-)
Strunz_1975
User
Beiträge: 36
Registriert: Freitag 13. Dezember 2019, 17:14

Wenn ich jetzt das eingebe:
python3 db.py /home/werner/Downloads /home/werner

kommt:

Code: Alles auswählen

Traceback (most recent call last):
  File "db.py", line 53, in <module>
    main()
  File "db.py", line 48, in main
    Path(args.unpacked_dir).resolve(), Path(args.restore_dir).resolve()
  File "db.py", line 30, in reconstruct_files
    with target_path.open("wb") as target_file:
  File "/usr/lib/python3.7/pathlib.py", line 1186, in open
    opener=self._opener)
  File "/usr/lib/python3.7/pathlib.py", line 1039, in _opener
    return self._accessor.open(self, flags, mode)
IsADirectoryError: [Errno 21] Is a directory: '/home/werner'

Benutzeravatar
__blackjack__
User
Beiträge: 13063
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Srunz_1975: So wie ich das sehe dürfen nur „leaf directories“ von dem ersten Verzeichnis Dateien enthalten und die Dateien müssen auch alle Namen haben die nur aus einer Ziffernfolge bestehen (theoretisch dürfen sie noch mit einem "-"-Zeichen anfangen). Das trifft ziemlich sicher nicht auf das standardmässige "Download"-Verzeichnis in deinem Heimatverzeichnis zu. Darum auch die Fehlermeldung.

Das Programm könnte ein paar Prüfungen zu den ganzen Annahmen die es trifft vertragen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Strunz_1975
User
Beiträge: 36
Registriert: Freitag 13. Dezember 2019, 17:14

Ich versteh nur Bahnhof
Benutzeravatar
__blackjack__
User
Beiträge: 13063
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Srunz_1975: Das erste Argument — "/home/werner/Downloads" — ist falsch weil es nicht den Inhalt enthält den das Programm erwartet.

Das Programm erwartet einen Pfad zu einem Verzeichnis das einen Verzeichnisbaum enthält in dem jedes Verzeichnis (rekursiv) entweder ausschliesslich Verzeichnisse enthält oder ausschliesslich Dateien deren Namen nur aus Ziffern bestehen. Wenn diese Annahmen nicht zutreffen gibt's Probleme in Form von Ausnahmen. Entweder weil versucht wird Verzeichnisse wie Dateien zum schreiben zu öffnen — was nicht geht. Oder beim sortieren der Dateinamen wenn nicht alle Dateinamen zum sortieren in eine ganze Zahl umgewandelt werden können.

Beschwer Dich beim Autor des Programms das es keine bessere Hilfe gibt und eventuell auch das es robuster sein könnte. Das könnte nämlich zum einen die Annahmen im Code prüfen und sinnvoller darauf reagieren wenn sie nicht erfüllt sind, und bei so etwas ist es auch immer nett wenn man zumindest die *Option* hat dass nicht einfach bestehende Dateien überschrieben werden, oder zumindest eine ``--dry-run``-Option mit der so ein Programm nicht wirklich etwas macht, sondern einfach nur sagt was es machen würde, damit man das vorher mal sehen und prüfen kann.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
sparrow
User
Beiträge: 4183
Registriert: Freitag 17. April 2009, 10:28

@Srunz_1975: Woher kommt das Script denn und was glaubst du, was es tut?
Strunz_1975
User
Beiträge: 36
Registriert: Freitag 13. Dezember 2019, 17:14

Benutzeravatar
__blackjack__
User
Beiträge: 13063
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Srunz_1975: Und da dann diese Antwort: https://askubuntu.com/questions/1123058 ... re#1124608

Wie kommst Du da dann darauf das Dein "Download"-Verzeichnis das erste Argument sein sollte‽ Da steht doch deutlich das man nach entpacken der `difftar.gz`-Dateien zwei Verzeichnisse hat ("snapshot/" und "multivol_snapshot/") und das Python-Program ein Verzeichnis im "multivol_snapshot/"-Verzeichnis als erstes Argument erwartet.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Strunz_1975
User
Beiträge: 36
Registriert: Freitag 13. Dezember 2019, 17:14

Und dann sollte als zweites Argument, der Speicherort angegeben werden, richtig?
Strunz_1975
User
Beiträge: 36
Registriert: Freitag 13. Dezember 2019, 17:14

Dann so:

python3 db.py multivol_snapshot/ /home/werner/
Antworten