Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitfcbced8

Browse files
authored
Merge pull request#1872 from python-gitlab/jlvillal/as_dict
feat: add `asdict()` and `to_json()` methods to Gitlab Objects
2 parents2c90fd0 +08ac071 commitfcbced8

File tree

3 files changed

+148
-13
lines changed

3 files changed

+148
-13
lines changed

‎docs/api-usage.rst‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,38 @@ the value on the object is accepted:
214214
issue.my_super_awesome_feature_flag="random_value"
215215
issue.save()
216216
217+
You can get a dictionary representation copy of the Gitlab Object. Modifications made to
218+
the dictionary will have no impact on the GitLab Object.
219+
220+
* `asdict()` method. Returns a dictionary representation of the Gitlab object.
221+
* `attributes` property. Returns a dictionary representation of the Gitlab
222+
object. Also returns any relevant parent object attributes.
223+
224+
..note::
225+
226+
`attributes` returns the parent object attributes that are defined in
227+
`object._from_parent_attrs`. What this can mean is that for example a `ProjectIssue`
228+
object will have a `project_id` key in the dictionary returned from `attributes` but
229+
`asdict()` will not.
230+
231+
232+
..code-block::python
233+
234+
project= gl.projects.get(1)
235+
project_dict= project.asdict()
236+
237+
# Or a dictionary representation also containing some of the parent attributes
238+
issue= project.issues.get(1)
239+
attribute_dict= issue.attributes
240+
241+
You can get a JSON string represenation of the Gitlab Object. For example:
242+
243+
..code-block::python
244+
245+
project= gl.projects.get(1)
246+
print(project.to_json())
247+
# Use arguments supported by `json.dump()`
248+
print(project.to_json(sort_keys=True,indent=4))
217249
218250
Base types
219251
==========

‎gitlab/base.py‎

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
# You should have received a copy of the GNU Lesser General Public License
1616
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717

18+
importcopy
1819
importimportlib
20+
importjson
1921
importpprint
2022
importtextwrap
2123
fromtypesimportModuleType
@@ -151,15 +153,26 @@ def __getattr__(self, name: str) -> Any:
151153
def__setattr__(self,name:str,value:Any)->None:
152154
self.__dict__["_updated_attrs"][name]=value
153155

156+
defasdict(self,*,with_parent_attrs:bool=False)->Dict[str,Any]:
157+
data= {}
158+
ifwith_parent_attrs:
159+
data.update(copy.deepcopy(self._parent_attrs))
160+
data.update(copy.deepcopy(self._attrs))
161+
data.update(copy.deepcopy(self._updated_attrs))
162+
returndata
163+
164+
@property
165+
defattributes(self)->Dict[str,Any]:
166+
returnself.asdict(with_parent_attrs=True)
167+
168+
defto_json(self,*,with_parent_attrs:bool=False,**kwargs:Any)->str:
169+
returnjson.dumps(self.asdict(with_parent_attrs=with_parent_attrs),**kwargs)
170+
154171
def__str__(self)->str:
155-
data=self._attrs.copy()
156-
data.update(self._updated_attrs)
157-
returnf"{type(self)} =>{data}"
172+
returnf"{type(self)} =>{self.asdict()}"
158173

159174
defpformat(self)->str:
160-
data=self._attrs.copy()
161-
data.update(self._updated_attrs)
162-
returnf"{type(self)} =>\n{pprint.pformat(data)}"
175+
returnf"{type(self)} =>\n{pprint.pformat(self.asdict())}"
163176

164177
defpprint(self)->None:
165178
print(self.pformat())
@@ -250,13 +263,6 @@ def encoded_id(self) -> Optional[Union[int, str]]:
250263
obj_id=gitlab.utils.EncodedId(obj_id)
251264
returnobj_id
252265

253-
@property
254-
defattributes(self)->Dict[str,Any]:
255-
d=self.__dict__["_updated_attrs"].copy()
256-
d.update(self.__dict__["_attrs"])
257-
d.update(self.__dict__["_parent_attrs"])
258-
returnd
259-
260266

261267
classRESTObjectList:
262268
"""Generator object representing a list of RESTObject's.

‎tests/unit/test_base.py‎

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ class FakeManager(base.RESTManager):
3636
_path="/tests"
3737

3838

39+
classFakeParent:
40+
id=42
41+
42+
43+
classFakeManagerWithParent(base.RESTManager):
44+
_path="/tests/{test_id}/cases"
45+
_obj_cls=FakeObject
46+
_from_parent_attrs= {"test_id":"id"}
47+
48+
3949
@pytest.fixture
4050
deffake_gitlab():
4151
returnFakeGitlab()
@@ -46,6 +56,21 @@ def fake_manager(fake_gitlab):
4656
returnFakeManager(fake_gitlab)
4757

4858

59+
@pytest.fixture
60+
deffake_manager_with_parent(fake_gitlab):
61+
returnFakeManagerWithParent(fake_gitlab,parent=FakeParent)
62+
63+
64+
@pytest.fixture
65+
deffake_object(fake_manager):
66+
returnFakeObject(fake_manager, {"attr1":"foo","alist": [1,2,3]})
67+
68+
69+
@pytest.fixture
70+
deffake_object_with_parent(fake_manager_with_parent):
71+
returnFakeObject(fake_manager_with_parent, {"attr1":"foo","alist": [1,2,3]})
72+
73+
4974
classTestRESTManager:
5075
deftest_computed_path_simple(self):
5176
classMGR(base.RESTManager):
@@ -306,3 +331,75 @@ def test_repr(self, fake_manager):
306331

307332
FakeObject._id_attr=None
308333
assertrepr(obj)=="<FakeObject>"
334+
335+
deftest_attributes_get(self,fake_object):
336+
assertfake_object.attr1=="foo"
337+
result=fake_object.attributes
338+
assertresult== {"attr1":"foo","alist": [1,2,3]}
339+
340+
deftest_attributes_shows_updates(self,fake_object):
341+
# Updated attribute value is reflected in `attributes`
342+
fake_object.attr1="hello"
343+
assertfake_object.attributes== {"attr1":"hello","alist": [1,2,3]}
344+
assertfake_object.attr1=="hello"
345+
# New attribute is in `attributes`
346+
fake_object.new_attrib="spam"
347+
assertfake_object.attributes== {
348+
"attr1":"hello",
349+
"new_attrib":"spam",
350+
"alist": [1,2,3],
351+
}
352+
353+
deftest_attributes_is_copy(self,fake_object):
354+
# Modifying the dictionary does not cause modifications to the object
355+
result=fake_object.attributes
356+
result["alist"].append(10)
357+
assertresult== {"attr1":"foo","alist": [1,2,3,10]}
358+
assertfake_object.attributes== {"attr1":"foo","alist": [1,2,3]}
359+
360+
deftest_attributes_has_parent_attrs(self,fake_object_with_parent):
361+
assertfake_object_with_parent.attr1=="foo"
362+
result=fake_object_with_parent.attributes
363+
assertresult== {"attr1":"foo","alist": [1,2,3],"test_id":"42"}
364+
365+
deftest_asdict(self,fake_object):
366+
assertfake_object.attr1=="foo"
367+
result=fake_object.asdict()
368+
assertresult== {"attr1":"foo","alist": [1,2,3]}
369+
370+
deftest_asdict_no_parent_attrs(self,fake_object_with_parent):
371+
assertfake_object_with_parent.attr1=="foo"
372+
result=fake_object_with_parent.asdict()
373+
assertresult== {"attr1":"foo","alist": [1,2,3]}
374+
assert"test_id"notinfake_object_with_parent.asdict()
375+
assert"test_id"notinfake_object_with_parent.asdict(with_parent_attrs=False)
376+
assert"test_id"infake_object_with_parent.asdict(with_parent_attrs=True)
377+
378+
deftest_asdict_modify_dict_does_not_change_object(self,fake_object):
379+
result=fake_object.asdict()
380+
# Demonstrate modifying the dictionary does not modify the object
381+
result["attr1"]="testing"
382+
result["alist"].append(4)
383+
assertresult== {"attr1":"testing","alist": [1,2,3,4]}
384+
assertfake_object.attr1=="foo"
385+
assertfake_object.alist== [1,2,3]
386+
387+
deftest_asdict_modify_dict_does_not_change_object2(self,fake_object):
388+
# Modify attribute and then ensure modifying a list in the returned dict won't
389+
# modify the list in the object.
390+
fake_object.attr1= [9,7,8]
391+
assertfake_object.asdict()== {
392+
"attr1": [9,7,8],
393+
"alist": [1,2,3],
394+
}
395+
result=fake_object.asdict()
396+
result["attr1"].append(1)
397+
assertfake_object.asdict()== {
398+
"attr1": [9,7,8],
399+
"alist": [1,2,3],
400+
}
401+
402+
deftest_asdict_modify_object(self,fake_object):
403+
# asdict() returns the updated value
404+
fake_object.attr1="spam"
405+
assertfake_object.asdict()== {"attr1":"spam","alist": [1,2,3]}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp