# HG changeset patch # User Eric Sumner # Date 2015-01-14 22:24:16 # 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 --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -145,6 +145,7 @@ future, dropping the stream may become a preserve. """ +import errno import sys import util import struct @@ -484,6 +485,8 @@ class unpackermixin(object): 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 @@ class unpackermixin(object): """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