diff options
author | Antoine Pitrou <solipsis@pitrou.net> | 2009-01-03 18:41:49 (GMT) |
---|---|---|
committer | Antoine Pitrou <solipsis@pitrou.net> | 2009-01-03 18:41:49 (GMT) |
commit | 38a66adccbef4a2b2e0ad57024a2398939f47ec2 (patch) | |
tree | b5663d1f8cd3f844cddba6794d0cbc262c5cc4db /Lib/wsgiref | |
parent | ffe431d8bda82db8e478930fc46a0764fcbe879b (diff) | |
download | cpython-38a66adccbef4a2b2e0ad57024a2398939f47ec2.zip cpython-38a66adccbef4a2b2e0ad57024a2398939f47ec2.tar.gz cpython-38a66adccbef4a2b2e0ad57024a2398939f47ec2.tar.bz2 |
Issue #4718: Adapt the wsgiref package so that it actually works with Python 3.x,
in accordance with http://www.wsgi.org/wsgi/Amendments_1.0
Diffstat (limited to 'Lib/wsgiref')
-rw-r--r-- | Lib/wsgiref/handlers.py | 34 | ||||
-rw-r--r-- | Lib/wsgiref/headers.py | 25 | ||||
-rw-r--r-- | Lib/wsgiref/simple_server.py | 9 | ||||
-rw-r--r-- | Lib/wsgiref/util.py | 4 | ||||
-rw-r--r-- | Lib/wsgiref/validate.py | 26 |
5 files changed, 70 insertions, 28 deletions
diff --git a/Lib/wsgiref/handlers.py b/Lib/wsgiref/handlers.py index 8231620..e082823 100644 --- a/Lib/wsgiref/handlers.py +++ b/Lib/wsgiref/handlers.py @@ -157,19 +157,29 @@ class BaseHandler: elif self.headers is not None: raise AssertionError("Headers already set!") - assert type(status) is str,"Status must be a string" + status = self._convert_string_type(status, "Status") assert len(status)>=4,"Status must be at least 4 characters" assert int(status[:3]),"Status message must begin w/3-digit code" assert status[3]==" ", "Status message must have a space after code" - if __debug__: - for name,val in headers: - assert type(name) is str,"Header names must be strings" - assert type(val) is str,"Header values must be strings" - assert not is_hop_by_hop(name),"Hop-by-hop headers not allowed" + + str_headers = [] + for name,val in headers: + name = self._convert_string_type(name, "Header name") + val = self._convert_string_type(val, "Header value") + str_headers.append((name, val)) + assert not is_hop_by_hop(name),"Hop-by-hop headers not allowed" + self.status = status - self.headers = self.headers_class(headers) + self.headers = self.headers_class(str_headers) return self.write + def _convert_string_type(self, value, title): + """Convert/check value type.""" + if isinstance(value, str): + return value + assert isinstance(value, bytes), \ + "{0} must be a string or bytes object (not {1})".format(title, value) + return str(value, "iso-8859-1") def send_preamble(self): """Transmit version/status/date/server, via self._write()""" @@ -188,7 +198,8 @@ class BaseHandler: def write(self, data): """'write()' callable as specified by PEP 333""" - assert type(data) is str,"write() argument must be string" + assert isinstance(data, (str, bytes)), \ + "write() argument must be a string or bytes" if not self.status: raise AssertionError("write() before start_response()") @@ -382,8 +393,13 @@ class SimpleHandler(BaseHandler): self.environ.update(self.base_env) def _write(self,data): + if isinstance(data, str): + try: + data = data.encode("iso-8859-1") + except UnicodeEncodeError: + raise ValueError("Unicode data must contain only code points" + " representable in ISO-8859-1 encoding") self.stdout.write(data) - self._write = self.stdout.write def _flush(self): self.stdout.flush() diff --git a/Lib/wsgiref/headers.py b/Lib/wsgiref/headers.py index c3774bb..83074af 100644 --- a/Lib/wsgiref/headers.py +++ b/Lib/wsgiref/headers.py @@ -44,7 +44,19 @@ class Headers: def __init__(self,headers): if not isinstance(headers, list): raise TypeError("Headers must be a list of name/value tuples") - self._headers = headers + self._headers = [] + for k, v in headers: + k = self._convert_string_type(k) + v = self._convert_string_type(v) + self._headers.append((k, v)) + + def _convert_string_type(self, value): + """Convert/check value type.""" + if isinstance(value, str): + return value + assert isinstance(value, bytes), ("Header names/values must be" + " a string or bytes object (not {0})".format(value)) + return str(value, "iso-8859-1") def __len__(self): """Return the total number of headers, including duplicates.""" @@ -53,7 +65,8 @@ class Headers: def __setitem__(self, name, val): """Set the value of a header.""" del self[name] - self._headers.append((name, val)) + self._headers.append( + (self._convert_string_type(name), self._convert_string_type(val))) def __delitem__(self,name): """Delete all occurrences of a header, if present. @@ -152,7 +165,8 @@ class Headers: and value 'value'.""" result = self.get(name) if result is None: - self._headers.append((name,value)) + self._headers.append((self._convert_string_type(name), + self._convert_string_type(value))) return value else: return result @@ -176,13 +190,16 @@ class Headers: """ parts = [] if _value is not None: + _value = self._convert_string_type(_value) parts.append(_value) for k, v in _params.items(): + k = self._convert_string_type(k) if v is None: parts.append(k.replace('_', '-')) else: + v = self._convert_string_type(v) parts.append(_formatparam(k.replace('_', '-'), v)) - self._headers.append((_name, "; ".join(parts))) + self._headers.append((self._convert_string_type(_name), "; ".join(parts))) diff --git a/Lib/wsgiref/simple_server.py b/Lib/wsgiref/simple_server.py index a82c80a..da14144 100644 --- a/Lib/wsgiref/simple_server.py +++ b/Lib/wsgiref/simple_server.py @@ -111,8 +111,7 @@ class WSGIRequestHandler(BaseHTTPRequestHandler): if length: env['CONTENT_LENGTH'] = length - for h in self.headers: - k,v = h.split(':',1) + for k, v in self.headers.items(): k=k.replace('-','_').upper(); v=v.strip() if k in env: continue # skip content length, type,etc. @@ -168,11 +167,11 @@ def demo_app(environ,start_response): stdout = StringIO() print("Hello world!", file=stdout) print(file=stdout) - h = environ.items(); h.sort() + h = sorted(environ.items()) for k,v in h: print(k,'=',repr(v), file=stdout) - start_response("200 OK", [('Content-Type','text/plain')]) - return [stdout.getvalue()] + start_response(b"200 OK", [(b'Content-Type',b'text/plain; charset=utf-8')]) + return [stdout.getvalue().encode("utf-8")] def make_server( diff --git a/Lib/wsgiref/util.py b/Lib/wsgiref/util.py index 2686b66..937be60 100644 --- a/Lib/wsgiref/util.py +++ b/Lib/wsgiref/util.py @@ -149,8 +149,8 @@ def setup_testing_defaults(environ): environ.setdefault('wsgi.multithread', 0) environ.setdefault('wsgi.multiprocess', 0) - from io import StringIO - environ.setdefault('wsgi.input', StringIO("")) + from io import StringIO, BytesIO + environ.setdefault('wsgi.input', BytesIO()) environ.setdefault('wsgi.errors', StringIO()) environ.setdefault('wsgi.url_scheme',guess_scheme(environ)) diff --git a/Lib/wsgiref/validate.py b/Lib/wsgiref/validate.py index fbd3536..2df3f9f 100644 --- a/Lib/wsgiref/validate.py +++ b/Lib/wsgiref/validate.py @@ -127,6 +127,13 @@ def assert_(cond, *args): if not cond: raise AssertionError(*args) +def check_string_type(value, title): + if isinstance(value, str): + return value + assert isinstance(value, bytes), \ + "{0} must be a string or bytes object (not {1})".format(title, value) + return str(value, "iso-8859-1") + def validator(application): """ @@ -188,14 +195,14 @@ class InputWrapper: self.input = wsgi_input def read(self, *args): - assert_(len(args) <= 1) + assert_(len(args) == 1) v = self.input.read(*args) - assert_(isinstance(v, str)) + assert_(isinstance(v, bytes)) return v def readline(self): v = self.input.readline() - assert_(isinstance(v, str)) + assert_(isinstance(v, bytes)) return v def readlines(self, *args): @@ -203,7 +210,7 @@ class InputWrapper: lines = self.input.readlines(*args) assert_(isinstance(lines, list)) for line in lines: - assert_(isinstance(line, str)) + assert_(isinstance(line, bytes)) return lines def __iter__(self): @@ -241,7 +248,7 @@ class WriteWrapper: self.writer = wsgi_writer def __call__(self, s): - assert_(isinstance(s, str)) + assert_(isinstance(s, (str, bytes))) self.writer(s) class PartialIteratorWrapper: @@ -364,8 +371,7 @@ def check_errors(wsgi_errors): % (wsgi_errors, attr)) def check_status(status): - assert_(isinstance(status, str), - "Status must be a string (not %r)" % status) + status = check_string_type(status, "Status") # Implicitly check that we can turn it into an integer: status_code = status.split(None, 1)[0] assert_(len(status_code) == 3, @@ -389,6 +395,8 @@ def check_headers(headers): % (item, type(item))) assert_(len(item) == 2) name, value = item + name = check_string_type(name, "Header name") + value = check_string_type(value, "Header value") assert_(name.lower() != 'status', "The Status header cannot be used; it conflicts with CGI " "script, and HTTP status is not given through headers " @@ -404,11 +412,13 @@ def check_headers(headers): % (value, bad_header_value_re.search(value).group(0))) def check_content_type(status, headers): + status = check_string_type(status, "Status") code = int(status.split(None, 1)[0]) # @@: need one more person to verify this interpretation of RFC 2616 # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html NO_MESSAGE_BODY = (204, 304) for name, value in headers: + name = check_string_type(name, "Header name") if name.lower() == 'content-type': if code not in NO_MESSAGE_BODY: return @@ -426,6 +436,6 @@ def check_iterator(iterator): # Technically a string is legal, which is why it's a really bad # idea, because it may cause the response to be returned # character-by-character - assert_(not isinstance(iterator, str), + assert_(not isinstance(iterator, (str, bytes)), "You should not return a string as your application iterator, " "instead return a single-item list containing that string.") |