mirror of
https://github.com/python/cpython.git
synced 2025-10-03 05:35:59 +00:00
Issue #16220: wsgiref now always calls close() on an iterable response.
Patch by Brent Tubbs.
This commit is contained in:
commit
d3a3e640b6
4 changed files with 29 additions and 93 deletions
|
@ -41,9 +41,6 @@ class MockHandler(WSGIRequestHandler):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def hello_app(environ,start_response):
|
def hello_app(environ,start_response):
|
||||||
start_response("200 OK", [
|
start_response("200 OK", [
|
||||||
('Content-Type','text/plain'),
|
('Content-Type','text/plain'),
|
||||||
|
@ -65,28 +62,6 @@ def run_amock(app=hello_app, data=b"GET / HTTP/1.0\n\n"):
|
||||||
|
|
||||||
return out.getvalue(), err.getvalue()
|
return out.getvalue(), err.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def compare_generic_iter(make_it,match):
|
def compare_generic_iter(make_it,match):
|
||||||
"""Utility to compare a generic 2.1/2.2+ iterator with an iterable
|
"""Utility to compare a generic 2.1/2.2+ iterator with an iterable
|
||||||
|
|
||||||
|
@ -124,10 +99,6 @@ def compare_generic_iter(make_it,match):
|
||||||
raise AssertionError("Too many items from .__next__()", it)
|
raise AssertionError("Too many items from .__next__()", it)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class IntegrationTests(TestCase):
|
class IntegrationTests(TestCase):
|
||||||
|
|
||||||
def check_hello(self, out, has_length=True):
|
def check_hello(self, out, has_length=True):
|
||||||
|
@ -201,8 +172,6 @@ class IntegrationTests(TestCase):
|
||||||
out)
|
out)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class UtilityTests(TestCase):
|
class UtilityTests(TestCase):
|
||||||
|
|
||||||
def checkShift(self,sn_in,pi_in,part,sn_out,pi_out):
|
def checkShift(self,sn_in,pi_in,part,sn_out,pi_out):
|
||||||
|
@ -241,11 +210,6 @@ class UtilityTests(TestCase):
|
||||||
util.setup_testing_defaults(kw)
|
util.setup_testing_defaults(kw)
|
||||||
self.assertEqual(util.request_uri(kw,query),uri)
|
self.assertEqual(util.request_uri(kw,query),uri)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def checkFW(self,text,size,match):
|
def checkFW(self,text,size,match):
|
||||||
|
|
||||||
def make_it(text=text,size=size):
|
def make_it(text=text,size=size):
|
||||||
|
@ -264,7 +228,6 @@ class UtilityTests(TestCase):
|
||||||
it.close()
|
it.close()
|
||||||
self.assertTrue(it.filelike.closed)
|
self.assertTrue(it.filelike.closed)
|
||||||
|
|
||||||
|
|
||||||
def testSimpleShifts(self):
|
def testSimpleShifts(self):
|
||||||
self.checkShift('','/', '', '/', '')
|
self.checkShift('','/', '', '/', '')
|
||||||
self.checkShift('','/x', 'x', '/x', '')
|
self.checkShift('','/x', 'x', '/x', '')
|
||||||
|
@ -272,7 +235,6 @@ class UtilityTests(TestCase):
|
||||||
self.checkShift('/a','/x/y', 'x', '/a/x', '/y')
|
self.checkShift('/a','/x/y', 'x', '/a/x', '/y')
|
||||||
self.checkShift('/a','/x/', 'x', '/a/x', '/')
|
self.checkShift('/a','/x/', 'x', '/a/x', '/')
|
||||||
|
|
||||||
|
|
||||||
def testNormalizedShifts(self):
|
def testNormalizedShifts(self):
|
||||||
self.checkShift('/a/b', '/../y', '..', '/a', '/y')
|
self.checkShift('/a/b', '/../y', '..', '/a', '/y')
|
||||||
self.checkShift('', '/../y', '..', '', '/y')
|
self.checkShift('', '/../y', '..', '', '/y')
|
||||||
|
@ -286,7 +248,6 @@ class UtilityTests(TestCase):
|
||||||
self.checkShift('/a/b', '/x//', 'x', '/a/b/x', '/')
|
self.checkShift('/a/b', '/x//', 'x', '/a/b/x', '/')
|
||||||
self.checkShift('/a/b', '/.', None, '/a/b', '')
|
self.checkShift('/a/b', '/.', None, '/a/b', '')
|
||||||
|
|
||||||
|
|
||||||
def testDefaults(self):
|
def testDefaults(self):
|
||||||
for key, value in [
|
for key, value in [
|
||||||
('SERVER_NAME','127.0.0.1'),
|
('SERVER_NAME','127.0.0.1'),
|
||||||
|
@ -306,7 +267,6 @@ class UtilityTests(TestCase):
|
||||||
]:
|
]:
|
||||||
self.checkDefault(key,value)
|
self.checkDefault(key,value)
|
||||||
|
|
||||||
|
|
||||||
def testCrossDefaults(self):
|
def testCrossDefaults(self):
|
||||||
self.checkCrossDefault('HTTP_HOST',"foo.bar",SERVER_NAME="foo.bar")
|
self.checkCrossDefault('HTTP_HOST',"foo.bar",SERVER_NAME="foo.bar")
|
||||||
self.checkCrossDefault('wsgi.url_scheme',"https",HTTPS="on")
|
self.checkCrossDefault('wsgi.url_scheme',"https",HTTPS="on")
|
||||||
|
@ -316,7 +276,6 @@ class UtilityTests(TestCase):
|
||||||
self.checkCrossDefault('SERVER_PORT',"80",HTTPS="foo")
|
self.checkCrossDefault('SERVER_PORT',"80",HTTPS="foo")
|
||||||
self.checkCrossDefault('SERVER_PORT',"443",HTTPS="on")
|
self.checkCrossDefault('SERVER_PORT',"443",HTTPS="on")
|
||||||
|
|
||||||
|
|
||||||
def testGuessScheme(self):
|
def testGuessScheme(self):
|
||||||
self.assertEqual(util.guess_scheme({}), "http")
|
self.assertEqual(util.guess_scheme({}), "http")
|
||||||
self.assertEqual(util.guess_scheme({'HTTPS':"foo"}), "http")
|
self.assertEqual(util.guess_scheme({'HTTPS':"foo"}), "http")
|
||||||
|
@ -324,10 +283,6 @@ class UtilityTests(TestCase):
|
||||||
self.assertEqual(util.guess_scheme({'HTTPS':"yes"}), "https")
|
self.assertEqual(util.guess_scheme({'HTTPS':"yes"}), "https")
|
||||||
self.assertEqual(util.guess_scheme({'HTTPS':"1"}), "https")
|
self.assertEqual(util.guess_scheme({'HTTPS':"1"}), "https")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def testAppURIs(self):
|
def testAppURIs(self):
|
||||||
self.checkAppURI("http://127.0.0.1/")
|
self.checkAppURI("http://127.0.0.1/")
|
||||||
self.checkAppURI("http://127.0.0.1/spam", SCRIPT_NAME="/spam")
|
self.checkAppURI("http://127.0.0.1/spam", SCRIPT_NAME="/spam")
|
||||||
|
@ -452,15 +407,6 @@ class TestHandler(ErrorHandler):
|
||||||
raise # for testing, we want to see what's happening
|
raise # for testing, we want to see what's happening
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class HandlerTests(TestCase):
|
class HandlerTests(TestCase):
|
||||||
|
|
||||||
def checkEnvironAttrs(self, handler):
|
def checkEnvironAttrs(self, handler):
|
||||||
|
@ -501,7 +447,6 @@ class HandlerTests(TestCase):
|
||||||
h=TestHandler(); h.setup_environ()
|
h=TestHandler(); h.setup_environ()
|
||||||
self.assertEqual(h.environ['wsgi.url_scheme'],'http')
|
self.assertEqual(h.environ['wsgi.url_scheme'],'http')
|
||||||
|
|
||||||
|
|
||||||
def testAbstractMethods(self):
|
def testAbstractMethods(self):
|
||||||
h = BaseHandler()
|
h = BaseHandler()
|
||||||
for name in [
|
for name in [
|
||||||
|
@ -510,7 +455,6 @@ class HandlerTests(TestCase):
|
||||||
self.assertRaises(NotImplementedError, getattr(h,name))
|
self.assertRaises(NotImplementedError, getattr(h,name))
|
||||||
self.assertRaises(NotImplementedError, h._write, "test")
|
self.assertRaises(NotImplementedError, h._write, "test")
|
||||||
|
|
||||||
|
|
||||||
def testContentLength(self):
|
def testContentLength(self):
|
||||||
# Demo one reason iteration is better than write()... ;)
|
# Demo one reason iteration is better than write()... ;)
|
||||||
|
|
||||||
|
@ -602,7 +546,6 @@ class HandlerTests(TestCase):
|
||||||
"\r\n".encode("iso-8859-1")+MSG))
|
"\r\n".encode("iso-8859-1")+MSG))
|
||||||
self.assertIn("AssertionError", h.stderr.getvalue())
|
self.assertIn("AssertionError", h.stderr.getvalue())
|
||||||
|
|
||||||
|
|
||||||
def testHeaderFormats(self):
|
def testHeaderFormats(self):
|
||||||
|
|
||||||
def non_error_app(e,s):
|
def non_error_app(e,s):
|
||||||
|
@ -662,40 +605,27 @@ class HandlerTests(TestCase):
|
||||||
b"data",
|
b"data",
|
||||||
h.stdout.getvalue())
|
h.stdout.getvalue())
|
||||||
|
|
||||||
# This epilogue is needed for compatibility with the Python 2.5 regrtest module
|
def testCloseOnError(self):
|
||||||
|
side_effects = {'close_called': False}
|
||||||
|
MSG = b"Some output has been sent"
|
||||||
|
def error_app(e,s):
|
||||||
|
s("200 OK",[])(MSG)
|
||||||
|
class CrashyIterable(object):
|
||||||
|
def __iter__(self):
|
||||||
|
while True:
|
||||||
|
yield b'blah'
|
||||||
|
raise AssertionError("This should be caught by handler")
|
||||||
|
def close(self):
|
||||||
|
side_effects['close_called'] = True
|
||||||
|
return CrashyIterable()
|
||||||
|
|
||||||
|
h = ErrorHandler()
|
||||||
|
h.run(error_app)
|
||||||
|
self.assertEqual(side_effects['close_called'], True)
|
||||||
|
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
support.run_unittest(__name__)
|
support.run_unittest(__name__)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test_main()
|
test_main()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# the above lines intentionally left blank
|
|
||||||
|
|
|
@ -174,11 +174,13 @@ class BaseHandler:
|
||||||
in the event loop to iterate over the data, and to call
|
in the event loop to iterate over the data, and to call
|
||||||
'self.close()' once the response is finished.
|
'self.close()' once the response is finished.
|
||||||
"""
|
"""
|
||||||
if not self.result_is_file() or not self.sendfile():
|
try:
|
||||||
for data in self.result:
|
if not self.result_is_file() or not self.sendfile():
|
||||||
self.write(data)
|
for data in self.result:
|
||||||
self.finish_content()
|
self.write(data)
|
||||||
self.close()
|
self.finish_content()
|
||||||
|
finally:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
def get_scheme(self):
|
def get_scheme(self):
|
||||||
|
|
|
@ -1178,6 +1178,7 @@ Alberto Trevino
|
||||||
Matthias Troffaes
|
Matthias Troffaes
|
||||||
John Tromp
|
John Tromp
|
||||||
Jason Trowbridge
|
Jason Trowbridge
|
||||||
|
Brent Tubbs
|
||||||
Anthony Tuininga
|
Anthony Tuininga
|
||||||
Erno Tukia
|
Erno Tukia
|
||||||
David Turner
|
David Turner
|
||||||
|
|
|
@ -59,6 +59,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #16220: wsgiref now always calls close() on an iterable response.
|
||||||
|
Patch by Brent Tubbs.
|
||||||
|
|
||||||
- Issue #16270: urllib may hang when used for retrieving files via FTP by using
|
- Issue #16270: urllib may hang when used for retrieving files via FTP by using
|
||||||
a context manager. Patch by Giampaolo Rodola'.
|
a context manager. Patch by Giampaolo Rodola'.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue