[lxc-devel] [pylxd/master] Remove overzealous '_' -> '-' subsitution in _APINode

ajkavanagh on Github lxc-bot at linuxcontainers.org
Tue Apr 17 15:30:30 UTC 2018


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 608 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180417/7d5f8d9f/attachment.bin>
-------------- next part --------------
From 27420d33c6ee230fd9099d680fcf170a8840e7cb Mon Sep 17 00:00:00 2001
From: Alex Kavanagh <alex at ajkavanagh.co.uk>
Date: Tue, 17 Apr 2018 16:27:12 +0100
Subject: [PATCH] Remove overzealous '_' -> '-' subsitution in _APINode

The fix for storage pools in the __getattr__() in _APINode has
affected the names of containers and snapshots, if '_' are used in
the name.  This reverts most of that fix, adds tests, and special cases
'storage_pools' -> 'storage-pools' as an attribute ONLY, and not as
a name of a item.

Fixes: #295
---
 pylxd/client.py            | 37 +++++++++++++++++++++++++------------
 pylxd/tests/test_client.py | 45 ++++++++++++++++++++-------------------------
 2 files changed, 45 insertions(+), 37 deletions(-)

diff --git a/pylxd/client.py b/pylxd/client.py
index e8622e3..22d5902 100644
--- a/pylxd/client.py
+++ b/pylxd/client.py
@@ -60,20 +60,33 @@ def __init__(self, api_endpoint, cert=None, verify=True, timeout=None):
             self.session.verify = verify
 
     def __getattr__(self, name):
-        # name here correspoinds to the model name in the LXD API
-        # and, as such, must have underscores replaced with hyphens
-        return self.__class__(
-            '{}/{}'.format(self._api_endpoint, name.replace('_', '-')),
-            cert=self.session.cert, verify=self.session.verify)
+        """Converts attribute lookup into the next /<segment> of an api
+        url.
+
+        :param name: the next segment
+        :type name: str
+        :returns: new _APINode with /<name> on the end
+        :rtype: _APINode
+        """
+        # Special case for storage_pools which needs to become 'storage-pools'
+        if name == 'storage_pools':
+            name = 'storage-pools'
+        return self.__class__('{}/{}'.format(self._api_endpoint, name),
+                              cert=self.session.cert,
+                              verify=self.session.verify)
 
     def __getitem__(self, item):
-        # item here correspoinds to the model name in the LXD API
-        # and, as such, must have underscores replaced with hyphens
-        return self.__class__(
-            '{}/{}'.format(self._api_endpoint, item.replace('_', '-')),
-            cert=self.session.cert,
-            verify=self.session.verify,
-            timeout=self._timeout)
+        """This converts python api.thing[name] -> ".../thing/name"
+
+        :param item: the 'thing' in the square-braces in a python expr.
+        :type item: str
+        :returns: A new _APINode(with the new item tagged on as /<item>
+        :rtype: _APINode
+        """
+        return self.__class__('{}/{}'.format(self._api_endpoint, item),
+                              cert=self.session.cert,
+                              verify=self.session.verify,
+                              timeout=self._timeout)
 
     def _assert_response(self, response, allowed_status_codes=(200,),
                          stream=False, is_api=True):
diff --git a/pylxd/tests/test_client.py b/pylxd/tests/test_client.py
index 5762c5a..f0d48db 100644
--- a/pylxd/tests/test_client.py
+++ b/pylxd/tests/test_client.py
@@ -246,31 +246,45 @@ class TestAPINode(unittest.TestCase):
     def test_getattr(self):
         """API Nodes can use object notation for nesting."""
         node = client._APINode('http://test.com')
-
         new_node = node.test
-
         self.assertEqual(
             'http://test.com/test', new_node._api_endpoint)
 
+    def test_getattr_storage_pools(self):
+        """API node with storage_pool should be storage-pool"""
+        node = client._APINode('http://test.com')
+        new_node = node.test.storage_pools
+        self.assertEqual(
+            'http://test.com/test/storage-pools', new_node._api_endpoint)
+        # other _ should stay as they were.
+        new_node = node.test.some_thing
+        self.assertEqual(
+            'http://test.com/test/some_thing', new_node._api_endpoint)
+
     def test_getitem(self):
         """API Nodes can use dict notation for nesting."""
         node = client._APINode('http://test.com')
-
         new_node = node['test']
-
         self.assertEqual(
             'http://test.com/test', new_node._api_endpoint)
 
+    def test_getitem_leave_underscores_alone(self):
+        """Bug 295 erronously changed underscores to '-' -- let's make sure
+        it doens't happend again
+        """
+        node = client._APINode('http://test.com')
+        new_node = node.thing['my_snapshot']
+        self.assertEqual(
+            'http://test.com/thing/my_snapshot', new_node._api_endpoint)
+
     def test_session_http(self):
         """HTTP nodes return the default requests session."""
         node = client._APINode('http://test.com')
-
         self.assertIsInstance(node.session, requests.Session)
 
     def test_session_unix_socket(self):
         """HTTP nodes return a requests_unixsocket session."""
         node = client._APINode('http+unix://test.com')
-
         self.assertIsInstance(node.session, requests_unixsocket.Session)
 
     @mock.patch('pylxd.client.requests.Session')
@@ -284,9 +298,7 @@ def test_get(self, Session):
         Session.return_value = session
 
         node = client._APINode('http://test.com')
-
         node.get()
-
         session.get.assert_called_once_with('http://test.com', timeout=None)
 
     @mock.patch('pylxd.client.requests.Session')
@@ -298,11 +310,8 @@ def test_post(self, Session):
         })
         session = mock.Mock(**{'post.return_value': response})
         Session.return_value = session
-
         node = client._APINode('http://test.com')
-
         node.post()
-
         session.post.assert_called_once_with('http://test.com', timeout=None)
 
     @mock.patch('pylxd.client.requests.Session')
@@ -314,9 +323,7 @@ def test_post_200_not_sync(self, Session):
         })
         session = mock.Mock(**{'post.return_value': response})
         Session.return_value = session
-
         node = client._APINode('http://test.com')
-
         self.assertRaises(
             exceptions.LXDAPIException,
             node.post)
@@ -330,9 +337,7 @@ def test_post_missing_type_200(self, Session):
         })
         session = mock.Mock(**{'post.return_value': response})
         Session.return_value = session
-
         node = client._APINode('http://test.com')
-
         self.assertRaises(
             exceptions.LXDAPIException,
             node.post)
@@ -346,11 +351,8 @@ def test_put(self, Session):
         })
         session = mock.Mock(**{'put.return_value': response})
         Session.return_value = session
-
         node = client._APINode('http://test.com')
-
         node.put()
-
         session.put.assert_called_once_with('http://test.com', timeout=None)
 
     @mock.patch('pylxd.client.requests.Session')
@@ -362,11 +364,8 @@ def test_delete(self, Session):
         })
         session = mock.Mock(**{'delete.return_value': response})
         Session.return_value = session
-
         node = client._APINode('http://test.com')
-
         node.delete()
-
         session.delete.assert_called_once_with('http://test.com', timeout=None)
 
 
@@ -377,9 +376,7 @@ class TestWebsocketClient(unittest.TestCase):
     def test_handshake_ok(self):
         """A `message` attribute of an empty list is created."""
         ws_client = client._WebsocketClient('ws://an/fake/path')
-
         ws_client.handshake_ok()
-
         self.assertEqual([], ws_client.messages)
 
     @requires_ws4py
@@ -388,7 +385,5 @@ def test_received_message(self):
         message = mock.Mock(data=json.dumps({'test': 'data'}).encode('utf-8'))
         ws_client = client._WebsocketClient('ws://an/fake/path')
         ws_client.handshake_ok()
-
         ws_client.received_message(message)
-
         self.assertEqual({'test': 'data'}, ws_client.messages[0])


More information about the lxc-devel mailing list