Repository: boto/boto

Issue:
builtins.UnboundLocalError: local variable 'e' referenced before assignment
Check out this line:

https://github.com/boto/boto/blob/develop/boto/connection.py#L1025

You're referencing `e` outside of the except close, which is a no-no in Python 3. 



Discussion:
I was hitting this because I was referencing the bucket rather than the bucket/\* in my security.

"arn:aws:s3:::mybucket"
"arn:aws:s3:::mybucket/*"

I added both to the security policy and stopped hitting this error.

Brian, you're missing the point. This error is an error in the error-handling code, which prevents the actual error from being displayed. You fixed your actual error which is great, but this bug is still a problem because it prevents you from seeing what the actual error is. 

No, I get the point.  I agree it is an annoying bug.  I just posted it so other people might avoid wasting time tracking down one particular cause of the issue.

Agreed, this is a Python 3 bug that we should solve. Pull requests welcome if anybody wants to take a stab at fixing it, otherwise I can take a look soon.

This is related to #2511, btw.


Resolving patch:
diff --git a/boto/connection.py b/boto/connection.py
--- a/boto/connection.py
+++ b/boto/connection.py
@@ -903,7 +903,7 @@ def _mexe(self, request, sender=None, override_num_retries=None,
         boto.log.debug('Params: %s' % request.params)
         response = None
         body = None
-        e = None
+        ex = None
         if override_num_retries is None:
             num_retries = config.getint('Boto', 'num_retries', self.num_retries)
         else:
@@ -1002,6 +1002,7 @@ def _mexe(self, request, sender=None, override_num_retries=None,
                 connection = self.new_http_connection(request.host, request.port,
                                                       self.is_secure)
                 response = e.response
+                ex = e
             except self.http_exceptions as e:
                 for unretryable in self.http_unretryable_exceptions:
                     if isinstance(e, unretryable):
@@ -1013,6 +1014,7 @@ def _mexe(self, request, sender=None, override_num_retries=None,
                                e.__class__.__name__)
                 connection = self.new_http_connection(request.host, request.port,
                                                       self.is_secure)
+                ex = e
             time.sleep(next_sleep)
             i += 1
         # If we made it here, it's because we have exhausted our retries
@@ -1023,8 +1025,8 @@ def _mexe(self, request, sender=None, override_num_retries=None,
             self.request_hook.handle_request_data(request, response, error=True)
         if response:
             raise BotoServerError(response.status, response.reason, body)
-        elif e:
-            raise
+        elif ex:
+            raise ex
         else:
             msg = 'Please report this exception as a Boto Issue!'
             raise BotoClientError(msg)


Test patch:
diff --git a/tests/unit/test_connection.py b/tests/unit/test_connection.py
--- a/tests/unit/test_connection.py
+++ b/tests/unit/test_connection.py
@@ -387,6 +387,26 @@ def test_temp_failure(self):
                                  'POST')
         self.assertEqual(resp.read(), b"{'test': 'success'}")
 
+    def test_unhandled_exception(self):
+        HTTPretty.register_uri(HTTPretty.POST,
+                               'https://%s/temp_exception/' % self.region.endpoint,
+                               responses=[])
+
+        def fake_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
+                      source_address=None):
+            raise socket.timeout('fake error')
+
+        socket.create_connection = fake_connection
+
+        conn = self.region.connect(aws_access_key_id='access_key',
+                                   aws_secret_access_key='secret')
+        conn.num_retries = 0
+        with self.assertRaises(socket.error):
+            resp = conn.make_request('myCmd1',
+                                     {'par1': 'foo', 'par2': 'baz'},
+                                     '/temp_exception/',
+                                     'POST')
+
     def test_connection_close(self):
         """Check connection re-use after close header is received"""
         HTTPretty.register_uri(HTTPretty.POST,