#/usr/bin/python -i """Demonstrates a bug or quirk in urllib To run the test: - execute this file in python with the -i flag (e.g. python -i simpletest.py) - wait for both transfers to finish - start the transfers again: >>> doGet() - one of the two transfers should fail 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.) Example: rowen% pw -i simpletest.py Running sitecustomize.pyc Start retrieving 'ftp://ftp.astro.washington.edu/pub/users/rowen/python/RO%202004-10-13.zip' Start retrieving 'ftp://ftp.astro.washington.edu/pub/users/rowen/python/RO%202004-05-21.zip' >>> Done retrieving 'ftp://ftp.astro.washington.edu/pub/users/rowen/python/RO%202004-05-21.zip'; read 299377 bytes Done retrieving 'ftp://ftp.astro.washington.edu/pub/users/rowen/python/RO%202004-10-13.zip'; read 321845 bytes (at this point I waited until I saw the that both xfers finished before typing doGet()...) doGet() Start retrieving 'ftp://ftp.astro.washington.edu/pub/users/rowen/python/RO%202004-10-13.zip' Start retrieving 'ftp://ftp.astro.washington.edu/pub/users/rowen/python/RO%202004-05-21.zip' Retrieval of 'ftp://ftp.astro.washington.edu/pub/users/rowen/python/RO%202004-05-21.zip' failed with error: [Errno ftp error] 200 Type set to A. >>> Done retrieving 'ftp://ftp.astro.washington.edu/pub/users/rowen/python/RO%202004-10-13.zip'; read 321845 bytes """ import os import sys import threading 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() 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 self._fromFile = urllib.urlopen(self.fromURL) self._totBytes = self._fromFile.info().getheader("Content-Length") if self._totBytes: self._totBytes = int(self._totBytes) while True: nextData = self._fromFile.read(8192) if not nextData: break self._readBytes += len(nextData) 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') def doGet(): for name in names: GetFile(prefix + name) doGet()