Repository: boto/botocore

Issue:
get_bucket_location returns NoneType error when client error is added
## Problem

When adding a client error through `get_bucket_location`, rather than raise a `ClientError` (or the specified Service Error Code, we instead receive;

 `AttributeError: 'NoneType' object has no attribute 'stream'`

## Working Example (with `get_bucket_logging`)

```
import boto3
from botocore.stub import Stubber

client = boto3.client('s3')
stubber = Stubber(client)
stubber.add_client_error('get_bucket_logging', 'NoSuchBucket')
stubber.activate()

# Raises botocore.errorfactory.NoSuchBucket
client.get_bucket_logging(Bucket="meh")
```
## Non-working Example (with `get_bucket_location`)

```
import boto3
from botocore.stub import Stubber

client = boto3.client('s3')
stubber = Stubber(client)
stubber.add_client_error('get_bucket_location', 'NoSuchBucket')
stubber.activate()

# Raises an AttributeError from within awsrequest.py
client.get_bucket_location(Bucket="meh")
```
The full stack response from this error is;
```
Traceback (most recent call last):
  File "untitled.py", line 24, in <module>
    client.get_bucket_location(Bucket="meh")
  File "/usr/local/lib/python3.7/site-packages/botocore/client.py", line 357, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/usr/local/lib/python3.7/site-packages/botocore/client.py", line 655, in _make_api_call
    model=operation_model, context=request_context
  File "/usr/local/lib/python3.7/site-packages/botocore/hooks.py", line 356, in emit
    return self._emitter.emit(aliased_event_name, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/botocore/hooks.py", line 228, in emit
    return self._emit(event_name, kwargs)
  File "/usr/local/lib/python3.7/site-packages/botocore/hooks.py", line 211, in _emit
    response = handler(**kwargs)
  File "/usr/local/lib/python3.7/site-packages/botocore/handlers.py", line 485, in parse_get_bucket_location
    response_body = http_response.content
  File "/usr/local/lib/python3.7/site-packages/botocore/awsrequest.py", line 587, in content
    self._content = bytes().join(self.raw.stream()) or bytes()
AttributeError: 'NoneType' object has no attribute 'stream'
```
## Summary

I've tried searching open/closed issues but can't find something similar, nor a working example online using `get_bucket_location`. I fear this might be a bug within `botocore.stub` itself. Any help would be greatly appreciated! Thanks.




Discussion:

Patch:
diff --git a/.changes/next-release/bugfix-Stubber-96340.json b/.changes/next-release/bugfix-Stubber-96340.json
new file mode 100644
index 000000000..4e3897ebf
--- /dev/null
+++ b/.changes/next-release/bugfix-Stubber-96340.json
@@ -0,0 +1,5 @@
+{
+  "type": "bugfix",
+  "category": "Stubber",
+  "description": "fixes `#1884 <https://github.com/boto/botocore/issues/1884>`__"
+}
diff --git a/botocore/handlers.py b/botocore/handlers.py
index daa228866..a312f5c20 100644
--- a/botocore/handlers.py
+++ b/botocore/handlers.py
@@ -484,8 +484,7 @@ def parse_get_bucket_location(parsed, http_response, **kwargs):
     # The "parsed" passed in only has the ResponseMetadata
     # filled out.  This handler will fill in the LocationConstraint
     # value.
-    if 'LocationConstraint' in parsed:
-        # Response already set - a stub?
+    if http_response.raw is None:
         return
     response_body = http_response.content
     parser = xml.etree.cElementTree.XMLParser(


Test patch:
diff --git a/tests/functional/test_stub.py b/tests/functional/test_stub.py
index f44b722b4..752ba2249 100644
--- a/tests/functional/test_stub.py
+++ b/tests/functional/test_stub.py
@@ -313,3 +313,21 @@ class TestStubber(unittest.TestCase):
             actual_response = self.client.list_objects(**expected_params)
             self.assertEqual(desired_response, actual_response)
         self.stubber.assert_no_pending_responses()
+
+    def test_parse_get_bucket_location(self):
+        error_code = "NoSuchBucket"
+        error_message = "The specified bucket does not exist"
+        self.stubber.add_client_error(
+            'get_bucket_location', error_code, error_message)
+        self.stubber.activate()
+
+        with self.assertRaises(ClientError):
+            self.client.get_bucket_location(Bucket='foo')
+
+    def test_parse_get_bucket_location_returns_response(self):
+        service_response = {"LocationConstraint": "us-west-2"}
+        self.stubber.add_response('get_bucket_location',service_response)
+        self.stubber.activate()
+        response = self.client.get_bucket_location(Bucket='foo')
+        self.assertEqual(response, service_response)
+
diff --git a/tests/unit/test_handlers.py b/tests/unit/test_handlers.py
index 551d18f5d..a41bacb29 100644
--- a/tests/unit/test_handlers.py
+++ b/tests/unit/test_handlers.py
@@ -921,13 +921,6 @@ class TestHandlers(BaseSessionTest):
         self.assertEqual(parsed['CommonPrefixes'][0]['Prefix'],
                          u'\xe7\xf6s% asd\x08 c')
 
-    def test_get_bucket_location_optional(self):
-        # This handler should no-op if another hook (i.e. stubber) has already
-        # filled in response
-        response = {"LocationConstraint": "eu-west-1"}
-        handlers.parse_get_bucket_location(response, None),
-        self.assertEqual(response["LocationConstraint"], "eu-west-1")
-
     def test_set_operation_specific_signer_no_auth_type(self):
         signing_name = 'myservice'
         context = {'auth_type': None}