summaryrefslogtreecommitdiffstats
path: root/Doc/lib/libmultifile.tex
blob: 0741aa1304f7495d207502c62f1be1a7597cfd93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
% Documentation by ESR
\section{Standard Module \module{multifile}}
\stmodindex{multiFile}
\label{module-multifile}

The \code{MultiFile} object enables you to treat sections of a text
file as file-like input objects, with EOF being returned by
\code{readline} when a given delimiter pattern is encountered.  The
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.

\begin{classdesc}{MultiFile}{fp[, seekable=1]}
Create a multi-file.  You must instantiate this class with an input
object argument for MultiFile to get lines from, such as as a file
object returned by \code{open}.

MultiFile only ever looks at the input object's \code{readline},
\code{seek} and \code{tell} methods, and the latter two are only
needed if you want to random-access the multifile sections. To use
MultiFile on a non-seekable stream object, set the optional seekable
argument to 0; this will avoid using the input object's \code{seek}
and \code{tell} at all.
\end{classdesc}

It will be useful to know that in MultiFile's view of the world, text
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.

\subsection{MultiFile Objects}
\label{MultiFile-objects}

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

\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 and passed back as EOF.  All subsequent
reads will also be passed back as EOF, until a \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}{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
boundary, return EOF and set \code{self.last} to 1 or 0 according as
the match is or is not an end-marker.  If the line matches any other
stacked boundary, raise an error.  If the line is a real EOF, raise an
error unless all boundaries have been popped.
\end{methoddesc}

\begin{methoddesc}{readlines}{str}
Read all lines, up to the next section.  Return them as a list of strings
\end{methoddesc}

\begin{methoddesc}{read}{str}
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}

\begin{methoddesc}{next}{str}
Skip lines to the next section (that is, read lines until a
section-divider or end-marker has been consumed).  Return 1 if there
is such a section, 0 if an end-marker is seen.  Re-enable the
most-recently-pushed boundary.
\end{methoddesc}

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

\begin{methoddesc}{seek}{str, pos, whence=0}
Seek.  Seek indices are relative to the start of the current section.
The pos and whence arguments are interpreted as for a file seek.
\end{methoddesc}

\begin{methoddesc}{next}{str}
Tell.  Tell indices are relative to the start of the current section.
\end{methoddesc}

\begin{methoddesc}{is_data}{str}
Return true if a 1 is certainly data and 0 if it might be a section
boundary.  As written, it tests for a prefix other than '--' at start of
line (which all MIME boundaries have) but it is declared so it can be
overridden in derived classes.

Note that this test is used intended as a fast guard for the real
boundary tests; if it always returns 0 it will merely slow processing,
not cause it to fail.
\end{methoddesc}

\begin{methoddesc}{section_divider}{str}
Turn a boundary into a section-divider line.  By default, this
method prepends '--' (which MIME section boundaries have) but 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. 
\end{methoddesc}

\begin{methoddesc}{end_marker}{str}
Turn a boundary string into an end-marker line.  By default, this
method prepends '--' and appends '--' (like a 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.
\end{methoddesc}

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

\begin{memberdesc}{level}
\end{memberdesc}

\begin{memberdesc}{last}
1 if the last EOF passed back was for an end-of-message marker, 0 otherwise. 
\end{memberdesc}

Example:

\begin{verbatim}
    fp = MultiFile(sys.stdin, 0)
    fp.push(outer_boundary)
    message1 = fp.readlines()
    # We should now be either at real EOF or stopped on a message
    # boundary. Re-enable the outer boundary.
    fp.next()
    # Read another message with the same delimiter
    message2 = fp.readlines()
    # Re-enable that delimiter again
    fp.next()
    # Now look for a message subpart with a different boundary
    fp.push(inner_boundary)
    sub_header = fp.readlines()
    # If no exception has been thrown, we're looking at the start of
    # the message subpart.  Reset and grab the subpart
    fp.next()
    sub_body = fp.readlines()
    # Got it.  Now pop the inner boundary to re-enable the outer one.
    fp.pop()
    # Read to next outer boundary
    message3 = fp.readlines()
\end{verbatim}