Mercurial > public > mercurial-scm > hg-stable
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) |