Skip to content

Commit f0526b9

Browse files
authored
Updated the wsgiref + xmlrpc libraries + associated tests - v3.13.11 (#6634)
* Updated the wsgiref library + test * Uncommented fixed wsgi tests * Updated the xmlrpc library + test * Added expectedFailure to test_request_length in test_wsgiref * Annotated doc_xmlrpc test failures/errors
1 parent eea6cc4 commit f0526b9

File tree

11 files changed

+179
-172
lines changed

11 files changed

+179
-172
lines changed

Lib/test/test_docxmlrpc.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,6 @@ def test_valid_get_response(self):
100100
# Server raises an exception if we don't start to read the data
101101
response.read()
102102

103-
# TODO: RUSTPYTHON
104-
@unittest.expectedFailure
105103
def test_get_css(self):
106104
self.client.request("GET", "/pydoc.css")
107105
response = self.client.getresponse()
@@ -121,6 +119,7 @@ def test_invalid_get_response(self):
121119

122120
response.read()
123121

122+
@unittest.skip('TODO: RUSTPYTHON; http.client.RemoteDisconnected: Remote end closed connection without response')
124123
def test_lambda(self):
125124
"""Test that lambda functionality stays the same. The output produced
126125
currently is, I suspect invalid because of the unencoded brackets in the
@@ -163,6 +162,7 @@ def test_autolinking(self):
163162

164163
@make_request_and_skipIf(sys.flags.optimize >= 2,
165164
"Docstrings are omitted with -O2 and above")
165+
@unittest.skip('TODO: RUSTPYTHON; http.client.RemoteDisconnected: Remote end closed connection without response')
166166
def test_system_methods(self):
167167
"""Test the presence of three consecutive system.* methods.
168168
@@ -190,6 +190,7 @@ def test_system_methods(self):
190190
b'<br>\nThis&nbsp;server&nbsp;does&nbsp;NOT&nbsp;support&nbsp;system'
191191
b'.methodSignature.</tt></dd></dl>'), response)
192192

193+
@unittest.skip('TODO: RUSTPYTHON; http.client.RemoteDisconnected: Remote end closed connection without response')
193194
def test_autolink_dotted_methods(self):
194195
"""Test that selfdot values are made strong automatically in the
195196
documentation."""
@@ -199,6 +200,7 @@ def test_autolink_dotted_methods(self):
199200
self.assertIn(b"""Try&nbsp;self.<strong>add</strong>,&nbsp;too.""",
200201
response.read())
201202

203+
@unittest.skip('TODO: RUSTPYTHON; http.client.RemoteDisconnected: Remote end closed connection without response')
202204
def test_annotations(self):
203205
""" Test that annotations works as expected """
204206
self.client.request("GET", "/")
@@ -212,6 +214,7 @@ def test_annotations(self):
212214
b'method_annotation</strong></a>(x: bytes)</dt></dl>'),
213215
response.read())
214216

217+
@unittest.skip('TODO: RUSTPYTHON; TypeError: HTMLDoc.heading() missing 2 required positional arguments: "fgcol" and "bgcol"')
215218
def test_server_title_escape(self):
216219
# bpo-38243: Ensure that the server title and documentation
217220
# are escaped for HTML.

Lib/test/test_wsgiref.py

Lines changed: 21 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from unittest import mock
22
from test import support
3-
from test.support import warnings_helper
3+
from test.support import socket_helper
44
from test.test_httpservers import NoLogRequestHandler
55
from unittest import TestCase
66
from wsgiref.util import setup_testing_defaults
@@ -80,41 +80,26 @@ def run_amock(app=hello_app, data=b"GET / HTTP/1.0\n\n"):
8080

8181
return out.getvalue(), err.getvalue()
8282

83-
def compare_generic_iter(make_it,match):
84-
"""Utility to compare a generic 2.1/2.2+ iterator with an iterable
8583

86-
If running under Python 2.2+, this tests the iterator using iter()/next(),
87-
as well as __getitem__. 'make_it' must be a function returning a fresh
84+
def compare_generic_iter(make_it, match):
85+
"""Utility to compare a generic iterator with an iterable
86+
87+
This tests the iterator using iter()/next().
88+
'make_it' must be a function returning a fresh
8889
iterator to be tested (since this may test the iterator twice)."""
8990

9091
it = make_it()
91-
n = 0
92+
if not iter(it) is it:
93+
raise AssertionError
9294
for item in match:
93-
if not it[n]==item: raise AssertionError
94-
n+=1
95-
try:
96-
it[n]
97-
except IndexError:
98-
pass
99-
else:
100-
raise AssertionError("Too many items from __getitem__",it)
101-
95+
if not next(it) == item:
96+
raise AssertionError
10297
try:
103-
iter, StopIteration
104-
except NameError:
98+
next(it)
99+
except StopIteration:
105100
pass
106101
else:
107-
# Only test iter mode under 2.2+
108-
it = make_it()
109-
if not iter(it) is it: raise AssertionError
110-
for item in match:
111-
if not next(it) == item: raise AssertionError
112-
try:
113-
next(it)
114-
except StopIteration:
115-
pass
116-
else:
117-
raise AssertionError("Too many items from .__next__()", it)
102+
raise AssertionError("Too many items from .__next__()", it)
118103

119104

120105
class IntegrationTests(TestCase):
@@ -149,10 +134,11 @@ def test_environ(self):
149134
b"Python test,Python test 2;query=test;/path/"
150135
)
151136

137+
@unittest.expectedFailure # TODO: RUSTPYTHON; http library needs to be updated
152138
def test_request_length(self):
153139
out, err = run_amock(data=b"GET " + (b"x" * 65537) + b" HTTP/1.0\n\n")
154140
self.assertEqual(out.splitlines()[0],
155-
b"HTTP/1.0 414 Request-URI Too Long")
141+
b"HTTP/1.0 414 URI Too Long")
156142

157143
def test_validated_hello(self):
158144
out, err = run_amock(validator(hello_app))
@@ -264,7 +250,7 @@ def app(environ, start_response):
264250
class WsgiHandler(NoLogRequestHandler, WSGIRequestHandler):
265251
pass
266252

267-
server = make_server(support.HOST, 0, app, handler_class=WsgiHandler)
253+
server = make_server(socket_helper.HOST, 0, app, handler_class=WsgiHandler)
268254
self.addCleanup(server.server_close)
269255
interrupted = threading.Event()
270256

@@ -339,7 +325,6 @@ def checkReqURI(self,uri,query=1,**kw):
339325
util.setup_testing_defaults(kw)
340326
self.assertEqual(util.request_uri(kw,query),uri)
341327

342-
@warnings_helper.ignore_warnings(category=DeprecationWarning)
343328
def checkFW(self,text,size,match):
344329

345330
def make_it(text=text,size=size):
@@ -358,15 +343,6 @@ def make_it(text=text,size=size):
358343
it.close()
359344
self.assertTrue(it.filelike.closed)
360345

361-
# TODO: RUSTPYTHON
362-
@unittest.expectedFailure
363-
def test_filewrapper_getitem_deprecation(self):
364-
wrapper = util.FileWrapper(StringIO('foobar'), 3)
365-
with self.assertWarnsRegex(DeprecationWarning,
366-
r'Use iterator protocol instead'):
367-
# This should have returned 'bar'.
368-
self.assertEqual(wrapper[1], 'foo')
369-
370346
def testSimpleShifts(self):
371347
self.checkShift('','/', '', '/', '')
372348
self.checkShift('','/x', 'x', '/x', '')
@@ -473,6 +449,10 @@ def testHopByHop(self):
473449
for alt in hop, hop.title(), hop.upper(), hop.lower():
474450
self.assertFalse(util.is_hop_by_hop(alt))
475451

452+
@unittest.expectedFailure # TODO: RUSTPYTHON
453+
def test_filewrapper_getitem_deprecation(self):
454+
return super().test_filewrapper_getitem_deprecation()
455+
476456
class HeaderTests(TestCase):
477457

478458
def testMappingInterface(self):
@@ -581,7 +561,7 @@ def testEnviron(self):
581561
# Test handler.environ as a dict
582562
expected = {}
583563
setup_testing_defaults(expected)
584-
# Handler inherits os_environ variables which are not overriden
564+
# Handler inherits os_environ variables which are not overridden
585565
# by SimpleHandler.add_cgi_vars() (SimpleHandler.base_env)
586566
for key, value in os_environ.items():
587567
if key not in expected:
@@ -821,8 +801,6 @@ def flush(self):
821801
b"Hello, world!",
822802
written)
823803

824-
# TODO: RUSTPYTHON
825-
@unittest.expectedFailure
826804
def testClientConnectionTerminations(self):
827805
environ = {"SERVER_PROTOCOL": "HTTP/1.0"}
828806
for exception in (
@@ -841,8 +819,6 @@ def write(self, b):
841819

842820
self.assertFalse(stderr.getvalue())
843821

844-
# TODO: RUSTPYTHON
845-
@unittest.expectedFailure
846822
def testDontResetInternalStateOnException(self):
847823
class CustomException(ValueError):
848824
pass

Lib/test/test_xmlrpc.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,55 +1042,46 @@ def test_path2(self):
10421042
self.assertEqual(p.add(6,8), 6+8)
10431043
self.assertRaises(xmlrpclib.Fault, p.pow, 6, 8)
10441044

1045-
@unittest.expectedFailure # TODO: RUSTPYTHON
10461045
@support.requires_resource('walltime')
10471046
def test_path3(self):
10481047
p = xmlrpclib.ServerProxy(URL+"/is/broken")
10491048
self.assertRaises(xmlrpclib.Fault, p.add, 6, 8)
10501049

1051-
@unittest.expectedFailure # TODO: RUSTPYTHON
10521050
@support.requires_resource('walltime')
10531051
def test_invalid_path(self):
10541052
p = xmlrpclib.ServerProxy(URL+"/invalid")
10551053
self.assertRaises(xmlrpclib.Fault, p.add, 6, 8)
10561054

1057-
@unittest.expectedFailure # TODO: RUSTPYTHON
10581055
@support.requires_resource('walltime')
10591056
def test_path_query_fragment(self):
10601057
p = xmlrpclib.ServerProxy(URL+"/foo?k=v#frag")
10611058
self.assertEqual(p.test(), "/foo?k=v#frag")
10621059

1063-
@unittest.expectedFailure # TODO: RUSTPYTHON
10641060
@support.requires_resource('walltime')
10651061
def test_path_fragment(self):
10661062
p = xmlrpclib.ServerProxy(URL+"/foo#frag")
10671063
self.assertEqual(p.test(), "/foo#frag")
10681064

1069-
@unittest.expectedFailure # TODO: RUSTPYTHON
10701065
@support.requires_resource('walltime')
10711066
def test_path_query(self):
10721067
p = xmlrpclib.ServerProxy(URL+"/foo?k=v")
10731068
self.assertEqual(p.test(), "/foo?k=v")
10741069

1075-
@unittest.expectedFailure # TODO: RUSTPYTHON
10761070
@support.requires_resource('walltime')
10771071
def test_empty_path(self):
10781072
p = xmlrpclib.ServerProxy(URL)
10791073
self.assertEqual(p.test(), "/RPC2")
10801074

1081-
@unittest.expectedFailure # TODO: RUSTPYTHON
10821075
@support.requires_resource('walltime')
10831076
def test_root_path(self):
10841077
p = xmlrpclib.ServerProxy(URL + "/")
10851078
self.assertEqual(p.test(), "/")
10861079

1087-
@unittest.expectedFailure # TODO: RUSTPYTHON
10881080
@support.requires_resource('walltime')
10891081
def test_empty_path_query(self):
10901082
p = xmlrpclib.ServerProxy(URL + "?k=v")
10911083
self.assertEqual(p.test(), "?k=v")
10921084

1093-
@unittest.expectedFailure # TODO: RUSTPYTHON
10941085
@support.requires_resource('walltime')
10951086
def test_empty_path_fragment(self):
10961087
p = xmlrpclib.ServerProxy(URL + "#frag")
@@ -1142,7 +1133,6 @@ def test_two(self):
11421133

11431134
#test special attribute access on the serverproxy, through the __call__
11441135
#function.
1145-
@unittest.skip("TODO: RUSTPYTHON, appears to hang")
11461136
class KeepaliveServerTestCase2(BaseKeepaliveServerTestCase):
11471137
#ask for two keepalive requests to be handled.
11481138
request_count=2

Lib/wsgiref/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
* validate -- validation wrapper that sits between an app and a server
1414
to detect errors in either
1515
16+
* types -- collection of WSGI-related types for static type checking
17+
1618
To-Do:
1719
1820
* cgi_gateway -- Run WSGI apps under CGI (pending a deployment standard)

Lib/wsgiref/handlers.py

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ def run(self, application):
136136
self.setup_environ()
137137
self.result = application(self.environ, self.start_response)
138138
self.finish_response()
139+
except (ConnectionAbortedError, BrokenPipeError, ConnectionResetError):
140+
# We expect the client to close the connection abruptly from time
141+
# to time.
142+
return
139143
except:
140144
try:
141145
self.handle_error()
@@ -179,7 +183,16 @@ def finish_response(self):
179183
for data in self.result:
180184
self.write(data)
181185
self.finish_content()
182-
finally:
186+
except:
187+
# Call close() on the iterable returned by the WSGI application
188+
# in case of an exception.
189+
if hasattr(self.result, 'close'):
190+
self.result.close()
191+
raise
192+
else:
193+
# We only call close() when no exception is raised, because it
194+
# will set status, result, headers, and environ fields to None.
195+
# See bpo-29183 for more details.
183196
self.close()
184197

185198

@@ -215,8 +228,7 @@ def start_response(self, status, headers,exc_info=None):
215228
if exc_info:
216229
try:
217230
if self.headers_sent:
218-
# Re-raise original exception if headers sent
219-
raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
231+
raise
220232
finally:
221233
exc_info = None # avoid dangling circular ref
222234
elif self.headers is not None:
@@ -225,18 +237,25 @@ def start_response(self, status, headers,exc_info=None):
225237
self.status = status
226238
self.headers = self.headers_class(headers)
227239
status = self._convert_string_type(status, "Status")
228-
assert len(status)>=4,"Status must be at least 4 characters"
229-
assert status[:3].isdigit(), "Status message must begin w/3-digit code"
230-
assert status[3]==" ", "Status message must have a space after code"
240+
self._validate_status(status)
231241

232242
if __debug__:
233243
for name, val in headers:
234244
name = self._convert_string_type(name, "Header name")
235245
val = self._convert_string_type(val, "Header value")
236-
assert not is_hop_by_hop(name),"Hop-by-hop headers not allowed"
246+
assert not is_hop_by_hop(name),\
247+
f"Hop-by-hop header, '{name}: {val}', not allowed"
237248

238249
return self.write
239250

251+
def _validate_status(self, status):
252+
if len(status) < 4:
253+
raise AssertionError("Status must be at least 4 characters")
254+
if not status[:3].isdigit():
255+
raise AssertionError("Status message must begin w/3-digit code")
256+
if status[3] != " ":
257+
raise AssertionError("Status message must have a space after code")
258+
240259
def _convert_string_type(self, value, title):
241260
"""Convert/check value type."""
242261
if type(value) is str:
@@ -456,10 +475,7 @@ def _write(self,data):
456475
from warnings import warn
457476
warn("SimpleHandler.stdout.write() should not do partial writes",
458477
DeprecationWarning)
459-
while True:
460-
data = data[result:]
461-
if not data:
462-
break
478+
while data := data[result:]:
463479
result = self.stdout.write(data)
464480

465481
def _flush(self):

Lib/wsgiref/simple_server.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,6 @@ def get_environ(self):
8484

8585
env['PATH_INFO'] = urllib.parse.unquote(path, 'iso-8859-1')
8686
env['QUERY_STRING'] = query
87-
88-
host = self.address_string()
89-
if host != self.client_address[0]:
90-
env['REMOTE_HOST'] = host
9187
env['REMOTE_ADDR'] = self.client_address[0]
9288

9389
if self.headers.get('content-type') is None:
@@ -127,7 +123,8 @@ def handle(self):
127123
return
128124

129125
handler = ServerHandler(
130-
self.rfile, self.wfile, self.get_stderr(), self.get_environ()
126+
self.rfile, self.wfile, self.get_stderr(), self.get_environ(),
127+
multithread=False,
131128
)
132129
handler.request_handler = self # backpointer for logging
133130
handler.run(self.server.get_app())

0 commit comments

Comments
 (0)