libmultifile.tex 6.18 KB
Newer Older
Fred Drake's avatar
Fred Drake committed
1
\section{\module{multifile} ---
2
         Support for files containing distinct parts}
3

4
\declaremodule{standard}{multifile}
Fred Drake's avatar
Fred Drake committed
5
\modulesynopsis{Support for reading files which contain distinct
6 7
                parts, such as some MIME data.}
\sectionauthor{Eric S. Raymond}{esr@snark.thyrsus.com}
8

9

Fred Drake's avatar
Fred Drake committed
10 11 12
The \class{MultiFile} object enables you to treat sections of a text
file as file-like input objects, with \code{''} being returned by
\method{readline()} when a given delimiter pattern is encountered.  The
13 14 15 16
defaults of this class are designed to make it useful for parsing
MIME multipart messages, but by subclassing it and overriding methods 
it can be easily adapted for more general use.

Fred Drake's avatar
Fred Drake committed
17
\begin{classdesc}{MultiFile}{fp\optional{, seekable}}
18
Create a multi-file.  You must instantiate this class with an input
Fred Drake's avatar
Fred Drake committed
19 20 21 22 23 24 25 26 27 28
object argument for the \class{MultiFile} instance to get lines from,
such as as a file object returned by \function{open()}.

\class{MultiFile} only ever looks at the input object's
\method{readline()}, \method{seek()} and \method{tell()} methods, and
the latter two are only needed if you want random access to the
individual MIME parts. To use \class{MultiFile} on a non-seekable
stream object, set the optional \var{seekable} argument to false; this
will prevent using the input object's \method{seek()} and
\method{tell()} methods.
29 30
\end{classdesc}

Fred Drake's avatar
Fred Drake committed
31
It will be useful to know that in \class{MultiFile}'s view of the world, text
32 33 34 35 36
is composed of three kinds of lines: data, section-dividers, and
end-markers.  MultiFile is designed to support parsing of
messages that may have multiple nested message parts, each with its
own pattern for section-divider and end-marker lines.

Fred Drake's avatar
Fred Drake committed
37 38

\subsection{MultiFile Objects \label{MultiFile-objects}}
39 40 41 42 43 44

A \class{MultiFile} instance has the following methods:

\begin{methoddesc}{readline}{str}
Read a line.  If the line is data (not a section-divider or end-marker
or real EOF) return it.  If the line matches the most-recently-stacked
45
boundary, return \code{''} and set \code{self.last} to 1 or 0 according as
46
the match is or is not an end-marker.  If the line matches any other
Fred Drake's avatar
Fred Drake committed
47 48 49
stacked boundary, raise an error.  On encountering end-of-file on the
underlying stream object, the method raises \exception{Error} unless
all boundaries have been popped.
50 51 52
\end{methoddesc}

\begin{methoddesc}{readlines}{str}
Fred Drake's avatar
Fred Drake committed
53
Return all lines remaining in this part as a list of strings.
54 55
\end{methoddesc}

Fred Drake's avatar
Fred Drake committed
56
\begin{methoddesc}{read}{}
57 58 59 60
Read all lines, up to the next section.  Return them as a single
(multiline) string.  Note that this doesn't take a size argument!
\end{methoddesc}

Fred Drake's avatar
Fred Drake committed
61
\begin{methoddesc}{seek}{pos\optional{, whence}}
62
Seek.  Seek indices are relative to the start of the current section.
Fred Drake's avatar
Fred Drake committed
63 64
The \var{pos} and \var{whence} arguments are interpreted as for a file
seek.
65 66
\end{methoddesc}

Fred Drake's avatar
Fred Drake committed
67 68
\begin{methoddesc}{tell}{}
Return the file position relative to the start of the current section.
69 70
\end{methoddesc}

71 72 73 74 75 76 77
\begin{methoddesc}{next}{}
Skip lines to the next section (that is, read lines until a
section-divider or end-marker has been consumed).  Return true if
there is such a section, false if an end-marker is seen.  Re-enable
the most-recently-pushed boundary.
\end{methoddesc}

78
\begin{methoddesc}{is_data}{str}
Fred Drake's avatar
Fred Drake committed
79
Return true if \var{str} is data and false if it might be a section
80
boundary.  As written, it tests for a prefix other than \code{'-}\code{-'} at
Fred Drake's avatar
Fred Drake committed
81 82
start of line (which all MIME boundaries have) but it is declared so
it can be overridden in derived classes.
83 84

Note that this test is used intended as a fast guard for the real
Fred Drake's avatar
Fred Drake committed
85 86
boundary tests; if it always returns false it will merely slow
processing, not cause it to fail.
87 88
\end{methoddesc}

89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
\begin{methoddesc}{push}{str}
Push a boundary string.  When an appropriately decorated version of
this boundary is found as an input line, it will be interpreted as a
section-divider or end-marker.  All subsequent
reads will return the empty string to indicate end-of-file, until a
call to \method{pop()} removes the boundary a or \method{next()} call
reenables it.

It is possible to push more than one boundary.  Encountering the
most-recently-pushed boundary will return EOF; encountering any other
boundary will raise an error.
\end{methoddesc}

\begin{methoddesc}{pop}{}
Pop a section boundary.  This boundary will no longer be interpreted
as EOF.
\end{methoddesc}

107 108
\begin{methoddesc}{section_divider}{str}
Turn a boundary into a section-divider line.  By default, this
109
method prepends \code{'-}\code{-'} (which MIME section boundaries have) but
Fred Drake's avatar
Fred Drake committed
110 111 112
it is declared so it can be overridden in derived classes.  This
method need not append LF or CR-LF, as comparison with the result
ignores trailing whitespace. 
113 114 115 116
\end{methoddesc}

\begin{methoddesc}{end_marker}{str}
Turn a boundary string into an end-marker line.  By default, this
117
method prepends \code{'-}\code{-'} and appends \code{'-}\code{-'} (like a
Fred Drake's avatar
Fred Drake committed
118 119 120
MIME-multipart end-of-message marker) but it is declared so it can be
be overridden in derived classes.  This method need not append LF or
CR-LF, as comparison with the result ignores trailing whitespace.
121 122 123 124 125
\end{methoddesc}

Finally, \class{MultiFile} instances have two public instance variables:

\begin{memberdesc}{level}
Fred Drake's avatar
Fred Drake committed
126
Nesting depth of the current part.
127 128 129
\end{memberdesc}

\begin{memberdesc}{last}
Fred Drake's avatar
Fred Drake committed
130
True if the last end-of-file was for an end-of-message marker. 
131 132
\end{memberdesc}

Fred Drake's avatar
Fred Drake committed
133

Fred Drake's avatar
Fred Drake committed
134
\subsection{\class{MultiFile} Example \label{multifile-example}}
135
\sectionauthor{Skip Montanaro}{skip@mojam.com}
136 137

\begin{verbatim}
138
import mimetools
139
import multifile
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
import StringIO

def extract_mime_part_matching(stream, mimetype):
    """Return the first element in a multipart MIME message on stream
    matching mimetype."""

    msg = mimetools.Message(stream)
    msgtype = msg.gettype()
    params = msg.getplist()

    data = StringIO.StringIO()
    if msgtype[:10] == "multipart/":

        file = multifile.MultiFile(stream)
        file.push(msg.getparam("boundary"))
        while file.next():
            submsg = mimetools.Message(file)
            try:
                data = StringIO.StringIO()
                mimetools.decode(file, data, submsg.getencoding())
            except ValueError:
                continue
            if submsg.gettype() == mimetype:
                break
        file.pop()
    return data.getvalue()
166
\end{verbatim}