summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn L. Villalovos <john@sodarock.com>2022-07-20 08:38:31 -0700
committerJohn L. Villalovos <john@sodarock.com>2022-07-20 08:38:31 -0700
commite5affc8749797293c1373c6af96334f194875038 (patch)
tree91fbf3e2ade71e17da2083f4b3b9702e4fbc3cde
parentf6b6e18f96f4cdf67c8c53ae79e6a8259dcce9ee (diff)
downloadgitlab-e5affc8749797293c1373c6af96334f194875038.tar.gz
fix: results returned by `attributes` property to show updates
Previously the `attributes` method would show the original values in a Gitlab Object even if they had been updated. Correct this so that the updated value will be returned. Also use copy.deepcopy() to ensure that modifying the dictionary returned can not also modify the object.
-rw-r--r--gitlab/base.py10
-rw-r--r--tests/unit/test_base.py26
2 files changed, 32 insertions, 4 deletions
diff --git a/gitlab/base.py b/gitlab/base.py
index 920617b..7de76e0 100644
--- a/gitlab/base.py
+++ b/gitlab/base.py
@@ -15,6 +15,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import copy
import importlib
import pprint
import textwrap
@@ -243,10 +244,11 @@ class RESTObject:
@property
def attributes(self) -> Dict[str, Any]:
- d = self.__dict__["_updated_attrs"].copy()
- d.update(self.__dict__["_attrs"])
- d.update(self.__dict__["_parent_attrs"])
- return d
+ data = {}
+ data.update(copy.deepcopy(self._parent_attrs))
+ data.update(copy.deepcopy(self._attrs))
+ data.update(copy.deepcopy(self._updated_attrs))
+ return data
class RESTObjectList:
diff --git a/tests/unit/test_base.py b/tests/unit/test_base.py
index ea4ad9e..53e484e 100644
--- a/tests/unit/test_base.py
+++ b/tests/unit/test_base.py
@@ -46,6 +46,11 @@ def fake_manager(fake_gitlab):
return FakeManager(fake_gitlab)
+@pytest.fixture
+def fake_object(fake_manager):
+ return FakeObject(fake_manager, {"attr1": [1, 2, 3]})
+
+
class TestRESTManager:
def test_computed_path_simple(self):
class MGR(base.RESTManager):
@@ -306,3 +311,24 @@ class TestRESTObject:
FakeObject._id_attr = None
assert repr(obj) == "<FakeObject>"
+
+ def test_attributes_get(self, fake_object):
+ assert fake_object.attr1 == [1, 2, 3]
+ result = fake_object.attributes
+ assert result == {"attr1": [1, 2, 3]}
+
+ def test_attributes_shows_updates(self, fake_object):
+ # Updated attribute value is reflected in `attributes`
+ fake_object.attr1 = "hello"
+ assert fake_object.attributes == {"attr1": "hello"}
+ assert fake_object.attr1 == "hello"
+ # New attribute is in `attributes`
+ fake_object.new_attrib = "spam"
+ assert fake_object.attributes == {"attr1": "hello", "new_attrib": "spam"}
+
+ def test_attributes_is_copy(self, fake_object):
+ # Modifying the dictionary does not cause modifications to the object
+ result = fake_object.attributes
+ result["attr1"].append(10)
+ assert result == {"attr1": [1, 2, 3, 10]}
+ assert fake_object.attributes == {"attr1": [1, 2, 3]}