httpsendfile: record progress information during read()
authorAugie Fackler <durin42@gmail.com>
Fri, 10 Dec 2010 13:31:06 -0600
changeset 13115 bda5f35fbf67
parent 13114 8f29a08e7bbc
child 13116 c36dad4f6e54
httpsendfile: record progress information during read() This allows us to provide deterministic progress information during transfer of bundle data over HTTP. This is required because we currently buffer the bundle data to local disk prior to transfer since wsgiref lacks chunked transfer-coding support.
mercurial/httprepo.py
mercurial/url.py
--- a/mercurial/httprepo.py	Tue Dec 07 15:50:28 2010 +0100
+++ b/mercurial/httprepo.py	Fri Dec 10 13:31:06 2010 -0600
@@ -160,7 +160,7 @@
                     break
 
         tempname = changegroup.writebundle(cg, None, type)
-        fp = url.httpsendfile(tempname, "rb")
+        fp = url.httpsendfile(self.ui, tempname, "rb")
         headers = {'Content-Type': 'application/mercurial-0.1'}
 
         try:
--- a/mercurial/url.py	Tue Dec 07 15:50:28 2010 +0100
+++ b/mercurial/url.py	Fri Dec 10 13:31:06 2010 -0600
@@ -258,18 +258,36 @@
     defines a __len__ attribute to feed the Content-Length header.
     """
 
-    def __init__(self, *args, **kwargs):
+    def __init__(self, ui, *args, **kwargs):
         # We can't just "self._data = open(*args, **kwargs)" here because there
         # is an "open" function defined in this module that shadows the global
         # one
+        self.ui = ui
         self._data = __builtin__.open(*args, **kwargs)
-        self.read = self._data.read
         self.seek = self._data.seek
         self.close = self._data.close
         self.write = self._data.write
+        self._len = os.fstat(self._data.fileno()).st_size
+        self._pos = 0
+        self._total = len(self) / 1024 * 2
+
+    def read(self, *args, **kwargs):
+        try:
+            ret = self._data.read(*args, **kwargs)
+        except EOFError:
+            self.ui.progress(_('sending'), None)
+        self._pos += len(ret)
+        # We pass double the max for total because we currently have
+        # to send the bundle twice in the case of a server that
+        # requires authentication. Since we can't know until we try
+        # once whether authentication will be required, just lie to
+        # the user and maybe the push succeeds suddenly at 50%.
+        self.ui.progress(_('sending'), self._pos / 1024,
+                         unit=_('kb'), total=self._total)
+        return ret
 
     def __len__(self):
-        return os.fstat(self._data.fileno()).st_size
+        return self._len
 
 def _gen_sendfile(connection):
     def _sendfile(self, data):