Korrekter Weg um stdout stummzustellen

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.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich lade etwas mit wget herunter, weil ich kein urlopen verwenden möchte, da der kein Resuming von abgebrochenen Downloads unterstützt. Ich möchte jetzt nur Meldungen von stderr angezeigt bekommen. Bisher mache ich das so:

Code: Alles auswählen

from subprocess import call, PIPE

for nr in range(1, 100):
    url = 'http://chaosradio.ccc.de/archive/chaosradio_express_%03d.mp3' % nr
    print 'Lade %s ...' % url
    call(['wget', '-c', url], stdout=PIPE)
Ist das der richtige Weg, wenn ich es plattformübergreifend machen möchte?

Sebastian

EDIT: Ich weiß jetzt gar nicht ob es hundert Sendungen gibt. Ich hatte das nur vor kurzem für einen Python-Anfänger umgeschrieben, da dessen Code ursprünglich so aussah:

Code: Alles auswählen

from os import system

for sendung in range (1,100,1):
    index="00"+str(sendung)
    index=index[-3:]
    link="http://chaosradio.ccc.de/archive/chaosradio_express_"+index+".mp3"
    system("wget -c "+link)
    system("clear")
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Seit wann ist ``wget`` platformübergreifend verfügbar? Wenn es dir nur um Resume geht, dass geht AFAIR mit Bordmitteln auch.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Leonidas hat geschrieben:Seit wann ist ``wget`` platformübergreifend verfügbar?
Ich bezog mich mit "plattformübergreifend" eigentlich nur auf das Stummstellen von stdout.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Was die Bordmittel angeht: Ich hatte nur Sachen im Internet gefunden, wo doch einige Zeilen Code nötig waren, um das Ganze zu implementieren. Und es war mir dann etwas zu viel Aufwand, mich da hinein zu lesen.
Benutzeravatar
veers
User
Beiträge: 1219
Registriert: Mittwoch 28. Februar 2007, 20:01
Wohnort: Zürich (CH)
Kontaktdaten:

http://29a.ch/git/gitweb.cgi?p=lanshark ... =HEAD#l277

Diese Funtktion downloaded eine Datei mit Resume support. :wink:
[url=http://29a.ch/]My Website - 29a.ch[/url]
"If privacy is outlawed, only outlaws will have privacy." - Phil Zimmermann
abgdf

Seit wann ist ``wget`` platformübergreifend verfügbar?
Schon länger:

http://gnuwin32.sourceforge.net/packages/wget.htm

Gruß
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

abgdf hat geschrieben:
Seit wann ist ``wget`` platformübergreifend verfügbar?
Schon länger:

http://gnuwin32.sourceforge.net/packages/wget.htm
Das ist mir bekannt, schließlich habe ich mir mein Debian 3.1 mit Jidgo-lite auf Windows zusammengebaut. Mir ging es darum, wie wahrscheinlich es ist, so etwas auf dem System vorzufinden. Dürfte wohl so um die 0,1% der Nutzer installiert haben.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
lunar

Naja, das Ausliefern von wget mit der eigenen Anwendung ist ja nicht verboten, man kann es ja als "Package Data" installieren lassen ;) Ob das angesichts der Möglichkeit, Bordmittel zu nutzen, sinnvoll ist, sei mal dahingestellt ;)
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ja, aber um mal zur Ausgangsfrage zurückzukommen: In die Pipe leiten und nichts weiter damit machen ist also der übliche Weg?
BlackJack

Nein, Du musst die Ausgabe(n) in eine "Datei" umleiten, die das auch alles "schluckt". Wenn Du `subprocess.PIPE` verwendest, musst Du die Ausgaben auch auslesen, sonst läuft irgend wann der Puffer für die Pipe voll und das Programm blockiert. So ein "schwarzes Loch" bekommt man, wenn man den Dateinamen von `os.devnull` nimmt und damit eine Datei zum schreiben öffnet.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Danke für den Hinweis mit devnull.

Ich hab jetzt einen simplen Downloader/Resumer mit Python-Bordmitteln gemacht:

http://paste.pocoo.org/show/90095/
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Schreit ja förmlich nach `with`! :D

Zudem würde ich den Datei-Teil in eine den Rest aufrufende Funktion auslagern und das, was momentan an `f.write()` geht, `yield`en.

Naja, dann kann ich's auch gleich umsetzen:

Code: Alles auswählen

from __future__ import with_statement
from os.path import getsize
from urllib2 import Request, urlopen


def download(url, bytes_loaded=0, buffer_size=1048576):  # 1 mb
    request = Request(url)
    request.add_header('Range', 'bytes=%d-' % bytes_loaded)
    data = urlopen(request)
    while True:
        buffer = data.read(buffer_size)
        if buffer == '':
            break
        yield buffer

def resume(url, filename):
    incoming_data = download(url, getsize(filename))
    with open(filename, 'a') as f:
        map(f.write, incoming_data)
Zuletzt geändert von Y0Gi am Dienstag 4. November 2008, 16:42, insgesamt 2-mal geändert.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Das With-Statement hatte ich diesmal einfach vergessen. Map und Yield sind mir noch nicht so vertraut, aber ich denke daß ich den Sinn von den beiden jetzt einigermaßen verstehe. Mit dem direkten in die Datei schreiben, wollte ich vermeiden daß ich im Extremfall irgendwann hunderte MB an Daten im Speicher liegen habe, bevor das ganze in die Datei geschrieben wird. Und das ist bei deinem Vorschlag doch der Fall, oder?

EDIT: Achso, nee. Es wird ja ein Generator durch yield erzeugt und dann ruft map immer f.write() auf, wenn ein neues MB dazukommt, richtig?

Trotzdem lässt sich IMHO darüber streiten, ob man das jetzt unbedingt in zwei Funktionen zergliedern muss. Und BUFFER_SIZE könnte man wie ich finde genau so gut als Keyword-Argument mit nem Standardwert von 1MB an die Funktion übergeben.
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

@YOGi: outfile in Zeile 20 soll doch sicher filename sein?
MfG
HWK
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Und wenn ich dann auch nochmal meinen Senf dazu geben darf: Ich denke nicht, dass man bei der Wahl von Variablen-Namen halbe Romane schreiben muss (bytes_loaded, incoming_data). Das ergibt sich für gewöhnlich aus dem Zusammenhang. Um's mal zu überspitzen: Wie wär's mit incoming_data_from_current_download? ;) Und wenn man erwähnen möchte, dass die Angaben in Bytes sind, dann kann ich das auch in den Docstring schreiben. Zudem sollte man konsequenterweise "temp" in "buffer" umbenennen, wenn man weiter oben auch BUFFER_SIZE sagt.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

snafu hat geschrieben:Und wenn ich dann auch nochmal meinen Senf dazu geben darf: Ich denke nicht, dass man bei der Wahl von Variablen-Namen halbe Romane schreiben muss (bytes_loaded, incoming_data).
Ich halte das für durchaus sinnvoll. Variablennamen dokumentieren den Code so dass sie das Verständnis des Codes ermöglichen, nicht dass das Verständnis des Codes nötig ist, um die Motivation hinter den Variablennamen zu verstehen. Siehe Kenny Tiltons Gedanken zu AA, BB, CC & DD. AA ist natürlich schon übertrieben, aber der Hauptgedanke kommt IMHO rüber.

Hrhr, ich wollte schon immer mal kzo zitieren.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Den Artikel kenne ich schon und ich bin ja auch dafür, aussagekräftige Namen zu verwenden. Aber bei so einem kleinen Skript wie in dem Beispiel, finde ich, dass schon klar ist, woher "data" kommt. Ich will jetzt auch nicht drauf rumreiten. Falls du dich jetzt auf ein größeres Projekt beziehst, so denke ich, dass "data" und "incoming_data" in etwa die selbe Aussagekraft haben. Wenn ich explizit unterscheiden müsste zwischen Daten von einer Webseite, Nutzereingaben und Informationen aus einer Datei, wäre IMHO sowas in der Art passender: "site_data", "user_input", "file_content" (in allen drei Fällen kommen gewissermaßen Daten herein, aber die kommen ja eigentlich immer herein, wenn man sie nicht gerade selbst definiert). Der "halbe Roman" war eher so gemeint, dass man nicht unbedingt ins Detail bei der Benennung (respektive: Beschreibung) einer Variable gehen muss, wenn es für das Verständnis des Codes eher unnötig ist. Denn ich finde wie gesagt, auch ein Außenstehender wird nicht tagelang grübeln müssen, wenn er eine Download-Funktion sieht, die an "data" gebunden ist und mit der wiederum zwei Zeilen tiefer etwas anderes gemacht wird. 30 Zeilen tiefer wäre es natürlich etwas anderes, aber eigentlich versucht man ja, möglichst "ortsnah" mit seinen Variablen zu arbeiten...
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

HWK hat geschrieben:@YOGi: outfile in Zeile 20 soll doch sicher filename sein?
Jup, klaro. Danke.

snafu hat geschrieben:Es wird ja ein Generator durch yield erzeugt und dann ruft map immer f.write() auf, wenn ein neues MB dazukommt, richtig?
Richtig.
snafu hat geschrieben:Und BUFFER_SIZE könnte man wie ich finde genau so gut als Keyword-Argument mit nem Standardwert von 1MB an die Funktion übergeben.
Klar, das ist sicher eine gute Idee. Hast du aber nicht gemacht, sondern den Wert mitten im Funktionsrumpf hardkodiert ;)
snafu hat geschrieben:Ich denke nicht, dass man bei der Wahl von Variablen-Namen halbe Romane schreiben muss (bytes_loaded, incoming_data).
Jein. `loaded` hört sich für mich von der Namensgebung zu allererst mal nach einem Namen für einen Boolean an. Da ich also auf Anhieb verwirrt war, habe ich ihr einen - wie ich finde - etwas unmissverständlicheren Namen gegeben.
`incoming_data` muss nicht sein, aber `data` war mir auch zu doof, da es eigentlich rein gar nichts aussagt. Ich hätte vielleicht `download` verwendet, aber so heißt ja die Funktion bereits (und da deren Name ein Verb ist [nur leider auch ein Nomen], ist er an sich eine gute Wahl).
snafu hat geschrieben:Zudem sollte man konsequenterweise "temp" in "buffer" umbenennen, wenn man weiter oben auch BUFFER_SIZE sagt.
Auch richtig. Aber ich kann dir ja nicht *alles* hinterher tragen, oder? ;)
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Code: Alles auswählen

Python 2.5.2 (r252:60911, Sep 29 2008, 21:15:13) 
[GCC 4.3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> buffer
<type 'buffer'>
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Naja, für die Daten, die man hat, würde ich dann eher das Präteritum "downloaded" verwenden.

@str1442: Danke, das habe ich gar nicht bedacht. Na, dann "buf" oder sowas. Oder halt einfach bei "temp" bleiben. :)

EDIT: Und an der Stelle von "data" vielleicht eher sowas wie "url", falls der Name den tatsächlich so schlecht sien sollte. Selbst wenn auch dies wohl auch nicht hundertprozentig passt. In solchen Fällen schreibe ich dann aber lieber was hin, als mir stundenlang Gedanken um den perfekten Namen zu machen, wenn ich ehrlich bin. Manchmal lese ich mir das auch ein paar Tage später nochmal durch und habe dann den Geistesblitz. ;)
Antworten