# # This module supplies an interface to read from files with # RFC822 style 'continued lines', with comments and blank lines # excised. import re class StartingContinuedLine(Exception): pass # This skips lines that are entirely blank up to a comment start # character. skipre = re.compile('^\s*(#|$)') class RFC822File: def __init__(self, fp): self._f = fp self.closed = 0 # Lineno is the line number of the start of the possibly # continued line. curln is the current true line number. # accum is the accumulated line to date. self._lineno = 0 self._curln = 0 self._accum = '' def close(self): self._f.close() self._f = None self.closed = 1 def _getline(self): while 1: l = self._f.readline() # Bail on EOF: if not l: break # We must count even blanks and comments. self._curln += 1 # Skip blanks and comments. if skipre.match(l): continue # Is this a continued line, or not? if self._accum and l[0] in ' \t': self._accum = self._accum.rstrip() + \ ' ' + l.lstrip() elif l[0] in ' \t': raise StartingContinuedLine, "The first real line, at line number %d, is a continuation." % (self._curln,) else: res = None # If accum is non-null, we have just finished # off another (possibly continued) line and # want to return it. Otherwise, this is the # first real line in the file. if self._accum: res = (self._lineno, self._accum) self._lineno = self._curln self._accum = l if res: return res # We have seen an EOF. Return anything accumulated, # and set it so that we will continue to return EOF. res = (self._lineno, self._accum) self._accum = '' return res def readcontline(self): return self._getline()[1] def readcontline_ex(self): res = self._getline() if res[1] == '': return '' else: return res def fromfile(fp): return RFC822File(fp) # Note that only 'r' based modes make any sense: r, rb, U, Ur. # Generally, don't do that. def openfile(filename, mode = 'r'): return RFC822File(open(filename, mode))