PageRenderTime 256ms CodeModel.GetById 141ms app.highlight 12ms RepoModel.GetById 98ms app.codeStats 0ms

/Lib/multifile.py

http://unladen-swallow.googlecode.com/
Python | 162 lines | 159 code | 0 blank | 3 comment | 6 complexity | 896d7ff83a78284f65b771dd20051385 MD5 | raw file
  1"""A readline()-style interface to the parts of a multipart message.
  2
  3The MultiFile class makes each part of a multipart message "feel" like
  4an ordinary file, as long as you use fp.readline().  Allows recursive
  5use, for nested multipart messages.  Probably best used together
  6with module mimetools.
  7
  8Suggested use:
  9
 10real_fp = open(...)
 11fp = MultiFile(real_fp)
 12
 13"read some lines from fp"
 14fp.push(separator)
 15while 1:
 16        "read lines from fp until it returns an empty string" (A)
 17        if not fp.next(): break
 18fp.pop()
 19"read remaining lines from fp until it returns an empty string"
 20
 21The latter sequence may be used recursively at (A).
 22It is also allowed to use multiple push()...pop() sequences.
 23
 24If seekable is given as 0, the class code will not do the bookkeeping
 25it normally attempts in order to make seeks relative to the beginning of the
 26current file part.  This may be useful when using MultiFile with a non-
 27seekable stream object.
 28"""
 29from warnings import warn
 30warn("the multifile module has been deprecated since Python 2.5",
 31        DeprecationWarning, stacklevel=2)
 32del warn
 33
 34__all__ = ["MultiFile","Error"]
 35
 36class Error(Exception):
 37    pass
 38
 39class 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 + "--"