summaryrefslogtreecommitdiff
path: root/wsgitools/filters.py
diff options
context:
space:
mode:
Diffstat (limited to 'wsgitools/filters.py')
-rw-r--r--wsgitools/filters.py71
1 files changed, 29 insertions, 42 deletions
diff --git a/wsgitools/filters.py b/wsgitools/filters.py
index d691f74..ed976a2 100644
--- a/wsgitools/filters.py
+++ b/wsgitools/filters.py
@@ -10,18 +10,9 @@ __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
-try:
- next
-except NameError:
- def next(it):
- return it.next()
+from wsgitools.internal import str2bytes
__all__.append("CloseableIterator")
class CloseableIterator(object):
@@ -40,7 +31,7 @@ class CloseableIterator(object):
@rtype: gen()
"""
return self
- def next(self):
+ def __next__(self):
"""iterator interface"""
if not self.iterators:
raise StopIteration
@@ -49,6 +40,8 @@ class CloseableIterator(object):
except StopIteration:
self.iterators.pop(0)
return next(self)
+ def next(self):
+ return self.__next__()
__all__.append("CloseableList")
class CloseableList(list):
@@ -131,15 +124,15 @@ class BaseWSGIFilter(object):
"""For each string that is either written by the C{write} callable or
returned from the wrapped wsgi application this method is invoked. It
must return a string.
- @type data: str
- @rtype: str
+ @type data: bytes
+ @rtype: bytes
"""
return data
def append_data(self):
"""This function can be used to append data to the response. A list of
strings or some kind of iterable yielding strings has to be returned.
The default is to return an empty list.
- @rtype: gen([str])
+ @rtype: gen([bytes])
"""
return []
def handle_close(self):
@@ -161,7 +154,7 @@ class WSGIFilterMiddleware(object):
def __call__(self, environ, start_response):
"""wsgi interface
@type environ: {str, str}
- @rtype: gen([str])
+ @rtype: gen([bytes])
"""
assert isinstance(environ, dict)
reqfilter = self.filterclass()
@@ -205,7 +198,7 @@ class WSGIFilterMiddleware(object):
# default arguments. Also note that neither ' nor " are considered printable.
# For escape_string to be reversible \ is also not considered printable.
def escape_string(string, replacer=list(map(
- lambda i: chr(i) if chr(i).isalnum() or
+ lambda i: chr(i) if str2bytes(chr(i)).isalnum() or
chr(i) in '!#$%&()*+,-./:;<=>?@[]^_`{|}~ ' else
r"\x%2.2x" % i,
range(256)))):
@@ -224,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
@@ -276,11 +272,7 @@ class RequestLogWSGIFilter(BaseWSGIFilter):
self.status = status.split()[0]
return status
def filter_data(self, data):
- """BaseWSGIFilter interface
- @type data: str
- @rtype: str
- """
- assert isinstance(data, str)
+ assert isinstance(data, bytes)
self.length += len(data)
return data
def handle_close(self):
@@ -315,30 +307,31 @@ class TimerWSGIFilter(BaseWSGIFilter):
def creator(cls, pattern):
"""Returns a function creating L{TimerWSGIFilter}s with a given pattern
beeing a string of exactly eight bytes.
- @type pattern: str
+ @type pattern: bytes
"""
return lambda:cls(pattern)
- def __init__(self, pattern="?GenTime"):
+ def __init__(self, pattern=b"?GenTime"):
"""
@type pattern: str
"""
BaseWSGIFilter.__init__(self)
+ assert isinstance(pattern, bytes)
self.pattern = pattern
self.start = time.time()
def filter_data(self, data):
"""BaseWSGIFilter interface
- @type data: str
- @rtype: str
+ @type data: bytes
+ @rtype: bytes
"""
if data == self.pattern:
- return "%8.3g" % (time.time() - self.start)
+ return str2bytes("%8.3g" % (time.time() - self.start))
return data
__all__.append("EncodeWSGIFilter")
class EncodeWSGIFilter(BaseWSGIFilter):
"""Encodes all body data (no headers) with given charset.
@note: This violates the wsgi standard as it requires unicode objects
- whereas wsgi mandates the use of str.
+ whereas wsgi mandates the use of bytes.
"""
@classmethod
def creator(cls, charset):
@@ -356,7 +349,7 @@ class EncodeWSGIFilter(BaseWSGIFilter):
def filter_data(self, data):
"""BaseWSGIFilter interface
@type data: str
- @rtype: str
+ @rtype: bytes
"""
return data.encode(self.charset)
def filter_header(self, header, value):
@@ -400,7 +393,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):
@@ -423,10 +416,6 @@ class GzipWSGIFilter(BaseWSGIFilter):
headers.append(("Content-encoding", "gzip"))
return headers
def filter_data(self, data):
- """BaseWSGIFilter interface
- @type data: str
- @rtype: str
- """
if not self.compress:
return data
self.gzip.write(data)
@@ -434,11 +423,9 @@ 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
- @rtype: [str]
- """
if not self.compress:
return []
self.gzip.close()
@@ -449,7 +436,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):
@@ -460,14 +447,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
@@ -477,12 +464,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