Skip to content

Commit

Permalink
Redirect file-like object, don't use isinstance
Browse files Browse the repository at this point in the history
This fixes aws/aws-cli#544 and aws/aws-cli#654

The issue here is that the CLI uses a file like object
that implements .read() and .seek(), but doesn't actually
inherit from the file/_IOBase.
  • Loading branch information
jamesls committed Feb 20, 2014
1 parent 544f1bd commit 240bed2
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 2 deletions.
5 changes: 4 additions & 1 deletion botocore/awsrequest.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,13 @@ def __init__(self, original_request):

def reset_stream_on_redirect(self, response, **kwargs):
if response.status_code in REDIRECT_STATI and \
isinstance(self.body, file_type):
self._looks_like_file(self.body):
logger.debug("Redirect received, rewinding stream: %s", self.body)
self.reset_stream()

def _looks_like_file(self, body):
return hasattr(body, 'read') and hasattr(body, 'seek')

def reset_stream(self):
# Trying to reset a stream when there is a no stream will
# just immediately return. It's not an error, it will produce
Expand Down
30 changes: 29 additions & 1 deletion tests/unit/test_awsrequest.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,42 @@ def test_cannot_reset_stream_raises_error(self):
# This means that we read the body:
body.read()
# And create a response object that indicates
# a redirect.
# a redirect
fake_response = Mock()
fake_response.status_code = 307

# Then requests calls our reset_stream hook.
with self.assertRaises(UnseekableStreamError):
self.prepared_request.reset_stream_on_redirect(fake_response)

def test_duck_type_for_file_check(self):
# As part of determining whether or not we can rewind a stream
# we first need to determine if the thing is a file like object.
# We should not be using an isinstance check. Instead, we should
# be using duck type checks.
class LooksLikeFile(object):
def __init__(self):
self.seek_called = False

def read(self, amount=None):
pass

def seek(self, where):
self.seek_called = True

looks_like_file = LooksLikeFile()
self.prepared_request.body = looks_like_file

fake_response = Mock()
fake_response.status_code = 307

# Then requests calls our reset_stream hook.
self.prepared_request.reset_stream_on_redirect(fake_response)

# The stream should now be reset.
self.assertTrue(looks_like_file.seek_called)



if __name__ == "__main__":
unittest.main()

0 comments on commit 240bed2

Please sign in to comment.