diff --git a/mercurial/archival.py b/mercurial/archival.py --- a/mercurial/archival.py +++ b/mercurial/archival.py @@ -195,34 +195,11 @@ class tarit(object): if self.fileobj: self.fileobj.close() -class tellable(object): - '''provide tell method for zipfile.ZipFile when writing to http - response file object.''' - - def __init__(self, fp): - self.fp = fp - self.offset = 0 - - def __getattr__(self, key): - return getattr(self.fp, key) - - def write(self, s): - self.fp.write(s) - self.offset += len(s) - - def tell(self): - return self.offset - class zipit(object): '''write archive to zip file or stream. can write uncompressed, or compressed with deflate.''' def __init__(self, dest, mtime, compress=True): - if not isinstance(dest, bytes): - try: - dest.tell() - except (AttributeError, IOError): - dest = tellable(dest) self.z = zipfile.ZipFile(pycompat.fsdecode(dest), r'w', compress and zipfile.ZIP_DEFLATED or zipfile.ZIP_STORED) diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py --- a/mercurial/hgweb/request.py +++ b/mercurial/hgweb/request.py @@ -290,6 +290,37 @@ def parserequestfromenv(env, bodyfh): headers=headers, bodyfh=bodyfh) +class offsettrackingwriter(object): + """A file object like object that is append only and tracks write count. + + Instances are bound to a callable. This callable is called with data + whenever a ``write()`` is attempted. + + Instances track the amount of written data so they can answer ``tell()`` + requests. + + The intent of this class is to wrap the ``write()`` function returned by + a WSGI ``start_response()`` function. Since ``write()`` is a callable and + not a file object, it doesn't implement other file object methods. + """ + def __init__(self, writefn): + self._write = writefn + self._offset = 0 + + def write(self, s): + res = self._write(s) + # Some Python objects don't report the number of bytes written. + if res is None: + self._offset += len(s) + else: + self._offset += res + + def flush(self): + pass + + def tell(self): + return self._offset + class wsgiresponse(object): """Represents a response to a WSGI request. diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py --- a/mercurial/hgweb/webcommands.py +++ b/mercurial/hgweb/webcommands.py @@ -24,6 +24,9 @@ from .common import ( paritygen, staticfile, ) +from . import ( + request as requestmod, +) from .. import ( archival, @@ -1215,7 +1218,9 @@ def archive(web, req, tmpl): req.headers.extend(headers) req.respond(HTTP_OK, mimetype) - archival.archive(web.repo, req, cnode, artype, prefix=name, + bodyfh = requestmod.offsettrackingwriter(req.write) + + archival.archive(web.repo, bodyfh, cnode, artype, prefix=name, matchfn=match, subrepos=web.configbool("web", "archivesubrepos")) return []