comparison mercurial/bundle2.py @ 24026:3daef83a1873

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.
author Eric Sumner <ericsumner@fb.com>
date Wed, 14 Jan 2015 14:24:16 -0800
parents a3f7c781786b
children 9881a1437799
comparison
equal deleted inserted replaced
24025:bbb011f4eb32 24026:3daef83a1873
143 channel usable. But none of the part read from an abort are processed. In the 143 channel usable. But none of the part read from an abort are processed. In the
144 future, dropping the stream may become an option for channel we do not care to 144 future, dropping the stream may become an option for channel we do not care to
145 preserve. 145 preserve.
146 """ 146 """
147 147
148 import errno
148 import sys 149 import sys
149 import util 150 import util
150 import struct 151 import struct
151 import urllib 152 import urllib
152 import string 153 import string
482 class unpackermixin(object): 483 class unpackermixin(object):
483 """A mixin to extract bytes and struct data from a stream""" 484 """A mixin to extract bytes and struct data from a stream"""
484 485
485 def __init__(self, fp): 486 def __init__(self, fp):
486 self._fp = fp 487 self._fp = fp
488 self._seekable = (util.safehasattr(fp, 'seek') and
489 util.safehasattr(fp, 'tell'))
487 490
488 def _unpack(self, format): 491 def _unpack(self, format):
489 """unpack this struct format from the stream""" 492 """unpack this struct format from the stream"""
490 data = self._readexact(struct.calcsize(format)) 493 data = self._readexact(struct.calcsize(format))
491 return _unpack(format, data) 494 return _unpack(format, data)
492 495
493 def _readexact(self, size): 496 def _readexact(self, size):
494 """read exactly <size> bytes from the stream""" 497 """read exactly <size> bytes from the stream"""
495 return changegroup.readexactly(self._fp, size) 498 return changegroup.readexactly(self._fp, size)
496 499
500 def seek(self, offset, whence):
501 """move the underlying file pointer"""
502 if self._seekable:
503 return self._fp.seek(offset, whence)
504 else:
505 raise NotImplementedError(_('File pointer is not seekable'))
506
507 def tell(self):
508 """return the file offset, or None if file is not seekable"""
509 if self._seekable:
510 try:
511 return self._fp.tell()
512 except IOError, e:
513 if e.errno == errno.ESPIPE:
514 self._seekable = False
515 else:
516 raise
517 return None
518
519 def close(self):
520 """close underlying file"""
521 if util.safehasattr(self._fp, 'close'):
522 return self._fp.close()
497 523
498 class unbundle20(unpackermixin): 524 class unbundle20(unpackermixin):
499 """interpret a bundle2 stream 525 """interpret a bundle2 stream
500 526
501 This class is fed with a binary stream and yields parts through its 527 This class is fed with a binary stream and yields parts through its