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

Commit796c700

Browse files
committed
fix(base): allow persisting attributes when updating object
1 parentb563cdc commit796c700

File tree

7 files changed

+109
-7
lines changed

7 files changed

+109
-7
lines changed

‎docs/api-usage.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,37 @@ a project (the previous example used 2 API calls):
190190
project= gl.projects.get(1,lazy=True)# no API call
191191
project.star()# API call
192192
193+
.. _persist_attributes:
194+
195+
Persisting local attributes
196+
===========================
197+
198+
When methods manipulate an existing object, such as with ``refresh()`` and ``save()``,
199+
the object will only have attributes that were returned by the server. In some cases,
200+
such as when the initial request fetches attributes that are needed later for additional
201+
processing, this may not be desired:
202+
203+
..code-block::python
204+
205+
project= gl.projects.get(1,statistics=True)
206+
project.statistics
207+
208+
project.refresh()
209+
project.statistics# AttributeError
210+
211+
To avoid this, pass ``persist_attributes=True`` to ``refresh()``/``save()`` calls:
212+
213+
..code-block::python
214+
215+
project= gl.projects.get(1,statistics=True)
216+
project.statistics
217+
218+
project.refresh(persist_attributes=True)
219+
project.statistics
220+
221+
The ``persist_attributes`` setting is itself persisted in the object and can be reused
222+
for later ``refresh()`` and ``save()`` calls.
223+
193224
Pagination
194225
==========
195226

‎docs/faq.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,8 @@ How can I clone the repository of a project?
3131
print(project.attributes) # displays all the attributes
3232
git_url = project.ssh_url_to_repo
3333
subprocess.call(['git', 'clone', git_url])
34+
35+
I get an ``AttributeError`` when accessing attributes after ``save()`` or ``refresh()``.
36+
You are most likely trying to access an attribute that was not returned
37+
by the server on the second request. Use the ``persist_attributes=True``
38+
argument to override this - see:ref:`persist_attributes`.

‎gitlab/base.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class RESTObject(object):
4545
_attrs:Dict[str,Any]
4646
_module:ModuleType
4747
_parent_attrs:Dict[str,Any]
48+
_persist_attrs:bool
4849
_short_print_attr:Optional[str]=None
4950
_updated_attrs:Dict[str,Any]
5051
manager:"RESTManager"
@@ -59,6 +60,7 @@ def __init__(self, manager: "RESTManager", attrs: Dict[str, Any]) -> None:
5960
}
6061
)
6162
self.__dict__["_parent_attrs"]=self.manager.parent_attrs
63+
self.__dict__["_persist_attrs"]=False
6264
self._create_managers()
6365

6466
def__getstate__(self)->Dict[str,Any]:
@@ -153,7 +155,11 @@ def _create_managers(self) -> None:
153155

154156
def_update_attrs(self,new_attrs:Dict[str,Any])->None:
155157
self.__dict__["_updated_attrs"]= {}
156-
self.__dict__["_attrs"]=new_attrs
158+
159+
ifself.__dict__["_persist_attrs"]isTrue:
160+
self.__dict__["_attrs"].update(new_attrs)
161+
else:
162+
self.__dict__["_attrs"]=new_attrs
157163

158164
defget_id(self):
159165
"""Returns the id of the resource."""

‎gitlab/mixins.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,12 @@ class RefreshMixin(_RestObjectBase):
162162
manager:base.RESTManager
163163

164164
@exc.on_http_error(exc.GitlabGetError)
165-
defrefresh(self,**kwargs:Any)->None:
165+
defrefresh(self,persist_attributes:bool=None,**kwargs:Any)->None:
166166
"""Refresh a single object from server.
167167
168168
Args:
169+
persist_attributes: Whether to keep existing local attributes that
170+
were not fetched from the server on refresh
169171
**kwargs: Extra options to send to the server (e.g. sudo)
170172
171173
Returns None (updates the object)
@@ -174,6 +176,9 @@ def refresh(self, **kwargs: Any) -> None:
174176
GitlabAuthenticationError: If authentication is not correct
175177
GitlabGetError: If the server cannot perform the request
176178
"""
179+
ifpersist_attributesisnotNone:
180+
self.__dict__["_persist_attrs"]=persist_attributes
181+
177182
ifself._id_attr:
178183
path="%s/%s"% (self.manager.path,self.id)
179184
else:
@@ -529,18 +534,23 @@ def _get_updated_data(self) -> Dict[str, Any]:
529534

530535
returnupdated_data
531536

532-
defsave(self,**kwargs:Any)->None:
537+
defsave(self,persist_attributes:bool=None,**kwargs:Any)->None:
533538
"""Save the changes made to the object to the server.
534539
535540
The object is updated to match what the server returns.
536541
537542
Args:
543+
persist_attributes: Whether to keep existing local attributes that
544+
were not fetched from the server on save
538545
**kwargs: Extra options to send to the server (e.g. sudo)
539546
540547
Raise:
541548
GitlabAuthenticationError: If authentication is not correct
542549
GitlabUpdateError: If the server cannot perform the request
543550
"""
551+
ifpersist_attributesisnotNone:
552+
self.__dict__["_persist_attrs"]=persist_attributes
553+
544554
updated_data=self._get_updated_data()
545555
# Nothing to update. Server fails if sent an empty dict.
546556
ifnotupdated_data:

‎gitlab/tests/test_base.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,25 @@ def test_update_attrs(self, fake_manager):
128128
assert {"foo":"foo","bar":"bar"}==obj._attrs
129129
assert {}==obj._updated_attrs
130130

131-
deftest_update_attrs_deleted(self,fake_manager):
132-
obj=FakeObject(fake_manager, {"foo":"foo","bar":"bar"})
133-
obj.bar="baz"
131+
@pytest.mark.parametrize(
132+
"initial_attrs,persist_attrs,assigned_attr,expected_attrs",
133+
[
134+
({"foo":"foo","bar":"bar"},None,"baz", {"foo":"foo"}),
135+
({"foo":"foo","bar":"bar"},False,"baz", {"foo":"foo"}),
136+
({"foo":"foo","bar":"bar"},True,"baz", {"foo":"foo","bar":"baz"}),
137+
],
138+
)
139+
deftest_update_attrs_deleted(
140+
self,fake_manager,initial_attrs,persist_attrs,assigned_attr,expected_attrs
141+
):
142+
obj=FakeObject(fake_manager,initial_attrs)
143+
obj._attrs["bar"]=assigned_attr
144+
145+
ifpersist_attrsisnotNone:
146+
obj.__dict__["_persist_attrs"]=persist_attrs
147+
134148
obj._update_attrs({"foo":"foo"})
135-
assert{"foo":"foo"}==obj._attrs
149+
assertexpected_attrs==obj._attrs
136150
assert {}==obj._updated_attrs
137151

138152
deftest_dir_unique(self,fake_manager):

‎tools/functional/api/test_projects.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,24 @@ def test_project_stars(project):
234234
assertproject.star_count==0
235235

236236

237+
@pytest.mark.parametrize(
238+
"refresh_kwargs,hasattr_before,hasattr_after",
239+
[
240+
({},True,False),
241+
({"persist_attributes":True},True,True),
242+
({"persist_attributes":False},True,False),
243+
],
244+
)
245+
deftest_project_statistics_after_refresh(
246+
gl,project,refresh_kwargs,hasattr_before,hasattr_after
247+
):
248+
project=gl.projects.get(project.id,statistics=True)
249+
asserthasattr(project,"statistics")==hasattr_before
250+
251+
project.refresh(**refresh_kwargs)
252+
asserthasattr(project,"statistics")==hasattr_after
253+
254+
237255
deftest_project_tags(project,project_file):
238256
tag=project.tags.create({"tag_name":"v1.0","ref":"master"})
239257
assertlen(project.tags.list())==1

‎tools/functional/api/test_users.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,24 @@ def test_user_custom_attributes(gl, user):
142142
assertlen(user.customattributes.list())==0
143143

144144

145+
@pytest.mark.parametrize(
146+
"save_kwargs,hasattr_before,hasattr_after",
147+
[
148+
({},True,False),
149+
({"persist_attributes":True},True,True),
150+
({"persist_attributes":False},True,False),
151+
],
152+
)
153+
deftest_user_custom_attributes_after_save(
154+
gl,user,save_kwargs,hasattr_before,hasattr_after
155+
):
156+
user=gl.users.get(user.id,with_custom_attributes=True)
157+
asserthasattr(user,"custom_attributes")==hasattr_before
158+
159+
user.save(**save_kwargs)
160+
asserthasattr(user,"custom_attributes")==hasattr_after
161+
162+
145163
deftest_user_impersonation_tokens(gl,user):
146164
token=user.impersonationtokens.create(
147165
{"name":"token1","scopes": ["api","read_user"]}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp