Skip to content

Commit

Permalink
refactor(request_helpers.Body): Make method definitions explicit
Browse files Browse the repository at this point in the history
Rather than generating read, readline, and readlines on the fly,
define them explicitly. This is less DRY, but it:

* Makes pylint happy
* Is easier for runtime to optimize/JIT
* Allows us to use proper param names and docstrings
  • Loading branch information
kgriffs committed Apr 14, 2014
1 parent 82ae94f commit b0167f4
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 22 deletions.
90 changes: 68 additions & 22 deletions falcon/request_helpers.py
Expand Up @@ -60,39 +60,85 @@ class Body(object):
This class normalizes wsgi.input behavior between WSGI servers
by implementing non-blocking behavior for the cases mentioned
above.
Args:
stream: Instance of socket._fileobject from environ['wsgi.input']
stream_len: Expected content length of the stream.
"""

def __init__(self, stream, stream_len):
"""Initialize the request body instance.
self.stream = stream
self.stream_len = stream_len

def __iter__(self):
return self

def __next__(self):
return next(self.stream)

next = __next__

def _read(self, size, target):
"""Helper function for proxing reads to the underlying stream.
Args:
stream: Instance of socket._fileobject from environ['wsgi.input']
stream_len: Expected content length of the stream.
size (int): Maximum number of bytes/characters to read.
Will be coerced, if None or -1, to `self.stream_len`. Will
likewise be coerced if greater than `self.stream_len`, so
that if the stream doesn't follow standard io semantics,
the read won't block.
target (callable): Once `size` has been fixed up, this function
will be called to actually do the work.
Returns:
Data read from the stream, as returned by `target`.
"""

self.stream = stream
self.stream_len = stream_len
if size is None or size == -1 or size > self.stream_len:
size = self.stream_len

return target(size)

def _make_stream_reader(func):
def read(size=None):
if size is None or size > self.stream_len:
size = self.stream_len
def read(self, size=None):
"""Read from the stream.
return func(size)
Args:
size (int): Maximum number of bytes/characters to read.
Defaults to reading until EOF.
return read
Returns:
Data read from the stream.
# NOTE(kgriffs): All of the wrapped methods take a single argument,
# which is a size AKA length AKA limit, always in bytes/characters.
# This is consistent with Gunicorn's "Body" class.
for attr in ('read', 'readline', 'readlines'):
target = getattr(self.stream, attr)
setattr(self, attr, _make_stream_reader(target))
"""

def __iter__(self):
return self
return self._read(size, self.stream.read)

def __next__(self):
return next(self.stream)
def readline(self, limit=None):
"""Read a line from the stream.
next = __next__
Args:
limit (int): Maximum number of bytes/characters to read.
Defaults to reading until EOF.
Returns:
Data read from the stream.
"""

return self._read(limit, self.stream.readline)

def readlines(self, hint=None):
"""Read lines from the stream.
Args:
hint (int): Maximum number of bytes/characters to read.
Defaults to reading until EOF.
Returns:
Data read from the stream.
"""

return self._read(hint, self.stream.readlines)
16 changes: 16 additions & 0 deletions tests/test_request_body.py
Expand Up @@ -133,10 +133,26 @@ def test_body_stream_wrapper(self):
body = request_helpers.Body(stream, expected_len)
self.assertEqual(body.readline(), expected_lines[0])

stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len)
self.assertEqual(body.readline(-1), expected_lines[0])

stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len)
self.assertEqual(body.readline(expected_len + 1), expected_lines[0])

stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len)
self.assertEqual(body.readlines(), expected_lines)

stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len)
self.assertEqual(body.readlines(-1), expected_lines)

stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len)
self.assertEqual(body.readlines(expected_len + 1), expected_lines)

stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len)
self.assertEqual(next(body), expected_lines[0])
Expand Down

0 comments on commit b0167f4

Please sign in to comment.