Seite 1 von 2

Exceptions abfangen und einbauen

Verfasst: Mittwoch 9. März 2011, 21:51
von feldmaus
Hi Alle,

schöner Titel :-) ich baue gerade an meinem Backup Skript die 1000te mit python dabei wollte ich ein paar exceptions einbauen, weiß aber nicht wie diese heißen. Das Skript nutzt rsync um Quelle und Ziel abzugleichen. Es werden mehrere Benutzer(Home Verzeichnisse) und Computer abgeglichen, einige sind nicht vorhanden oder nicht online. Daraus resultieren dann auch einige Fehlermeldungen.

Als Beispiel bekomme ich folgende Meldung:

Code: Alles auswählen

ssh: connect to host feld-bert port 22: No route to host
rsync: connection unexpectedly closed (0 bytes received so far) [Receiver]
rsync error: error in rsync protocol data stream (code 12) at io.c(601) [Receiver=3.0.7]
Der Rechner feld-bert ist zu diesem Zeitpunkt nicht online gewesen.

Muss ich dann except rsync error: schreiben in meinem code?

Grüße Markus

Re: Exceptions abfangen und einbauen

Verfasst: Donnerstag 10. März 2011, 00:08
von deets
Ohne Code ist sowas immer schwer zu beantworten.

Und rsync ist ja ein Kommandozeilen-Tool. Das schmeisst keine Python-Exceptions, die du fangen koenntest. Wie rufst du es denn auf? Ueber subprocess? Dann muesstest du auf den Rueckgabewert pruefen, und eventuell stderr einfangen (wobei das alleine nicht reicht, es ist ja legitim, auf stderr zb logging infos auszugeben)

Re: Exceptions abfangen und einbauen

Verfasst: Donnerstag 10. März 2011, 06:02
von feldmaus
Meine wichtige Zeile sieht so aus:

Code: Alles auswählen

subprocess.Popen(shlex.split('rsync --rsh="ssh -l '+BENUTZER+'" -a --delete-after  --stats '+katalog[eintrag]['Exclude']+' '+katalog[eintrag]['Server'][0]+BENUTZER+katalog[eintrag]['Server'][1]+' '+BENUTZER+katalog[eintrag]['Client'][0]+BENUTZER+katalog[eintrag]['Client'][1])).wait()
Was nach der Substitution der Variablen so aussehen könnte:

Code: Alles auswählen

subprocess.Popen(shlex.split('rsync --rsh="ssh -l '+markus+'" -a --delete-after  --stats '+'Quelle'+'Ziel')).wait()
Es geht ja nur ums Prinzip.

Re: Exceptions abfangen und einbauen

Verfasst: Donnerstag 10. März 2011, 08:28
von sparrow

Code: Alles auswählen

>>> a = subprocess.Popen("false").wait()
>>> a
1
>>> a = subprocess.Popen("true").wait()
>>> a
0
Edit:
Außerdem bin ich mir sicher, dass du die Zeilen Code in 2 Wochen nicht mehr verstehst.
Du solltest, schon alleine um die Übersichtlichkeit zu erhalten, den String mittels der Python-eigenen Formatierungsmöglichkeiten zusammen bauen.

Code: Alles auswählen

a = benutzer + ":" + server # nicht so schön
b = "%s:%s" % (benutzer, server) # besser
c = "%(user)s:%(server)s" % {'user' : benutzer, 'server' : server} # mit benannten Platzhaltern
Und wirklich inutuitiv ist die Vergabe deiner Variablennamen bzw. der Zugriff darauf eher nicht.
Zumindest erschließt sich mir nicht wirklcih was sich hier hinter verbergen könnte:

Code: Alles auswählen

katalog[eintrag]['Server'][0]+BENUTZER+katalog[eintrag]['Server'][1]

Re: Exceptions abfangen und einbauen

Verfasst: Donnerstag 10. März 2011, 09:20
von lunar
@feldmaus: Lies doch bitte die Dokumentation des "subprocess"-Moduls. "subprocess.check_call" ist genau die Funktion, die Du suchst.

Im Übrigen erschließt sich mir nicht, wieso Du eine Zeichenkette mit dem Befehl zusammenbastelst, nur um diese dann wieder in eine Liste zu trennen. Wieso nicht einfach gleich den Befehl als Liste zusammenbauen?

Re: Exceptions abfangen und einbauen

Verfasst: Donnerstag 10. März 2011, 11:32
von feldmaus
lunar hat geschrieben:Im Übrigen erschließt sich mir nicht, wieso Du eine Zeichenkette mit dem Befehl zusammenbastelst, nur um diese dann wieder in eine Liste zu trennen. Wieso nicht einfach gleich den Befehl als Liste zusammenbauen?
Das mache ich weil ich es nicht hin gekriegt habe einen funktionstüchtige Liste zu erstellen die eine so merkwürdige Kombination von Hochkommas, Gänsefüßchen unterbringt. :-) und das in eine Liste unterzubringen das funktioniert eventuell gar nicht zumindest hatte ich es nicht geschafft.

Ich habe im weiteren eine Frage zu subprocess.check_call(). Ich verstehe die Dokumentation nicht ganz. Was meinen die mit:
If the exit code was zero then return, otherwise raise CalledProcessError
Wenn ein Fehler entsteht wird das Objekt um 1 erhöht und es gibt aber kein return?

Ich habe check_call mal getestet:

Code: Alles auswählen

>>> b=subprocess.check_call(["ls", "-l", "/tmesl/"])
>>> StdErr: ls: Zugriff auf /tmesl/ nicht möglich: Datei oder Verzeichnis nicht gefunden
Traceback (innermost last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.6/subprocess.py", line 488, in check_call
    raise CalledProcessError(retcode, cmd)
CalledProcessError: Command '['ls', '-l', '/tmesl/']' returned non-zero exit status 2


>>> b
Traceback (innermost last):
  File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined
b enthält im Fehlerfall nicht den returncode, oder sehe ich da was falsch.

Grüße Markus

Re: Exceptions abfangen und einbauen

Verfasst: Donnerstag 10. März 2011, 11:45
von lunar
@feldmaus: Es muss funktionieren. Was glaubst Du denn, was "shlex.split()" zurückgibt!? Richtig, eine Liste, in der die Hochkommata und Gänsefüßchen im Übrigen gar nicht mehr enthalten sind, weil sie nur für Shell, aber nicht für das Betriebssystem Bedeutung haben.

Re: Exceptions abfangen und einbauen

Verfasst: Donnerstag 10. März 2011, 11:51
von feldmaus
lunar hat geschrieben:@feldmaus: Es muss funktionieren. Was glaubst Du denn, was "shlex.split()" zurückgibt!? Richtig, eine Liste, in der die Hochkommata und Gänsefüßchen im Übrigen gar nicht mehr enthalten sind, weil sie nur für Shell, aber nicht für das Betriebssystem Bedeutung haben.
Es mag sein das ich das hinbekomme nach laaaaaaaaangem Knobeln, aber es würde nicht viel übersichtlicher aussehen, glaube es mir ich habe es schon gesehen. Ich fand es sehr viel unübersichtlicher.

Grüße Markus

Re: Exceptions abfangen und einbauen

Verfasst: Donnerstag 10. März 2011, 12:03
von feldmaus
Sinnvoll für mich wäre zu wissen ob es den Benutzer nicht gab oder das Quell- oder Ziel-Verzeichnis nicht stimmte.

@lunar
Kannst Du mir bitte ein minimal beispiel geben wie ich an die Fehlercodes von rsync komme mit subprocess.check_call(). In meinem obigen Beispiel bekomme ich im Fehlerfall nix.

Grüße Markus

Re: Exceptions abfangen und einbauen

Verfasst: Donnerstag 10. März 2011, 12:38
von sparrow
feldmaus hat geschrieben:Ich habe im weiteren eine Frage zu subprocess.check_call(). Ich verstehe die Dokumentation nicht ganz. Was meinen die mit:
If the exit code was zero then return, otherwise raise CalledProcessError
Wenn ein Fehler entsteht wird das Objekt um 1 erhöht und es gibt aber kein return?

Ich habe check_call mal getestet:

Code: Alles auswählen

>>> b=subprocess.check_call(["ls", "-l", "/tmesl/"])
>>> StdErr: ls: Zugriff auf /tmesl/ nicht möglich: Datei oder Verzeichnis nicht gefunden
Traceback (innermost last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.6/subprocess.py", line 488, in check_call
    raise CalledProcessError(retcode, cmd)
CalledProcessError: Command '['ls', '-l', '/tmesl/']' returned non-zero exit status 2


>>> b
Traceback (innermost last):
  File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined
Nein, da steht "Wenn der Return-Code 0 ist (also kein Fehler) kehrt die Funktion zurück. Anderenfalls wird eine CalledProcessError-Exception ausgelöst. Die wiederrum kannst du mit except fangen."
Funktioniert doch gut in deinem Beispiel? Es kommt zu einem Fehler, Return-Code ist 2 statt 0, also wird eine Exception geworfen.

Wenn du nur den Return-Code auswerten willst aber keine Exception (du hattest danach ja gefragt), dann schau mal in meinem letzten Post.

Gruß
Sparrow

Re: Exceptions abfangen und einbauen

Verfasst: Donnerstag 10. März 2011, 13:33
von BlackJack
@feldmaus: Das gleich als Liste anzugeben ist übersichtlicher, oder zumindest auf keinen Fall unübersichtlicher. Es fällt ein Funktionsaufruf weg und der Ausdruck an sich wird doch viel einfacher weil eine Menge ``+`` und Anführungszeichen wegfallen. Wenn Du das nicht findest, dann hast Du etwas falsch gemacht.

Was wirklich unübersichtlich ist, sind diese verschachtelten Index/Schlüsselzugriffe. Und -- speziell ohne Syntaxhervorhebung -- den Überblick zu behalten was in einer Zeichenkette ist und was ausserhalb, eben weil Du da Anführungszeichen mit in Zeichenketten hast und alles so ohne Leerzeichen aneinander gequetscht ist.

Re: Exceptions abfangen und einbauen

Verfasst: Donnerstag 10. März 2011, 13:35
von lunar
@feldmaus: Du hast doch bereits festgestellt, dass "check_call()" im Fehlerfall eine Ausnahme des Typs "CalledProcessError" auslöst. In der Dokumentation zu "check_call()" sind die Attribute dieser Ausnahme ausgeführt, unter anderem auch dasjenige, welches den Rückgabewert des Prozesses enthält. Wie Du eine solche Ausnahme abfängst, solltest Du eigentlich wissen. Falls nicht, dann lies das Tutorial.

Ich glaube Dir im Übrigen auch nicht, dass der Befehl als Liste unübersichtlicher wäre. Unübersichtlich ist das, was Du jetzt machst: Den Befehl mit Zeichenkettenoperationen zusammenzubasteln, und Gänsefüsschen zur korrekten Maskierung der Werte für "shlex.split()" einzustreuen. Den Befehl als Liste zusammenzubauen, machst sowohl das "+" als auch die Gänsefüsschen vollkommen überflüssig. Das darfst Du mir ruhig glauben, denn ich habe das auch schon gesehen, und nicht nur einmal…

Re: Exceptions abfangen und einbauen

Verfasst: Mittwoch 16. März 2011, 02:43
von feldmaus
sparrow hat geschrieben:Wenn du nur den Return-Code auswerten willst aber keine Exception (du hattest danach ja gefragt), dann schau mal in meinem letzten Post.
Meinst Du etwa:

Code: Alles auswählen

`except Exception, e:` An die Fehlernachricht kommst du dann per `str(e)`
Muss ich einfach nur Exception schreiben oder meinst Du eine spezielle Ausnahme?
Ich bekomme jetzt allerdings die Meldung:
Fehler: [Errno 2] No such file or directory

Und weiß nicht ob er meinen Benutzer nicht finden kann, oder ein anderer Fehler aufgetreten ist.

Und ich versuche gerade das ganze in eine Liste umzuwandeln:

Code: Alles auswählen

subprocess.Popen(['rsync --rsh="ssh -l ', BENUTZER, '" -a --delete-after  --stats ', katalog[eintrag]['Exclude'], ' ', BENUTZER, katalog[eintrag]['Client'][0], BENUTZER, katalog[eintrag]['Client'][1], ' ', katalog[eintrag]['Server'][0], BENUTZER, katalog[eintrag]['Server'][1]]).call().wait()
Grüße Markus

Re: Exceptions abfangen und einbauen

Verfasst: Mittwoch 16. März 2011, 04:07
von BlackJack
@feldmaus: Bei einer Liste musst Du das Programm und die einzelnen Argumente als einzelne Elemente der Liste übergeben. Das Programm heisst sicher nicht 'rsync --rsh="ssh -l ' sondern nur 'rsync'. Und ein ' ' in der Liste würde bedeuten Du übergibst da ein einzelnes Leerzeichen als Argument. So etwas kommt doch in dem Aufruf gar nicht vor -- das ist doch nur das Begrenzungszeichen für die Shell, damit die weiss wo sie die Argumente aufteilen muss, um sie dem Programm zu übergeben.

Etwas lesbarer aber ungetestet und mit vielleicht unpassenden Namen für die Einträge aus den verschachtelten Datenstrukturen:

Code: Alles auswählen

entry = katalog[eintrag]
server_prefix, server_postfix = entry['Server']
client_prefix, client_postfix = entry['Client']
return_code = subprocess.Popen(
    [
        'rsync',
        '--rsh=ssh -l ' + BENUTZER, # Assumes login names contain no whitespace.
        '-a',
        '--delete-after',
        '--stats',
        entry['Exclude'],
        server_prefix + BENUTZER + server_postfix,
        BENUTZER + client_prefix + BENUTZER + client_postfix,
     ]
).wait()

Re: Exceptions abfangen und einbauen

Verfasst: Mittwoch 16. März 2011, 07:08
von snafu
@BlackJack:

Den Kommentar in deinem Code verstehe ich nicht. Der Benutzername würde doch auch mit Whitespace als ein Argument gesehen werden, oder nicht? Gerade deshalb nimmt man doch eine Liste und spart es sich, ständig zur Sicherheit ein `repr()` um benutzerdefinierte Argumente zu setzen.

Re: Exceptions abfangen und einbauen

Verfasst: Mittwoch 16. März 2011, 08:40
von BlackJack
@snafu: Ich ging jetzt von Unix aus, da können Login-Namen keine Leerzeichen enthalten.

Es geht dort bei dem Benutzernamen nicht um ein Argument für das Programm, sondern dem Programm wird an der Stelle eine Zeichenkette mit einem Programmaufruf für ein anderes Programm als *ein* Argument übergeben!

Re: Exceptions abfangen und einbauen

Verfasst: Mittwoch 16. März 2011, 11:18
von feldmaus
BlackJack hat geschrieben:

Code: Alles auswählen

...
return_code = subprocess.Popen(
    [
        'rsync',
        '--rsh=ssh -l ' + BENUTZER, # Assumes login names contain no whitespace.
        ...]
).wait()
Das könnte man dann doch auch in zwei Zeilen Schreiben:

Code: Alles auswählen

...
return_code = subprocess.Popen(
    [
        'rsync',
        '--rsh="ssh',
        '-l',
        BENUTZER,
        ...]
).wait()
Außerdem muss da doch noch ein Anführungszeichen zwischen? Muss das -l nicht auch in eine eigene Zeile?

Ich glaube subprocess.Popen funktioniert anders als ich mir das gerade vorstelle?

Re: Exceptions abfangen und einbauen

Verfasst: Mittwoch 16. März 2011, 11:21
von lunar
@feldmaus: Nein, so könnte man das nicht schreiben. "--rsh='ssh -l benutzer'" ist ein Argument und muss folglich auch zusammen in der Liste stehen.

Re: Exceptions abfangen und einbauen

Verfasst: Mittwoch 16. März 2011, 11:36
von feldmaus
lunar hat geschrieben:@feldmaus: Nein, so könnte man das nicht schreiben. "--rsh='ssh -l benutzer'" ist ein Argument und muss folglich auch zusammen in der Liste stehen.
Also wird Argument für Argument auf Korrektheit überprüft?

Re: Exceptions abfangen und einbauen

Verfasst: Mittwoch 16. März 2011, 12:34
von cofi
Es wird gar nichts ueberprueft. Ein Listenelement enthaelt ein Argument fuer den Aufruf und das `--rsh` Argument ist nunmal ein kompletter Programmaufruf bis hin zum Benutzer.