#/usr/bin/python -i """Demonstrates a bug or quirk in urllib To run the test, run this script. When it tries to download the two files in `doGet()` for a second time, you may see a message like Retrieval of 'ftp://ftp.gnome.org/pub/debian/dists/stable/main/source/Sources.bz2' failed with error: [Errno ftp error] 200 Switching to Binary mode. More remarks by bug submitter: The bug is not seen if only 1 file is transferred and allowed to finish before the next one starts. Thus if the files transfer so quickly that one xfer finishes before the 2nd begins then you may not see the problem, in which case pick bigger files or use a computer with a slower connection. """ import gc import threading import time import urllib class GetFile: def __init__(self, fromURL): self.fromURL = fromURL self._fromFile = None self._readBytes = 0 self._totBytes = None self._getThread = threading.Thread(name="get", target=self._getTask) self._getThread.setDaemon(True) self._getThread.start() def _cleanup(self, exception=None): """Clean up everything. Must only be called from the _getTask thread. """ if self._fromFile: self._fromFile.close() global transfersInProgress transfersInProgress -= 1 if exception: print "Retrieval of %r failed with error: %s" % ( self.fromURL, exception) else: print "Done retrieving %r; read %s bytes" % ( self.fromURL, self._readBytes) def _getTask(self): """Retrieve the file in a background thread. Do not call directly; use self._getThread.start() instead. """ try: print "Start retrieving %r" % self.fromURL global transfersInProgress transfersInProgress += 1 self._fromFile = urllib.urlopen(self.fromURL) self._totBytes = self._fromFile.info().getheader("Content-Length") if self._totBytes: self._totBytes = int(self._totBytes) print "Total bytes:", self._totBytes while True: nextData = self._fromFile.read(8192) if not nextData: break self._readBytes += len(nextData) # Seems to be needed to avoid long freezing after the first # run of `doGet` gc.collect() try: ratio = 100.0 * self._readBytes / self._totBytes except AttributeError: # `_totBytes` not set pass else: print int(ratio) * "#", int(ratio), "%" self._cleanup() except Exception, e: self._cleanup(e) # prefix = 'ftp://ftp.astro.washington.edu/pub/users/rowen/python/' # names = ('RO%202004-10-13.zip', 'RO%202004-05-21.zip') prefix = 'ftp://ftp.gnome.org/pub/debian/dists/stable/main/source/' names = ('Sources.bz2', 'Sources.bz2') transfersInProgress = 0 def doGet(): for name in names: GetFile(prefix + name) time.sleep(0.1) while transfersInProgress: time.sleep(0.1) doGet() time.sleep(3) doGet()