comparison mercurial/bundle2.py @ 31889:a02e773008f5

bundle2: move 'seek' and 'tell' methods off the unpackermixin class These methods are unrelated to unpacking. They are used internally by the 'unbundlepart' class only. So me move them there as private methods. In the same go, we clarify their internal role in the their docstring.
author Pierre-Yves David <pierre-yves.david@ens-lyon.org>
date Sun, 09 Apr 2017 19:09:07 +0200
parents cd7aaf344d83
children ad41739c6b2b
comparison
equal deleted inserted replaced
31888:1c398f7f4aa4 31889:a02e773008f5
615 class unpackermixin(object): 615 class unpackermixin(object):
616 """A mixin to extract bytes and struct data from a stream""" 616 """A mixin to extract bytes and struct data from a stream"""
617 617
618 def __init__(self, fp): 618 def __init__(self, fp):
619 self._fp = fp 619 self._fp = fp
620 self._seekable = (util.safehasattr(fp, 'seek') and
621 util.safehasattr(fp, 'tell'))
622 620
623 def _unpack(self, format): 621 def _unpack(self, format):
624 """unpack this struct format from the stream 622 """unpack this struct format from the stream
625 623
626 This method is meant for internal usage by the bundle2 protocol only. 624 This method is meant for internal usage by the bundle2 protocol only.
638 They directly manipulate the low level stream including bundle2 level 636 They directly manipulate the low level stream including bundle2 level
639 instruction. 637 instruction.
640 638
641 Do not use it to implement higher-level logic or methods.""" 639 Do not use it to implement higher-level logic or methods."""
642 return changegroup.readexactly(self._fp, size) 640 return changegroup.readexactly(self._fp, size)
643
644 def seek(self, offset, whence=0):
645 """move the underlying file pointer"""
646 if self._seekable:
647 return self._fp.seek(offset, whence)
648 else:
649 raise NotImplementedError(_('File pointer is not seekable'))
650
651 def tell(self):
652 """return the file offset, or None if file is not seekable"""
653 if self._seekable:
654 try:
655 return self._fp.tell()
656 except IOError as e:
657 if e.errno == errno.ESPIPE:
658 self._seekable = False
659 else:
660 raise
661 return None
662 641
663 def getunbundler(ui, fp, magicstring=None): 642 def getunbundler(ui, fp, magicstring=None):
664 """return a valid unbundler object for a given magicstring""" 643 """return a valid unbundler object for a given magicstring"""
665 if magicstring is None: 644 if magicstring is None:
666 magicstring = changegroup.readexactly(fp, 4) 645 magicstring = changegroup.readexactly(fp, 4)
1109 class unbundlepart(unpackermixin): 1088 class unbundlepart(unpackermixin):
1110 """a bundle part read from a bundle""" 1089 """a bundle part read from a bundle"""
1111 1090
1112 def __init__(self, ui, header, fp): 1091 def __init__(self, ui, header, fp):
1113 super(unbundlepart, self).__init__(fp) 1092 super(unbundlepart, self).__init__(fp)
1093 self._seekable = (util.safehasattr(fp, 'seek') and
1094 util.safehasattr(fp, 'tell'))
1114 self.ui = ui 1095 self.ui = ui
1115 # unbundle state attr 1096 # unbundle state attr
1116 self._headerdata = header 1097 self._headerdata = header
1117 self._headeroffset = 0 1098 self._headeroffset = 0
1118 self._initialized = False 1099 self._initialized = False
1156 1137
1157 def _payloadchunks(self, chunknum=0): 1138 def _payloadchunks(self, chunknum=0):
1158 '''seek to specified chunk and start yielding data''' 1139 '''seek to specified chunk and start yielding data'''
1159 if len(self._chunkindex) == 0: 1140 if len(self._chunkindex) == 0:
1160 assert chunknum == 0, 'Must start with chunk 0' 1141 assert chunknum == 0, 'Must start with chunk 0'
1161 self._chunkindex.append((0, super(unbundlepart, self).tell())) 1142 self._chunkindex.append((0, self._tellfp()))
1162 else: 1143 else:
1163 assert chunknum < len(self._chunkindex), \ 1144 assert chunknum < len(self._chunkindex), \
1164 'Unknown chunk %d' % chunknum 1145 'Unknown chunk %d' % chunknum
1165 super(unbundlepart, self).seek(self._chunkindex[chunknum][1]) 1146 self._seekfp(self._chunkindex[chunknum][1])
1166 1147
1167 pos = self._chunkindex[chunknum][0] 1148 pos = self._chunkindex[chunknum][0]
1168 payloadsize = self._unpack(_fpayloadsize)[0] 1149 payloadsize = self._unpack(_fpayloadsize)[0]
1169 indebug(self.ui, 'payload chunk size: %i' % payloadsize) 1150 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1170 while payloadsize: 1151 while payloadsize:
1178 else: 1159 else:
1179 result = self._readexact(payloadsize) 1160 result = self._readexact(payloadsize)
1180 chunknum += 1 1161 chunknum += 1
1181 pos += payloadsize 1162 pos += payloadsize
1182 if chunknum == len(self._chunkindex): 1163 if chunknum == len(self._chunkindex):
1183 self._chunkindex.append((pos, 1164 self._chunkindex.append((pos, self._tellfp()))
1184 super(unbundlepart, self).tell()))
1185 yield result 1165 yield result
1186 payloadsize = self._unpack(_fpayloadsize)[0] 1166 payloadsize = self._unpack(_fpayloadsize)[0]
1187 indebug(self.ui, 'payload chunk size: %i' % payloadsize) 1167 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1188 1168
1189 def _findchunk(self, pos): 1169 def _findchunk(self, pos):
1271 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk)) 1251 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk))
1272 adjust = self.read(internaloffset) 1252 adjust = self.read(internaloffset)
1273 if len(adjust) != internaloffset: 1253 if len(adjust) != internaloffset:
1274 raise error.Abort(_('Seek failed\n')) 1254 raise error.Abort(_('Seek failed\n'))
1275 self._pos = newpos 1255 self._pos = newpos
1256
1257 def _seekfp(self, offset, whence=0):
1258 """move the underlying file pointer
1259
1260 This method is meant for internal usage by the bundle2 protocol only.
1261 They directly manipulate the low level stream including bundle2 level
1262 instruction.
1263
1264 Do not use it to implement higher-level logic or methods."""
1265 if self._seekable:
1266 return self._fp.seek(offset, whence)
1267 else:
1268 raise NotImplementedError(_('File pointer is not seekable'))
1269
1270 def _tellfp(self):
1271 """return the file offset, or None if file is not seekable
1272
1273 This method is meant for internal usage by the bundle2 protocol only.
1274 They directly manipulate the low level stream including bundle2 level
1275 instruction.
1276
1277 Do not use it to implement higher-level logic or methods."""
1278 if self._seekable:
1279 try:
1280 return self._fp.tell()
1281 except IOError as e:
1282 if e.errno == errno.ESPIPE:
1283 self._seekable = False
1284 else:
1285 raise
1286 return None
1276 1287
1277 # These are only the static capabilities. 1288 # These are only the static capabilities.
1278 # Check the 'getrepocaps' function for the rest. 1289 # Check the 'getrepocaps' function for the rest.
1279 capabilities = {'HG20': (), 1290 capabilities = {'HG20': (),
1280 'error': ('abort', 'unsupportedcontent', 'pushraced', 1291 'error': ('abort', 'unsupportedcontent', 'pushraced',