Home | History | Annotate | Download | only in python2.7
      1 """A readline()-style interface to the parts of a multipart message.
      2 
      3 The MultiFile class makes each part of a multipart message "feel" like
      4 an ordinary file, as long as you use fp.readline().  Allows recursive
      5 use, for nested multipart messages.  Probably best used together
      6 with module mimetools.
      7 
      8 Suggested use:
      9 
     10 real_fp = open(...)
     11 fp = MultiFile(real_fp)
     12 
     13 "read some lines from fp"
     14 fp.push(separator)
     15 while 1:
     16         "read lines from fp until it returns an empty string" (A)
     17         if not fp.next(): break
     18 fp.pop()
     19 "read remaining lines from fp until it returns an empty string"
     20 
     21 The latter sequence may be used recursively at (A).
     22 It is also allowed to use multiple push()...pop() sequences.
     23 
     24 If seekable is given as 0, the class code will not do the bookkeeping
     25 it normally attempts in order to make seeks relative to the beginning of the
     26 current file part.  This may be useful when using MultiFile with a non-
     27 seekable stream object.
     28 """
     29 from warnings import warn
     30 warn("the multifile module has been deprecated since Python 2.5",
     31         DeprecationWarning, stacklevel=2)
     32 del warn
     33 
     34 __all__ = ["MultiFile","Error"]
     35 
     36 class Error(Exception):
     37     pass
     38 
     39 class MultiFile:
     40 
     41     seekable = 0
     42 
     43     def __init__(self, fp, seekable=1):
     44         self.fp = fp
     45         self.stack = []
     46         self.level = 0
     47         self.last = 0
     48         if seekable:
     49             self.seekable = 1
     50             self.start = self.fp.tell()
     51             self.posstack = []
     52 
     53     def tell(self):
     54         if self.level > 0:
     55             return self.lastpos
     56         return self.fp.tell() - self.start
     57 
     58     def seek(self, pos, whence=0):
     59         here = self.tell()
     60         if whence:
     61             if whence == 1:
     62                 pos = pos + here
     63             elif whence == 2:
     64                 if self.level > 0:
     65                     pos = pos + self.lastpos
     66                 else:
     67                     raise Error, "can't use whence=2 yet"
     68         if not 0 <= pos <= here or \
     69                         self.level > 0 and pos > self.lastpos:
     70             raise Error, 'bad MultiFile.seek() call'
     71         self.fp.seek(pos + self.start)
     72         self.level = 0
     73         self.last = 0
     74 
     75     def readline(self):
     76         if self.level > 0:
     77             return ''
     78         line = self.fp.readline()
     79         # Real EOF?
     80         if not line:
     81             self.level = len(self.stack)
     82             self.last = (self.level > 0)
     83             if self.last:
     84                 raise Error, 'sudden EOF in MultiFile.readline()'
     85             return ''
     86         assert self.level == 0
     87         # Fast check to see if this is just data
     88         if self.is_data(line):
     89             return line
     90         else:
     91             # Ignore trailing whitespace on marker lines
     92             marker = line.rstrip()
     93         # No?  OK, try to match a boundary.
     94         # Return the line (unstripped) if we don't.
     95         for i, sep in enumerate(reversed(self.stack)):
     96             if marker == self.section_divider(sep):
     97                 self.last = 0
     98                 break
     99             elif marker == self.end_marker(sep):
    100                 self.last = 1
    101                 break
    102         else:
    103             return line
    104         # We only get here if we see a section divider or EOM line
    105         if self.seekable:
    106             self.lastpos = self.tell() - len(line)
    107         self.level = i+1
    108         if self.level > 1:
    109             raise Error,'Missing endmarker in MultiFile.readline()'
    110         return ''
    111 
    112     def readlines(self):
    113         list = []
    114         while 1:
    115             line = self.readline()
    116             if not line: break
    117             list.append(line)
    118         return list
    119 
    120     def read(self): # Note: no size argument -- read until EOF only!
    121         return ''.join(self.readlines())
    122 
    123     def next(self):
    124         while self.readline(): pass
    125         if self.level > 1 or self.last:
    126             return 0
    127         self.level = 0
    128         self.last = 0
    129         if self.seekable:
    130             self.start = self.fp.tell()
    131         return 1
    132 
    133     def push(self, sep):
    134         if self.level > 0:
    135             raise Error, 'bad MultiFile.push() call'
    136         self.stack.append(sep)
    137         if self.seekable:
    138             self.posstack.append(self.start)
    139             self.start = self.fp.tell()
    140 
    141     def pop(self):
    142         if self.stack == []:
    143             raise Error, 'bad MultiFile.pop() call'
    144         if self.level <= 1:
    145             self.last = 0
    146         else:
    147             abslastpos = self.lastpos + self.start
    148         self.level = max(0, self.level - 1)
    149         self.stack.pop()
    150         if self.seekable:
    151             self.start = self.posstack.pop()
    152             if self.level > 0:
    153                 self.lastpos = abslastpos - self.start
    154 
    155     def is_data(self, line):
    156         return line[:2] != '--'
    157 
    158     def section_divider(self, str):
    159         return "--" + str
    160 
    161     def end_marker(self, str):
    162         return "--" + str + "--"
    163