comparison mercurial/byterange.py @ 3673:eb0b4a2d70a9

white space and line break cleanups
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 17 Nov 2006 08:06:54 +0100
parents 6513ba7d858a
children c301f15c965a
comparison
equal deleted inserted replaced
3672:e8730b5b8a32 3673:eb0b4a2d70a9
119 this object was created with a range tuple of (500,899), 119 this object was created with a range tuple of (500,899),
120 tell() will return 0 when at byte position 500 of the file. 120 tell() will return 0 when at byte position 500 of the file.
121 """ 121 """
122 return (self.realpos - self.firstbyte) 122 return (self.realpos - self.firstbyte)
123 123
124 def seek(self,offset,whence=0): 124 def seek(self, offset, whence=0):
125 """Seek within the byte range. 125 """Seek within the byte range.
126 Positioning is identical to that described under tell(). 126 Positioning is identical to that described under tell().
127 """ 127 """
128 assert whence in (0, 1, 2) 128 assert whence in (0, 1, 2)
129 if whence == 0: # absolute seek 129 if whence == 0: # absolute seek
168 size = (self.lastbyte - self.realpos) 168 size = (self.lastbyte - self.realpos)
169 else: 169 else:
170 size = (self.lastbyte - self.realpos) 170 size = (self.lastbyte - self.realpos)
171 return size 171 return size
172 172
173 def _do_seek(self,offset): 173 def _do_seek(self, offset):
174 """Seek based on whether wrapped object supports seek(). 174 """Seek based on whether wrapped object supports seek().
175 offset is relative to the current position (self.realpos). 175 offset is relative to the current position (self.realpos).
176 """ 176 """
177 assert offset >= 0 177 assert offset >= 0
178 if not hasattr(self.fo, 'seek'): 178 if not hasattr(self.fo, 'seek'):
179 self._poor_mans_seek(offset) 179 self._poor_mans_seek(offset)
180 else: 180 else:
181 self.fo.seek(self.realpos + offset) 181 self.fo.seek(self.realpos + offset)
182 self.realpos+= offset 182 self.realpos += offset
183 183
184 def _poor_mans_seek(self,offset): 184 def _poor_mans_seek(self, offset):
185 """Seek by calling the wrapped file objects read() method. 185 """Seek by calling the wrapped file objects read() method.
186 This is used for file like objects that do not have native 186 This is used for file like objects that do not have native
187 seek support. The wrapped objects read() method is called 187 seek support. The wrapped objects read() method is called
188 to manually seek to the desired position. 188 to manually seek to the desired position.
189 offset -- read this number of bytes from the wrapped 189 offset -- read this number of bytes from the wrapped
197 if (pos + bufsize) > offset: 197 if (pos + bufsize) > offset:
198 bufsize = offset - pos 198 bufsize = offset - pos
199 buf = self.fo.read(bufsize) 199 buf = self.fo.read(bufsize)
200 if len(buf) != bufsize: 200 if len(buf) != bufsize:
201 raise RangeError('Requested Range Not Satisfiable') 201 raise RangeError('Requested Range Not Satisfiable')
202 pos+= bufsize 202 pos += bufsize
203 203
204 class FileRangeHandler(urllib2.FileHandler): 204 class FileRangeHandler(urllib2.FileHandler):
205 """FileHandler subclass that adds Range support. 205 """FileHandler subclass that adds Range support.
206 This class handles Range headers exactly like an HTTP 206 This class handles Range headers exactly like an HTTP
207 server would. 207 server would.
219 if host: 219 if host:
220 host, port = urllib.splitport(host) 220 host, port = urllib.splitport(host)
221 if port or socket.gethostbyname(host) not in self.get_names(): 221 if port or socket.gethostbyname(host) not in self.get_names():
222 raise urllib2.URLError('file not on local host') 222 raise urllib2.URLError('file not on local host')
223 fo = open(localfile,'rb') 223 fo = open(localfile,'rb')
224 brange = req.headers.get('Range',None) 224 brange = req.headers.get('Range', None)
225 brange = range_header_to_tuple(brange) 225 brange = range_header_to_tuple(brange)
226 assert brange != () 226 assert brange != ()
227 if brange: 227 if brange:
228 (fb,lb) = brange 228 (fb, lb) = brange
229 if lb == '': lb = size 229 if lb == '':
230 lb = size
230 if fb < 0 or fb > size or lb > size: 231 if fb < 0 or fb > size or lb > size:
231 raise RangeError('Requested Range Not Satisfiable') 232 raise RangeError('Requested Range Not Satisfiable')
232 size = (lb - fb) 233 size = (lb - fb)
233 fo = RangeableFileObject(fo, (fb,lb)) 234 fo = RangeableFileObject(fo, (fb, lb))
234 headers = mimetools.Message(StringIO( 235 headers = mimetools.Message(StringIO(
235 'Content-Type: %s\nContent-Length: %d\nLast-modified: %s\n' % 236 'Content-Type: %s\nContent-Length: %d\nLast-modified: %s\n' %
236 (mtype or 'text/plain', size, modified))) 237 (mtype or 'text/plain', size, modified)))
237 return urllib.addinfourl(fo, headers, 'file:'+file) 238 return urllib.addinfourl(fo, headers, 'file:'+file)
238 239
290 value in ('a', 'A', 'i', 'I', 'd', 'D'): 291 value in ('a', 'A', 'i', 'I', 'd', 'D'):
291 type = value.upper() 292 type = value.upper()
292 293
293 # -- range support modifications start here 294 # -- range support modifications start here
294 rest = None 295 rest = None
295 range_tup = range_header_to_tuple(req.headers.get('Range',None)) 296 range_tup = range_header_to_tuple(req.headers.get('Range', None))
296 assert range_tup != () 297 assert range_tup != ()
297 if range_tup: 298 if range_tup:
298 (fb,lb) = range_tup 299 (fb, lb) = range_tup
299 if fb > 0: rest = fb 300 if fb > 0:
301 rest = fb
300 # -- range support modifications end here 302 # -- range support modifications end here
301 303
302 fp, retrlen = fw.retrfile(file, type, rest) 304 fp, retrlen = fw.retrfile(file, type, rest)
303 305
304 # -- range support modifications start here 306 # -- range support modifications start here
305 if range_tup: 307 if range_tup:
306 (fb,lb) = range_tup 308 (fb, lb) = range_tup
307 if lb == '': 309 if lb == '':
308 if retrlen is None or retrlen == 0: 310 if retrlen is None or retrlen == 0:
309 raise RangeError('Requested Range Not Satisfiable due to unobtainable file length.') 311 raise RangeError('Requested Range Not Satisfiable due to unobtainable file length.')
310 lb = retrlen 312 lb = retrlen
311 retrlen = lb - fb 313 retrlen = lb - fb
312 if retrlen < 0: 314 if retrlen < 0:
313 # beginning of range is larger than file 315 # beginning of range is larger than file
314 raise RangeError('Requested Range Not Satisfiable') 316 raise RangeError('Requested Range Not Satisfiable')
315 else: 317 else:
316 retrlen = lb - fb 318 retrlen = lb - fb
317 fp = RangeableFileObject(fp, (0,retrlen)) 319 fp = RangeableFileObject(fp, (0, retrlen))
318 # -- range support modifications end here 320 # -- range support modifications end here
319 321
320 headers = "" 322 headers = ""
321 mtype = mimetypes.guess_type(req.get_full_url())[0] 323 mtype = mimetypes.guess_type(req.get_full_url())[0]
322 if mtype: 324 if mtype:
338 # this ftpwrapper code is copied directly from 340 # this ftpwrapper code is copied directly from
339 # urllib. The only enhancement is to add the rest 341 # urllib. The only enhancement is to add the rest
340 # argument and pass it on to ftp.ntransfercmd 342 # argument and pass it on to ftp.ntransfercmd
341 def retrfile(self, file, type, rest=None): 343 def retrfile(self, file, type, rest=None):
342 self.endtransfer() 344 self.endtransfer()
343 if type in ('d', 'D'): cmd = 'TYPE A'; isdir = 1 345 if type in ('d', 'D'):
344 else: cmd = 'TYPE ' + type; isdir = 0 346 cmd = 'TYPE A'
347 isdir = 1
348 else:
349 cmd = 'TYPE ' + type
350 isdir = 0
345 try: 351 try:
346 self.ftp.voidcmd(cmd) 352 self.ftp.voidcmd(cmd)
347 except ftplib.all_errors: 353 except ftplib.all_errors:
348 self.init() 354 self.init()
349 self.ftp.voidcmd(cmd) 355 self.ftp.voidcmd(cmd)
370 raise IOError, ('ftp error', reason), sys.exc_info()[2] 376 raise IOError, ('ftp error', reason), sys.exc_info()[2]
371 if not conn: 377 if not conn:
372 # Set transfer mode to ASCII! 378 # Set transfer mode to ASCII!
373 self.ftp.voidcmd('TYPE A') 379 self.ftp.voidcmd('TYPE A')
374 # Try a directory listing 380 # Try a directory listing
375 if file: cmd = 'LIST ' + file 381 if file:
376 else: cmd = 'LIST' 382 cmd = 'LIST ' + file
383 else:
384 cmd = 'LIST'
377 conn = self.ftp.ntransfercmd(cmd) 385 conn = self.ftp.ntransfercmd(cmd)
378 self.busy = 1 386 self.busy = 1
379 # Pass back both a suitably decorated object and a retrieval length 387 # Pass back both a suitably decorated object and a retrieval length
380 return (addclosehook(conn[0].makefile('rb'), 388 return (addclosehook(conn[0].makefile('rb'),
381 self.endtransfer), conn[1]) 389 self.endtransfer), conn[1])
399 Return () if range_header does not conform to the range spec 407 Return () if range_header does not conform to the range spec
400 pattern. 408 pattern.
401 409
402 """ 410 """
403 global _rangere 411 global _rangere
404 if range_header is None: return None 412 if range_header is None:
413 return None
405 if _rangere is None: 414 if _rangere is None:
406 import re 415 import re
407 _rangere = re.compile(r'^bytes=(\d{1,})-(\d*)') 416 _rangere = re.compile(r'^bytes=(\d{1,})-(\d*)')
408 match = _rangere.match(range_header) 417 match = _rangere.match(range_header)
409 if match: 418 if match:
410 tup = range_tuple_normalize(match.group(1,2)) 419 tup = range_tuple_normalize(match.group(1, 2))
411 if tup and tup[1]: 420 if tup and tup[1]:
412 tup = (tup[0],tup[1]+1) 421 tup = (tup[0], tup[1]+1)
413 return tup 422 return tup
414 return () 423 return ()
415 424
416 def range_tuple_to_header(range_tup): 425 def range_tuple_to_header(range_tup):
417 """Convert a range tuple to a Range header value. 426 """Convert a range tuple to a Range header value.
418 Return a string of the form "bytes=<firstbyte>-<lastbyte>" or None 427 Return a string of the form "bytes=<firstbyte>-<lastbyte>" or None
419 if no range is needed. 428 if no range is needed.
420 """ 429 """
421 if range_tup is None: return None 430 if range_tup is None:
431 return None
422 range_tup = range_tuple_normalize(range_tup) 432 range_tup = range_tuple_normalize(range_tup)
423 if range_tup: 433 if range_tup:
424 if range_tup[1]: 434 if range_tup[1]:
425 range_tup = (range_tup[0],range_tup[1] - 1) 435 range_tup = (range_tup[0], range_tup[1] - 1)
426 return 'bytes=%s-%s' % range_tup 436 return 'bytes=%s-%s' % range_tup
427 437
428 def range_tuple_normalize(range_tup): 438 def range_tuple_normalize(range_tup):
429 """Normalize a (first_byte,last_byte) range tuple. 439 """Normalize a (first_byte,last_byte) range tuple.
430 Return a tuple whose first element is guaranteed to be an int 440 Return a tuple whose first element is guaranteed to be an int
431 and whose second element will be '' (meaning: the last byte) or 441 and whose second element will be '' (meaning: the last byte) or
432 an int. Finally, return None if the normalized tuple == (0,'') 442 an int. Finally, return None if the normalized tuple == (0,'')
433 as that is equivelant to retrieving the entire file. 443 as that is equivelant to retrieving the entire file.
434 """ 444 """
435 if range_tup is None: return None 445 if range_tup is None:
446 return None
436 # handle first byte 447 # handle first byte
437 fb = range_tup[0] 448 fb = range_tup[0]
438 if fb in (None,''): fb = 0 449 if fb in (None, ''):
439 else: fb = int(fb) 450 fb = 0
451 else:
452 fb = int(fb)
440 # handle last byte 453 # handle last byte
441 try: lb = range_tup[1] 454 try:
442 except IndexError: lb = '' 455 lb = range_tup[1]
456 except IndexError:
457 lb = ''
443 else: 458 else:
444 if lb is None: lb = '' 459 if lb is None:
445 elif lb != '': lb = int(lb) 460 lb = ''
461 elif lb != '':
462 lb = int(lb)
446 # check if range is over the entire file 463 # check if range is over the entire file
447 if (fb,lb) == (0,''): return None 464 if (fb, lb) == (0, ''):
465 return None
448 # check that the range is valid 466 # check that the range is valid
449 if lb < fb: raise RangeError('Invalid byte range: %s-%s' % (fb,lb)) 467 if lb < fb:
450 return (fb,lb) 468 raise RangeError('Invalid byte range: %s-%s' % (fb, lb))
469 return (fb, lb)