# HG changeset patch # User Eric Sumner # Date 1421274256 28800 # Node ID 3daef83a18733a1f849b2d48878a7c9b410f0f35 # Parent bbb011f4eb32769c26b4f76e11b941aa845ec20e bundle2.unpackermixin: control for underlying file descriptor This patch adds seek(), tell(), and close() implementations for unpackermixin which forward to the file descriptor's implementation if possible. A future patch will use this to make bundle2.unbundlepart seekable, which will in turn make it usable as a file descriptor for bundlerepo. diff -r bbb011f4eb32 -r 3daef83a1873 mercurial/bundle2.py --- a/mercurial/bundle2.py Fri Jan 16 15:34:03 2015 -0800 +++ b/mercurial/bundle2.py Wed Jan 14 14:24:16 2015 -0800 @@ -145,6 +145,7 @@ preserve. """ +import errno import sys import util import struct @@ -484,6 +485,8 @@ def __init__(self, fp): self._fp = fp + self._seekable = (util.safehasattr(fp, 'seek') and + util.safehasattr(fp, 'tell')) def _unpack(self, format): """unpack this struct format from the stream""" @@ -494,6 +497,29 @@ """read exactly bytes from the stream""" return changegroup.readexactly(self._fp, size) + def seek(self, offset, whence): + """move the underlying file pointer""" + if self._seekable: + return self._fp.seek(offset, whence) + else: + raise NotImplementedError(_('File pointer is not seekable')) + + def tell(self): + """return the file offset, or None if file is not seekable""" + if self._seekable: + try: + return self._fp.tell() + except IOError, e: + if e.errno == errno.ESPIPE: + self._seekable = False + else: + raise + return None + + def close(self): + """close underlying file""" + if util.safehasattr(self._fp, 'close'): + return self._fp.close() class unbundle20(unpackermixin): """interpret a bundle2 stream