[docs]classFile(FileProxyMixin):DEFAULT_CHUNK_SIZE=64*2**10def__init__(self,file,name=None):self.file=fileifnameisNone:name=getattr(file,"name",None)self.name=nameifhasattr(file,"mode"):self.mode=file.modedef__str__(self):returnself.nameor""def__repr__(self):return"<%s: %s>"%(self.__class__.__name__,selfor"None")def__bool__(self):returnbool(self.name)def__len__(self):returnself.size@cached_propertydefsize(self):ifhasattr(self.file,"size"):returnself.file.sizeifhasattr(self.file,"name"):try:returnos.path.getsize(self.file.name)except(OSError,TypeError):passifhasattr(self.file,"tell")andhasattr(self.file,"seek"):pos=self.file.tell()self.file.seek(0,os.SEEK_END)size=self.file.tell()self.file.seek(pos)returnsizeraiseAttributeError("Unable to determine the file's size.")
[docs]defchunks(self,chunk_size=None):""" Read the file and yield chunks of ``chunk_size`` bytes (defaults to ``File.DEFAULT_CHUNK_SIZE``). """chunk_size=chunk_sizeorself.DEFAULT_CHUNK_SIZEtry:self.seek(0)except(AttributeError,UnsupportedOperation):passwhileTrue:data=self.read(chunk_size)ifnotdata:breakyielddata
[docs]defmultiple_chunks(self,chunk_size=None):""" Return ``True`` if you can expect multiple chunks. NB: If a particular file representation is in memory, subclasses should always return ``False`` -- there's no good reason to read from memory in chunks. """returnself.size>(chunk_sizeorself.DEFAULT_CHUNK_SIZE)
[docs]def__iter__(self):# Iterate over this file-like object by newlinesbuffer_=Noneforchunkinself.chunks():forlineinchunk.splitlines(True):ifbuffer_:ifendswith_cr(buffer_)andnotequals_lf(line):# Line split after a \r newline; yield buffer_.yieldbuffer_# Continue with line.else:# Line either split without a newline (line# continues after buffer_) or with \r\n# newline (line == b'\n').line=buffer_+line# buffer_ handled, clear it.buffer_=None# If this is the end of a \n or \r\n line, yield.ifendswith_lf(line):yieldlineelse:buffer_=lineifbuffer_isnotNone:yieldbuffer_
[docs]defopen(self,mode=None,*args,**kwargs):ifnotself.closed:self.seek(0)elifself.nameandos.path.exists(self.name):self.file=open(self.name,modeorself.mode,*args,**kwargs)else:raiseValueError("The file cannot be reopened.")returnself
[docs]classContentFile(File):""" A File-like object that takes just raw content, rather than an actual file. """def__init__(self,content,name=None):stream_class=StringIOifisinstance(content,str)elseBytesIOsuper().__init__(stream_class(content),name=name)self.size=len(content)def__str__(self):return"Raw content"def__bool__(self):returnTruedefopen(self,mode=None):self.seek(0)returnselfdefclose(self):passdefwrite(self,data):self.__dict__.pop("size",None)# Clear the computed size.returnself.file.write(data)
defendswith_cr(line):"""Return True if line (a text or bytestring) ends with '\r'."""returnline.endswith("\r"ifisinstance(line,str)elseb"\r")defendswith_lf(line):"""Return True if line (a text or bytestring) ends with '\n'."""returnline.endswith("\n"ifisinstance(line,str)elseb"\n")defequals_lf(line):"""Return True if line (a text or bytestring) equals '\n'."""returnline==("\n"ifisinstance(line,str)elseb"\n")