0001 """A readline()-style interface to the parts of a multipart message. 0002 0003 The MultiFile class makes each part of a multipart message "feel" like 0004 an ordinary file, as long as you use fp.readline(). Allows recursive 0005 use, for nested multipart messages. Probably best used together 0006 with module mimetools. 0007 0008 Suggested use: 0009 0010 real_fp = open(...) 0011 fp = MultiFile(real_fp) 0012 0013 "read some lines from fp" 0014 fp.push(separator) 0015 while 1: 0016 "read lines from fp until it returns an empty string" (A) 0017 if not fp.next(): break 0018 fp.pop() 0019 "read remaining lines from fp until it returns an empty string" 0020 0021 The latter sequence may be used recursively at (A). 0022 It is also allowed to use multiple push()...pop() sequences. 0023 0024 If seekable is given as 0, the class code will not do the bookkeeping 0025 it normally attempts in order to make seeks relative to the beginning of the 0026 current file part. This may be useful when using MultiFile with a non- 0027 seekable stream object. 0028 """ 0029 0030 __all__ = ["MultiFile","Error"] 0031 0032 class Error(Exception): 0033 pass 0034 0035 class MultiFile: 0036 0037 seekable = 0 0038 0039 def __init__(self, fp, seekable=1): 0040 self.fp = fp 0041 self.stack = [] 0042 self.level = 0 0043 self.last = 0 0044 if seekable: 0045 self.seekable = 1 0046 self.start = self.fp.tell() 0047 self.posstack = [] 0048 0049 def tell(self): 0050 if self.level > 0: 0051 return self.lastpos 0052 return self.fp.tell() - self.start 0053 0054 def seek(self, pos, whence=0): 0055 here = self.tell() 0056 if whence: 0057 if whence == 1: 0058 pos = pos + here 0059 elif whence == 2: 0060 if self.level > 0: 0061 pos = pos + self.lastpos 0062 else: 0063 raise Error, "can't use whence=2 yet" 0064 if not 0 <= pos <= here or \ 0065 self.level > 0 and pos > self.lastpos: 0066 raise Error, 'bad MultiFile.seek() call' 0067 self.fp.seek(pos + self.start) 0068 self.level = 0 0069 self.last = 0 0070 0071 def readline(self): 0072 if self.level > 0: 0073 return '' 0074 line = self.fp.readline() 0075 # Real EOF? 0076 if not line: 0077 self.level = len(self.stack) 0078 self.last = (self.level > 0) 0079 if self.last: 0080 raise Error, 'sudden EOF in MultiFile.readline()' 0081 return '' 0082 assert self.level == 0 0083 # Fast check to see if this is just data 0084 if self.is_data(line): 0085 return line 0086 else: 0087 # Ignore trailing whitespace on marker lines 0088 marker = line.rstrip() 0089 # No? OK, try to match a boundary. 0090 # Return the line (unstripped) if we don't. 0091 for i, sep in enumerate(reversed(self.stack)): 0092 if marker == self.section_divider(sep): 0093 self.last = 0 0094 break 0095 elif marker == self.end_marker(sep): 0096 self.last = 1 0097 break 0098 else: 0099 return line 0100 # We only get here if we see a section divider or EOM line 0101 if self.seekable: 0102 self.lastpos = self.tell() - len(line) 0103 self.level = i+1 0104 if self.level > 1: 0105 raise Error,'Missing endmarker in MultiFile.readline()' 0106 return '' 0107 0108 def readlines(self): 0109 list = [] 0110 while 1: 0111 line = self.readline() 0112 if not line: break 0113 list.append(line) 0114 return list 0115 0116 def read(self): # Note: no size argument -- read until EOF only! 0117 return ''.join(self.readlines()) 0118 0119 def next(self): 0120 while self.readline(): pass 0121 if self.level > 1 or self.last: 0122 return 0 0123 self.level = 0 0124 self.last = 0 0125 if self.seekable: 0126 self.start = self.fp.tell() 0127 return 1 0128 0129 def push(self, sep): 0130 if self.level > 0: 0131 raise Error, 'bad MultiFile.push() call' 0132 self.stack.append(sep) 0133 if self.seekable: 0134 self.posstack.append(self.start) 0135 self.start = self.fp.tell() 0136 0137 def pop(self): 0138 if self.stack == []: 0139 raise Error, 'bad MultiFile.pop() call' 0140 if self.level <= 1: 0141 self.last = 0 0142 else: 0143 abslastpos = self.lastpos + self.start 0144 self.level = max(0, self.level - 1) 0145 self.stack.pop() 0146 if self.seekable: 0147 self.start = self.posstack.pop() 0148 if self.level > 0: 0149 self.lastpos = abslastpos - self.start 0150 0151 def is_data(self, line): 0152 return line[:2] != '--' 0153 0154 def section_divider(self, str): 0155 return "--" + str 0156 0157 def end_marker(self, str): 0158 return "--" + str + "--" 0159
Generated by PyXR 0.9.4