mirror of
https://github.com/python/cpython.git
synced 2026-01-06 15:32:22 +00:00
Remove the multifile module as deprecated by PEP 4.
This commit is contained in:
parent
ce261952e6
commit
d3343cb8a1
4 changed files with 2 additions and 350 deletions
|
|
@ -1,191 +0,0 @@
|
|||
|
||||
:mod:`multifile` --- Support for files containing distinct parts
|
||||
================================================================
|
||||
|
||||
.. module:: multifile
|
||||
:synopsis: Support for reading files which contain distinct parts, such as some MIME data.
|
||||
:deprecated:
|
||||
.. sectionauthor:: Eric S. Raymond <esr@snark.thyrsus.com>
|
||||
|
||||
|
||||
.. deprecated:: 2.5
|
||||
The :mod:`email` package should be used in preference to the :mod:`multifile`
|
||||
module. This module is present only to maintain backward compatibility.
|
||||
|
||||
The :class:`MultiFile` object enables you to treat sections of a text file as
|
||||
file-like input objects, with ``''`` being returned by :meth:`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.
|
||||
|
||||
|
||||
.. class:: MultiFile(fp[, seekable])
|
||||
|
||||
Create a multi-file. You must instantiate this class with an input object
|
||||
argument for the :class:`MultiFile` instance to get lines from, such as a file
|
||||
object returned by :func:`open`.
|
||||
|
||||
:class:`MultiFile` only ever looks at the input object's :meth:`readline`,
|
||||
:meth:`seek` and :meth:`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 *seekable* argument to false; this
|
||||
will prevent using the input object's :meth:`seek` and :meth:`tell` methods.
|
||||
|
||||
It will be useful to know that in :class:`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.
|
||||
|
||||
|
||||
.. seealso::
|
||||
|
||||
Module :mod:`email`
|
||||
Comprehensive email handling package; supersedes the :mod:`multifile` module.
|
||||
|
||||
|
||||
.. _multifile-objects:
|
||||
|
||||
MultiFile Objects
|
||||
-----------------
|
||||
|
||||
A :class:`MultiFile` instance has the following methods:
|
||||
|
||||
|
||||
.. method:: MultiFile.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
|
||||
``''`` and set ``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. On
|
||||
encountering end-of-file on the underlying stream object, the method raises
|
||||
:exc:`Error` unless all boundaries have been popped.
|
||||
|
||||
|
||||
.. method:: MultiFile.readlines(str)
|
||||
|
||||
Return all lines remaining in this part as a list of strings.
|
||||
|
||||
|
||||
.. method:: MultiFile.read()
|
||||
|
||||
Read all lines, up to the next section. Return them as a single (multiline)
|
||||
string. Note that this doesn't take a size argument!
|
||||
|
||||
|
||||
.. method:: MultiFile.seek(pos[, whence])
|
||||
|
||||
Seek. Seek indices are relative to the start of the current section. The *pos*
|
||||
and *whence* arguments are interpreted as for a file seek.
|
||||
|
||||
|
||||
.. method:: MultiFile.tell()
|
||||
|
||||
Return the file position relative to the start of the current section.
|
||||
|
||||
|
||||
.. method:: MultiFile.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.
|
||||
|
||||
|
||||
.. method:: MultiFile.is_data(str)
|
||||
|
||||
Return true if *str* is data and false 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 false it will merely slow processing, not cause it
|
||||
to fail.
|
||||
|
||||
|
||||
.. method:: MultiFile.push(str)
|
||||
|
||||
Push a boundary string. When a decorated version of this boundary is found as
|
||||
an input line, it will be interpreted as a section-divider or end-marker
|
||||
(depending on the decoration, see :rfc:`2045`). All subsequent reads will
|
||||
return the empty string to indicate end-of-file, until a call to :meth:`pop`
|
||||
removes the boundary a or :meth:`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.
|
||||
|
||||
|
||||
.. method:: MultiFile.pop()
|
||||
|
||||
Pop a section boundary. This boundary will no longer be interpreted as EOF.
|
||||
|
||||
|
||||
.. method:: MultiFile.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.
|
||||
|
||||
|
||||
.. method:: MultiFile.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 overridden in derived classes. This
|
||||
method need not append LF or CR-LF, as comparison with the result ignores
|
||||
trailing whitespace.
|
||||
|
||||
Finally, :class:`MultiFile` instances have two public instance variables:
|
||||
|
||||
|
||||
.. attribute:: MultiFile.level
|
||||
|
||||
Nesting depth of the current part.
|
||||
|
||||
|
||||
.. attribute:: MultiFile.last
|
||||
|
||||
True if the last end-of-file was for an end-of-message marker.
|
||||
|
||||
|
||||
.. _multifile-example:
|
||||
|
||||
:class:`MultiFile` Example
|
||||
--------------------------
|
||||
|
||||
.. sectionauthor:: Skip Montanaro <skip@pobox.com>
|
||||
|
||||
|
||||
::
|
||||
|
||||
import mimetools
|
||||
import multifile
|
||||
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()
|
||||
|
||||
|
|
@ -17,7 +17,6 @@ on the Internet.
|
|||
mailbox.rst
|
||||
mimetools.rst
|
||||
mimetypes.rst
|
||||
multifile.rst
|
||||
rfc822.rst
|
||||
base64.rst
|
||||
binhex.rst
|
||||
|
|
|
|||
158
Lib/multifile.py
158
Lib/multifile.py
|
|
@ -1,158 +0,0 @@
|
|||
"""A readline()-style interface to the parts of a multipart message.
|
||||
|
||||
The MultiFile class makes each part of a multipart message "feel" like
|
||||
an ordinary file, as long as you use fp.readline(). Allows recursive
|
||||
use, for nested multipart messages. Probably best used together
|
||||
with module mimetools.
|
||||
|
||||
Suggested use:
|
||||
|
||||
real_fp = open(...)
|
||||
fp = MultiFile(real_fp)
|
||||
|
||||
"read some lines from fp"
|
||||
fp.push(separator)
|
||||
while 1:
|
||||
"read lines from fp until it returns an empty string" (A)
|
||||
if not fp.next(): break
|
||||
fp.pop()
|
||||
"read remaining lines from fp until it returns an empty string"
|
||||
|
||||
The latter sequence may be used recursively at (A).
|
||||
It is also allowed to use multiple push()...pop() sequences.
|
||||
|
||||
If seekable is given as 0, the class code will not do the bookkeeping
|
||||
it normally attempts in order to make seeks relative to the beginning of the
|
||||
current file part. This may be useful when using MultiFile with a non-
|
||||
seekable stream object.
|
||||
"""
|
||||
|
||||
__all__ = ["MultiFile","Error"]
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
class MultiFile:
|
||||
|
||||
seekable = 0
|
||||
|
||||
def __init__(self, fp, seekable=1):
|
||||
self.fp = fp
|
||||
self.stack = []
|
||||
self.level = 0
|
||||
self.last = 0
|
||||
if seekable:
|
||||
self.seekable = 1
|
||||
self.start = self.fp.tell()
|
||||
self.posstack = []
|
||||
|
||||
def tell(self):
|
||||
if self.level > 0:
|
||||
return self.lastpos
|
||||
return self.fp.tell() - self.start
|
||||
|
||||
def seek(self, pos, whence=0):
|
||||
here = self.tell()
|
||||
if whence:
|
||||
if whence == 1:
|
||||
pos = pos + here
|
||||
elif whence == 2:
|
||||
if self.level > 0:
|
||||
pos = pos + self.lastpos
|
||||
else:
|
||||
raise Error("can't use whence=2 yet")
|
||||
if not 0 <= pos <= here or \
|
||||
self.level > 0 and pos > self.lastpos:
|
||||
raise Error('bad MultiFile.seek() call')
|
||||
self.fp.seek(pos + self.start)
|
||||
self.level = 0
|
||||
self.last = 0
|
||||
|
||||
def readline(self):
|
||||
if self.level > 0:
|
||||
return ''
|
||||
line = self.fp.readline()
|
||||
# Real EOF?
|
||||
if not line:
|
||||
self.level = len(self.stack)
|
||||
self.last = (self.level > 0)
|
||||
if self.last:
|
||||
raise Error('sudden EOF in MultiFile.readline()')
|
||||
return ''
|
||||
assert self.level == 0
|
||||
# Fast check to see if this is just data
|
||||
if self.is_data(line):
|
||||
return line
|
||||
else:
|
||||
# Ignore trailing whitespace on marker lines
|
||||
marker = line.rstrip()
|
||||
# No? OK, try to match a boundary.
|
||||
# Return the line (unstripped) if we don't.
|
||||
for i, sep in enumerate(reversed(self.stack)):
|
||||
if marker == self.section_divider(sep):
|
||||
self.last = 0
|
||||
break
|
||||
elif marker == self.end_marker(sep):
|
||||
self.last = 1
|
||||
break
|
||||
else:
|
||||
return line
|
||||
# We only get here if we see a section divider or EOM line
|
||||
if self.seekable:
|
||||
self.lastpos = self.tell() - len(line)
|
||||
self.level = i+1
|
||||
if self.level > 1:
|
||||
raise Error('Missing endmarker in MultiFile.readline()')
|
||||
return ''
|
||||
|
||||
def readlines(self):
|
||||
list = []
|
||||
while 1:
|
||||
line = self.readline()
|
||||
if not line: break
|
||||
list.append(line)
|
||||
return list
|
||||
|
||||
def read(self): # Note: no size argument -- read until EOF only!
|
||||
return ''.join(self.readlines())
|
||||
|
||||
def next(self):
|
||||
while self.readline(): pass
|
||||
if self.level > 1 or self.last:
|
||||
return 0
|
||||
self.level = 0
|
||||
self.last = 0
|
||||
if self.seekable:
|
||||
self.start = self.fp.tell()
|
||||
return 1
|
||||
|
||||
def push(self, sep):
|
||||
if self.level > 0:
|
||||
raise Error('bad MultiFile.push() call')
|
||||
self.stack.append(sep)
|
||||
if self.seekable:
|
||||
self.posstack.append(self.start)
|
||||
self.start = self.fp.tell()
|
||||
|
||||
def pop(self):
|
||||
if self.stack == []:
|
||||
raise Error('bad MultiFile.pop() call')
|
||||
if self.level <= 1:
|
||||
self.last = 0
|
||||
else:
|
||||
abslastpos = self.lastpos + self.start
|
||||
self.level = max(0, self.level - 1)
|
||||
self.stack.pop()
|
||||
if self.seekable:
|
||||
self.start = self.posstack.pop()
|
||||
if self.level > 0:
|
||||
self.lastpos = abslastpos - self.start
|
||||
|
||||
def is_data(self, line):
|
||||
return line[:2] != '--'
|
||||
|
||||
def section_divider(self, str):
|
||||
return "--" + str
|
||||
|
||||
def end_marker(self, str):
|
||||
return "--" + str + "--"
|
||||
|
|
@ -21,6 +21,8 @@ Extension Modules
|
|||
Library
|
||||
-------
|
||||
|
||||
- The multifile module has been removed.
|
||||
|
||||
- The SocketServer module has been renamed to socketserver.
|
||||
|
||||
- Fix the __all__ setting on 'collections' to include UserList and UserString.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue