Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e159422

Browse files
committed
Update wsgiref for PEP 3333, and fix errors introduced into the test suite by converting type() checks to isinstance().
(When WSGI specifies a built-in type, it does NOT mean "this type or a subclass" -- it means 'type(x) is SpecifiedType'.)
1 parent 5a43f72 commit e159422

4 files changed

Lines changed: 67 additions & 93 deletions

File tree

Lib/test/test_wsgiref.py

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def hello_app(environ,start_response):
4747
('Content-Type','text/plain'),
4848
('Date','Mon, 05 Jun 2006 18:49:54 GMT')
4949
])
50-
return ["Hello, world!"]
50+
return [b"Hello, world!"]
5151

5252
def run_amock(app=hello_app, data=b"GET / HTTP/1.0\n\n"):
5353
server = make_server("", 80, app, MockServer, MockHandler)
@@ -165,7 +165,7 @@ def bad_app(environ,start_response):
165165
def test_wsgi_input(self):
166166
def bad_app(e,s):
167167
e["wsgi.input"].read()
168-
s(b"200 OK", [(b"Content-Type", b"text/plain; charset=utf-8")])
168+
s("200 OK", [("Content-Type", "text/plain; charset=utf-8")])
169169
return [b"data"]
170170
out, err = run_amock(validator(bad_app))
171171
self.assertTrue(out.endswith(
@@ -177,8 +177,8 @@ def bad_app(e,s):
177177

178178
def test_bytes_validation(self):
179179
def app(e, s):
180-
s(b"200 OK", [
181-
(b"Content-Type", b"text/plain; charset=utf-8"),
180+
s("200 OK", [
181+
("Content-Type", "text/plain; charset=utf-8"),
182182
("Date", "Wed, 24 Dec 2008 13:29:32 GMT"),
183183
])
184184
return [b"data"]
@@ -420,29 +420,6 @@ def testExtras(self):
420420
'\r\n'
421421
)
422422

423-
def testBytes(self):
424-
h = Headers([
425-
(b"Content-Type", b"text/plain; charset=utf-8"),
426-
])
427-
self.assertEqual("text/plain; charset=utf-8", h.get("Content-Type"))
428-
429-
h[b"Foo"] = bytes(b"bar")
430-
self.assertEqual("bar", h.get("Foo"))
431-
self.assertEqual("bar", h.get(b"Foo"))
432-
433-
h.setdefault(b"Bar", b"foo")
434-
self.assertEqual("foo", h.get("Bar"))
435-
self.assertEqual("foo", h.get(b"Bar"))
436-
437-
h.add_header(b'content-disposition', b'attachment',
438-
filename=b'bud.gif')
439-
self.assertEqual('attachment; filename="bud.gif"',
440-
h.get("content-disposition"))
441-
442-
del h['content-disposition']
443-
self.assertNotIn(b'content-disposition', h)
444-
445-
446423
class ErrorHandler(BaseCGIHandler):
447424
"""Simple handler subclass for testing BaseHandler"""
448425

@@ -529,10 +506,10 @@ def testContentLength(self):
529506

530507
def trivial_app1(e,s):
531508
s('200 OK',[])
532-
return [e['wsgi.url_scheme']]
509+
return [e['wsgi.url_scheme'].encode('iso-8859-1')]
533510

534511
def trivial_app2(e,s):
535-
s('200 OK',[])(e['wsgi.url_scheme'])
512+
s('200 OK',[])(e['wsgi.url_scheme'].encode('iso-8859-1'))
536513
return []
537514

538515
def trivial_app3(e,s):
@@ -590,13 +567,13 @@ def error_app(e,s):
590567
("Status: %s\r\n"
591568
"Content-Type: text/plain\r\n"
592569
"Content-Length: %d\r\n"
593-
"\r\n%s" % (h.error_status,len(h.error_body),h.error_body)
594-
).encode("iso-8859-1"))
570+
"\r\n" % (h.error_status,len(h.error_body))).encode('iso-8859-1')
571+
+ h.error_body)
595572

596573
self.assertIn("AssertionError", h.stderr.getvalue())
597574

598575
def testErrorAfterOutput(self):
599-
MSG = "Some output has been sent"
576+
MSG = b"Some output has been sent"
600577
def error_app(e,s):
601578
s("200 OK",[])(MSG)
602579
raise AssertionError("This should be caught by handler")
@@ -605,7 +582,7 @@ def error_app(e,s):
605582
h.run(error_app)
606583
self.assertEqual(h.stdout.getvalue(),
607584
("Status: 200 OK\r\n"
608-
"\r\n"+MSG).encode("iso-8859-1"))
585+
"\r\n".encode("iso-8859-1")+MSG))
609586
self.assertIn("AssertionError", h.stderr.getvalue())
610587

611588

@@ -654,8 +631,8 @@ def non_error_app(e,s):
654631

655632
def testBytesData(self):
656633
def app(e, s):
657-
s(b"200 OK", [
658-
(b"Content-Type", b"text/plain; charset=utf-8"),
634+
s("200 OK", [
635+
("Content-Type", "text/plain; charset=utf-8"),
659636
])
660637
return [b"data"]
661638

Lib/wsgiref/handlers.py

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class BaseHandler:
4646
traceback_limit = None # Print entire traceback to self.get_stderr()
4747
error_status = "500 Internal Server Error"
4848
error_headers = [('Content-Type','text/plain')]
49-
error_body = "A server error occurred. Please contact the administrator."
49+
error_body = b"A server error occurred. Please contact the administrator."
5050

5151
# State variables (don't mess with these)
5252
status = result = None
@@ -137,7 +137,7 @@ def cleanup_headers(self):
137137
self.set_content_length()
138138

139139
def start_response(self, status, headers,exc_info=None):
140-
"""'start_response()' callable as specified by PEP 333"""
140+
"""'start_response()' callable as specified by PEP 3333"""
141141

142142
if exc_info:
143143
try:
@@ -149,49 +149,48 @@ def start_response(self, status, headers,exc_info=None):
149149
elif self.headers is not None:
150150
raise AssertionError("Headers already set!")
151151

152+
self.status = status
153+
self.headers = self.headers_class(headers)
152154
status = self._convert_string_type(status, "Status")
153155
assert len(status)>=4,"Status must be at least 4 characters"
154156
assert int(status[:3]),"Status message must begin w/3-digit code"
155157
assert status[3]==" ", "Status message must have a space after code"
156158

157-
str_headers = []
158-
for name,val in headers:
159-
name = self._convert_string_type(name, "Header name")
160-
val = self._convert_string_type(val, "Header value")
161-
str_headers.append((name, val))
162-
assert not is_hop_by_hop(name),"Hop-by-hop headers not allowed"
159+
if __debug__:
160+
for name, val in headers:
161+
name = self._convert_string_type(name, "Header name")
162+
val = self._convert_string_type(val, "Header value")
163+
assert not is_hop_by_hop(name),"Hop-by-hop headers not allowed"
163164

164-
self.status = status
165-
self.headers = self.headers_class(str_headers)
166165
return self.write
167166

168167
def _convert_string_type(self, value, title):
169168
"""Convert/check value type."""
170-
if isinstance(value, str):
169+
if type(value) is str:
171170
return value
172-
assert isinstance(value, bytes), \
173-
"{0} must be a string or bytes object (not {1})".format(title, value)
174-
return str(value, "iso-8859-1")
171+
raise AssertionError(
172+
"{0} must be of type str (got {1})".format(title, repr(value))
173+
)
175174

176175
def send_preamble(self):
177176
"""Transmit version/status/date/server, via self._write()"""
178177
if self.origin_server:
179178
if self.client_is_modern():
180-
self._write('HTTP/%s %s\r\n' % (self.http_version,self.status))
179+
self._write(('HTTP/%s %s\r\n' % (self.http_version,self.status)).encode('iso-8859-1'))
181180
if 'Date' not in self.headers:
182181
self._write(
183-
'Date: %s\r\n' % format_date_time(time.time())
182+
('Date: %s\r\n' % format_date_time(time.time())).encode('iso-8859-1')
184183
)
185184
if self.server_software and 'Server' not in self.headers:
186-
self._write('Server: %s\r\n' % self.server_software)
185+
self._write(('Server: %s\r\n' % self.server_software).encode('iso-8859-1'))
187186
else:
188-
self._write('Status: %s\r\n' % self.status)
187+
self._write(('Status: %s\r\n' % self.status).encode('iso-8859-1'))
189188

190189
def write(self, data):
191-
"""'write()' callable as specified by PEP 333"""
190+
"""'write()' callable as specified by PEP 3333"""
192191

193-
assert isinstance(data, (str, bytes)), \
194-
"write() argument must be a string or bytes"
192+
assert type(data) is bytes, \
193+
"write() argument must be a bytes instance"
195194

196195
if not self.status:
197196
raise AssertionError("write() before start_response()")
@@ -256,7 +255,7 @@ def send_headers(self):
256255
self.headers_sent = True
257256
if not self.origin_server or self.client_is_modern():
258257
self.send_preamble()
259-
self._write(str(self.headers))
258+
self._write(bytes(self.headers))
260259

261260

262261
def result_is_file(self):
@@ -376,12 +375,6 @@ def add_cgi_vars(self):
376375
self.environ.update(self.base_env)
377376

378377
def _write(self,data):
379-
if isinstance(data, str):
380-
try:
381-
data = data.encode("iso-8859-1")
382-
except UnicodeEncodeError:
383-
raise ValueError("Unicode data must contain only code points"
384-
" representable in ISO-8859-1 encoding")
385378
self.stdout.write(data)
386379

387380
def _flush(self):

Lib/wsgiref/headers.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,20 @@ class Headers:
3030
"""Manage a collection of HTTP response headers"""
3131

3232
def __init__(self,headers):
33-
if not isinstance(headers, list):
33+
if type(headers) is not list:
3434
raise TypeError("Headers must be a list of name/value tuples")
35-
self._headers = []
36-
for k, v in headers:
37-
k = self._convert_string_type(k)
38-
v = self._convert_string_type(v)
39-
self._headers.append((k, v))
35+
self._headers = headers
36+
if __debug__:
37+
for k, v in headers:
38+
self._convert_string_type(k)
39+
self._convert_string_type(v)
4040

4141
def _convert_string_type(self, value):
4242
"""Convert/check value type."""
43-
if isinstance(value, str):
43+
if type(value) is str:
4444
return value
45-
assert isinstance(value, bytes), ("Header names/values must be"
46-
" a string or bytes object (not {0})".format(value))
47-
return str(value, "iso-8859-1")
45+
raise AssertionError("Header names/values must be"
46+
" of type str (got {0})".format(repr(value)))
4847

4948
def __len__(self):
5049
"""Return the total number of headers, including duplicates."""
@@ -139,6 +138,9 @@ def __str__(self):
139138
suitable for direct HTTP transmission."""
140139
return '\r\n'.join(["%s: %s" % kv for kv in self._headers]+['',''])
141140

141+
def __bytes__(self):
142+
return str(self).encode('iso-8859-1')
143+
142144
def setdefault(self,name,value):
143145
"""Return first matching header value for 'name', or 'value'
144146

Lib/wsgiref/validate.py

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,10 @@ def assert_(cond, *args):
128128
raise AssertionError(*args)
129129

130130
def check_string_type(value, title):
131-
if isinstance(value, str):
131+
if type (value) is str:
132132
return value
133-
assert isinstance(value, bytes), \
134-
"{0} must be a string or bytes object (not {1})".format(title, value)
135-
return str(value, "iso-8859-1")
133+
raise AssertionError(
134+
"{0} must be of type str (got {1})".format(title, repr(value)))
136135

137136
def validator(application):
138137

@@ -197,20 +196,21 @@ def __init__(self, wsgi_input):
197196
def read(self, *args):
198197
assert_(len(args) == 1)
199198
v = self.input.read(*args)
200-
assert_(isinstance(v, bytes))
199+
assert_(type(v) is bytes)
201200
return v
202201

203-
def readline(self):
204-
v = self.input.readline()
205-
assert_(isinstance(v, bytes))
202+
def readline(self, *args):
203+
assert_(len(args) <= 1)
204+
v = self.input.readline(*args)
205+
assert_(type(v) is bytes)
206206
return v
207207

208208
def readlines(self, *args):
209209
assert_(len(args) <= 1)
210210
lines = self.input.readlines(*args)
211-
assert_(isinstance(lines, list))
211+
assert_(type(lines) is list)
212212
for line in lines:
213-
assert_(isinstance(line, bytes))
213+
assert_(type(line) is bytes)
214214
return lines
215215

216216
def __iter__(self):
@@ -229,7 +229,7 @@ def __init__(self, wsgi_errors):
229229
self.errors = wsgi_errors
230230

231231
def write(self, s):
232-
assert_(isinstance(s, str))
232+
assert_(type(s) is str)
233233
self.errors.write(s)
234234

235235
def flush(self):
@@ -248,7 +248,7 @@ def __init__(self, wsgi_writer):
248248
self.writer = wsgi_writer
249249

250250
def __call__(self, s):
251-
assert_(isinstance(s, (str, bytes)))
251+
assert_(type(s) is bytes)
252252
self.writer(s)
253253

254254
class PartialIteratorWrapper:
@@ -275,6 +275,8 @@ def __next__(self):
275275
assert_(not self.closed,
276276
"Iterator read after closed")
277277
v = next(self.iterator)
278+
if type(v) is not bytes:
279+
assert_(False, "Iterator yielded non-bytestring (%r)" % (v,))
278280
if self.check_start_response is not None:
279281
assert_(self.check_start_response,
280282
"The application returns and we started iterating over its body, but start_response has not yet been called")
@@ -294,7 +296,7 @@ def __del__(self):
294296
"Iterator garbage collected without being closed")
295297

296298
def check_environ(environ):
297-
assert_(isinstance(environ, dict),
299+
assert_(type(environ) is dict,
298300
"Environment is not of the right type: %r (environment: %r)"
299301
% (type(environ), environ))
300302

@@ -321,11 +323,11 @@ def check_environ(environ):
321323
if '.' in key:
322324
# Extension, we don't care about its type
323325
continue
324-
assert_(isinstance(environ[key], str),
326+
assert_(type(environ[key]) is str,
325327
"Environmental variable %s is not a string: %r (value: %r)"
326328
% (key, type(environ[key]), environ[key]))
327329

328-
assert_(isinstance(environ['wsgi.version'], tuple),
330+
assert_(type(environ['wsgi.version']) is tuple,
329331
"wsgi.version should be a tuple (%r)" % (environ['wsgi.version'],))
330332
assert_(environ['wsgi.url_scheme'] in ('http', 'https'),
331333
"wsgi.url_scheme unknown: %r" % environ['wsgi.url_scheme'])
@@ -385,12 +387,12 @@ def check_status(status):
385387
% status, WSGIWarning)
386388

387389
def check_headers(headers):
388-
assert_(isinstance(headers, list),
390+
assert_(type(headers) is list,
389391
"Headers (%r) must be of type list: %r"
390392
% (headers, type(headers)))
391393
header_names = {}
392394
for item in headers:
393-
assert_(isinstance(item, tuple),
395+
assert_(type(item) is tuple,
394396
"Individual headers (%r) must be of type tuple: %r"
395397
% (item, type(item)))
396398
assert_(len(item) == 2)
@@ -428,14 +430,14 @@ def check_content_type(status, headers):
428430
assert_(0, "No Content-Type header found in headers (%s)" % headers)
429431

430432
def check_exc_info(exc_info):
431-
assert_(exc_info is None or isinstance(exc_info, tuple),
433+
assert_(exc_info is None or type(exc_info) is tuple,
432434
"exc_info (%r) is not a tuple: %r" % (exc_info, type(exc_info)))
433435
# More exc_info checks?
434436

435437
def check_iterator(iterator):
436-
# Technically a string is legal, which is why it's a really bad
438+
# Technically a bytestring is legal, which is why it's a really bad
437439
# idea, because it may cause the response to be returned
438440
# character-by-character
439441
assert_(not isinstance(iterator, (str, bytes)),
440442
"You should not return a string as your application iterator, "
441-
"instead return a single-item list containing that string.")
443+
"instead return a single-item list containing a bytestring.")

0 commit comments

Comments
 (0)