Vega strike Python Modules doc  0.5.1
Documentation of the " Modules " folder of Vega strike
 All Data Structures Namespaces Files Functions Variables
multifile.py
Go to the documentation of this file.
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 
30 __all__ = ["MultiFile","Error"]
31 
32 class Error(Exception):
33  pass
34 
35 class MultiFile:
36 
37  seekable = 0
38 
39  def __init__(self, fp, seekable=1):
40  self.fp = fp
41  self.stack = [] # Grows down
42  self.level = 0
43  self.last = 0
44  self.readahead = ""
45  if seekable:
46  self.seekable = 1
47  self.start = self.fp.tell()
48  self.posstack = [] # Grows down
49 
50  def tell(self):
51  if self.level > 0:
52  return self.lastpos
53  return self.fp.tell() - len(self.readahead) - self.start
54 
55  def seek(self, pos, whence=0):
56  here = self.tell()
57  if whence:
58  if whence == 1:
59  pos = pos + here
60  elif whence == 2:
61  if self.level > 0:
62  pos = pos + self.lastpos
63  else:
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)
69  self.level = 0
70  self.last = 0
71  self.readahead = ""
72 
73  def readline(self):
74  if not self.readahead:
75  self.readahead = self._readline()
76  line = self.readahead
77  if line:
78  self.readahead = self._readline()
79  if not self.readahead:
80  if line[-2:] == "\r\n":
81  line = line[:-2]
82  elif line[-1:] == "\n":
83  line = line[:-1]
84  return line
85 
86  def _readline(self):
87  if self.level > 0:
88  return ''
89  line = self.fp.readline()
90  # Real EOF?
91  if not line:
92  self.level = len(self.stack)
93  self.last = (self.level > 0)
94  if self.last:
95  raise Error, 'sudden EOF in MultiFile.readline()'
96  return ''
97  assert self.level == 0
98  # Fast check to see if this is just data
99  if self.is_data(line):
100  return line
101  else:
102  # Ignore trailing whitespace on marker lines
103  marker = line.rstrip()
104  # No? OK, try to match a boundary.
105  # Return the line (unstripped) if we don't.
106  for i in range(len(self.stack)):
107  sep = self.stack[i]
108  if marker == self.section_divider(sep):
109  self.last = 0
110  break
111  elif marker == self.end_marker(sep):
112  self.last = 1
113  break
114  else:
115  return line
116  # We only get here if we see a section divider or EOM line
117  if self.seekable:
118  self.lastpos = self.tell() - len(line)
119  self.level = i+1
120  if self.level > 1:
121  raise Error,'Missing endmarker in MultiFile.readline()'
122  return ''
123 
124  def readlines(self):
125  list = []
126  while 1:
127  line = self.readline()
128  if not line: break
129  list.append(line)
130  return list
131 
132  def read(self): # Note: no size argument -- read until EOF only!
133  return ''.join(self.readlines())
134 
135  def next(self):
136  while self.readline(): pass
137  if self.level > 1 or self.last:
138  return 0
139  self.level = 0
140  self.last = 0
141  if self.seekable:
142  self.start = self.fp.tell()
143  return 1
144 
145  def push(self, sep):
146  if self.level > 0:
147  raise Error, 'bad MultiFile.push() call'
148  self.stack.insert(0, sep)
149  if self.seekable:
150  self.posstack.insert(0, self.start)
151  self.start = self.fp.tell()
152 
153  def pop(self):
154  if self.stack == []:
155  raise Error, 'bad MultiFile.pop() call'
156  if self.level <= 1:
157  self.last = 0
158  else:
159  abslastpos = self.lastpos + self.start
160  self.level = max(0, self.level - 1)
161  del self.stack[0]
162  if self.seekable:
163  self.start = self.posstack[0]
164  del self.posstack[0]
165  if self.level > 0:
166  self.lastpos = abslastpos - self.start
167 
168  def is_data(self, line):
169  return line[:2] != '--'
170 
171  def section_divider(self, str):
172  return "--" + str
173 
174  def end_marker(self, str):
175  return "--" + str + "--"