1""" 2fs.filelike 3=========== 4 5This module takes care of the groundwork for implementing and manipulating 6objects that provide a rich file-like interface, including reading, writing, 7seeking and iteration. 8 9The main class is FileLikeBase, which implements the entire file-like interface 10on top of primitive _read(), _write(), _seek(), _tell() and _truncate() methods. 11Subclasses may implement any or all of these methods to obtain the related 12higher-level file behaviors. 13 14Other useful classes include: 15 16 * StringIO: a version of the builtin StringIO class, patched to more 17 closely preserve the semantics of a standard file. 18 19 * FileWrapper: a generic base class for wrappers around a filelike object 20 (think e.g. compression or decryption). 21 22 * SpooledTemporaryFile: a version of the builtin SpooledTemporaryFile 23 class, patched to more closely preserve the 24 semantics of a standard file. 25 26 * LimitBytesFile: a filelike wrapper that limits the total bytes read 27 from a file; useful for turning a socket into a file 28 without reading past end-of-data. 29 30""" 31# Copyright (C) 2006-2009, Ryan Kelly 32# All rights reserved; available under the terms of the MIT License. 33 34import tempfile as _tempfile 35 36import fs 37 38 39class NotReadableError(IOError): 40 pass 41class NotWritableError(IOError): 42 pass 43class NotSeekableError(IOError): 44 pass 45class NotTruncatableError(IOError): 46 pass 47 48import six 49from six import PY3, b 50 51if PY3: 52 from six import BytesIO as _StringIO 53else: 54 try: 55 from cStringIO import StringIO as _StringIO 56 except ImportError: 57 from StringIO import StringIO as _StringIO 58 59 60class FileLikeBase(object): 61 """Base class for implementing file-like objects. 62 63 This class takes a lot of the legwork out of writing file-like objects 64 with a rich interface. It implements the higher-level file-like methods 65 on top of five primitive methods: _read, _write, _seek, _tell and 66 _truncate. See their docstrings for precise details on how these methods 67 behave. 68 69 Subclasses then need only implement some subset of these methods for 70 rich file-like interface compatibility. They may of course override 71 other methods as desired. 72 73 The class is missing the following attributes and methods, which don't 74 really make sense for anything but real files: 75 76 * fileno() 77 * isatty() 78 * encoding 79 * mode 80 * name 81 * newlines 82 83 Unlike standard file objects, all read methods share the same buffer 84 and so can be freely mixed (e.g. read(), readline(), next(), ...). 85 86 This class understands and will accept the following mode strings, 87 with any additional characters being ignored: 88 89 * r - open the file for reading only. 90 * r+ - open the file for reading and writing. 91 * r- - open the file for streamed reading; do not allow seek/tell. 92 * w - open the file for writing only; create the file if 93 it doesn't exist; truncate it to zero length. 94 * w+ - open the file for reading and writing; create the file 95 if it doesn't exist; truncate it to zero length. 96 * w- - open the file for streamed writing; do not allow seek/tell. 97 * a - open the file for writing only; create the file if it 98 doesn't exist; place pointer at end of file. 99 * a+ - open the file for reading and writing; create the file 100 if it doesn't exist; place pointer at end of file. 101 102 These are mostly standard except for the "-" indicator, which has 103 been added for efficiency purposes in cases where seeking can be 104 expensive to simulate (e.g. compressed files). Note that any file 105 opened for both reading and writing must also support seeking. 106 """ 107 108 def __init__(self,bufsize=1024*64): 109 """FileLikeBase Constructor. 110 111 The optional argument 'bufsize' specifies the number of bytes to 112 read at a time when looking for a newline character. Setting this to 113 a larger number when lines are long should improve efficiency. 114 """ 115 super(FileLikeBase, self).__init__() 116 # File-like attributes 117 self.closed = False 118 self.softspace = 0 119 # Our own attributes 120 self._bufsize = bufsize # buffer size for chunked reading 121 self._rbuffer = None # data that's been read but not returned 122 self._wbuffer = None # data that's been given but not written 123 self._sbuffer = None # data between real & apparent file pos 124 self._soffset = 0 # internal offset of file pointer 125 126 # 127 # The following five methods are the ones that subclasses are expected 128 # to implement. Carefully check their docstrings. 129 # 130 131 def _read(self,sizehint=-1): 132 """Read approximately <sizehint> bytes from the file-like object. 133 134 This method is to be implemented by subclasses that wish to be 135 readable. It should read approximately <sizehint> bytes from the 136 file and return them as a string. If <sizehint> is missing or 137 less than or equal to zero, try to read all the remaining contents. 138 139 The method need not guarantee any particular number of bytes - 140 it may return more bytes than requested, or fewer. If needed the 141 size hint may be completely ignored. It may even return an empty 142 string if no data is yet available. 143 144 Because of this, the method must return None to signify that EOF 145 has been reached. The higher-level methods will never indicate EOF 146 until None has been read from _read(). Once EOF is reached, it 147 should be safe to call _read() again, immediately returning None. 148 """ 149 raise NotReadableError("Object not readable") 150 151 def _write(self,string,flushing=False): 152 """Write the given string to the file-like object. 153 154 This method must be implemented by subclasses wishing to be writable. 155 It must attempt to write as much of the given data as possible to the 156 file, but need not guarantee that it is all written. It may return 157 None to indicate that all data was written, or return as a string any 158 data that could not be written. 159 160 If the keyword argument 'flushing' is true, it indicates that the 161 internal write buffers are being flushed, and *all* the given data 162 is expected to be written to the file. If unwritten data is returned 163 when 'flushing' is true, an IOError will be raised. 164 """ 165 raise NotWritableError("Object not writable") 166 167 def _seek(self,offset,whence): 168 """Set the file's internal position pointer, approximately. 169 170 This method should set the file's position to approximately 'offset' 171 bytes relative to the position specified by 'whence'. If it is 172 not possible to position the pointer exactly at the given offset, 173 it should be positioned at a convenient *smaller* offset and the 174 file data between the real and apparent position should be returned. 175 176 At minimum, this method must implement the ability to seek to 177 the start of the file, i.e. offset=0 and whence=0. If more 178 complex seeks are difficult to implement then it may raise 179 NotImplementedError to have them simulated (inefficiently) by 180 the higher-level machinery of this class. 181 """ 182 raise NotSeekableError("Object not seekable") 183 184 def _tell(self): 185 """Get the location of the file's internal position pointer. 186 187 This method must be implemented by subclasses that wish to be 188 seekable, and must return the position of the file's internal 189 pointer. 190 191 Due to buffering, the position seen by users of this class 192 (the "apparent position") may be different to the position 193 returned by this method (the "actual position"). 194 """ 195 raise NotSeekableError("Object not seekable") 196 197 def _truncate(self,size): 198 """Truncate the file's size to <size>. 199 200 This method must be implemented by subclasses that wish to be 201 truncatable. It must truncate the file to exactly the given size 202 or fail with an IOError. 203 204 Note that <size> will never be None; if it was not specified by the 205 user then it is calculated as the file's apparent position (which may 206 be different to its actual position due to buffering). 207 """ 208 raise NotTruncatableError("Object not truncatable") 209 210 # 211 # The following methods provide the public API of the filelike object. 212 # Subclasses shouldn't need to mess with these (except perhaps for 213 # close() and flush()) 214 # 215 216 def _check_mode(self,mode,mstr=None): 217 """Check whether the file may be accessed in the given mode. 218 219 'mode' must be one of "r" or "w", and this function returns False 220 if the file-like object has a 'mode' attribute, and it does not 221 permit access in that mode. If there is no 'mode' attribute, 222 it defaults to "r+". 223 224 If seek support is not required, use "r-" or "w-" as the mode string. 225 226 To check a mode string other than self.mode, pass it in as the 227 second argument. 228 """ 229 if mstr is None: 230 try: 231 mstr = self.mode 232 except AttributeError: 233 mstr = "r+" 234 if "+" in mstr: 235 return True 236 if "-" in mstr and "-" not in mode: 237 return False 238 if "r" in mode: 239 if "r" not in mstr: 240 return False 241 if "w" in mode: 242 if "w" not in mstr and "a" not in mstr: 243 return False 244 return True 245 246 def _assert_mode(self,mode,mstr=None): 247 """Check whether the file may be accessed in the given mode. 248 249 This method is equivalent to _check_assert(), but raises IOError 250 instead of returning False. 251 """ 252 if mstr is None: 253 try: 254 mstr = self.mode 255 except AttributeError: 256 mstr = "r+" 257 if "+" in mstr: 258 return True 259 if "-" in mstr and "-" not in mode: 260 raise NotSeekableError("File does not support seeking.") 261 if "r" in mode: 262 if "r" not in mstr: 263 raise NotReadableError("File not opened for reading") 264 if "w" in mode: 265 if "w" not in mstr and "a" not in mstr: 266 raise NotWritableError("File not opened for writing") 267 return True 268 269 def flush(self): 270 """Flush internal write buffer, if necessary.""" 271 if self.closed: 272 raise IOError("File has been closed") 273 if self._check_mode("w-") and self._wbuffer is not None: 274 buffered = b("") 275 if self._sbuffer: 276 buffered = buffered + self._sbuffer 277 self._sbuffer = None 278 buffered = buffered + self._wbuffer 279 self._wbuffer = None 280 leftover = self._write(buffered,flushing=True) 281 if leftover and not isinstance(leftover, int): 282 raise IOError("Could not flush write buffer.") 283 284 def close(self): 285 """Flush write buffers and close the file. 286 287 The file may not be accessed further once it is closed. 288 """ 289 # Errors in subclass constructors can cause this to be called without 290 # having called FileLikeBase.__init__(). Since we need the attrs it 291 # initializes in cleanup, ensure we call it here. 292 if not hasattr(self,"closed"): 293 FileLikeBase.__init__(self) 294 if not self.closed: 295 self.flush() 296 self.closed = True 297 298 def __del__(self): 299 self.close() 300 301 def __enter__(self): 302 return self 303 304 def __exit__(self,exc_type,exc_val,exc_tb): 305 self.close() 306 return False 307 308 def next(self): 309 """next() method complying with the iterator protocol. 310 311 File-like objects are their own iterators, with each call to 312 next() returning subsequent lines from the file. 313 """ 314 ln = self.readline() 315 if ln == b(""): 316 raise StopIteration() 317 return ln 318 319 def __iter__(self): 320 return self 321 322 def truncate(self,size=None): 323 """Truncate the file to the given size. 324 325 If <size> is not specified or is None, the current file position is 326 used. Note that this method may fail at runtime if the underlying 327 filelike object is not truncatable. 328 """ 329 if "-" in getattr(self,"mode",""): 330 raise NotTruncatableError("File is not seekable, can't truncate.") 331 if self._wbuffer: 332 self.flush() 333 if size is None: 334 size = self.tell() 335 self._truncate(size) 336 337 def seek(self,offset,whence=0): 338 """Move the internal file pointer to the given location.""" 339 if whence > 2 or whence < 0: 340 raise ValueError("Invalid value for 'whence': " + str(whence)) 341 if "-" in getattr(self,"mode",""): 342 raise NotSeekableError("File is not seekable.") 343 # Ensure that there's nothing left in the write buffer 344 if self._wbuffer: 345 self.flush() 346 # Adjust for any data left in the read buffer 347 if whence == 1 and self._rbuffer: 348 offset = offset - len(self._rbuffer) 349 self._rbuffer = None 350 # Adjust for any discrepancy in actual vs apparent seek position 351 if whence == 1: 352 if self._sbuffer: 353 offset = offset + len(self._sbuffer) 354 if self._soffset: 355 offset = offset + self._soffset 356 self._sbuffer = None 357 self._soffset = 0 358 # Shortcut the special case of staying put. 359 # As per posix, this has already cases the buffers to be flushed. 360 if offset == 0 and whence == 1: 361 return 362 # Catch any failed attempts to read while simulating seek 363 try: 364 # Try to do a whence-wise seek if it is implemented. 365 sbuf = None 366 try: 367 sbuf = self._seek(offset,whence) 368 except NotImplementedError: 369 # Try to simulate using an absolute seek. 370 try: 371 if whence == 1: 372 offset = self._tell() + offset 373 elif whence == 2: 374 if hasattr(self,"size"): 375 offset = self.size + offset 376 else: 377 self._do_read_rest() 378 offset = self.tell() + offset 379 else: 380 # absolute seek already failed, don't try again 381 raise NotImplementedError 382 sbuf = self._seek(offset,0) 383 except NotImplementedError: 384 # Simulate by reseting to start 385 self._seek(0,0) 386 self._soffset = offset 387 finally: 388 self._sbuffer = sbuf 389 except NotReadableError: 390 raise NotSeekableError("File not readable, can't simulate seek") 391 392 def tell(self): 393 """Determine current position of internal file pointer.""" 394 # Need to adjust for unread/unwritten data in buffers 395 pos = self._tell() 396 if self._rbuffer: 397 pos = pos - len(self._rbuffer) 398 if self._wbuffer: 399 pos = pos + len(self._wbuffer) 400 if self._sbuffer: 401 pos = pos + len(self._sbuffer) 402 if self._soffset: 403 pos = pos + self._soffset 404 return pos 405 406 def read(self,size=-1): 407 """Read at most 'size' bytes from the file. 408 409 Bytes are returned as a string. If 'size' is negative, zero or 410 missing, the remainder of the file is read. If EOF is encountered 411 immediately, the empty string is returned. 412 """ 413 if self.closed: 414 raise IOError("File has been closed") 415 self._assert_mode("r-") 416 return self._do_read(size) 417 418 def _do_read(self,size): 419 """Private method to read from the file. 420 421 This method behaves the same as self.read(), but skips some 422 permission and sanity checks. It is intended for use in simulating 423 seek(), where we may want to read (and discard) information from 424 a file not opened in read mode. 425 426 Note that this may still fail if the file object actually can't 427 be read from - it just won't check whether the mode string gives 428 permission. 429 """ 430 # If we were previously writing, ensure position is correct 431 if self._wbuffer is not None: 432 self.seek(0,1) 433 # Discard any data that should have been seeked over 434 if self._sbuffer: 435 s = len(self._sbuffer) 436 self._sbuffer = None 437 self.read(s) 438 elif self._soffset: 439 s = self._soffset 440 self._soffset = 0 441 while s > self._bufsize: 442 self._do_read(self._bufsize) 443 s -= self._bufsize 444 self._do_read(s) 445 # Should the entire file be read? 446 if size < 0: 447 if self._rbuffer: 448 data = [self._rbuffer] 449 else: 450 data = [] 451 self._rbuffer = b("") 452 newData = self._read() 453 while newData is not None: 454 data.append(newData) 455 newData = self._read() 456 output = b("").join(data) 457 # Otherwise, we need to return a specific amount of data 458 else: 459 if self._rbuffer: 460 newData = self._rbuffer 461 data = [newData] 462 else: 463 newData = b("") 464 data = [] 465 sizeSoFar = len(newData) 466 while sizeSoFar < size: 467 newData = self._read(size-sizeSoFar) 468 if not newData: 469 break 470 data.append(newData) 471 sizeSoFar += len(newData) 472 data = b("").join(data) 473 if sizeSoFar > size: 474 # read too many bytes, store in the buffer 475 self._rbuffer = data[size:] 476 data = data[:size] 477 else: 478 self._rbuffer = b("") 479 output = data 480 return output 481 482 def _do_read_rest(self): 483 """Private method to read the file through to EOF.""" 484 data = self._do_read(self._bufsize) 485 while data != b(""): 486 data = self._do_read(self._bufsize) 487 488 def readline(self,size=-1): 489 """Read a line from the file, or at most <size> bytes.""" 490 bits = [] 491 indx = -1 492 sizeSoFar = 0 493 while indx == -1: 494 nextBit = self.read(self._bufsize) 495 bits.append(nextBit) 496 sizeSoFar += len(nextBit) 497 if not nextBit: 498 break 499 if size > 0 and sizeSoFar >= size: 500 break 501 indx = nextBit.find(b("\n")) 502 # If not found, return whole string up to <size> length 503 # Any leftovers are pushed onto front of buffer 504 if indx == -1: 505 data = b("").join(bits) 506 if size > 0 and sizeSoFar > size: 507 extra = data[size:] 508 data = data[:size] 509 self._rbuffer = extra + self._rbuffer 510 return data 511 # If found, push leftovers onto front of buffer 512 # Add one to preserve the newline in the return value 513 indx += 1 514 extra = bits[-1][indx:] 515 bits[-1] = bits[-1][:indx] 516 self._rbuffer = extra + self._rbuffer 517 return b("").join(bits) 518 519 def readlines(self,sizehint=-1): 520 """Return a list of all lines in the file.""" 521 return [ln for ln in self] 522 523 def xreadlines(self): 524 """Iterator over lines in the file - equivalent to iter(self).""" 525 return iter(self) 526 527 def write(self,string): 528 """Write the given string to the file.""" 529 if self.closed: 530 raise IOError("File has been closed") 531 self._assert_mode("w-") 532 # If we were previously reading, ensure position is correct 533 if self._rbuffer is not None: 534 self.seek(0, 1) 535 # If we're actually behind the apparent position, we must also 536 # write the data in the gap. 537 if self._sbuffer: 538 string = self._sbuffer + string 539 self._sbuffer = None 540 elif self._soffset: 541 s = self._soffset 542 self._soffset = 0 543 try: 544 string = self._do_read(s) + string 545 except NotReadableError: 546 raise NotSeekableError("File not readable, could not complete simulation of seek") 547 self.seek(0, 0) 548 if self._wbuffer: 549 string = self._wbuffer + string 550 leftover = self._write(string) 551 if leftover is None or isinstance(leftover, int): 552 self._wbuffer = b("") 553 return len(string) - (leftover or 0) 554 else: 555 self._wbuffer = leftover 556 return len(string) - len(leftover) 557 558 def writelines(self,seq): 559 """Write a sequence of lines to the file.""" 560 for ln in seq: 561 self.write(ln) 562 563 564class FileWrapper(FileLikeBase): 565 """Base class for objects that wrap a file-like object. 566 567 This class provides basic functionality for implementing file-like 568 objects that wrap another file-like object to alter its functionality 569 in some way. It takes care of house-keeping duties such as flushing 570 and closing the wrapped file. 571 572 Access to the wrapped file is given by the attribute wrapped_file. 573 By convention, the subclass's constructor should accept this as its 574 first argument and pass it to its superclass's constructor in the 575 same position. 576 577 This class provides a basic implementation of _read() and _write() 578 which just calls read() and write() on the wrapped object. Subclasses 579 will probably want to override these. 580 """ 581 582 _append_requires_overwrite = False 583 584 def __init__(self,wrapped_file,mode=None): 585 """FileWrapper constructor. 586 587 'wrapped_file' must be a file-like object, which is to be wrapped 588 in another file-like object to provide additional functionality. 589 590 If given, 'mode' must be the access mode string under which 591 the wrapped file is to be accessed. If not given or None, it 592 is looked up on the wrapped file if possible. Otherwise, it 593 is not set on the object. 594 """ 595 # This is used for working around flush/close inefficiencies 596 self.__closing = False 597 super(FileWrapper,self).__init__() 598 self.wrapped_file = wrapped_file 599 if mode is None: 600 self.mode = getattr(wrapped_file,"mode","r+") 601 else: 602 self.mode = mode 603 self._validate_mode() 604 # Copy useful attributes of wrapped_file 605 if hasattr(wrapped_file,"name"): 606 self.name = wrapped_file.name 607 # Respect append-mode setting 608 if "a" in self.mode: 609 if self._check_mode("r"): 610 self.wrapped_file.seek(0) 611 self.seek(0,2) 612 613 def _validate_mode(self): 614 """Check that various file-mode conditions are satisfied.""" 615 # If append mode requires overwriting the underlying file, 616 # if must not be opened in append mode. 617 if self._append_requires_overwrite: 618 if self._check_mode("w"): 619 if "a" in getattr(self.wrapped_file,"mode",""): 620 raise ValueError("Underlying file can't be in append mode") 621 622 def __del__(self): 623 # Errors in subclass constructors could result in this being called 624 # without invoking FileWrapper.__init__. Establish some simple 625 # invariants to prevent errors in this case. 626 if not hasattr(self,"wrapped_file"): 627 self.wrapped_file = None 628 if not hasattr(self,"_FileWrapper__closing"): 629 self.__closing = False 630 # Close the wrapper and the underlying file independently, so the 631 # latter is still closed on cleanup even if the former errors out. 632 try: 633 if FileWrapper is not None: 634 super(FileWrapper,self).close() 635 finally: 636 if hasattr(getattr(self,"wrapped_file",None),"close"): 637 self.wrapped_file.close() 638 639 def close(self): 640 """Close the object for reading/writing.""" 641 # The superclass implementation of this will call flush(), 642 # which calls flush() on our wrapped object. But we then call 643 # close() on it, which will call its flush() again! To avoid 644 # this inefficiency, our flush() will not flush the wrapped 645 # file when we're closing. 646 if not self.closed: 647 self.__closing = True 648 super(FileWrapper,self).close() 649 if hasattr(self.wrapped_file,"close"): 650 self.wrapped_file.close() 651 652 def flush(self): 653 """Flush the write buffers of the file.""" 654 super(FileWrapper,self).flush() 655 if not self.__closing and hasattr(self.wrapped_file,"flush"): 656 self.wrapped_file.flush() 657 658 def _read(self,sizehint=-1): 659 data = self.wrapped_file.read(sizehint) 660 if data == b(""): 661 return None 662 return data 663 664 def _write(self,string,flushing=False): 665 self.wrapped_file.write(string) 666 667 def _seek(self,offset,whence): 668 self.wrapped_file.seek(offset,whence) 669 670 def _tell(self): 671 return self.wrapped_file.tell() 672 673 def _truncate(self,size): 674 return self.wrapped_file.truncate(size) 675 676 677class StringIO(FileWrapper): 678 """StringIO wrapper that more closely matches standard file behavior. 679 680 This is a simple compatibility wrapper around the native StringIO class 681 which fixes some corner-cases of its behavior. Specifically: 682 683 * adding __enter__ and __exit__ methods 684 * having truncate(size) zero-fill when growing the file 685 686 """ 687 688 def __init__(self,data=None,mode=None): 689 wrapped_file = _StringIO() 690 if data is not None: 691 wrapped_file.write(data) 692 wrapped_file.seek(0) 693 super(StringIO,self).__init__(wrapped_file,mode) 694 695 def getvalue(self): 696 return self.wrapped_file.getvalue() 697 698 def _truncate(self,size): 699 pos = self.wrapped_file.tell() 700 self.wrapped_file.truncate(size) 701 curlen = len(self.wrapped_file.getvalue()) 702 if size > curlen: 703 self.wrapped_file.seek(curlen) 704 try: 705 self.wrapped_file.write(b("\x00")*(size-curlen)) 706 finally: 707 self.wrapped_file.seek(pos) 708 709 710class SpooledTemporaryFile(FileWrapper): 711 """SpooledTemporaryFile wrapper with some compatibility fixes. 712 713 This is a simple compatibility wrapper around the native class of the 714 same name, fixing some corner-cases of its behavior. Specifically: 715 716 * have truncate() accept a size argument 717 * roll to disk is seeking past the max in-memory size 718 * use improved StringIO class from this module 719 720 """ 721 722 def __init__(self,max_size=0,mode="w+b",bufsize=-1,*args,**kwds): 723 try: 724 stf_args = (max_size,mode,bufsize) + args 725 wrapped_file = _tempfile.SpooledTemporaryFile(*stf_args,**kwds) 726 wrapped_file._file = StringIO() 727 #wrapped_file._file = six.BytesIO() 728 self.__is_spooled = True 729 except AttributeError: 730 ntf_args = (mode,bufsize) + args 731 wrapped_file = _tempfile.NamedTemporaryFile(*ntf_args,**kwds) 732 self.__is_spooled = False 733 super(SpooledTemporaryFile,self).__init__(wrapped_file) 734 735 def _seek(self,offset,whence): 736 if self.__is_spooled: 737 max_size = self.wrapped_file._max_size 738 if whence == fs.SEEK_SET: 739 if offset > max_size: 740 self.wrapped_file.rollover() 741 elif whence == fs.SEEK_CUR: 742 if offset + self.wrapped_file.tell() > max_size: 743 self.wrapped_file.rollover() 744 else: 745 if offset > 0: 746 self.wrapped_file.rollover() 747 self.wrapped_file.seek(offset,whence) 748 749 def _truncate(self,size): 750 if self.__is_spooled: 751 self.wrapped_file._file.truncate(size) 752 else: 753 self.wrapped_file.truncate(size) 754 755 def fileno(self): 756 return self.wrapped_file.fileno() 757 758 759class LimitBytesFile(FileWrapper): 760 """Filelike wrapper to limit bytes read from a stream.""" 761 762 def __init__(self,size,fileobj,*args,**kwds): 763 self.size = size 764 self.__remaining = size 765 super(LimitBytesFile,self).__init__(fileobj,*args,**kwds) 766 767 def _read(self,sizehint=-1): 768 if self.__remaining <= 0: 769 return None 770 if sizehint is None or sizehint < 0 or sizehint > self.__remaining: 771 sizehint = self.__remaining 772 data = super(LimitBytesFile,self)._read(sizehint) 773 if data is not None: 774 self.__remaining -= len(data) 775 return data 776 777 778