home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pytho152.zip / emx / lib / python1.5 / multifile.py < prev    next >
Text File  |  2000-08-10  |  4KB  |  161 lines

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