1 """A readline()-style interface to the parts of a multipart message.
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
11 fp = MultiFile(real_fp)
13 "read some lines from fp"
16 "read lines from fp until it returns an empty string" (A)
17 if not fp.next(): break
19 "read remaining lines from fp until it returns an empty string"
21 The latter sequence may be used recursively at (A).
22 It is also allowed to use multiple push()...pop() sequences.
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.
30 __all__
= ["MultiFile","Error"]
32 class Error(Exception):
39 def __init__(self
, fp
, seekable
=1):
41 self
.stack
= [] # Grows down
47 self
.start
= self
.fp
.tell()
48 self
.posstack
= [] # Grows down
53 return self
.fp
.tell() - len(self
.readahead
) - self
.start
55 def seek(self
, pos
, whence
=0):
62 pos
= pos
+ self
.lastpos
64 raise Error
, "can't use whence=2 yet"
65 if not 0 <= pos
<= here
or \
66 self
.level
> 0 and pos
> self
.lastpos
:
67 raise Error
, 'bad MultiFile.seek() call'
68 self
.fp
.seek(pos
+ self
.start
)
74 if not self
.readahead
:
75 self
.readahead
= self
._readline
()
78 self
.readahead
= self
._readline
()
79 if not self
.readahead
and line
[-1:] == "\n":
86 line
= self
.fp
.readline()
89 self
.level
= len(self
.stack
)
90 self
.last
= (self
.level
> 0)
92 raise Error
, 'sudden EOF in MultiFile.readline()'
94 assert self
.level
== 0
95 # Fast check to see if this is just data
96 if self
.is_data(line
):
99 # Ignore trailing whitespace on marker lines
100 marker
= line
.rstrip()
101 # No? OK, try to match a boundary.
102 # Return the line (unstripped) if we don't.
103 for i
in range(len(self
.stack
)):
105 if marker
== self
.section_divider(sep
):
108 elif marker
== self
.end_marker(sep
):
113 # We only get here if we see a section divider or EOM line
115 self
.lastpos
= self
.tell() - len(line
)
118 raise Error
,'Missing endmarker in MultiFile.readline()'
124 line
= self
.readline()
129 def read(self
): # Note: no size argument -- read until EOF only!
130 return ''.join(self
.readlines())
133 while self
.readline(): pass
134 if self
.level
> 1 or self
.last
:
139 self
.start
= self
.fp
.tell()
144 raise Error
, 'bad MultiFile.push() call'
145 self
.stack
.insert(0, sep
)
147 self
.posstack
.insert(0, self
.start
)
148 self
.start
= self
.fp
.tell()
152 raise Error
, 'bad MultiFile.pop() call'
156 abslastpos
= self
.lastpos
+ self
.start
157 self
.level
= max(0, self
.level
- 1)
160 self
.start
= self
.posstack
[0]
163 self
.lastpos
= abslastpos
- self
.start
165 def is_data(self
, line
):
166 return line
[:2] != '--'
168 def section_divider(self
, str):
171 def end_marker(self
, str):
172 return "--" + str + "--"