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

Commit510ec30

Browse files
authored
Merge pull request#1699 from python-gitlab/jlvillal/arrays
fix: use the [] after key names for array variables in `params`
2 parents194ee01 +1af44ce commit510ec30

File tree

7 files changed

+134
-31
lines changed

7 files changed

+134
-31
lines changed

‎gitlab/mixins.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,12 @@ def list(self, **kwargs: Any) -> Union[base.RESTObjectList, List[base.RESTObject
238238
GitlabListError: If the server cannot perform the request
239239
"""
240240

241-
data,_=utils._transform_types(kwargs,self._types,transform_files=False)
241+
data,_=utils._transform_types(
242+
data=kwargs,
243+
custom_types=self._types,
244+
transform_data=True,
245+
transform_files=False,
246+
)
242247

243248
ifself.gitlab.per_page:
244249
data.setdefault("per_page",self.gitlab.per_page)
@@ -303,7 +308,9 @@ def create(
303308
data= {}
304309

305310
self._create_attrs.validate_attrs(data=data)
306-
data,files=utils._transform_types(data,self._types)
311+
data,files=utils._transform_types(
312+
data=data,custom_types=self._types,transform_data=False
313+
)
307314

308315
# Handle specific URL for creation
309316
path=kwargs.pop("path",self.path)
@@ -370,7 +377,9 @@ def update(
370377
ifself._obj_clsisnotNoneandself._obj_cls._id_attrisnotNone:
371378
excludes= [self._obj_cls._id_attr]
372379
self._update_attrs.validate_attrs(data=new_data,excludes=excludes)
373-
new_data,files=utils._transform_types(new_data,self._types)
380+
new_data,files=utils._transform_types(
381+
data=new_data,custom_types=self._types,transform_data=False
382+
)
374383

375384
http_method=self._get_update_method()
376385
result=http_method(path,post_data=new_data,files=files,**kwargs)

‎gitlab/types.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ def get(self) -> Any:
6363
defset_from_cli(self,cli_value:Any)->None:
6464
self._value=cli_value
6565

66-
defget_for_api(self)->Any:
67-
returnself._value
66+
defget_for_api(self,*,key:str)->Tuple[str,Any]:
67+
return(key,self._value)
6868

6969

7070
class_ListArrayAttribute(GitlabAttribute):
@@ -76,20 +76,28 @@ def set_from_cli(self, cli_value: str) -> None:
7676
else:
7777
self._value= [item.strip()foritemincli_value.split(",")]
7878

79-
defget_for_api(self)->str:
79+
defget_for_api(self,*,key:str)->Tuple[str,str]:
8080
# Do not comma-split single value passed as string
8181
ifisinstance(self._value,str):
82-
returnself._value
82+
return(key,self._value)
8383

8484
ifTYPE_CHECKING:
8585
assertisinstance(self._value,list)
86-
return",".join([str(x)forxinself._value])
86+
return(key,",".join([str(x)forxinself._value]))
8787

8888

8989
classArrayAttribute(_ListArrayAttribute):
9090
"""To support `array` types as documented in
9191
https://docs.gitlab.com/ee/api/#array"""
9292

93+
defget_for_api(self,*,key:str)->Tuple[str,Any]:
94+
ifisinstance(self._value,str):
95+
return (f"{key}[]",self._value)
96+
97+
ifTYPE_CHECKING:
98+
assertisinstance(self._value,list)
99+
return (f"{key}[]",self._value)
100+
93101

94102
classCommaSeparatedListAttribute(_ListArrayAttribute):
95103
"""For values which are sent to the server as a Comma Separated Values
@@ -98,8 +106,8 @@ class CommaSeparatedListAttribute(_ListArrayAttribute):
98106

99107

100108
classLowercaseStringAttribute(GitlabAttribute):
101-
defget_for_api(self)->str:
102-
returnstr(self._value).lower()
109+
defget_for_api(self,*,key:str)->Tuple[str,str]:
110+
return(key,str(self._value).lower())
103111

104112

105113
classFileAttribute(GitlabAttribute):

‎gitlab/utils.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,34 +55,53 @@ def response_content(
5555

5656

5757
def_transform_types(
58-
data:Dict[str,Any],custom_types:dict,*,transform_files:Optional[bool]=True
58+
data:Dict[str,Any],
59+
custom_types:dict,
60+
*,
61+
transform_data:bool,
62+
transform_files:Optional[bool]=True,
5963
)->Tuple[dict,dict]:
6064
"""Copy the data dict with attributes that have custom types and transform them
6165
before being sent to the server.
6266
63-
If``transform_files`` is ``True`` (default), also populates the ``files`` dict for
67+
``transform_files``: If ``True`` (default), also populates the ``files`` dict for
6468
FileAttribute types with tuples to prepare fields for requests' MultipartEncoder:
6569
https://toolbelt.readthedocs.io/en/latest/user.html#multipart-form-data-encoder
6670
71+
``transform_data``: If ``True`` transforms the ``data`` dict with fields
72+
suitable for encoding as query parameters for GitLab's API:
73+
https://docs.gitlab.com/ee/api/#encoding-api-parameters-of-array-and-hash-types
74+
6775
Returns:
6876
A tuple of the transformed data dict and files dict"""
6977

7078
# Duplicate data to avoid messing with what the user sent us
7179
data=data.copy()
80+
ifnottransform_filesandnottransform_data:
81+
returndata, {}
82+
7283
files= {}
7384

74-
forattr_name,type_clsincustom_types.items():
85+
forattr_name,attr_classincustom_types.items():
7586
ifattr_namenotindata:
7687
continue
7788

78-
type_obj=type_cls(data[attr_name])
89+
gitlab_attribute=attr_class(data[attr_name])
7990

80-
# if the typeif FileAttribute we need to pass the data as file
81-
iftransform_filesandisinstance(type_obj,types.FileAttribute):
82-
key=type_obj.get_file_name(attr_name)
91+
# if the typeis FileAttribute we need to pass the data as file
92+
ifisinstance(gitlab_attribute,types.FileAttribute)andtransform_files:
93+
key=gitlab_attribute.get_file_name(attr_name)
8394
files[attr_name]= (key,data.pop(attr_name))
84-
else:
85-
data[attr_name]=type_obj.get_for_api()
95+
continue
96+
97+
ifnottransform_data:
98+
continue
99+
100+
ifisinstance(gitlab_attribute,types.GitlabAttribute):
101+
key,value=gitlab_attribute.get_for_api(key=attr_name)
102+
ifkey!=attr_name:
103+
deldata[attr_name]
104+
data[key]=value
86105

87106
returndata,files
88107

@@ -94,6 +113,8 @@ def copy_dict(
94113
)->None:
95114
fork,vinsrc.items():
96115
ifisinstance(v,dict):
116+
# NOTE(jlvillal): This provides some support for the `hash` type
117+
# https://docs.gitlab.com/ee/api/#hash
97118
# Transform dict values to new attributes. For example:
98119
# custom_attributes: {'foo', 'bar'} =>
99120
# "custom_attributes['foo']": "bar"

‎tests/functional/api/test_groups.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ def test_groups(gl):
9999
assertlen(group1.members.list())==3
100100
assertlen(group2.members.list())==2
101101

102+
# Test `user_ids` array
103+
result=group1.members.list(user_ids=[user.id,99999])
104+
assertlen(result)==1
105+
assertresult[0].id==user.id
106+
102107
group1.members.delete(user.id)
103108
assertusernotingroup1.members.list()
104109
assertgroup1.members_all.list()

‎tests/unit/mixins/test_mixin_methods.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,25 @@ class M(ListMixin, FakeManager):
205205
assertresponses.assert_call_count(url,2)isTrue
206206

207207

208+
@responses.activate
209+
deftest_list_mixin_with_attributes(gl):
210+
classM(ListMixin,FakeManager):
211+
_types= {"my_array":gl_types.ArrayAttribute}
212+
213+
url="http://localhost/api/v4/tests"
214+
responses.add(
215+
method=responses.GET,
216+
headers={},
217+
url=url,
218+
json=[],
219+
status=200,
220+
match=[responses.matchers.query_param_matcher({"my_array[]": ["1","2","3"]})],
221+
)
222+
223+
mgr=M(gl)
224+
mgr.list(iterator=True,my_array=[1,2,3])
225+
226+
208227
@responses.activate
209228
deftest_list_other_url(gl):
210229
classM(ListMixin,FakeManager):
@@ -295,6 +314,25 @@ class M(CreateMixin, FakeManager):
295314
assertresponses.assert_call_count(url,1)isTrue
296315

297316

317+
@responses.activate
318+
deftest_create_mixin_with_attributes(gl):
319+
classM(CreateMixin,FakeManager):
320+
_types= {"my_array":gl_types.ArrayAttribute}
321+
322+
url="http://localhost/api/v4/tests"
323+
responses.add(
324+
method=responses.POST,
325+
headers={},
326+
url=url,
327+
json={},
328+
status=200,
329+
match=[responses.matchers.json_params_matcher({"my_array": [1,2,3]})],
330+
)
331+
332+
mgr=M(gl)
333+
mgr.create({"my_array": [1,2,3]})
334+
335+
298336
deftest_update_mixin_missing_attrs(gl):
299337
classM(UpdateMixin,FakeManager):
300338
_update_attrs=gl_types.RequiredOptional(

‎tests/unit/test_types.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def test_gitlab_attribute_get():
7373

7474
o.set_from_cli("whatever2")
7575
asserto.get()=="whatever2"
76-
asserto.get_for_api()=="whatever2"
76+
asserto.get_for_api(key="spam")==("spam","whatever2")
7777

7878
o=types.GitlabAttribute()
7979
asserto._valueisNone
@@ -100,42 +100,42 @@ def test_array_attribute_empty_input():
100100
deftest_array_attribute_get_for_api_from_cli():
101101
o=types.ArrayAttribute()
102102
o.set_from_cli("foo,bar,baz")
103-
asserto.get_for_api()=="foo,bar,baz"
103+
asserto.get_for_api(key="spam")==("spam[]", ["foo","bar","baz"])
104104

105105

106106
deftest_array_attribute_get_for_api_from_list():
107107
o=types.ArrayAttribute(["foo","bar","baz"])
108-
asserto.get_for_api()=="foo,bar,baz"
108+
asserto.get_for_api(key="spam")==("spam[]", ["foo","bar","baz"])
109109

110110

111111
deftest_array_attribute_get_for_api_from_int_list():
112112
o=types.ArrayAttribute([1,9,7])
113-
asserto.get_for_api()=="1,9,7"
113+
asserto.get_for_api(key="spam")==("spam[]", [1,9,7])
114114

115115

116116
deftest_array_attribute_does_not_split_string():
117117
o=types.ArrayAttribute("foo")
118-
asserto.get_for_api()=="foo"
118+
asserto.get_for_api(key="spam")==("spam[]","foo")
119119

120120

121121
# CommaSeparatedListAttribute tests
122122
deftest_csv_string_attribute_get_for_api_from_cli():
123123
o=types.CommaSeparatedListAttribute()
124124
o.set_from_cli("foo,bar,baz")
125-
asserto.get_for_api()=="foo,bar,baz"
125+
asserto.get_for_api(key="spam")==("spam","foo,bar,baz")
126126

127127

128128
deftest_csv_string_attribute_get_for_api_from_list():
129129
o=types.CommaSeparatedListAttribute(["foo","bar","baz"])
130-
asserto.get_for_api()=="foo,bar,baz"
130+
asserto.get_for_api(key="spam")==("spam","foo,bar,baz")
131131

132132

133133
deftest_csv_string_attribute_get_for_api_from_int_list():
134134
o=types.CommaSeparatedListAttribute([1,9,7])
135-
asserto.get_for_api()=="1,9,7"
135+
asserto.get_for_api(key="spam")==("spam","1,9,7")
136136

137137

138138
# LowercaseStringAttribute tests
139139
deftest_lowercase_string_attribute_get_for_api():
140140
o=types.LowercaseStringAttribute("FOO")
141-
asserto.get_for_api()=="foo"
141+
asserto.get_for_api(key="spam")==("spam","foo")

‎tests/unit/test_utils.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def test_remove_none_from_dict(dictionary, expected):
155155

156156
deftest_transform_types_copies_data_with_empty_files():
157157
data= {"attr":"spam"}
158-
new_data,files=utils._transform_types(data, {})
158+
new_data,files=utils._transform_types(data, {},transform_data=True)
159159

160160
assertnew_dataisnotdata
161161
assertnew_data==data
@@ -165,7 +165,7 @@ def test_transform_types_copies_data_with_empty_files():
165165
deftest_transform_types_with_transform_files_populates_files():
166166
custom_types= {"attr":types.FileAttribute}
167167
data= {"attr":"spam"}
168-
new_data,files=utils._transform_types(data,custom_types)
168+
new_data,files=utils._transform_types(data,custom_types,transform_data=True)
169169

170170
assertnew_data== {}
171171
assertfiles["attr"]== ("attr","spam")
@@ -174,7 +174,29 @@ def test_transform_types_with_transform_files_populates_files():
174174
deftest_transform_types_without_transform_files_populates_data_with_empty_files():
175175
custom_types= {"attr":types.FileAttribute}
176176
data= {"attr":"spam"}
177-
new_data,files=utils._transform_types(data,custom_types,transform_files=False)
177+
new_data,files=utils._transform_types(
178+
data,custom_types,transform_files=False,transform_data=True
179+
)
178180

179181
assertnew_data== {"attr":"spam"}
180182
assertfiles== {}
183+
184+
185+
deftest_transform_types_params_array():
186+
data= {"attr": [1,2,3]}
187+
custom_types= {"attr":types.ArrayAttribute}
188+
new_data,files=utils._transform_types(data,custom_types,transform_data=True)
189+
190+
assertnew_dataisnotdata
191+
assertnew_data== {"attr[]": [1,2,3]}
192+
assertfiles== {}
193+
194+
195+
deftest_transform_types_not_params_array():
196+
data= {"attr": [1,2,3]}
197+
custom_types= {"attr":types.ArrayAttribute}
198+
new_data,files=utils._transform_types(data,custom_types,transform_data=False)
199+
200+
assertnew_dataisnotdata
201+
assertnew_data==data
202+
assertfiles== {}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp