From fd38036b9f1693f8f368851d40928bc5922ce606 Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Thu, 28 Jun 2012 16:38:03 +0200 Subject: remove workarounds for missing next() and hashlib --- test.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'test.py') diff --git a/test.py b/test.py index 1acf5aa..f46a512 100755 --- a/test.py +++ b/test.py @@ -9,18 +9,9 @@ try: import cStringIO as io except ImportError: import StringIO as io -try: - from hashlib import md5 -except ImportError: - from md5 import md5 +from hashlib import md5 import sys -try: - next -except NameError: - def next(iterator): - return iterator.next() - class Request: def __init__(self, case): """ -- cgit v1.2.3 From 7e2e9173b2afcc2a8dca9e6047d0b82ad70c9dff Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Thu, 28 Jun 2012 22:38:28 +0200 Subject: first part of bytes conversion Convert the request body data from str to bytes. This replaces all StringIOs with BytesIOs (removing backwards one more backwards compatibility). Also all character sequences involved in request bodies get a b"" prefix. The StaticContent application takes bytes instead of str (no difference for py2x). The GzipWSGIFilter needs a fixed as a truncate of a BytesIO does not rewind the stream position. --- test.py | 43 +++++++++++++++++++----------------------- wsgitools/applications.py | 6 +++--- wsgitools/authentication.py | 4 ++-- wsgitools/filters.py | 21 ++++++++------------- wsgitools/middlewares.py | 11 +++-------- wsgitools/scgi/asynchronous.py | 9 ++------- 6 files changed, 37 insertions(+), 57 deletions(-) (limited to 'test.py') diff --git a/test.py b/test.py index f46a512..f2e8910 100755 --- a/test.py +++ b/test.py @@ -3,12 +3,7 @@ import unittest import doctest import wsgiref.validate -# Cannot use io module as it is broken in 2.6. -# Writing a str to a io.StringIO results in an exception. -try: - import cStringIO as io -except ImportError: - import StringIO as io +import io from hashlib import md5 import sys @@ -27,7 +22,7 @@ class Request: QUERY_STRING="") self.environ.update({ "wsgi.version": (1, 0), - "wsgi.input": io.StringIO(), + "wsgi.input": io.BytesIO(), "wsgi.errors": sys.stderr, "wsgi.url_scheme": "http", "wsgi.multithread": False, @@ -122,14 +117,14 @@ class Result: self.testcase.fail("header %s not found" % name) def get_data(self): - return "".join(self.writtendata) + "".join(self.returneddata) + return b"".join(self.writtendata) + b"".join(self.returneddata) from wsgitools import applications class StaticContentTest(unittest.TestCase): def setUp(self): self.app = applications.StaticContent( - "200 Found", [("Content-Type", "text/plain")], "nothing") + "200 Found", [("Content-Type", "text/plain")], b"nothing") self.req = Request(self) def testGet(self): @@ -146,7 +141,7 @@ class StaticContentTest(unittest.TestCase): class StaticFileTest(unittest.TestCase): def setUp(self): - self.app = applications.StaticFile(io.StringIO("success"), "200 Found", + self.app = applications.StaticFile(io.BytesIO(b"success"), "200 Found", [("Content-Type", "text/plain")]) self.req = Request(self) @@ -167,7 +162,7 @@ from wsgitools import digest class AuthDigestMiddlewareTest(unittest.TestCase): def setUp(self): self.staticapp = applications.StaticContent( - "200 Found", [("Content-Type", "text/plain")], "success") + "200 Found", [("Content-Type", "text/plain")], b"success") token_gen = digest.AuthTokenGenerator("foo", lambda _: "baz") self.app = digest.AuthDigestMiddleware( wsgiref.validate.validator(self.staticapp), token_gen) @@ -232,28 +227,28 @@ from wsgitools import middlewares def writing_application(environ, start_response): write = start_response("404 Not found", [("Content-Type", "text/plain")]) write = start_response("200 Ok", [("Content-Type", "text/plain")]) - write("first") - yield "" - yield "second" + write(b"first") + yield b"" + yield b"second" def write_only_application(environ, start_response): write = start_response("200 Ok", [("Content-Type", "text/plain")]) - write("first") - write("second") - yield "" + write(b"first") + write(b"second") + yield b"" class NoWriteCallableMiddlewareTest(unittest.TestCase): def testWrite(self): app = middlewares.NoWriteCallableMiddleware(writing_application) res = Request(self)(app) self.assertEqual(res.writtendata, []) - self.assertEqual("".join(res.returneddata), "firstsecond") + self.assertEqual(b"".join(res.returneddata), b"firstsecond") def testWriteOnly(self): app = middlewares.NoWriteCallableMiddleware(write_only_application) res = Request(self)(app) self.assertEqual(res.writtendata, []) - self.assertEqual("".join(res.returneddata), "firstsecond") + self.assertEqual(b"".join(res.returneddata), b"firstsecond") class StupidIO: """file-like without tell method, so StaticFile is not able to @@ -273,7 +268,7 @@ class StupidIO: class ContentLengthMiddlewareTest(unittest.TestCase): def setUp(self): - self.staticapp = applications.StaticFile(StupidIO("success"), + self.staticapp = applications.StaticFile(StupidIO(b"success"), "200 Found", [("Content-Type", "text/plain")]) self.app = middlewares.ContentLengthMiddleware(self.staticapp, maxstore=10) @@ -296,7 +291,7 @@ class ContentLengthMiddlewareTest(unittest.TestCase): class BasicAuthMiddlewareTest(unittest.TestCase): def setUp(self): self.staticapp = applications.StaticContent( - "200 Found", [("Content-Type", "text/plain")], "success") + "200 Found", [("Content-Type", "text/plain")], b"success") checkpw = middlewares.DictAuthChecker({"bar": "baz"}) self.app = middlewares.BasicAuthMiddleware( wsgiref.validate.validator(self.staticapp), checkpw) @@ -340,13 +335,13 @@ import gzip class GzipWSGIFilterTest(unittest.TestCase): def testSimple(self): app = applications.StaticContent("200 Found", - [("Content-Type", "text/plain")], "nothing") + [("Content-Type", "text/plain")], b"nothing") app = filters.WSGIFilterMiddleware(app, filters.GzipWSGIFilter) req = Request(self) req.environ["HTTP_ACCEPT_ENCODING"] = "gzip" res = req(app) - data = gzip.GzipFile(fileobj=io.StringIO(res.get_data())).read() - self.assertEqual(data, "nothing") + data = gzip.GzipFile(fileobj=io.BytesIO(res.get_data())).read() + self.assertEqual(data, b"nothing") def alltests(case): return unittest.TestLoader().loadTestsFromTestCase(case) diff --git a/wsgitools/applications.py b/wsgitools/applications.py index 8a02fe8..cdaf0ae 100644 --- a/wsgitools/applications.py +++ b/wsgitools/applications.py @@ -21,7 +21,7 @@ class StaticContent: @type headers: list @param headers: is a list of C{(header, value)} pairs being delivered as HTTP headers - @type content: basestring + @type content: bytes @param content: contains the data to be delivered to the client. It is either a string or some kind of iterable yielding strings. @type anymethod: boolean @@ -30,12 +30,12 @@ class StaticContent: """ assert isinstance(status, str) assert isinstance(headers, list) - assert isinstance(content, basestring) or hasattr(content, "__iter__") + assert isinstance(content, bytes) or hasattr(content, "__iter__") self.status = status self.headers = headers self.anymethod = anymethod length = -1 - if isinstance(content, basestring): + if isinstance(content, bytes): self.content = [content] length = len(content) else: diff --git a/wsgitools/authentication.py b/wsgitools/authentication.py index 963dc00..c076d7f 100644 --- a/wsgitools/authentication.py +++ b/wsgitools/authentication.py @@ -97,8 +97,8 @@ class AuthenticationMiddleware: @param exception: reason for the authentication failure """ status = "401 Authorization required" - html = "401 Authorization required" \ - "

401 Authorization required

" + html = b"401 Authorization required" \ + b"

401 Authorization required

" headers = [("Content-Type", "text/html"), self.www_authenticate(exception), ("Content-Length", str(len(html)))] diff --git a/wsgitools/filters.py b/wsgitools/filters.py index 4305c9d..6f90903 100644 --- a/wsgitools/filters.py +++ b/wsgitools/filters.py @@ -10,13 +10,7 @@ __all__ = [] import sys import time import gzip -# Cannot use io module as it is broken in 2.6. -# Writing a str to a io.StringIO results in an exception. -try: - import cStringIO as io -except ImportError: - import StringIO as io - +import io __all__.append("CloseableIterator") class CloseableIterator: @@ -397,7 +391,7 @@ class GzipWSGIFilter(BaseWSGIFilter): acceptenc = map(str.strip, acceptenc) if "gzip" in acceptenc: self.compress = True - self.sio = io.StringIO() + self.sio = io.BytesIO() self.gzip = gzip.GzipFile(fileobj=self.sio, mode="w") return environ def filter_header(self, headername, headervalue): @@ -431,6 +425,7 @@ class GzipWSGIFilter(BaseWSGIFilter): self.gzip.flush() data = self.sio.getvalue() self.sio.truncate(0) + self.sio.seek(0) return data def append_data(self): """BaseWSGIFilter interface @@ -446,7 +441,7 @@ class ReusableWSGIInputFilter(BaseWSGIFilter): """Make C{environ["wsgi.input"]} readable multiple times. Although this is not required by the standard it is sometimes desirable to read C{wsgi.input} multiple times. This filter will therefore replace that variable with a - C{StringIO} instance which provides a C{seek} method. + C{BytesIO} instance which provides a C{seek} method. """ @classmethod def creator(cls, maxrequestsize): @@ -457,14 +452,14 @@ class ReusableWSGIInputFilter(BaseWSGIFilter): adapter to eat this data.) @type maxrequestsize: int @param maxrequestsize: is the maximum number of bytes to store in the - C{StringIO} + C{BytesIO} """ return lambda:cls(maxrequestsize) def __init__(self, maxrequestsize=65536): """ReusableWSGIInputFilters constructor. @type maxrequestsize: int @param maxrequestsize: is the maximum number of bytes to store in the - C{StringIO}, see L{creator} + C{BytesIO}, see L{creator} """ BaseWSGIFilter.__init__(self) self.maxrequestsize = maxrequestsize @@ -474,12 +469,12 @@ class ReusableWSGIInputFilter(BaseWSGIFilter): @type environ: {str: str} """ - if isinstance(environ["wsgi.input"], io.StringIO): + if isinstance(environ["wsgi.input"], io.BytesIO): return environ # nothing to be done # XXX: is this really a good idea? use with care environ["wsgitools.oldinput"] = environ["wsgi.input"] - data = io.StringIO(environ["wsgi.input"].read(self.maxrequestsize)) + data = io.BytesIO(environ["wsgi.input"].read(self.maxrequestsize)) environ["wsgi.input"] = data return environ diff --git a/wsgitools/middlewares.py b/wsgitools/middlewares.py index dbf2020..e6ede9d 100644 --- a/wsgitools/middlewares.py +++ b/wsgitools/middlewares.py @@ -5,12 +5,7 @@ import sys import cgitb import binascii import collections -# Cannot use io module as it is broken in 2.6. -# Writing a str to a io.StringIO results in an exception. -try: - import cStringIO as io -except ImportError: - import StringIO as io +import io if sys.version_info[0] >= 3: def exc_info_for_raise(exc_info): @@ -60,7 +55,7 @@ __all__.append("NoWriteCallableMiddleware") class NoWriteCallableMiddleware: """This middleware wraps a wsgi application that needs the return value of C{start_response} function to a wsgi application that doesn't need one by - writing the data to a C{StringIO} and then making it be the first result + writing the data to a C{BytesIO} and then making it be the first result element.""" def __init__(self, app): """Wraps wsgi application app.""" @@ -72,7 +67,7 @@ class NoWriteCallableMiddleware: """ assert isinstance(environ, dict) todo = [None] - sio = io.StringIO() + sio = io.BytesIO() gotiterdata = False def write_calleable(data): assert not gotiterdata diff --git a/wsgitools/scgi/asynchronous.py b/wsgitools/scgi/asynchronous.py index 1dee283..7eb1a30 100644 --- a/wsgitools/scgi/asynchronous.py +++ b/wsgitools/scgi/asynchronous.py @@ -1,14 +1,9 @@ __all__ = [] import asyncore +import io import socket import sys -# Cannot use io module as it is broken in 2.6. -# Writing a str to a io.StringIO results in an exception. -try: - import cStringIO as io -except ImportError: - import StringIO as io import errno from wsgitools.scgi import _convert_environ, FileWrapper @@ -48,7 +43,7 @@ class SCGIConnection(asyncore.dispatcher): self.wsgiiterator = None # wsgi application iterator self.outheaders = () # headers to be sent # () -> unset, (..,..) -> set, True -> sent - self.body = io.StringIO() # request body + self.body = io.BytesIO() # request body def _try_send_headers(self): if self.outheaders != True: -- cgit v1.2.3 From 472144ac68188056eb41c9cb198df04b454a1da2 Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Fri, 29 Jun 2012 08:47:51 +0200 Subject: fix hashlib, base64 and other bytes issues * hashlib.md5 wants bytes now. * string.decode("base64") is now base64.b64decode and works on bytes * binascii.unhexlify is now base64.b16decode and also works on bytes * str.isalnum accepts umlauts, use bytes.isalnum instead --- test.py | 19 ++++++++++------ wsgitools/digest.py | 59 +++++++++++++++++++++++++++++------------------- wsgitools/middlewares.py | 14 +++++++----- 3 files changed, 56 insertions(+), 36 deletions(-) (limited to 'test.py') diff --git a/test.py b/test.py index f2e8910..1183d63 100755 --- a/test.py +++ b/test.py @@ -1,5 +1,6 @@ #!/usr/bin/env python +import base64 import unittest import doctest import wsgiref.validate @@ -7,6 +8,8 @@ import io from hashlib import md5 import sys +from wsgitools.internal import bytes2str, str2bytes + class Request: def __init__(self, case): """ @@ -193,9 +196,9 @@ class AuthDigestMiddlewareTest(unittest.TestCase): res.getheader("WWW-Authenticate").split()))) nonce = nonce.split('"')[1] req = self.req.copy() - token = md5("bar:foo:%s" % password).hexdigest() - other = md5("GET:").hexdigest() - resp = md5("%s:%s:%s" % (token, nonce, other)).hexdigest() + token = md5(str2bytes("bar:foo:%s" % password)).hexdigest() + other = md5(str2bytes("GET:")).hexdigest() + resp = md5(str2bytes("%s:%s:%s" % (token, nonce, other))).hexdigest() req.setheader('http-authorization', 'Digest algorithm=md5,nonce="%s",' \ 'uri=,username=bar,response="%s"' % (nonce, resp)) res = req(self.app) @@ -213,9 +216,10 @@ class AuthDigestMiddlewareTest(unittest.TestCase): res.getheader("WWW-Authenticate").split()))) nonce = nonce.split('"')[1] req = self.req.copy() - token = md5("bar:foo:baz").hexdigest() - other = md5("GET:").hexdigest() - resp = md5("%s:%s:1:qux:auth:%s" % (token, nonce, other)).hexdigest() + token = md5(str2bytes("bar:foo:baz")).hexdigest() + other = md5(str2bytes("GET:")).hexdigest() + resp = "%s:%s:1:qux:auth:%s" % (token, nonce, other) + resp = md5(str2bytes(resp)).hexdigest() req.setheader('http-authorization', 'Digest algorithm=md5,nonce="%s",' \ 'uri=,username=bar,response="%s",qop=auth,nc=1,' \ 'cnonce=qux' % (nonce, resp)) @@ -318,7 +322,8 @@ class BasicAuthMiddlewareTest(unittest.TestCase): def doauth(self, password="baz", status=200): req = self.req.copy() - token = ("bar:%s" % password).encode("base64").strip() + token = "bar:%s" % password + token = bytes2str(base64.b64encode(str2bytes(token))) req.setheader('http-authorization', 'Basic %s' % token) res = req(self.app) res.status(status) diff --git a/wsgitools/digest.py b/wsgitools/digest.py index 4b5f8fb..532b371 100644 --- a/wsgitools/digest.py +++ b/wsgitools/digest.py @@ -14,32 +14,39 @@ database using C{DBAPI2NonceStore}. __all__ = [] import random -try: - from hashlib import md5 -except ImportError: - from md5 import md5 -import binascii import base64 +import hashlib import time import os +from wsgitools.internal import bytes2str, str2bytes from wsgitools.authentication import AuthenticationRequired, \ ProtocolViolation, AuthenticationMiddleware sysrand = random.SystemRandom() -def gen_rand_str(bytes=33): +def md5hex(data): + """ + @type data: str + @rtype: str + """ + return hashlib.md5(str2bytes(data)).hexdigest() + +def gen_rand_str(bytesentropy=33): """ Generates a string of random base64 characters. - @param bytes: is the number of random 8bit values to be used + @param bytesentropy: is the number of random 8bit values to be used + @rtype: str >>> gen_rand_str() != gen_rand_str() True """ - randnum = sysrand.getrandbits(bytes*8) - randstr = ("%%0%dX" % (2*bytes)) % randnum - randstr = binascii.unhexlify(randstr) - randstr = base64.encodestring(randstr).strip() + randnum = sysrand.getrandbits(bytesentropy*8) + randstr = ("%%0%dX" % (2*bytesentropy)) % randnum + randbytes = str2bytes(randstr) + randbytes = base64.b16decode(randbytes) + randbytes = base64.b64encode(randbytes) + randstr = bytes2str(randbytes) return randstr def parse_digest_response(data): @@ -120,6 +127,8 @@ def format_digest(mapping): assert isinstance(mapping, dict) result = [] for key, (value, needsquoting) in mapping.items(): + assert isinstance(key, str) + assert isinstance(value, str) if needsquoting: value = '"%s"' % value.replace('\\', '\\\\').replace('"', '\\"') else: @@ -172,8 +181,8 @@ class AbstractTokenGenerator: """ assert isinstance(username, str) assert isinstance(password, str) - token = md5("%s:%s:%s" % (username, self.realm, password)).hexdigest() - return token == self(username) + token = "%s:%s:%s" % (username, self.realm, password) + return md5hex(token) == self(username) __all__.append("AuthTokenGenerator") class AuthTokenGenerator(AbstractTokenGenerator): @@ -199,7 +208,7 @@ class AuthTokenGenerator(AbstractTokenGenerator): if password is None: return None a1 = "%s:%s:%s" % (username, self.realm, password) - return md5(a1).hexdigest() + return md5hex(a1) __all__.append("HtdigestTokenGenerator") class HtdigestTokenGenerator(AbstractTokenGenerator): @@ -367,7 +376,7 @@ class StatelessNonceStore(NonceStoreBase): token = "%s:%s:%s" % (nonce_time, nonce_value, self.server_secret) if ident is not None: token = "%s:%s" % (token, ident) - token = md5(token).hexdigest() + token = md5hex(token) return "%s:%s:%s" % (nonce_time, nonce_value, token) def checknonce(self, nonce, count=1, ident=None): @@ -387,7 +396,7 @@ class StatelessNonceStore(NonceStoreBase): token = "%s:%s:%s" % (nonce_time, nonce_value, self.server_secret) if ident is not None: token = "%s:%s" % (token, ident) - token = md5(token).hexdigest() + token = md5hex(token) if token != nonce_hash: return False @@ -449,7 +458,7 @@ class MemoryNonceStore(NonceStoreBase): token = "%s:%s:%s" % (nonce_time, nonce_value, self.server_secret) if ident is not None: token = "%s:%s" % (token, ident) - token = md5(token).hexdigest() + token = md5hex(token) return "%s:%s:%s" % (nonce_time, nonce_value, token) def checknonce(self, nonce, count=1, ident=None): @@ -468,7 +477,7 @@ class MemoryNonceStore(NonceStoreBase): token = "%s:%s:%s" % (nonce_time, nonce_value, self.server_secret) if ident is not None: token = "%s:%s" % (token, ident) - token = md5(token).hexdigest() + token = md5hex(token) if token != nonce_hash: return False @@ -595,7 +604,7 @@ class DBAPI2NonceStore(NonceStoreBase): token = "%s:%s" % (dbkey, self.server_secret) if ident is not None: token = "%s:%s" % (token, ident) - token = md5(token).hexdigest() + token = md5hex(token) return "%s:%s:%s" % (nonce_time, nonce_value, token) def checknonce(self, nonce, count=1, ident=None): @@ -604,19 +613,22 @@ class DBAPI2NonceStore(NonceStoreBase): count on returning True. @type nonce: str @type count: int + @type ident: str or None @rtype: bool """ try: nonce_time, nonce_value, nonce_hash = nonce.split(':') except ValueError: return False - if not nonce_time.isalnum() or not nonce_value.replace("+", ""). \ - replace("/", "").replace("=", "").isalnum(): + # use bytes.isalnum to avoid locale specific interpretation + if not str2bytes(nonce_time).isalnum() or \ + not str2bytes(nonce_value.replace("+", "").replace("/", "") \ + .replace("=", "")).isalnum(): return False token = "%s:%s:%s" % (nonce_time, nonce_value, self.server_secret) if ident is not None: token = "%s:%s" % (token, ident) - token = md5(token).hexdigest() + token = md5hex(token) if token != nonce_hash: return False @@ -681,7 +693,7 @@ class AuthDigestMiddleware(AuthenticationMiddleware): by a REMOTE_USER key before being passed to the wrapped application.""" authorization_method = "digest" - algorithms = {"md5": lambda data: md5(data).hexdigest()} + algorithms = {"md5": md5hex} def __init__(self, app, gentoken, maxage=300, maxuses=5, store=None): """ @param app: is the wsgi application to be served with authentication. @@ -708,6 +720,7 @@ class AuthDigestMiddleware(AuthenticationMiddleware): self.noncestore = store def authenticate(self, auth, environ): + assert isinstance(auth, str) try: credentials = parse_digest_response(auth) except ValueError: diff --git a/wsgitools/middlewares.py b/wsgitools/middlewares.py index e6ede9d..725deb1 100644 --- a/wsgitools/middlewares.py +++ b/wsgitools/middlewares.py @@ -1,12 +1,14 @@ __all__ = [] +import base64 import time import sys import cgitb -import binascii import collections import io +from wsgitools.internal import bytes2str, str2bytes + if sys.version_info[0] >= 3: def exc_info_for_raise(exc_info): return exc_info[0](exc_info[1]).with_traceback(exc_info[2]) @@ -347,14 +349,14 @@ class BasicAuthMiddleware(AuthenticationMiddleware): self.app401 = app401 def authenticate(self, auth, environ): - """ - @type environ: {str: object} - """ + assert isinstance(auth, str) assert isinstance(environ, dict) + auth = str2bytes(auth) try: - auth_info = auth.decode("base64") - except binascii.Error: + auth_info = base64.b64decode(auth) + except TypeError: raise ProtocolViolation("failed to base64 decode auth_info") + auth_info = bytes2str(auth_info) try: username, password = auth_info.split(':', 1) except ValueError: -- cgit v1.2.3 From ae660ee64afb068efd61274853d9d3e05300446e Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Thu, 6 Jun 2013 10:36:49 +0200 Subject: port CachingMiddlewareTest to py3k --- test.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'test.py') diff --git a/test.py b/test.py index 8e628cd..946ee29 100755 --- a/test.py +++ b/test.py @@ -309,24 +309,24 @@ class CachingMiddlewareTest(unittest.TestCase): if "maxage0" in environ["SCRIPT_NAME"]: headers.append(("Cache-Control", "max-age=0")) start_response("200 Found", headers) - return ["%d" % count] + return [str2bytes("%d" % count)] def testCache(self): res = Request(self)(self.cached) res.status(200) - self.assertEqual(res.get_data(), "1") + self.assertEqual(res.get_data(), b"1") res = Request(self)(self.cached) res.status(200) - self.assertEqual(res.get_data(), "1") + self.assertEqual(res.get_data(), b"1") def testNoCache(self): res = Request(self)(self.cached) res.status(200) - self.assertEqual(res.get_data(), "1") + self.assertEqual(res.get_data(), b"1") res = Request(self).setheader( "Cache-Control", "max-age=0")(self.cached) res.status(200) - self.assertEqual(res.get_data(), "2") + self.assertEqual(res.get_data(), b"2") class BasicAuthMiddlewareTest(unittest.TestCase): def setUp(self): -- cgit v1.2.3 From a5634dea0d4f24d6f27ba5d7e50c773979eec93c Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Thu, 6 Jun 2013 11:02:33 +0200 Subject: fix the py3k part of RequestLogWSGIFilterTest --- test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test.py') diff --git a/test.py b/test.py index 946ee29..cf40b92 100755 --- a/test.py +++ b/test.py @@ -376,7 +376,7 @@ import gzip class RequestLogWSGIFilterTest(unittest.TestCase): def testSimple(self): app = applications.StaticContent("200 Found", - [("Content-Type", "text/plain")], "nothing") + [("Content-Type", "text/plain")], b"nothing") log = io.StringIO() logfilter = filters.RequestLogWSGIFilter.creator(log) app = filters.WSGIFilterMiddleware(app, logfilter) -- cgit v1.2.3 From 27ed9839582c4fce9a0fff82281fb2e302be808e Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Thu, 6 Jun 2013 11:06:26 +0200 Subject: fix RequestLogWSGIFilterTest Clarify the type of the log file-like passed to RequestLogWSGIFilter. --- test.py | 5 ++++- wsgitools/filters.py | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'test.py') diff --git a/test.py b/test.py index cf40b92..2d1d29d 100755 --- a/test.py +++ b/test.py @@ -377,7 +377,10 @@ class RequestLogWSGIFilterTest(unittest.TestCase): def testSimple(self): app = applications.StaticContent("200 Found", [("Content-Type", "text/plain")], b"nothing") - log = io.StringIO() + if isinstance("x", bytes): + log = io.BytesIO() + else: + log = io.StringIO() logfilter = filters.RequestLogWSGIFilter.creator(log) app = filters.WSGIFilterMiddleware(app, logfilter) req = Request(self) diff --git a/wsgitools/filters.py b/wsgitools/filters.py index 882a0bf..471c949 100644 --- a/wsgitools/filters.py +++ b/wsgitools/filters.py @@ -217,6 +217,9 @@ class RequestLogWSGIFilter(BaseWSGIFilter): """Returns a function creating L{RequestLogWSGIFilter}s on given log file. log has to be a file-like object. @type log: file-like + @param log: elements of type str are written to the log. That means in + Py3.X the contents are decoded and in Py2.X the log is assumed + to be encoded in latin1. This follows the spirit of WSGI. @type flush: bool @param flush: if True, invoke the flush method on log after each write invocation -- cgit v1.2.3