python-gitlab 4.5.0__py3-none-any.whl → 4.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. gitlab/_version.py +1 -1
  2. gitlab/cli.py +20 -4
  3. gitlab/client.py +2 -2
  4. gitlab/mixins.py +22 -14
  5. gitlab/v4/cli.py +23 -11
  6. gitlab/v4/objects/__init__.py +1 -0
  7. gitlab/v4/objects/artifacts.py +5 -2
  8. gitlab/v4/objects/ci_lint.py +4 -4
  9. gitlab/v4/objects/commits.py +6 -6
  10. gitlab/v4/objects/container_registry.py +2 -2
  11. gitlab/v4/objects/deploy_keys.py +3 -1
  12. gitlab/v4/objects/deployments.py +2 -2
  13. gitlab/v4/objects/environments.py +1 -1
  14. gitlab/v4/objects/files.py +15 -7
  15. gitlab/v4/objects/geo_nodes.py +4 -4
  16. gitlab/v4/objects/groups.py +13 -7
  17. gitlab/v4/objects/integrations.py +3 -1
  18. gitlab/v4/objects/issues.py +6 -4
  19. gitlab/v4/objects/iterations.py +29 -2
  20. gitlab/v4/objects/jobs.py +11 -14
  21. gitlab/v4/objects/merge_request_approvals.py +8 -3
  22. gitlab/v4/objects/merge_requests.py +13 -12
  23. gitlab/v4/objects/milestones.py +4 -4
  24. gitlab/v4/objects/namespaces.py +3 -1
  25. gitlab/v4/objects/packages.py +4 -4
  26. gitlab/v4/objects/pipelines.py +4 -4
  27. gitlab/v4/objects/projects.py +21 -18
  28. gitlab/v4/objects/repositories.py +13 -9
  29. gitlab/v4/objects/runners.py +2 -2
  30. gitlab/v4/objects/secure_files.py +1 -1
  31. gitlab/v4/objects/service_accounts.py +18 -0
  32. gitlab/v4/objects/sidekiq.py +4 -4
  33. gitlab/v4/objects/snippets.py +3 -3
  34. gitlab/v4/objects/todos.py +2 -2
  35. gitlab/v4/objects/topics.py +2 -2
  36. gitlab/v4/objects/users.py +10 -10
  37. {python_gitlab-4.5.0.dist-info → python_gitlab-4.6.0.dist-info}/METADATA +3 -3
  38. {python_gitlab-4.5.0.dist-info → python_gitlab-4.6.0.dist-info}/RECORD +43 -42
  39. {python_gitlab-4.5.0.dist-info → python_gitlab-4.6.0.dist-info}/AUTHORS +0 -0
  40. {python_gitlab-4.5.0.dist-info → python_gitlab-4.6.0.dist-info}/COPYING +0 -0
  41. {python_gitlab-4.5.0.dist-info → python_gitlab-4.6.0.dist-info}/WHEEL +0 -0
  42. {python_gitlab-4.5.0.dist-info → python_gitlab-4.6.0.dist-info}/entry_points.txt +0 -0
  43. {python_gitlab-4.5.0.dist-info → python_gitlab-4.6.0.dist-info}/top_level.txt +0 -0
@@ -126,7 +126,7 @@ class ProjectIssue(
126
126
  resource_iteration_events: ProjectIssueResourceIterationEventManager
127
127
  resource_weight_events: ProjectIssueResourceWeightEventManager
128
128
 
129
- @cli.register_custom_action("ProjectIssue", ("to_project_id",))
129
+ @cli.register_custom_action(cls_names="ProjectIssue", required=("to_project_id",))
130
130
  @exc.on_http_error(exc.GitlabUpdateError)
131
131
  def move(self, to_project_id: int, **kwargs: Any) -> None:
132
132
  """Move the issue to another project.
@@ -146,7 +146,9 @@ class ProjectIssue(
146
146
  assert isinstance(server_data, dict)
147
147
  self._update_attrs(server_data)
148
148
 
149
- @cli.register_custom_action("ProjectIssue", ("move_after_id", "move_before_id"))
149
+ @cli.register_custom_action(
150
+ cls_names="ProjectIssue", required=("move_after_id", "move_before_id")
151
+ )
150
152
  @exc.on_http_error(exc.GitlabUpdateError)
151
153
  def reorder(
152
154
  self,
@@ -178,7 +180,7 @@ class ProjectIssue(
178
180
  assert isinstance(server_data, dict)
179
181
  self._update_attrs(server_data)
180
182
 
181
- @cli.register_custom_action("ProjectIssue")
183
+ @cli.register_custom_action(cls_names="ProjectIssue")
182
184
  @exc.on_http_error(exc.GitlabGetError)
183
185
  def related_merge_requests(self, **kwargs: Any) -> Dict[str, Any]:
184
186
  """List merge requests related to the issue.
@@ -199,7 +201,7 @@ class ProjectIssue(
199
201
  assert isinstance(result, dict)
200
202
  return result
201
203
 
202
- @cli.register_custom_action("ProjectIssue")
204
+ @cli.register_custom_action(cls_names="ProjectIssue")
203
205
  @exc.on_http_error(exc.GitlabGetError)
204
206
  def closed_by(self, **kwargs: Any) -> Dict[str, Any]:
205
207
  """List merge requests that will close the issue when merged.
@@ -1,3 +1,4 @@
1
+ from gitlab import types
1
2
  from gitlab.base import RESTManager, RESTObject
2
3
  from gitlab.mixins import ListMixin
3
4
 
@@ -16,11 +17,37 @@ class GroupIterationManager(ListMixin, RESTManager):
16
17
  _path = "/groups/{group_id}/iterations"
17
18
  _obj_cls = GroupIteration
18
19
  _from_parent_attrs = {"group_id": "id"}
19
- _list_filters = ("state", "search", "include_ancestors")
20
+ # When using the API, the "in" keyword collides with python's "in" keyword
21
+ # raising a SyntaxError.
22
+ # For this reason, we have to use the query_parameters argument:
23
+ # group.iterations.list(query_parameters={"in": "title"})
24
+ _list_filters = (
25
+ "include_ancestors",
26
+ "include_descendants",
27
+ "in",
28
+ "search",
29
+ "state",
30
+ "updated_after",
31
+ "updated_before",
32
+ )
33
+ _types = {"in": types.ArrayAttribute}
20
34
 
21
35
 
22
36
  class ProjectIterationManager(ListMixin, RESTManager):
23
37
  _path = "/projects/{project_id}/iterations"
24
38
  _obj_cls = GroupIteration
25
39
  _from_parent_attrs = {"project_id": "id"}
26
- _list_filters = ("state", "search", "include_ancestors")
40
+ # When using the API, the "in" keyword collides with python's "in" keyword
41
+ # raising a SyntaxError.
42
+ # For this reason, we have to use the query_parameters argument:
43
+ # project.iterations.list(query_parameters={"in": "title"})
44
+ _list_filters = (
45
+ "include_ancestors",
46
+ "include_descendants",
47
+ "in",
48
+ "search",
49
+ "state",
50
+ "updated_after",
51
+ "updated_before",
52
+ )
53
+ _types = {"in": types.ArrayAttribute}
gitlab/v4/objects/jobs.py CHANGED
@@ -16,7 +16,7 @@ __all__ = [
16
16
 
17
17
 
18
18
  class ProjectJob(RefreshMixin, RESTObject):
19
- @cli.register_custom_action("ProjectJob")
19
+ @cli.register_custom_action(cls_names="ProjectJob")
20
20
  @exc.on_http_error(exc.GitlabJobCancelError)
21
21
  def cancel(self, **kwargs: Any) -> Dict[str, Any]:
22
22
  """Cancel the job.
@@ -34,7 +34,7 @@ class ProjectJob(RefreshMixin, RESTObject):
34
34
  assert isinstance(result, dict)
35
35
  return result
36
36
 
37
- @cli.register_custom_action("ProjectJob")
37
+ @cli.register_custom_action(cls_names="ProjectJob")
38
38
  @exc.on_http_error(exc.GitlabJobRetryError)
39
39
  def retry(self, **kwargs: Any) -> Dict[str, Any]:
40
40
  """Retry the job.
@@ -52,7 +52,7 @@ class ProjectJob(RefreshMixin, RESTObject):
52
52
  assert isinstance(result, dict)
53
53
  return result
54
54
 
55
- @cli.register_custom_action("ProjectJob")
55
+ @cli.register_custom_action(cls_names="ProjectJob")
56
56
  @exc.on_http_error(exc.GitlabJobPlayError)
57
57
  def play(self, **kwargs: Any) -> None:
58
58
  """Trigger a job explicitly.
@@ -70,7 +70,7 @@ class ProjectJob(RefreshMixin, RESTObject):
70
70
  assert isinstance(result, dict)
71
71
  self._update_attrs(result)
72
72
 
73
- @cli.register_custom_action("ProjectJob")
73
+ @cli.register_custom_action(cls_names="ProjectJob")
74
74
  @exc.on_http_error(exc.GitlabJobEraseError)
75
75
  def erase(self, **kwargs: Any) -> None:
76
76
  """Erase the job (remove job artifacts and trace).
@@ -85,7 +85,7 @@ class ProjectJob(RefreshMixin, RESTObject):
85
85
  path = f"{self.manager.path}/{self.encoded_id}/erase"
86
86
  self.manager.gitlab.http_post(path, **kwargs)
87
87
 
88
- @cli.register_custom_action("ProjectJob")
88
+ @cli.register_custom_action(cls_names="ProjectJob")
89
89
  @exc.on_http_error(exc.GitlabCreateError)
90
90
  def keep_artifacts(self, **kwargs: Any) -> None:
91
91
  """Prevent artifacts from being deleted when expiration is set.
@@ -100,7 +100,7 @@ class ProjectJob(RefreshMixin, RESTObject):
100
100
  path = f"{self.manager.path}/{self.encoded_id}/artifacts/keep"
101
101
  self.manager.gitlab.http_post(path, **kwargs)
102
102
 
103
- @cli.register_custom_action("ProjectJob")
103
+ @cli.register_custom_action(cls_names="ProjectJob")
104
104
  @exc.on_http_error(exc.GitlabCreateError)
105
105
  def delete_artifacts(self, **kwargs: Any) -> None:
106
106
  """Delete artifacts of a job.
@@ -115,7 +115,7 @@ class ProjectJob(RefreshMixin, RESTObject):
115
115
  path = f"{self.manager.path}/{self.encoded_id}/artifacts"
116
116
  self.manager.gitlab.http_delete(path, **kwargs)
117
117
 
118
- @cli.register_custom_action("ProjectJob")
118
+ @cli.register_custom_action(cls_names="ProjectJob")
119
119
  @exc.on_http_error(exc.GitlabGetError)
120
120
  def artifacts(
121
121
  self,
@@ -156,7 +156,7 @@ class ProjectJob(RefreshMixin, RESTObject):
156
156
  result, streamed, action, chunk_size, iterator=iterator
157
157
  )
158
158
 
159
- @cli.register_custom_action("ProjectJob")
159
+ @cli.register_custom_action(cls_names="ProjectJob")
160
160
  @exc.on_http_error(exc.GitlabGetError)
161
161
  def artifact(
162
162
  self,
@@ -199,7 +199,7 @@ class ProjectJob(RefreshMixin, RESTObject):
199
199
  result, streamed, action, chunk_size, iterator=iterator
200
200
  )
201
201
 
202
- @cli.register_custom_action("ProjectJob")
202
+ @cli.register_custom_action(cls_names="ProjectJob")
203
203
  @exc.on_http_error(exc.GitlabGetError)
204
204
  def trace(
205
205
  self,
@@ -209,7 +209,7 @@ class ProjectJob(RefreshMixin, RESTObject):
209
209
  *,
210
210
  iterator: bool = False,
211
211
  **kwargs: Any,
212
- ) -> Dict[str, Any]:
212
+ ) -> Optional[Union[bytes, Iterator[Any]]]:
213
213
  """Get the job trace.
214
214
 
215
215
  Args:
@@ -236,12 +236,9 @@ class ProjectJob(RefreshMixin, RESTObject):
236
236
  )
237
237
  if TYPE_CHECKING:
238
238
  assert isinstance(result, requests.Response)
239
- return_value = utils.response_content(
239
+ return utils.response_content(
240
240
  result, streamed, action, chunk_size, iterator=iterator
241
241
  )
242
- if TYPE_CHECKING:
243
- assert isinstance(return_value, dict)
244
- return return_value
245
242
 
246
243
 
247
244
  class ProjectJobManager(RetrieveMixin, RESTManager):
@@ -89,6 +89,8 @@ class ProjectMergeRequestApprovalManager(GetWithoutIdMixin, UpdateMixin, RESTMan
89
89
  approver_ids: Optional[List[int]] = None,
90
90
  approver_group_ids: Optional[List[int]] = None,
91
91
  approval_rule_name: str = "name",
92
+ *,
93
+ approver_usernames: Optional[List[str]] = None,
92
94
  **kwargs: Any,
93
95
  ) -> RESTObject:
94
96
  """Change MR-level allowed approvers and approver groups.
@@ -104,6 +106,7 @@ class ProjectMergeRequestApprovalManager(GetWithoutIdMixin, UpdateMixin, RESTMan
104
106
  """
105
107
  approver_ids = approver_ids or []
106
108
  approver_group_ids = approver_group_ids or []
109
+ approver_usernames = approver_usernames or []
107
110
 
108
111
  data = {
109
112
  "name": approval_rule_name,
@@ -111,6 +114,7 @@ class ProjectMergeRequestApprovalManager(GetWithoutIdMixin, UpdateMixin, RESTMan
111
114
  "rule_type": "regular",
112
115
  "user_ids": approver_ids,
113
116
  "group_ids": approver_group_ids,
117
+ "usernames": approver_usernames,
114
118
  }
115
119
  if TYPE_CHECKING:
116
120
  assert self._parent is not None
@@ -118,12 +122,13 @@ class ProjectMergeRequestApprovalManager(GetWithoutIdMixin, UpdateMixin, RESTMan
118
122
  self._parent.approval_rules
119
123
  )
120
124
  # update any existing approval rule matching the name
121
- existing_approval_rules = approval_rules.list()
125
+ existing_approval_rules = approval_rules.list(iterator=True)
122
126
  for ar in existing_approval_rules:
123
127
  if ar.name == approval_rule_name:
124
128
  ar.user_ids = data["user_ids"]
125
129
  ar.approvals_required = data["approvals_required"]
126
130
  ar.group_ids = data["group_ids"]
131
+ ar.usernames = data["usernames"]
127
132
  ar.save()
128
133
  return ar
129
134
  # if there was no rule matching the rule name, create a new one
@@ -145,14 +150,14 @@ class ProjectMergeRequestApprovalRuleManager(CRUDMixin, RESTManager):
145
150
  "name",
146
151
  "approvals_required",
147
152
  ),
148
- optional=("user_ids", "group_ids"),
153
+ optional=("user_ids", "group_ids", "usernames"),
149
154
  )
150
155
  # Important: When approval_project_rule_id is set, the name, users and
151
156
  # groups of project-level rule will be copied. The approvals_required
152
157
  # specified will be used.
153
158
  _create_attrs = RequiredOptional(
154
159
  required=("name", "approvals_required"),
155
- optional=("approval_project_rule_id", "user_ids", "group_ids"),
160
+ optional=("approval_project_rule_id", "user_ids", "group_ids", "usernames"),
156
161
  )
157
162
 
158
163
  def get(
@@ -168,7 +168,7 @@ class ProjectMergeRequest(
168
168
  resourcestateevents: ProjectMergeRequestResourceStateEventManager
169
169
  reviewer_details: ProjectMergeRequestReviewerDetailManager
170
170
 
171
- @cli.register_custom_action("ProjectMergeRequest")
171
+ @cli.register_custom_action(cls_names="ProjectMergeRequest")
172
172
  @exc.on_http_error(exc.GitlabMROnBuildSuccessError)
173
173
  def cancel_merge_when_pipeline_succeeds(self, **kwargs: Any) -> Dict[str, str]:
174
174
  """Cancel merge when the pipeline succeeds.
@@ -197,7 +197,7 @@ class ProjectMergeRequest(
197
197
  assert isinstance(server_data, dict)
198
198
  return server_data
199
199
 
200
- @cli.register_custom_action("ProjectMergeRequest")
200
+ @cli.register_custom_action(cls_names="ProjectMergeRequest")
201
201
  @exc.on_http_error(exc.GitlabListError)
202
202
  def closes_issues(self, **kwargs: Any) -> RESTObjectList:
203
203
  """List issues that will close on merge."
@@ -222,7 +222,7 @@ class ProjectMergeRequest(
222
222
  manager = ProjectIssueManager(self.manager.gitlab, parent=self.manager._parent)
223
223
  return RESTObjectList(manager, ProjectIssue, data_list)
224
224
 
225
- @cli.register_custom_action("ProjectMergeRequest")
225
+ @cli.register_custom_action(cls_names="ProjectMergeRequest")
226
226
  @exc.on_http_error(exc.GitlabListError)
227
227
  def commits(self, **kwargs: Any) -> RESTObjectList:
228
228
  """List the merge request commits.
@@ -248,7 +248,9 @@ class ProjectMergeRequest(
248
248
  manager = ProjectCommitManager(self.manager.gitlab, parent=self.manager._parent)
249
249
  return RESTObjectList(manager, ProjectCommit, data_list)
250
250
 
251
- @cli.register_custom_action("ProjectMergeRequest", optional=("access_raw_diffs",))
251
+ @cli.register_custom_action(
252
+ cls_names="ProjectMergeRequest", optional=("access_raw_diffs",)
253
+ )
252
254
  @exc.on_http_error(exc.GitlabListError)
253
255
  def changes(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
254
256
  """List the merge request changes.
@@ -266,7 +268,7 @@ class ProjectMergeRequest(
266
268
  path = f"{self.manager.path}/{self.encoded_id}/changes"
267
269
  return self.manager.gitlab.http_get(path, **kwargs)
268
270
 
269
- @cli.register_custom_action("ProjectMergeRequest", (), ("sha",))
271
+ @cli.register_custom_action(cls_names="ProjectMergeRequest", optional=("sha",))
270
272
  @exc.on_http_error(exc.GitlabMRApprovalError)
271
273
  def approve(self, sha: Optional[str] = None, **kwargs: Any) -> Dict[str, Any]:
272
274
  """Approve the merge request.
@@ -295,7 +297,7 @@ class ProjectMergeRequest(
295
297
  self._update_attrs(server_data)
296
298
  return server_data
297
299
 
298
- @cli.register_custom_action("ProjectMergeRequest")
300
+ @cli.register_custom_action(cls_names="ProjectMergeRequest")
299
301
  @exc.on_http_error(exc.GitlabMRApprovalError)
300
302
  def unapprove(self, **kwargs: Any) -> None:
301
303
  """Unapprove the merge request.
@@ -317,7 +319,7 @@ class ProjectMergeRequest(
317
319
  assert isinstance(server_data, dict)
318
320
  self._update_attrs(server_data)
319
321
 
320
- @cli.register_custom_action("ProjectMergeRequest")
322
+ @cli.register_custom_action(cls_names="ProjectMergeRequest")
321
323
  @exc.on_http_error(exc.GitlabMRRebaseError)
322
324
  def rebase(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
323
325
  """Attempt to rebase the source branch onto the target branch
@@ -333,7 +335,7 @@ class ProjectMergeRequest(
333
335
  data: Dict[str, Any] = {}
334
336
  return self.manager.gitlab.http_put(path, post_data=data, **kwargs)
335
337
 
336
- @cli.register_custom_action("ProjectMergeRequest")
338
+ @cli.register_custom_action(cls_names="ProjectMergeRequest")
337
339
  @exc.on_http_error(exc.GitlabMRResetApprovalError)
338
340
  def reset_approvals(
339
341
  self, **kwargs: Any
@@ -351,7 +353,7 @@ class ProjectMergeRequest(
351
353
  data: Dict[str, Any] = {}
352
354
  return self.manager.gitlab.http_put(path, post_data=data, **kwargs)
353
355
 
354
- @cli.register_custom_action("ProjectMergeRequest")
356
+ @cli.register_custom_action(cls_names="ProjectMergeRequest")
355
357
  @exc.on_http_error(exc.GitlabGetError)
356
358
  def merge_ref(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
357
359
  """Attempt to merge changes between source and target branches into
@@ -367,9 +369,8 @@ class ProjectMergeRequest(
367
369
  return self.manager.gitlab.http_get(path, **kwargs)
368
370
 
369
371
  @cli.register_custom_action(
370
- "ProjectMergeRequest",
371
- (),
372
- (
372
+ cls_names="ProjectMergeRequest",
373
+ optional=(
373
374
  "merge_commit_message",
374
375
  "should_remove_source_branch",
375
376
  "merge_when_pipeline_succeeds",
@@ -31,7 +31,7 @@ __all__ = [
31
31
  class GroupMilestone(SaveMixin, ObjectDeleteMixin, RESTObject):
32
32
  _repr_attr = "title"
33
33
 
34
- @cli.register_custom_action("GroupMilestone")
34
+ @cli.register_custom_action(cls_names="GroupMilestone")
35
35
  @exc.on_http_error(exc.GitlabListError)
36
36
  def issues(self, **kwargs: Any) -> RESTObjectList:
37
37
  """List issues related to this milestone.
@@ -58,7 +58,7 @@ class GroupMilestone(SaveMixin, ObjectDeleteMixin, RESTObject):
58
58
  # FIXME(gpocentek): the computed manager path is not correct
59
59
  return RESTObjectList(manager, GroupIssue, data_list)
60
60
 
61
- @cli.register_custom_action("GroupMilestone")
61
+ @cli.register_custom_action(cls_names="GroupMilestone")
62
62
  @exc.on_http_error(exc.GitlabListError)
63
63
  def merge_requests(self, **kwargs: Any) -> RESTObjectList:
64
64
  """List the merge requests related to this milestone.
@@ -108,7 +108,7 @@ class ProjectMilestone(PromoteMixin, SaveMixin, ObjectDeleteMixin, RESTObject):
108
108
  _repr_attr = "title"
109
109
  _update_method = UpdateMethod.POST
110
110
 
111
- @cli.register_custom_action("ProjectMilestone")
111
+ @cli.register_custom_action(cls_names="ProjectMilestone")
112
112
  @exc.on_http_error(exc.GitlabListError)
113
113
  def issues(self, **kwargs: Any) -> RESTObjectList:
114
114
  """List issues related to this milestone.
@@ -135,7 +135,7 @@ class ProjectMilestone(PromoteMixin, SaveMixin, ObjectDeleteMixin, RESTObject):
135
135
  # FIXME(gpocentek): the computed manager path is not correct
136
136
  return RESTObjectList(manager, ProjectIssue, data_list)
137
137
 
138
- @cli.register_custom_action("ProjectMilestone")
138
+ @cli.register_custom_action(cls_names="ProjectMilestone")
139
139
  @exc.on_http_error(exc.GitlabListError)
140
140
  def merge_requests(self, **kwargs: Any) -> RESTObjectList:
141
141
  """List the merge requests related to this milestone.
@@ -24,7 +24,9 @@ class NamespaceManager(RetrieveMixin, RESTManager):
24
24
  def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> Namespace:
25
25
  return cast(Namespace, super().get(id=id, lazy=lazy, **kwargs))
26
26
 
27
- @cli.register_custom_action("NamespaceManager", ("namespace", "parent_id"))
27
+ @cli.register_custom_action(
28
+ cls_names="NamespaceManager", required=("namespace", "parent_id")
29
+ )
28
30
  @exc.on_http_error(exc.GitlabGetError)
29
31
  def exists(self, namespace: str, **kwargs: Any) -> Namespace:
30
32
  """Get existence of a namespace by path.
@@ -48,8 +48,8 @@ class GenericPackageManager(RESTManager):
48
48
  _from_parent_attrs = {"project_id": "id"}
49
49
 
50
50
  @cli.register_custom_action(
51
- "GenericPackageManager",
52
- ("package_name", "package_version", "file_name", "path"),
51
+ cls_names="GenericPackageManager",
52
+ required=("package_name", "package_version", "file_name", "path"),
53
53
  )
54
54
  @exc.on_http_error(exc.GitlabUploadError)
55
55
  def upload(
@@ -123,8 +123,8 @@ class GenericPackageManager(RESTManager):
123
123
  return self._obj_cls(self, attrs=attrs)
124
124
 
125
125
  @cli.register_custom_action(
126
- "GenericPackageManager",
127
- ("package_name", "package_version", "file_name"),
126
+ cls_names="GenericPackageManager",
127
+ required=("package_name", "package_version", "file_name"),
128
128
  )
129
129
  @exc.on_http_error(exc.GitlabGetError)
130
130
  def download(
@@ -60,7 +60,7 @@ class ProjectPipeline(RefreshMixin, ObjectDeleteMixin, RESTObject):
60
60
  test_report_summary: "ProjectPipelineTestReportSummaryManager"
61
61
  variables: "ProjectPipelineVariableManager"
62
62
 
63
- @cli.register_custom_action("ProjectPipeline")
63
+ @cli.register_custom_action(cls_names="ProjectPipeline")
64
64
  @exc.on_http_error(exc.GitlabPipelineCancelError)
65
65
  def cancel(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
66
66
  """Cancel the job.
@@ -75,7 +75,7 @@ class ProjectPipeline(RefreshMixin, ObjectDeleteMixin, RESTObject):
75
75
  path = f"{self.manager.path}/{self.encoded_id}/cancel"
76
76
  return self.manager.gitlab.http_post(path, **kwargs)
77
77
 
78
- @cli.register_custom_action("ProjectPipeline")
78
+ @cli.register_custom_action(cls_names="ProjectPipeline")
79
79
  @exc.on_http_error(exc.GitlabPipelineRetryError)
80
80
  def retry(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
81
81
  """Retry the job.
@@ -201,7 +201,7 @@ class ProjectPipelineSchedule(SaveMixin, ObjectDeleteMixin, RESTObject):
201
201
  variables: ProjectPipelineScheduleVariableManager
202
202
  pipelines: ProjectPipelineSchedulePipelineManager
203
203
 
204
- @cli.register_custom_action("ProjectPipelineSchedule")
204
+ @cli.register_custom_action(cls_names="ProjectPipelineSchedule")
205
205
  @exc.on_http_error(exc.GitlabOwnershipError)
206
206
  def take_ownership(self, **kwargs: Any) -> None:
207
207
  """Update the owner of a pipeline schedule.
@@ -219,7 +219,7 @@ class ProjectPipelineSchedule(SaveMixin, ObjectDeleteMixin, RESTObject):
219
219
  assert isinstance(server_data, dict)
220
220
  self._update_attrs(server_data)
221
221
 
222
- @cli.register_custom_action("ProjectPipelineSchedule")
222
+ @cli.register_custom_action(cls_names="ProjectPipelineSchedule")
223
223
  @exc.on_http_error(exc.GitlabPipelinePlayError)
224
224
  def play(self, **kwargs: Any) -> Dict[str, Any]:
225
225
  """Trigger a new scheduled pipeline, which runs immediately.
@@ -3,6 +3,7 @@ GitLab API:
3
3
  https://docs.gitlab.com/ee/api/projects.html
4
4
  """
5
5
 
6
+ import io
6
7
  from typing import (
7
8
  Any,
8
9
  Callable,
@@ -230,7 +231,7 @@ class Project(
230
231
  variables: ProjectVariableManager
231
232
  wikis: ProjectWikiManager
232
233
 
233
- @cli.register_custom_action("Project", ("forked_from_id",))
234
+ @cli.register_custom_action(cls_names="Project", required=("forked_from_id",))
234
235
  @exc.on_http_error(exc.GitlabCreateError)
235
236
  def create_fork_relation(self, forked_from_id: int, **kwargs: Any) -> None:
236
237
  """Create a forked from/to relation between existing projects.
@@ -246,7 +247,7 @@ class Project(
246
247
  path = f"/projects/{self.encoded_id}/fork/{forked_from_id}"
247
248
  self.manager.gitlab.http_post(path, **kwargs)
248
249
 
249
- @cli.register_custom_action("Project")
250
+ @cli.register_custom_action(cls_names="Project")
250
251
  @exc.on_http_error(exc.GitlabDeleteError)
251
252
  def delete_fork_relation(self, **kwargs: Any) -> None:
252
253
  """Delete a forked relation between existing projects.
@@ -261,7 +262,7 @@ class Project(
261
262
  path = f"/projects/{self.encoded_id}/fork"
262
263
  self.manager.gitlab.http_delete(path, **kwargs)
263
264
 
264
- @cli.register_custom_action("Project")
265
+ @cli.register_custom_action(cls_names="Project")
265
266
  @exc.on_http_error(exc.GitlabGetError)
266
267
  def languages(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
267
268
  """Get languages used in the project with percentage value.
@@ -276,7 +277,7 @@ class Project(
276
277
  path = f"/projects/{self.encoded_id}/languages"
277
278
  return self.manager.gitlab.http_get(path, **kwargs)
278
279
 
279
- @cli.register_custom_action("Project")
280
+ @cli.register_custom_action(cls_names="Project")
280
281
  @exc.on_http_error(exc.GitlabCreateError)
281
282
  def star(self, **kwargs: Any) -> None:
282
283
  """Star a project.
@@ -294,7 +295,7 @@ class Project(
294
295
  assert isinstance(server_data, dict)
295
296
  self._update_attrs(server_data)
296
297
 
297
- @cli.register_custom_action("Project")
298
+ @cli.register_custom_action(cls_names="Project")
298
299
  @exc.on_http_error(exc.GitlabDeleteError)
299
300
  def unstar(self, **kwargs: Any) -> None:
300
301
  """Unstar a project.
@@ -312,7 +313,7 @@ class Project(
312
313
  assert isinstance(server_data, dict)
313
314
  self._update_attrs(server_data)
314
315
 
315
- @cli.register_custom_action("Project")
316
+ @cli.register_custom_action(cls_names="Project")
316
317
  @exc.on_http_error(exc.GitlabCreateError)
317
318
  def archive(self, **kwargs: Any) -> None:
318
319
  """Archive a project.
@@ -330,7 +331,7 @@ class Project(
330
331
  assert isinstance(server_data, dict)
331
332
  self._update_attrs(server_data)
332
333
 
333
- @cli.register_custom_action("Project")
334
+ @cli.register_custom_action(cls_names="Project")
334
335
  @exc.on_http_error(exc.GitlabDeleteError)
335
336
  def unarchive(self, **kwargs: Any) -> None:
336
337
  """Unarchive a project.
@@ -349,7 +350,9 @@ class Project(
349
350
  self._update_attrs(server_data)
350
351
 
351
352
  @cli.register_custom_action(
352
- "Project", ("group_id", "group_access"), ("expires_at",)
353
+ cls_names="Project",
354
+ required=("group_id", "group_access"),
355
+ optional=("expires_at",),
353
356
  )
354
357
  @exc.on_http_error(exc.GitlabCreateError)
355
358
  def share(
@@ -378,7 +381,7 @@ class Project(
378
381
  }
379
382
  self.manager.gitlab.http_post(path, post_data=data, **kwargs)
380
383
 
381
- @cli.register_custom_action("Project", ("group_id",))
384
+ @cli.register_custom_action(cls_names="Project", required=("group_id",))
382
385
  @exc.on_http_error(exc.GitlabDeleteError)
383
386
  def unshare(self, group_id: int, **kwargs: Any) -> None:
384
387
  """Delete a shared project link within a group.
@@ -395,7 +398,7 @@ class Project(
395
398
  self.manager.gitlab.http_delete(path, **kwargs)
396
399
 
397
400
  # variables not supported in CLI
398
- @cli.register_custom_action("Project", ("ref", "token"))
401
+ @cli.register_custom_action(cls_names="Project", required=("ref", "token"))
399
402
  @exc.on_http_error(exc.GitlabCreateError)
400
403
  def trigger_pipeline(
401
404
  self,
@@ -426,7 +429,7 @@ class Project(
426
429
  assert isinstance(attrs, dict)
427
430
  return ProjectPipeline(self.pipelines, attrs)
428
431
 
429
- @cli.register_custom_action("Project")
432
+ @cli.register_custom_action(cls_names="Project")
430
433
  @exc.on_http_error(exc.GitlabHousekeepingError)
431
434
  def housekeeping(self, **kwargs: Any) -> None:
432
435
  """Start the housekeeping task.
@@ -442,7 +445,7 @@ class Project(
442
445
  path = f"/projects/{self.encoded_id}/housekeeping"
443
446
  self.manager.gitlab.http_post(path, **kwargs)
444
447
 
445
- @cli.register_custom_action("Project")
448
+ @cli.register_custom_action(cls_names="Project")
446
449
  @exc.on_http_error(exc.GitlabRestoreError)
447
450
  def restore(self, **kwargs: Any) -> None:
448
451
  """Restore a project marked for deletion.
@@ -457,7 +460,7 @@ class Project(
457
460
  path = f"/projects/{self.encoded_id}/restore"
458
461
  self.manager.gitlab.http_post(path, **kwargs)
459
462
 
460
- @cli.register_custom_action("Project", optional=("wiki",))
463
+ @cli.register_custom_action(cls_names="Project", optional=("wiki",))
461
464
  @exc.on_http_error(exc.GitlabGetError)
462
465
  def snapshot(
463
466
  self,
@@ -500,7 +503,7 @@ class Project(
500
503
  result, streamed, action, chunk_size, iterator=iterator
501
504
  )
502
505
 
503
- @cli.register_custom_action("Project", ("scope", "search"))
506
+ @cli.register_custom_action(cls_names="Project", required=("scope", "search"))
504
507
  @exc.on_http_error(exc.GitlabSearchError)
505
508
  def search(
506
509
  self, scope: str, search: str, **kwargs: Any
@@ -523,7 +526,7 @@ class Project(
523
526
  path = f"/projects/{self.encoded_id}/search"
524
527
  return self.manager.gitlab.http_list(path, query_data=data, **kwargs)
525
528
 
526
- @cli.register_custom_action("Project")
529
+ @cli.register_custom_action(cls_names="Project")
527
530
  @exc.on_http_error(exc.GitlabCreateError)
528
531
  def mirror_pull(self, **kwargs: Any) -> None:
529
532
  """Start the pull mirroring process for the project.
@@ -538,7 +541,7 @@ class Project(
538
541
  path = f"/projects/{self.encoded_id}/mirror/pull"
539
542
  self.manager.gitlab.http_post(path, **kwargs)
540
543
 
541
- @cli.register_custom_action("Project")
544
+ @cli.register_custom_action(cls_names="Project")
542
545
  @exc.on_http_error(exc.GitlabGetError)
543
546
  def mirror_pull_details(self, **kwargs: Any) -> Dict[str, Any]:
544
547
  """Get a project's pull mirror details.
@@ -561,7 +564,7 @@ class Project(
561
564
  assert isinstance(result, dict)
562
565
  return result
563
566
 
564
- @cli.register_custom_action("Project", ("to_namespace",))
567
+ @cli.register_custom_action(cls_names="Project", required=("to_namespace",))
565
568
  @exc.on_http_error(exc.GitlabTransferProjectError)
566
569
  def transfer(self, to_namespace: Union[int, str], **kwargs: Any) -> None:
567
570
  """Transfer a project to the given namespace ID
@@ -786,7 +789,7 @@ class ProjectManager(CRUDMixin, RESTManager):
786
789
  @exc.on_http_error(exc.GitlabImportError)
787
790
  def import_project(
788
791
  self,
789
- file: str,
792
+ file: io.BufferedReader,
790
793
  path: str,
791
794
  name: Optional[str] = None,
792
795
  namespace: Optional[str] = None,