← Back to team overview

cloud-init-dev team mailing list archive

[Merge] ~chad.smith/cloud-init:unittest-oauthlib-import into cloud-init:master

 

Chad Smith has proposed merging ~chad.smith/cloud-init:unittest-oauthlib-import into cloud-init:master.

Requested reviews:
  cloud-init commiters (cloud-init-dev)

For more details, see:
https://code.launchpad.net/~chad.smith/cloud-init/+git/cloud-init/+merge/329872

url_helper: dynamically import oauthlib import from inside oauth_headers

oauth_headers is the only function which requires oauthlib, move the import and ImportError handling inside this function to only attempt loading at runtime if called. This will allow us to build on platforms that don't have python-oauthlib installed by default. Add simple unittests around the missing oauthlib dependencies to make sure the function performs as intended and raises and NotImplementedError if oauthlib can't be imported.

-- 
Your team cloud-init commiters is requested to review the proposed merge of ~chad.smith/cloud-init:unittest-oauthlib-import into cloud-init:master.
diff --git a/cloudinit/tests/__init__.py b/cloudinit/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cloudinit/tests/__init__.py
diff --git a/cloudinit/tests/test_url_helper.py b/cloudinit/tests/test_url_helper.py
new file mode 100644
index 0000000..3ea08d8
--- /dev/null
+++ b/cloudinit/tests/test_url_helper.py
@@ -0,0 +1,41 @@
+# This file is part of cloud-init. See LICENSE file for license information.
+
+from cloudinit.url_helper import oauth_headers
+from tests.unittests.helpers import CiTestCase, mock, skipIf
+
+
+try:
+    import oauthlib
+    assert oauthlib  # avoid pyflakes error F401: import unused
+    _missing_oauthlib_dep = False
+except ImportError:
+    _missing_oauthlib_dep = True
+
+
+class TestOAuthHeaders(CiTestCase):
+
+    def test_oauth_headers_raises_not_implemented_when_oathlib_missing(self):
+        """oauth_headers raises a NotImplemented error when oauth absent."""
+        with mock.patch.dict('sys.modules', {'oauthlib': None}):
+            with self.assertRaises(NotImplementedError) as context_manager:
+                oauth_headers(1, 2, 3, 4, 5)
+        self.assertEqual(
+            'oauth support is not available',
+            str(context_manager.exception))
+
+    @skipIf(_missing_oauthlib_dep, "No python-oauthlib dependency")
+    @mock.patch('oauthlib.oauth1.Client')
+    def test_oauth_headers_calls_oathlibclient_when_available(self, m_client):
+        """oauth_headers raises a NotImplemented error when oauth absent."""
+        class fakeclient(object):
+            def sign(self, url):
+                # The first and 3rd item of the client.sign tuple are ignored
+                return ('junk', url, 'junk2')
+
+        m_client.return_value = fakeclient()
+
+        return_value = oauth_headers(
+            'url', 'consumer_key', 'token_key', 'token_secret',
+            'consumer_secret')
+        self.assertEqual('url', return_value)
+
diff --git a/cloudinit/url_helper.py b/cloudinit/url_helper.py
index c83061a..0e0f5b4 100644
--- a/cloudinit/url_helper.py
+++ b/cloudinit/url_helper.py
@@ -17,11 +17,6 @@ import time
 from email.utils import parsedate
 from functools import partial
 
-try:
-    import oauthlib.oauth1 as oauth1
-except ImportError:
-    oauth1 = None
-
 from requests import exceptions
 
 from six.moves.urllib.parse import (
@@ -492,8 +487,9 @@ class OauthUrlHelper(object):
 
 def oauth_headers(url, consumer_key, token_key, token_secret, consumer_secret,
                   timestamp=None):
-
-    if oauth1 is None:
+    try:
+        import oauthlib.oauth1 as oauth1
+    except ImportError:
         raise NotImplementedError('oauth support is not available')
 
     if timestamp:

Follow ups