Seite 1 von 2
Korrekter Weg um stdout stummzustellen
Verfasst: Montag 3. November 2008, 18:22
von snafu
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")
Verfasst: Montag 3. November 2008, 18:25
von Leonidas
Seit wann ist ``wget`` platformübergreifend verfügbar? Wenn es dir nur um Resume geht, dass geht AFAIR mit Bordmitteln auch.
Verfasst: Montag 3. November 2008, 18:27
von snafu
Leonidas hat geschrieben:Seit wann ist ``wget`` platformübergreifend verfügbar?
Ich bezog mich mit "plattformübergreifend" eigentlich nur auf das Stummstellen von stdout.
Verfasst: Montag 3. November 2008, 18:31
von snafu
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.
Verfasst: Montag 3. November 2008, 19:23
von veers
http://29a.ch/git/gitweb.cgi?p=lanshark ... =HEAD#l277
Diese Funtktion downloaded eine Datei mit Resume support.

Verfasst: Montag 3. November 2008, 21:15
von abgdf
Seit wann ist ``wget`` platformübergreifend verfügbar?
Schon länger:
http://gnuwin32.sourceforge.net/packages/wget.htm
Gruß
Verfasst: Montag 3. November 2008, 22:53
von Leonidas
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.
Verfasst: Montag 3. November 2008, 23:34
von 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

Verfasst: Dienstag 4. November 2008, 09:05
von snafu
Ja, aber um mal zur Ausgangsfrage zurückzukommen: In die Pipe leiten und nichts weiter damit machen ist also der übliche Weg?
Verfasst: Dienstag 4. November 2008, 10:48
von 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.
Verfasst: Dienstag 4. November 2008, 12:11
von snafu
Danke für den Hinweis mit devnull.
Ich hab jetzt einen simplen Downloader/Resumer mit Python-Bordmitteln gemacht:
http://paste.pocoo.org/show/90095/
Verfasst: Dienstag 4. November 2008, 12:49
von Y0Gi
Schreit ja förmlich nach `with`!
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)
Verfasst: Dienstag 4. November 2008, 13:07
von snafu
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.
Verfasst: Dienstag 4. November 2008, 13:44
von HWK
@YOGi: outfile in Zeile 20 soll doch sicher filename sein?
MfG
HWK
Verfasst: Dienstag 4. November 2008, 14:06
von snafu
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.
Verfasst: Dienstag 4. November 2008, 15:43
von Leonidas
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.
Verfasst: Dienstag 4. November 2008, 16:29
von snafu
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...
Verfasst: Dienstag 4. November 2008, 16:41
von Y0Gi
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?

Verfasst: Dienstag 4. November 2008, 16:52
von str1442
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'>
Verfasst: Dienstag 4. November 2008, 16:56
von snafu
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.
