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

Commit5628004

Browse files
committed
refactor: decouple CLI from custom method arguments
1 parent0e3c461 commit5628004

17 files changed

+159
-114
lines changed

‎gitlab/base.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@
1919
importpprint
2020
importtextwrap
2121
fromtypesimportModuleType
22-
fromtypingimportAny,Dict,Iterable,Optional,Type,Union
22+
fromtypingimportAny,Callable,Dict,Iterable,Optional,Type,Union
2323

2424
importgitlab
2525
fromgitlabimporttypesasg_types
2626
fromgitlab.exceptionsimportGitlabParsingError
27+
fromgitlab.typesimportF
2728

2829
from .clientimportGitlab,GitlabList
2930

@@ -328,6 +329,28 @@ def total(self) -> Optional[int]:
328329
returnself._list.total
329330

330331

332+
defcustom_attrs(
333+
required:tuple= (),optional:tuple= (),exclusive:tuple= ()
334+
)->Callable[[F],F]:
335+
"""Decorates a custom method to add a RequiredOptional attribute.
336+
337+
Args:
338+
required: A tuple of API attributes required in the custom method
339+
optional: A tuple of API attributes optional in the custom method
340+
exclusive: A tuple of mutually exclusive API attributes in the custom method
341+
"""
342+
343+
defdecorator(func:F)->F:
344+
setattr(
345+
func,
346+
"_custom_attrs",
347+
g_types.RequiredOptional(required,optional,exclusive),
348+
)
349+
returnfunc
350+
351+
returndecorator
352+
353+
331354
classRESTManager:
332355
"""Base class for CRUD operations on objects.
333356

‎gitlab/cli.py

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@
2323
importre
2424
importsys
2525
fromtypesimportModuleType
26-
fromtypingimportAny,Callable,cast,Dict,Optional,Tuple,Type,TypeVar,Union
26+
fromtypingimportAny,Callable,cast,Dict,Optional,Tuple,Type,Union
2727

2828
fromrequests.structuresimportCaseInsensitiveDict
2929

3030
importgitlab.config
3131
fromgitlab.baseimportRESTObject
32+
fromgitlab.typesimportF,RequiredOptional
3233

3334
# This regex is based on:
3435
# https://github.com/jpvanhal/inflection/blob/master/inflection/__init__.py
@@ -43,24 +44,18 @@
4344
custom_actions:Dict[str,Dict[str,Tuple[Tuple[str, ...],Tuple[str, ...],bool]]]= {}
4445

4546

46-
# For an explanation of how these type-hints work see:
47-
# https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators
48-
#
49-
# The goal here is that functions which get decorated will retain their types.
50-
__F=TypeVar("__F",bound=Callable[...,Any])
51-
52-
5347
defregister_custom_action(
5448
cls_names:Union[str,Tuple[str, ...]],
55-
mandatory:Tuple[str, ...]= (),
56-
optional:Tuple[str, ...]= (),
5749
custom_action:Optional[str]=None,
58-
)->Callable[[__F],__F]:
59-
defwrap(f:__F)->__F:
50+
)->Callable[[F],F]:
51+
defwrap(f:F)->F:
6052
@functools.wraps(f)
6153
defwrapped_f(*args:Any,**kwargs:Any)->Any:
6254
returnf(*args,**kwargs)
6355

56+
action=custom_actionorf.__name__.replace("_","-")
57+
custom_attrs=getattr(f,"_custom_attrs",RequiredOptional())
58+
6459
# in_obj defines whether the method belongs to the obj or the manager
6560
in_obj=True
6661
ifisinstance(cls_names,tuple):
@@ -76,10 +71,13 @@ def wrapped_f(*args: Any, **kwargs: Any) -> Any:
7671
iffinal_namenotincustom_actions:
7772
custom_actions[final_name]= {}
7873

79-
action=custom_actionorf.__name__.replace("_","-")
80-
custom_actions[final_name][action]= (mandatory,optional,in_obj)
74+
custom_actions[final_name][action]= (
75+
custom_attrs.required,
76+
custom_attrs.optional,
77+
in_obj,
78+
)
8179

82-
returncast(__F,wrapped_f)
80+
returncast(F,wrapped_f)
8381

8482
returnwrap
8583

‎gitlab/exceptions.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717

1818
importfunctools
19-
fromtypingimportAny,Callable,cast,Optional,Type,TYPE_CHECKING,TypeVar,Union
19+
fromtypingimportAny,Callable,cast,Optional,Type,TYPE_CHECKING,Union
20+
21+
fromgitlab.typesimportF
2022

2123

2224
classGitlabError(Exception):
@@ -286,14 +288,7 @@ class GitlabUnfollowError(GitlabOperationError):
286288
pass
287289

288290

289-
# For an explanation of how these type-hints work see:
290-
# https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators
291-
#
292-
# The goal here is that functions which get decorated will retain their types.
293-
__F=TypeVar("__F",bound=Callable[...,Any])
294-
295-
296-
defon_http_error(error:Type[Exception])->Callable[[__F],__F]:
291+
defon_http_error(error:Type[Exception])->Callable[[F],F]:
297292
"""Manage GitlabHttpError exceptions.
298293
299294
This decorator function can be used to catch GitlabHttpError exceptions
@@ -303,14 +298,14 @@ def on_http_error(error: Type[Exception]) -> Callable[[__F], __F]:
303298
The exception type to raise -- must inherit from GitlabError
304299
"""
305300

306-
defwrap(f:__F)->__F:
301+
defwrap(f:F)->F:
307302
@functools.wraps(f)
308303
defwrapped_f(*args:Any,**kwargs:Any)->Any:
309304
try:
310305
returnf(*args,**kwargs)
311306
exceptGitlabHttpErrorase:
312307
raiseerror(e.error_message,e.response_code,e.response_body)frome
313308

314-
returncast(__F,wrapped_f)
309+
returncast(F,wrapped_f)
315310

316311
returnwrap

‎gitlab/mixins.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -547,9 +547,8 @@ class AccessRequestMixin(_RestObjectBase):
547547
_updated_attrs:Dict[str,Any]
548548
manager:base.RESTManager
549549

550-
@cli.register_custom_action(
551-
("ProjectAccessRequest","GroupAccessRequest"), (), ("access_level",)
552-
)
550+
@cli.register_custom_action(("ProjectAccessRequest","GroupAccessRequest"))
551+
@base.custom_attrs(optional=("access_level",))
553552
@exc.on_http_error(exc.GitlabUpdateError)
554553
defapprove(
555554
self,access_level:int=gitlab.const.DEVELOPER_ACCESS,**kwargs:Any
@@ -721,7 +720,8 @@ def time_stats(self, **kwargs: Any) -> Dict[str, Any]:
721720
assertnotisinstance(result,requests.Response)
722721
returnresult
723722

724-
@cli.register_custom_action(("ProjectIssue","ProjectMergeRequest"), ("duration",))
723+
@cli.register_custom_action(("ProjectIssue","ProjectMergeRequest"))
724+
@base.custom_attrs(required=("duration",))
725725
@exc.on_http_error(exc.GitlabTimeTrackingError)
726726
deftime_estimate(self,duration:str,**kwargs:Any)->Dict[str,Any]:
727727
"""Set an estimated time of work for the object.
@@ -759,7 +759,8 @@ def reset_time_estimate(self, **kwargs: Any) -> Dict[str, Any]:
759759
assertnotisinstance(result,requests.Response)
760760
returnresult
761761

762-
@cli.register_custom_action(("ProjectIssue","ProjectMergeRequest"), ("duration",))
762+
@cli.register_custom_action(("ProjectIssue","ProjectMergeRequest"))
763+
@base.custom_attrs(required=("duration",))
763764
@exc.on_http_error(exc.GitlabTimeTrackingError)
764765
defadd_spent_time(self,duration:str,**kwargs:Any)->Dict[str,Any]:
765766
"""Add time spent working on the object.
@@ -833,9 +834,8 @@ def participants(self, **kwargs: Any) -> Dict[str, Any]:
833834

834835

835836
classBadgeRenderMixin(_RestManagerBase):
836-
@cli.register_custom_action(
837-
("GroupBadgeManager","ProjectBadgeManager"), ("link_url","image_url")
838-
)
837+
@cli.register_custom_action(("GroupBadgeManager","ProjectBadgeManager"))
838+
@base.custom_attrs(required=("link_url","image_url"))
839839
@exc.on_http_error(exc.GitlabRenderError)
840840
defrender(self,link_url:str,image_url:str,**kwargs:Any)->Dict[str,Any]:
841841
"""Preview link_url and image_url after interpolation.

‎gitlab/types.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717

1818
importdataclasses
19-
fromtypingimportAny,Dict,List,Optional,Tuple,TYPE_CHECKING
19+
fromtypingimportAny,Callable,Dict,List,Optional,Tuple,TYPE_CHECKING,TypeVar
20+
21+
# TypeVar for decorators so that decorated functions retain their signatures.
22+
# See https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators
23+
F=TypeVar("F",bound=Callable[...,Any])
2024

2125

2226
@dataclasses.dataclass(frozen=True)

‎gitlab/v4/objects/artifacts.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
fromgitlabimportcli
1010
fromgitlabimportexceptionsasexc
1111
fromgitlabimportutils
12-
fromgitlab.baseimportRESTManager,RESTObject
12+
fromgitlab.baseimportcustom_attrs,RESTManager,RESTObject
1313

1414
__all__= ["ProjectArtifact","ProjectArtifactManager"]
1515

@@ -25,9 +25,8 @@ class ProjectArtifactManager(RESTManager):
2525
_path="/projects/{project_id}/jobs/artifacts"
2626
_from_parent_attrs= {"project_id":"id"}
2727

28-
@cli.register_custom_action(
29-
"Project", ("ref_name","job"), ("job_token",),custom_action="artifacts"
30-
)
28+
@cli.register_custom_action("Project",custom_action="artifacts")
29+
@custom_attrs(required=("ref_name","job"),optional=("job_token",))
3130
def__call__(
3231
self,
3332
*args:Any,
@@ -62,9 +61,8 @@ def delete(self, **kwargs: Any) -> None:
6261
assertpathisnotNone
6362
self.gitlab.http_delete(path,**kwargs)
6463

65-
@cli.register_custom_action(
66-
"ProjectArtifactManager", ("ref_name","job"), ("job_token",)
67-
)
64+
@cli.register_custom_action("ProjectArtifactManager")
65+
@custom_attrs(required=("ref_name","job"),optional=("job_token",))
6866
@exc.on_http_error(exc.GitlabGetError)
6967
defdownload(
7068
self,
@@ -105,9 +103,8 @@ def download(
105103
assertisinstance(result,requests.Response)
106104
returnutils.response_content(result,streamed,action,chunk_size)
107105

108-
@cli.register_custom_action(
109-
"ProjectArtifactManager", ("ref_name","artifact_path","job")
110-
)
106+
@cli.register_custom_action("ProjectArtifactManager")
107+
@custom_attrs(required=("ref_name","artifact_path","job"))
111108
@exc.on_http_error(exc.GitlabGetError)
112109
defraw(
113110
self,

‎gitlab/v4/objects/commits.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
importrequests
44

55
importgitlab
6-
fromgitlabimportcli
6+
fromgitlabimportbase,cli
77
fromgitlabimportexceptionsasexc
88
fromgitlab.baseimportRESTManager,RESTObject
99
fromgitlab.mixinsimportCreateMixin,ListMixin,RefreshMixin,RetrieveMixin
@@ -46,7 +46,8 @@ def diff(self, **kwargs: Any) -> Union[gitlab.GitlabList, List[Dict[str, Any]]]:
4646
path=f"{self.manager.path}/{self.encoded_id}/diff"
4747
returnself.manager.gitlab.http_list(path,**kwargs)
4848

49-
@cli.register_custom_action("ProjectCommit", ("branch",))
49+
@cli.register_custom_action("ProjectCommit")
50+
@base.custom_attrs(required=("branch",))
5051
@exc.on_http_error(exc.GitlabCherryPickError)
5152
defcherry_pick(self,branch:str,**kwargs:Any)->None:
5253
"""Cherry-pick a commit into a branch.
@@ -63,7 +64,8 @@ def cherry_pick(self, branch: str, **kwargs: Any) -> None:
6364
post_data= {"branch":branch}
6465
self.manager.gitlab.http_post(path,post_data=post_data,**kwargs)
6566

66-
@cli.register_custom_action("ProjectCommit",optional=("type",))
67+
@cli.register_custom_action("ProjectCommit")
68+
@base.custom_attrs(optional=("type",))
6769
@exc.on_http_error(exc.GitlabGetError)
6870
defrefs(
6971
self,type:str="all",**kwargs:Any
@@ -105,7 +107,8 @@ def merge_requests(
105107
path=f"{self.manager.path}/{self.encoded_id}/merge_requests"
106108
returnself.manager.gitlab.http_list(path,**kwargs)
107109

108-
@cli.register_custom_action("ProjectCommit", ("branch",))
110+
@cli.register_custom_action("ProjectCommit")
111+
@base.custom_attrs(required=("branch",))
109112
@exc.on_http_error(exc.GitlabRevertError)
110113
defrevert(
111114
self,branch:str,**kwargs:Any

‎gitlab/v4/objects/container_registry.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
fromtypingimportAny,cast,TYPE_CHECKING,Union
22

3-
fromgitlabimportcli
3+
fromgitlabimportbase,cli
44
fromgitlabimportexceptionsasexc
55
fromgitlab.baseimportRESTManager,RESTObject
66
fromgitlab.mixinsimportDeleteMixin,ListMixin,ObjectDeleteMixin,RetrieveMixin
@@ -32,9 +32,9 @@ class ProjectRegistryTagManager(DeleteMixin, RetrieveMixin, RESTManager):
3232
_from_parent_attrs= {"project_id":"project_id","repository_id":"id"}
3333
_path="/projects/{project_id}/registry/repositories/{repository_id}/tags"
3434

35-
@cli.register_custom_action(
36-
"ProjectRegistryTagManager",
37-
("name_regex_delete",),
35+
@cli.register_custom_action("ProjectRegistryTagManager")
36+
@base.custom_attrs(
37+
required=("name_regex_delete",),
3838
optional=("keep_n","name_regex_keep","older_than"),
3939
)
4040
@exc.on_http_error(exc.GitlabDeleteError)

‎gitlab/v4/objects/deploy_keys.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
importrequests
44

5-
fromgitlabimportcli
5+
fromgitlabimportbase,cli
66
fromgitlabimportexceptionsasexc
77
fromgitlab.baseimportRESTManager,RESTObject
88
fromgitlab.mixinsimportCRUDMixin,ListMixin,ObjectDeleteMixin,SaveMixin
@@ -36,7 +36,8 @@ class ProjectKeyManager(CRUDMixin, RESTManager):
3636
_create_attrs=RequiredOptional(required=("title","key"),optional=("can_push",))
3737
_update_attrs=RequiredOptional(optional=("title","can_push"))
3838

39-
@cli.register_custom_action("ProjectKeyManager", ("key_id",))
39+
@cli.register_custom_action("ProjectKeyManager")
40+
@base.custom_attrs(required=("key_id",))
4041
@exc.on_http_error(exc.GitlabProjectDeployKeyError)
4142
defenable(
4243
self,key_id:int,**kwargs:Any

‎gitlab/v4/objects/files.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
importrequests
55

6-
fromgitlabimportcli
6+
fromgitlabimportbase,cli
77
fromgitlabimportexceptionsasexc
88
fromgitlabimportutils
99
fromgitlab.baseimportRESTManager,RESTObject
@@ -98,7 +98,8 @@ class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTMa
9898
optional=("encoding","author_email","author_name"),
9999
)
100100

101-
@cli.register_custom_action("ProjectFileManager", ("file_path","ref"))
101+
@cli.register_custom_action("ProjectFileManager")
102+
@base.custom_attrs(required=("file_path","ref"))
102103
# NOTE(jlvillal): Signature doesn't match UpdateMixin.update() so ignore
103104
# type error
104105
defget(# type: ignore
@@ -120,10 +121,10 @@ def get( # type: ignore
120121
"""
121122
returncast(ProjectFile,GetMixin.get(self,file_path,ref=ref,**kwargs))
122123

123-
@cli.register_custom_action(
124-
"ProjectFileManager",
125-
("file_path","branch","content","commit_message"),
126-
("encoding","author_email","author_name"),
124+
@cli.register_custom_action("ProjectFileManager")
125+
@base.custom_attrs(
126+
required=("file_path","branch","content","commit_message"),
127+
optional=("encoding","author_email","author_name"),
127128
)
128129
@exc.on_http_error(exc.GitlabCreateError)
129130
defcreate(
@@ -187,9 +188,8 @@ def update( # type: ignore
187188
assertisinstance(result,dict)
188189
returnresult
189190

190-
@cli.register_custom_action(
191-
"ProjectFileManager", ("file_path","branch","commit_message")
192-
)
191+
@cli.register_custom_action("ProjectFileManager")
192+
@base.custom_attrs(required=("file_path","branch","commit_message"))
193193
@exc.on_http_error(exc.GitlabDeleteError)
194194
# NOTE(jlvillal): Signature doesn't match DeleteMixin.delete() so ignore
195195
# type error
@@ -213,7 +213,8 @@ def delete( # type: ignore
213213
data= {"branch":branch,"commit_message":commit_message}
214214
self.gitlab.http_delete(path,query_data=data,**kwargs)
215215

216-
@cli.register_custom_action("ProjectFileManager", ("file_path","ref"))
216+
@cli.register_custom_action("ProjectFileManager")
217+
@base.custom_attrs(required=("file_path","ref"))
217218
@exc.on_http_error(exc.GitlabGetError)
218219
defraw(
219220
self,
@@ -254,7 +255,8 @@ def raw(
254255
assertisinstance(result,requests.Response)
255256
returnutils.response_content(result,streamed,action,chunk_size)
256257

257-
@cli.register_custom_action("ProjectFileManager", ("file_path","ref"))
258+
@cli.register_custom_action("ProjectFileManager")
259+
@base.custom_attrs(required=("file_path","ref"))
258260
@exc.on_http_error(exc.GitlabListError)
259261
defblame(self,file_path:str,ref:str,**kwargs:Any)->List[Dict[str,Any]]:
260262
"""Return the content of a file for a commit.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp