Commit 820c1200 authored by Barry Warsaw's avatar Barry Warsaw

Patch for issue 2848, mostly by Humberto Diogenes, with a couple of

small fixes by Barry.  This removes mimetools from the stdlib.
parent 75f25f2c
...@@ -89,7 +89,7 @@ def message(f, delimiter = ''): ...@@ -89,7 +89,7 @@ def message(f, delimiter = ''):
t = time.mktime(tt) t = time.mktime(tt)
else: else:
sys.stderr.write( sys.stderr.write(
'Unparseable date: %r\n' % (m.getheader('Date'),)) 'Unparseable date: %r\n' % (m.get('Date'),))
t = os.fstat(f.fileno())[stat.ST_MTIME] t = os.fstat(f.fileno())[stat.ST_MTIME]
print('From', email, time.ctime(t)) print('From', email, time.ctime(t))
# Copy RFC822 header # Copy RFC822 header
......
...@@ -249,6 +249,8 @@ def parse_multipart(fp, pdict): ...@@ -249,6 +249,8 @@ def parse_multipart(fp, pdict):
since it can call parse_multipart(). since it can call parse_multipart().
""" """
import http.client
boundary = "" boundary = ""
if 'boundary' in pdict: if 'boundary' in pdict:
boundary = pdict['boundary'] boundary = pdict['boundary']
...@@ -266,8 +268,8 @@ def parse_multipart(fp, pdict): ...@@ -266,8 +268,8 @@ def parse_multipart(fp, pdict):
data = None data = None
if terminator: if terminator:
# At start of next part. Read headers first. # At start of next part. Read headers first.
headers = mimetools.Message(fp) headers = http.client.parse_headers(fp)
clength = headers.getheader('content-length') clength = headers.get('content-length')
if clength: if clength:
try: try:
bytes = int(clength) bytes = int(clength)
......
...@@ -68,11 +68,7 @@ class Parser: ...@@ -68,11 +68,7 @@ class Parser:
data = fp.read(8192) data = fp.read(8192)
if not data: if not data:
break break
# XXX When Guido fixes TextIOWrapper.read() to act just like feedparser.feed(data)
# .readlines(), this...
feedparser.feed(str(data))
# ...gets reverted back to
#feedparser.feed(data)
return feedparser.close() return feedparser.close()
def parsestr(self, text, headersonly=False): def parsestr(self, text, headersonly=False):
......
...@@ -25,7 +25,6 @@ import time ...@@ -25,7 +25,6 @@ import time
import base64 import base64
import random import random
import socket import socket
import urllib
import warnings import warnings
from io import StringIO from io import StringIO
...@@ -235,6 +234,7 @@ def decode_params(params): ...@@ -235,6 +234,7 @@ def decode_params(params):
params is a sequence of 2-tuples containing (param name, string value). params is a sequence of 2-tuples containing (param name, string value).
""" """
import urllib
# Copy params so we don't mess with the original # Copy params so we don't mess with the original
params = params[:] params = params[:]
new_params = [] new_params = []
......
...@@ -67,8 +67,9 @@ Req-sent-unread-response _CS_REQ_SENT <response_class> ...@@ -67,8 +67,9 @@ Req-sent-unread-response _CS_REQ_SENT <response_class>
""" """
import io import io
import mimetools
import socket import socket
import email.parser
import email.message
from urlparse import urlsplit from urlparse import urlsplit
import warnings import warnings
...@@ -201,110 +202,52 @@ responses = { ...@@ -201,110 +202,52 @@ responses = {
# maximal amount of data to read at one time in _safe_read # maximal amount of data to read at one time in _safe_read
MAXAMOUNT = 1048576 MAXAMOUNT = 1048576
class HTTPMessage(mimetools.Message): class HTTPMessage(email.message.Message):
def getallmatchingheaders(self, name):
"""Find all header lines matching a given header name.
Look through the list of headers and find all lines matching a given
header name (and their continuation lines). A list of the lines is
returned, without interpretation. If the header does not occur, an
empty list is returned. If the header occurs multiple times, all
occurrences are returned. Case is not important in the header name.
def addheader(self, key, value):
"""Add header for field key handling repeats."""
prev = self.dict.get(key)
if prev is None:
self.dict[key] = value
else:
combined = ", ".join((prev, value))
self.dict[key] = combined
def addcontinue(self, key, more):
"""Add more field data from a continuation line."""
prev = self.dict[key]
self.dict[key] = prev + "\n " + more
def readheaders(self):
"""Read header lines.
Read header lines up to the entirely blank line that terminates them.
The (normally blank) line that ends the headers is skipped, but not
included in the returned list. If a non-header line ends the headers,
(which is an error), an attempt is made to backspace over it; it is
never included in the returned list.
The variable self.status is set to the empty string if all went well,
otherwise it is an error message. The variable self.headers is a
completely uninterpreted list of lines contained in the header (so
printing them will reproduce the header exactly as it appears in the
file).
If multiple header fields with the same name occur, they are combined
according to the rules in RFC 2616 sec 4.2:
Appending each subsequent field-value to the first, each separated
by a comma. The order in which header fields with the same field-name
are received is significant to the interpretation of the combined
field value.
""" """
# XXX The implementation overrides the readheaders() method of # XXX: copied from rfc822.Message for compatibility
# rfc822.Message. The base class design isn't amenable to name = name.lower() + ':'
# customized behavior here so the method here is a copy of the n = len(name)
# base class code with a few small changes. lst = []
hit = 0
self.dict = {} for line in self.keys():
self.unixfrom = '' if line[:n].lower() == name:
self.headers = hlist = [] hit = 1
self.status = '' elif not line[:1].isspace():
headerseen = "" hit = 0
firstline = 1 if hit:
startofline = unread = tell = None lst.append(line)
if hasattr(self.fp, 'unread'): return lst
unread = self.fp.unread
elif self.seekable: def parse_headers(fp):
tell = self.fp.tell """Parses only RFC2822 headers from a file pointer.
while True:
if tell: email Parser wants to see strings rather than bytes.
try: But a TextIOWrapper around self.rfile would buffer too many bytes
startofline = tell() from the stream, bytes which we later need to read as bytes.
except IOError: So we read the correct bytes here, as bytes, for email Parser
startofline = tell = None to parse.
self.seekable = 0
line = str(self.fp.readline(), "iso-8859-1") """
if not line: # XXX: Copied from http.server.BaseHTTPRequestHandler.parse_request,
self.status = 'EOF in headers' # maybe we can just call this function from there.
break headers = []
# Skip unix From name time lines while True:
if firstline and line.startswith('From '): line = fp.readline()
self.unixfrom = self.unixfrom + line headers.append(line)
continue if line in (b'\r\n', b'\n', b''):
firstline = 0 break
if headerseen and line[0] in ' \t': hstring = b''.join(headers).decode('iso-8859-1')
# XXX Not sure if continuation lines are handled properly
# for http and/or for repeating headers return email.parser.Parser(_class=HTTPMessage).parsestr(hstring)
# It's a continuation line.
hlist.append(line)
self.addcontinue(headerseen, line.strip())
continue
elif self.iscomment(line):
# It's a comment. Ignore it.
continue
elif self.islast(line):
# Note! No pushback here! The delimiter line gets eaten.
break
headerseen = self.isheader(line)
if headerseen:
# It's a legal header line, save it.
hlist.append(line)
self.addheader(headerseen, line[len(headerseen)+1:].strip())
continue
else:
# It's not a header line; throw it back and stop here.
if not self.dict:
self.status = 'No headers'
else:
self.status = 'Non-header line where header expected'
# Try to undo the read.
if unread:
unread(line)
elif tell:
self.fp.seek(startofline)
else:
self.status = self.status + '; bad seek'
break
class HTTPResponse: class HTTPResponse:
...@@ -418,19 +361,17 @@ class HTTPResponse: ...@@ -418,19 +361,17 @@ class HTTPResponse:
self.length = None self.length = None
self.chunked = 0 self.chunked = 0
self.will_close = 1 self.will_close = 1
self.msg = HTTPMessage(io.BytesIO()) self.msg = email.message_from_string('')
return return
self.msg = HTTPMessage(self.fp, 0) self.msg = parse_headers(self.fp)
if self.debuglevel > 0: if self.debuglevel > 0:
for hdr in self.msg.headers: for hdr in self.msg:
print("header:", hdr, end=" ") print("header:", hdr, end=" ")
# don't let the msg keep an fp
self.msg.fp = None
# are we using the chunked-style of transfer encoding? # are we using the chunked-style of transfer encoding?
tr_enc = self.msg.getheader("transfer-encoding") tr_enc = self.msg.get("transfer-encoding")
if tr_enc and tr_enc.lower() == "chunked": if tr_enc and tr_enc.lower() == "chunked":
self.chunked = 1 self.chunked = 1
self.chunk_left = None self.chunk_left = None
...@@ -443,7 +384,10 @@ class HTTPResponse: ...@@ -443,7 +384,10 @@ class HTTPResponse:
# do we have a Content-Length? # do we have a Content-Length?
# NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked" # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked"
self.length = None self.length = None
length = self.msg.getheader("content-length") length = self.msg.get("content-length")
# are we using the chunked-style of transfer encoding?
tr_enc = self.msg.get("transfer-encoding")
if length and not self.chunked: if length and not self.chunked:
try: try:
self.length = int(length) self.length = int(length)
...@@ -470,11 +414,11 @@ class HTTPResponse: ...@@ -470,11 +414,11 @@ class HTTPResponse:
self.will_close = 1 self.will_close = 1
def _check_close(self): def _check_close(self):
conn = self.msg.getheader("connection") conn = self.msg.get("connection")
if self.version == 11: if self.version == 11:
# An HTTP/1.1 proxy is assumed to stay open unless # An HTTP/1.1 proxy is assumed to stay open unless
# explicitly closed. # explicitly closed.
conn = self.msg.getheader("connection") conn = self.msg.get("connection")
if conn and "close" in conn.lower(): if conn and "close" in conn.lower():
return True return True
return False return False
...@@ -483,7 +427,7 @@ class HTTPResponse: ...@@ -483,7 +427,7 @@ class HTTPResponse:
# connections, using rules different than HTTP/1.1. # connections, using rules different than HTTP/1.1.
# For older HTTP, Keep-Alive indicates persistent connection. # For older HTTP, Keep-Alive indicates persistent connection.
if self.msg.getheader("keep-alive"): if self.msg.get("keep-alive"):
return False return False
# At least Akamai returns a "Connection: Keep-Alive" header, # At least Akamai returns a "Connection: Keep-Alive" header,
...@@ -492,7 +436,7 @@ class HTTPResponse: ...@@ -492,7 +436,7 @@ class HTTPResponse:
return False return False
# Proxy-Connection is a netscape hack. # Proxy-Connection is a netscape hack.
pconn = self.msg.getheader("proxy-connection") pconn = self.msg.get("proxy-connection")
if pconn and "keep-alive" in pconn.lower(): if pconn and "keep-alive" in pconn.lower():
return False return False
...@@ -644,7 +588,7 @@ class HTTPResponse: ...@@ -644,7 +588,7 @@ class HTTPResponse:
def getheader(self, name, default=None): def getheader(self, name, default=None):
if self.msg is None: if self.msg is None:
raise ResponseNotReady() raise ResponseNotReady()
return self.msg.getheader(name, default) return ', '.join(self.msg.get_all(name, default))
def getheaders(self): def getheaders(self):
"""Return list of (header, value) tuples.""" """Return list of (header, value) tuples."""
......
...@@ -1547,8 +1547,8 @@ class CookieJar: ...@@ -1547,8 +1547,8 @@ class CookieJar:
"""Return sequence of Cookie objects extracted from response object.""" """Return sequence of Cookie objects extracted from response object."""
# get cookie-attributes for RFC 2965 and Netscape protocols # get cookie-attributes for RFC 2965 and Netscape protocols
headers = response.info() headers = response.info()
rfc2965_hdrs = headers.getheaders("Set-Cookie2") rfc2965_hdrs = headers.get_all("Set-Cookie2", [])
ns_hdrs = headers.getheaders("Set-Cookie") ns_hdrs = headers.get_all("Set-Cookie", [])
rfc2965 = self._policy.rfc2965 rfc2965 = self._policy.rfc2965
netscape = self._policy.netscape netscape = self._policy.netscape
......
...@@ -95,10 +95,11 @@ import socket # For gethostbyaddr() ...@@ -95,10 +95,11 @@ import socket # For gethostbyaddr()
import shutil import shutil
import urllib import urllib
import select import select
import mimetools
import mimetypes import mimetypes
import posixpath import posixpath
import socketserver import socketserver
import email.message
import email.parser
# Default error message template # Default error message template
DEFAULT_ERROR_MESSAGE = """\ DEFAULT_ERROR_MESSAGE = """\
...@@ -211,7 +212,7 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): ...@@ -211,7 +212,7 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
- command, path and version are the broken-down request line; - command, path and version are the broken-down request line;
- headers is an instance of mimetools.Message (or a derived - headers is an instance of email.message.Message (or a derived
class) containing the header information; class) containing the header information;
- rfile is a file object open for reading positioned at the - rfile is a file object open for reading positioned at the
...@@ -326,7 +327,7 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): ...@@ -326,7 +327,7 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
if line in (b'\r\n', b'\n', b''): if line in (b'\r\n', b'\n', b''):
break break
hfile = io.StringIO(b''.join(headers).decode('iso-8859-1')) hfile = io.StringIO(b''.join(headers).decode('iso-8859-1'))
self.headers = self.MessageClass(hfile) self.headers = email.parser.Parser(_class=self.MessageClass).parse(hfile)
conntype = self.headers.get('Connection', "") conntype = self.headers.get('Connection', "")
if conntype.lower() == 'close': if conntype.lower() == 'close':
...@@ -524,8 +525,9 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): ...@@ -524,8 +525,9 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
# Set this to HTTP/1.1 to enable automatic keepalive # Set this to HTTP/1.1 to enable automatic keepalive
protocol_version = "HTTP/1.0" protocol_version = "HTTP/1.0"
# The Message-like class used to parse headers # MessageClass used to parse headers
MessageClass = mimetools.Message import http.client
MessageClass = http.client.HTTPMessage
# Table mapping response codes to messages; entries have the # Table mapping response codes to messages; entries have the
# form {code: (shortmessage, longmessage)}. # form {code: (shortmessage, longmessage)}.
...@@ -955,7 +957,7 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler): ...@@ -955,7 +957,7 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
if host != self.client_address[0]: if host != self.client_address[0]:
env['REMOTE_HOST'] = host env['REMOTE_HOST'] = host
env['REMOTE_ADDR'] = self.client_address[0] env['REMOTE_ADDR'] = self.client_address[0]
authorization = self.headers.getheader("authorization") authorization = self.headers.get("authorization")
if authorization: if authorization:
authorization = authorization.split() authorization = authorization.split()
if len(authorization) == 2: if len(authorization) == 2:
...@@ -973,14 +975,14 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler): ...@@ -973,14 +975,14 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
if len(authorization) == 2: if len(authorization) == 2:
env['REMOTE_USER'] = authorization[0] env['REMOTE_USER'] = authorization[0]
# XXX REMOTE_IDENT # XXX REMOTE_IDENT
if self.headers.typeheader is None: if self.headers.get('content-type') is None:
env['CONTENT_TYPE'] = self.headers.type env['CONTENT_TYPE'] = self.headers.get_content_type()
else: else:
env['CONTENT_TYPE'] = self.headers.typeheader env['CONTENT_TYPE'] = self.headers['content-type']
length = self.headers.getheader('content-length') length = self.headers.get('content-length')
if length: if length:
env['CONTENT_LENGTH'] = length env['CONTENT_LENGTH'] = length
referer = self.headers.getheader('referer') referer = self.headers.get('referer')
if referer: if referer:
env['HTTP_REFERER'] = referer env['HTTP_REFERER'] = referer
accept = [] accept = []
...@@ -990,10 +992,10 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler): ...@@ -990,10 +992,10 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
else: else:
accept = accept + line[7:].split(',') accept = accept + line[7:].split(',')
env['HTTP_ACCEPT'] = ','.join(accept) env['HTTP_ACCEPT'] = ','.join(accept)
ua = self.headers.getheader('user-agent') ua = self.headers.get('user-agent')
if ua: if ua:
env['HTTP_USER_AGENT'] = ua env['HTTP_USER_AGENT'] = ua
co = filter(None, self.headers.getheaders('cookie')) co = filter(None, self.headers.get_all('cookie', []))
if co: if co:
env['HTTP_COOKIE'] = ', '.join(co) env['HTTP_COOKIE'] = ', '.join(co)
# XXX Other HTTP_* headers # XXX Other HTTP_* headers
......
...@@ -1910,8 +1910,8 @@ def serve(port, callback=None, completer=None): ...@@ -1910,8 +1910,8 @@ def serve(port, callback=None, completer=None):
def __init__(self, fp, seekable=1): def __init__(self, fp, seekable=1):
Message = self.__class__ Message = self.__class__
Message.__bases__[0].__bases__[0].__init__(self, fp, seekable) Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
self.encodingheader = self.getheader('content-transfer-encoding') self.encodingheader = self.get('content-transfer-encoding')
self.typeheader = self.getheader('content-type') self.typeheader = self.get('content-type')
self.parsetype() self.parsetype()
self.parseplist() self.parseplist()
......
...@@ -421,9 +421,9 @@ class MailmanProxy(PureProxy): ...@@ -421,9 +421,9 @@ class MailmanProxy(PureProxy):
# These headers are required for the proper execution of Mailman. All # These headers are required for the proper execution of Mailman. All
# MTAs in existance seem to add these if the original message doesn't # MTAs in existance seem to add these if the original message doesn't
# have them. # have them.
if not msg.getheader('from'): if not msg.get('from'):
msg['From'] = mailfrom msg['From'] = mailfrom
if not msg.getheader('date'): if not msg.get('date'):
msg['Date'] = time.ctime(time.time()) msg['Date'] = time.ctime(time.time())
for rcpt, listname, command in listnames: for rcpt, listname, command in listnames:
print('sending message to', rcpt, file=DEBUGSTREAM) print('sending message to', rcpt, file=DEBUGSTREAM)
......
...@@ -193,9 +193,8 @@ class FakeResponse: ...@@ -193,9 +193,8 @@ class FakeResponse:
""" """
headers: list of RFC822-style 'Key: value' strings headers: list of RFC822-style 'Key: value' strings
""" """
import mimetools, io import email
f = io.StringIO("\n".join(headers)) self._headers = email.message_from_string("\n".join(headers))
self._headers = mimetools.Message(f)
self._url = url self._url = url
def info(self): return self._headers def info(self): return self._headers
......
...@@ -19,12 +19,14 @@ import threading ...@@ -19,12 +19,14 @@ import threading
import unittest import unittest
from test import support from test import support
class NoLogRequestHandler: class NoLogRequestHandler:
def log_message(self, *args): def log_message(self, *args):
# don't write log messages to stderr # don't write log messages to stderr
pass pass
def read(self, n=None):
return ''
class TestServerThread(threading.Thread): class TestServerThread(threading.Thread):
def __init__(self, test_object, request_handler): def __init__(self, test_object, request_handler):
......
...@@ -944,7 +944,7 @@ else: ...@@ -944,7 +944,7 @@ else:
url = 'https://%s:%d/%s' % ( url = 'https://%s:%d/%s' % (
HOST, server.port, os.path.split(CERTFILE)[1]) HOST, server.port, os.path.split(CERTFILE)[1])
f = urllib.urlopen(url) f = urllib.urlopen(url)
dlen = f.info().getheader("content-length") dlen = f.info().get("content-length")
if dlen and (int(dlen) > 0): if dlen and (int(dlen) > 0):
d2 = f.read(int(dlen)) d2 = f.read(int(dlen))
if support.verbose: if support.verbose:
......
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
import urllib import urllib
import http.client import http.client
import email.message
import io import io
import unittest import unittest
from test import support from test import support
import os import os
import mimetools
import tempfile import tempfile
def hexescape(char): def hexescape(char):
...@@ -78,7 +78,7 @@ class urlopen_FileTests(unittest.TestCase): ...@@ -78,7 +78,7 @@ class urlopen_FileTests(unittest.TestCase):
self.returned_obj.close() self.returned_obj.close()
def test_info(self): def test_info(self):
self.assert_(isinstance(self.returned_obj.info(), mimetools.Message)) self.assert_(isinstance(self.returned_obj.info(), email.message.Message))
def test_geturl(self): def test_geturl(self):
self.assertEqual(self.returned_obj.geturl(), self.pathname) self.assertEqual(self.returned_obj.geturl(), self.pathname)
...@@ -206,8 +206,8 @@ class urlretrieve_FileTests(unittest.TestCase): ...@@ -206,8 +206,8 @@ class urlretrieve_FileTests(unittest.TestCase):
# a headers value is returned. # a headers value is returned.
result = urllib.urlretrieve("file:%s" % support.TESTFN) result = urllib.urlretrieve("file:%s" % support.TESTFN)
self.assertEqual(result[0], support.TESTFN) self.assertEqual(result[0], support.TESTFN)
self.assert_(isinstance(result[1], mimetools.Message), self.assert_(isinstance(result[1], email.message.Message),
"did not get a mimetools.Message instance as second " "did not get a email.message.Message instance as second "
"returned value") "returned value")
def test_copy(self): def test_copy(self):
......
...@@ -349,18 +349,18 @@ class MockHTTPHandler(urllib2.BaseHandler): ...@@ -349,18 +349,18 @@ class MockHTTPHandler(urllib2.BaseHandler):
self._count = 0 self._count = 0
self.requests = [] self.requests = []
def http_open(self, req): def http_open(self, req):
import mimetools, http.client, copy import email, http.client, copy
from io import StringIO from io import StringIO
self.requests.append(copy.deepcopy(req)) self.requests.append(copy.deepcopy(req))
if self._count == 0: if self._count == 0:
self._count = self._count + 1 self._count = self._count + 1
name = http.client.responses[self.code] name = http.client.responses[self.code]
msg = mimetools.Message(StringIO(self.headers)) msg = email.message_from_string(self.headers)
return self.parent.error( return self.parent.error(
"http", req, MockFile(), self.code, name, msg) "http", req, MockFile(), self.code, name, msg)
else: else:
self.req = req self.req = req
msg = mimetools.Message(StringIO("\r\n\r\n")) msg = email.message_from_string("\r\n\r\n")
return MockResponse(200, "OK", msg, "", req.get_full_url()) return MockResponse(200, "OK", msg, "", req.get_full_url())
class MockPasswordManager: class MockPasswordManager:
......
#!/usr/bin/env python #!/usr/bin/env python
import mimetools import email
import threading import threading
import urlparse import urlparse
import urllib2 import urllib2
...@@ -443,10 +443,10 @@ class TestUrlopen(unittest.TestCase): ...@@ -443,10 +443,10 @@ class TestUrlopen(unittest.TestCase):
try: try:
open_url = urllib2.urlopen("http://localhost:%s" % handler.port) open_url = urllib2.urlopen("http://localhost:%s" % handler.port)
info_obj = open_url.info() info_obj = open_url.info()
self.assert_(isinstance(info_obj, mimetools.Message), self.assert_(isinstance(info_obj, email.message.Message),
"object returned by 'info' is not an instance of " "object returned by 'info' is not an instance of "
"mimetools.Message") "email.message.Message")
self.assertEqual(info_obj.getsubtype(), "plain") self.assertEqual(info_obj.get_content_subtype(), "plain")
finally: finally:
self.server.stop() self.server.stop()
......
...@@ -8,7 +8,6 @@ import socket ...@@ -8,7 +8,6 @@ import socket
import urllib2 import urllib2
import sys import sys
import os import os
import mimetools
def _retry_thrice(func, exc, *args, **kwargs): def _retry_thrice(func, exc, *args, **kwargs):
......
...@@ -7,7 +7,7 @@ import socket ...@@ -7,7 +7,7 @@ import socket
import urllib import urllib
import sys import sys
import os import os
import mimetools import email.message
def _open_with_retry(func, host, *args, **kwargs): def _open_with_retry(func, host, *args, **kwargs):
...@@ -87,10 +87,10 @@ class urlopenNetworkTests(unittest.TestCase): ...@@ -87,10 +87,10 @@ class urlopenNetworkTests(unittest.TestCase):
info_obj = open_url.info() info_obj = open_url.info()
finally: finally:
open_url.close() open_url.close()
self.assert_(isinstance(info_obj, mimetools.Message), self.assert_(isinstance(info_obj, email.message.Message),
"object returned by 'info' is not an instance of " "object returned by 'info' is not an instance of "
"mimetools.Message") "email.message.Message")
self.assertEqual(info_obj.getsubtype(), "html") self.assertEqual(info_obj.get_content_subtype(), "html")
def test_geturl(self): def test_geturl(self):
# Make sure same URL as opened is returned by geturl. # Make sure same URL as opened is returned by geturl.
...@@ -180,8 +180,8 @@ class urlretrieveNetworkTests(unittest.TestCase): ...@@ -180,8 +180,8 @@ class urlretrieveNetworkTests(unittest.TestCase):
# Make sure header returned as 2nd value from urlretrieve is good. # Make sure header returned as 2nd value from urlretrieve is good.
file_location, header = self.urlretrieve("http://www.python.org/") file_location, header = self.urlretrieve("http://www.python.org/")
os.unlink(file_location) os.unlink(file_location)
self.assert_(isinstance(header, mimetools.Message), self.assert_(isinstance(header, email.message.Message),
"header is not an instance of mimetools.Message") "header is not an instance of email.message.Message")
......
...@@ -6,7 +6,6 @@ import unittest ...@@ -6,7 +6,6 @@ import unittest
import xmlrpc.client as xmlrpclib import xmlrpc.client as xmlrpclib
import xmlrpc.server import xmlrpc.server
import threading import threading
import mimetools
import http.client import http.client
import socket import socket
import os import os
...@@ -452,12 +451,12 @@ class SimpleServerTestCase(unittest.TestCase): ...@@ -452,12 +451,12 @@ class SimpleServerTestCase(unittest.TestCase):
# This is a contrived way to make a failure occur on the server side # This is a contrived way to make a failure occur on the server side
# in order to test the _send_traceback_header flag on the server # in order to test the _send_traceback_header flag on the server
class FailingMessageClass(mimetools.Message): class FailingMessageClass(http.client.HTTPMessage):
def __getitem__(self, key): def get(self, key, failobj=None):
key = key.lower() key = key.lower()
if key == 'content-length': if key == 'content-length':
return 'I am broken' return 'I am broken'
return mimetools.Message.__getitem__(self, key) return super().get(key, failobj)
class FailingServerTestCase(unittest.TestCase): class FailingServerTestCase(unittest.TestCase):
...@@ -477,7 +476,8 @@ class FailingServerTestCase(unittest.TestCase): ...@@ -477,7 +476,8 @@ class FailingServerTestCase(unittest.TestCase):
# reset flag # reset flag
xmlrpc.server.SimpleXMLRPCServer._send_traceback_header = False xmlrpc.server.SimpleXMLRPCServer._send_traceback_header = False
# reset message class # reset message class
xmlrpc.server.SimpleXMLRPCRequestHandler.MessageClass = mimetools.Message default_class = http.client.HTTPMessage
xmlrpc.server.SimpleXMLRPCRequestHandler.MessageClass = default_class
def test_basic(self): def test_basic(self):
# check that flag is false by default # check that flag is false by default
...@@ -529,8 +529,8 @@ class FailingServerTestCase(unittest.TestCase): ...@@ -529,8 +529,8 @@ class FailingServerTestCase(unittest.TestCase):
if not is_unavailable_exception(e) and hasattr(e, "headers"): if not is_unavailable_exception(e) and hasattr(e, "headers"):
# We should get error info in the response # We should get error info in the response
expected_err = "invalid literal for int() with base 10: 'I am broken'" expected_err = "invalid literal for int() with base 10: 'I am broken'"
self.assertEqual(e.headers.get("x-exception"), expected_err) self.assertEqual(e.headers.get("X-exception"), expected_err)
self.assertTrue(e.headers.get("x-traceback") is not None) self.assertTrue(e.headers.get("X-traceback") is not None)
else: else:
self.fail('ProtocolError not raised') self.fail('ProtocolError not raised')
......
...@@ -17,12 +17,14 @@ The object returned by URLopener().open(file) will differ per ...@@ -17,12 +17,14 @@ The object returned by URLopener().open(file) will differ per
protocol. All you know is that is has methods read(), readline(), protocol. All you know is that is has methods read(), readline(),
readlines(), fileno(), close() and info(). The read*(), fileno() readlines(), fileno(), close() and info(). The read*(), fileno()
and close() methods work like those of open files. and close() methods work like those of open files.
The info() method returns a mimetools.Message object which can be The info() method returns a email.message.Message object which can be
used to query various info about the object, if available. used to query various info about the object, if available.
(mimetools.Message objects are queried with the getheader() method.) (email.message.Message objects provide a dict-like interface.)
""" """
import http.client import http.client
import email.message
import email
import os import os
import socket import socket
import sys import sys
...@@ -414,8 +416,7 @@ class URLopener: ...@@ -414,8 +416,7 @@ class URLopener:
def open_local_file(self, url): def open_local_file(self, url):
"""Use local file.""" """Use local file."""
import mimetypes, mimetools, email.utils import mimetypes, email.utils
from io import StringIO
host, file = splithost(url) host, file = splithost(url)
localname = url2pathname(file) localname = url2pathname(file)
try: try:
...@@ -425,9 +426,9 @@ class URLopener: ...@@ -425,9 +426,9 @@ class URLopener:
size = stats.st_size size = stats.st_size
modified = email.utils.formatdate(stats.st_mtime, usegmt=True) modified = email.utils.formatdate(stats.st_mtime, usegmt=True)
mtype = mimetypes.guess_type(url)[0] mtype = mimetypes.guess_type(url)[0]
headers = mimetools.Message(StringIO( headers = email.message_from_string(
'Content-Type: %s\nContent-Length: %d\nLast-modified: %s\n' % 'Content-Type: %s\nContent-Length: %d\nLast-modified: %s\n' %
(mtype or 'text/plain', size, modified))) (mtype or 'text/plain', size, modified))
if not host: if not host:
urlfile = file urlfile = file
if file[:1] == '/': if file[:1] == '/':
...@@ -448,8 +449,7 @@ class URLopener: ...@@ -448,8 +449,7 @@ class URLopener:
"""Use FTP protocol.""" """Use FTP protocol."""
if not isinstance(url, str): if not isinstance(url, str):
raise IOError('ftp error', 'proxy support for ftp protocol currently not implemented') raise IOError('ftp error', 'proxy support for ftp protocol currently not implemented')
import mimetypes, mimetools import mimetypes
from io import StringIO
host, path = splithost(url) host, path = splithost(url)
if not host: raise IOError('ftp error', 'no host given') if not host: raise IOError('ftp error', 'no host given')
host, port = splitport(host) host, port = splitport(host)
...@@ -498,7 +498,7 @@ class URLopener: ...@@ -498,7 +498,7 @@ class URLopener:
headers += "Content-Type: %s\n" % mtype headers += "Content-Type: %s\n" % mtype
if retrlen is not None and retrlen >= 0: if retrlen is not None and retrlen >= 0:
headers += "Content-Length: %d\n" % retrlen headers += "Content-Length: %d\n" % retrlen
headers = mimetools.Message(StringIO(headers)) headers = email.message_from_string(headers)
return addinfourl(fp, headers, "ftp:" + url) return addinfourl(fp, headers, "ftp:" + url)
except ftperrors() as msg: except ftperrors() as msg:
raise IOError('ftp error', msg).with_traceback(sys.exc_info()[2]) raise IOError('ftp error', msg).with_traceback(sys.exc_info()[2])
...@@ -514,7 +514,6 @@ class URLopener: ...@@ -514,7 +514,6 @@ class URLopener:
# mediatype := [ type "/" subtype ] *( ";" parameter ) # mediatype := [ type "/" subtype ] *( ";" parameter )
# data := *urlchar # data := *urlchar
# parameter := attribute "=" value # parameter := attribute "=" value
import mimetools
from io import StringIO from io import StringIO
try: try:
[type, data] = url.split(',', 1) [type, data] = url.split(',', 1)
...@@ -541,8 +540,8 @@ class URLopener: ...@@ -541,8 +540,8 @@ class URLopener:
msg.append('') msg.append('')
msg.append(data) msg.append(data)
msg = '\n'.join(msg) msg = '\n'.join(msg)
headers = email.message_from_string(msg)
f = StringIO(msg) f = StringIO(msg)
headers = mimetools.Message(f, 0)
#f.fileno = None # needed for addinfourl #f.fileno = None # needed for addinfourl
return addinfourl(f, headers, url) return addinfourl(f, headers, url)
...@@ -761,13 +760,10 @@ def ftperrors(): ...@@ -761,13 +760,10 @@ def ftperrors():
_noheaders = None _noheaders = None
def noheaders(): def noheaders():
"""Return an empty mimetools.Message object.""" """Return an empty email.message.Message object."""
global _noheaders global _noheaders
if _noheaders is None: if _noheaders is None:
import mimetools _noheaders = email.message.Message()
from io import StringIO
_noheaders = mimetools.Message(StringIO(), 0)
_noheaders.fp.close() # Recycle file descriptor
return _noheaders return _noheaders
......
...@@ -91,7 +91,7 @@ import base64 ...@@ -91,7 +91,7 @@ import base64
import hashlib import hashlib
import http.client import http.client
import io import io
import mimetools import email
import os import os
import posixpath import posixpath
import random import random
...@@ -549,9 +549,9 @@ class HTTPRedirectHandler(BaseHandler): ...@@ -549,9 +549,9 @@ class HTTPRedirectHandler(BaseHandler):
# Some servers (incorrectly) return multiple Location headers # Some servers (incorrectly) return multiple Location headers
# (so probably same goes for URI). Use first header. # (so probably same goes for URI). Use first header.
if 'location' in headers: if 'location' in headers:
newurl = headers.getheaders('location')[0] newurl = headers['location']
elif 'uri' in headers: elif 'uri' in headers:
newurl = headers.getheaders('uri')[0] newurl = headers['uri']
else: else:
return return
newurl = urlparse.urljoin(req.get_full_url(), newurl) newurl = urlparse.urljoin(req.get_full_url(), newurl)
...@@ -1050,7 +1050,7 @@ class AbstractHTTPHandler(BaseHandler): ...@@ -1050,7 +1050,7 @@ class AbstractHTTPHandler(BaseHandler):
http_class must implement the HTTPConnection API from http.client. http_class must implement the HTTPConnection API from http.client.
The addinfourl return value is a file-like object. It also The addinfourl return value is a file-like object. It also
has methods and attributes including: has methods and attributes including:
- info(): return a mimetools.Message object for the headers - info(): return a email.message.Message object for the headers
- geturl(): return the original request URL - geturl(): return the original request URL
- code: HTTP status code - code: HTTP status code
""" """
...@@ -1140,6 +1140,10 @@ def parse_keqv_list(l): ...@@ -1140,6 +1140,10 @@ def parse_keqv_list(l):
"""Parse list of key=value strings where keys are not duplicated.""" """Parse list of key=value strings where keys are not duplicated."""
parsed = {} parsed = {}
for elt in l: for elt in l:
# Because of a trailing comma in the auth string, elt could be the
# empty string.
if not elt:
continue
k, v = elt.split('=', 1) k, v = elt.split('=', 1)
if v[0] == '"' and v[-1] == '"': if v[0] == '"' and v[-1] == '"':
v = v[1:-1] v = v[1:-1]
...@@ -1222,9 +1226,9 @@ class FileHandler(BaseHandler): ...@@ -1222,9 +1226,9 @@ class FileHandler(BaseHandler):
size = stats.st_size size = stats.st_size
modified = email.utils.formatdate(stats.st_mtime, usegmt=True) modified = email.utils.formatdate(stats.st_mtime, usegmt=True)
mtype = mimetypes.guess_type(file)[0] mtype = mimetypes.guess_type(file)[0]
headers = mimetools.Message(StringIO( headers = email.message_from_string(
'Content-type: %s\nContent-length: %d\nLast-modified: %s\n' % 'Content-type: %s\nContent-length: %d\nLast-modified: %s\n' %
(mtype or 'text/plain', size, modified))) (mtype or 'text/plain', size, modified))
if host: if host:
host, port = splitport(host) host, port = splitport(host)
if not host or \ if not host or \
...@@ -1290,8 +1294,8 @@ class FTPHandler(BaseHandler): ...@@ -1290,8 +1294,8 @@ class FTPHandler(BaseHandler):
headers += "Content-type: %s\n" % mtype headers += "Content-type: %s\n" % mtype
if retrlen is not None and retrlen >= 0: if retrlen is not None and retrlen >= 0:
headers += "Content-length: %d\n" % retrlen headers += "Content-length: %d\n" % retrlen
headers = email.message_from_string(headers)
sf = StringIO(headers) sf = StringIO(headers)
headers = mimetools.Message(sf)
return addinfourl(fp, headers, req.get_full_url()) return addinfourl(fp, headers, req.get_full_url())
except ftplib.all_errors as msg: except ftplib.all_errors as msg:
raise URLError('ftp error: %s' % msg).with_traceback(sys.exc_info()[2]) raise URLError('ftp error: %s' % msg).with_traceback(sys.exc_info()[2])
......
...@@ -101,16 +101,16 @@ class WSGIRequestHandler(BaseHTTPRequestHandler): ...@@ -101,16 +101,16 @@ class WSGIRequestHandler(BaseHTTPRequestHandler):
env['REMOTE_HOST'] = host env['REMOTE_HOST'] = host
env['REMOTE_ADDR'] = self.client_address[0] env['REMOTE_ADDR'] = self.client_address[0]
if self.headers.typeheader is None: if self.headers.get('content-type') is None:
env['CONTENT_TYPE'] = self.headers.type env['CONTENT_TYPE'] = self.headers.get_content_type()
else: else:
env['CONTENT_TYPE'] = self.headers.typeheader env['CONTENT_TYPE'] = self.headers['content-type']
length = self.headers.getheader('content-length') length = self.headers.get('content-length')
if length: if length:
env['CONTENT_LENGTH'] = length env['CONTENT_LENGTH'] = length
for h in self.headers.headers: for h in self.headers:
k,v = h.split(':',1) k,v = h.split(':',1)
k=k.replace('-','_').upper(); v=v.strip() k=k.replace('-','_').upper(); v=v.strip()
if k in env: if k in env:
......
...@@ -14,7 +14,7 @@ class ErrorMessage(rfc822.Message): ...@@ -14,7 +14,7 @@ class ErrorMessage(rfc822.Message):
self.sub = '' self.sub = ''
def is_warning(self): def is_warning(self):
sub = self.getheader('Subject') sub = self.get('Subject')
if not sub: if not sub:
return 0 return 0
sub = sub.lower() sub = sub.lower()
......
...@@ -53,7 +53,7 @@ def _check1version(package, url, version, verbose=0): ...@@ -53,7 +53,7 @@ def _check1version(package, url, version, verbose=0):
print(' Cannot open:', arg) print(' Cannot open:', arg)
return -1, None, None return -1, None, None
msg = rfc822.Message(fp, seekable=0) msg = rfc822.Message(fp, seekable=0)
newversion = msg.getheader('current-version') newversion = msg.get('current-version')
if not newversion: if not newversion:
if verbose >= VERBOSE_EACHFILE: if verbose >= VERBOSE_EACHFILE:
print(' No "Current-Version:" header in URL or URL not found') print(' No "Current-Version:" header in URL or URL not found')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment