python-gitlab 5.6.0__py3-none-any.whl → 6.1.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 (103) hide show
  1. gitlab/__init__.py +0 -1
  2. gitlab/_backends/protocol.py +9 -13
  3. gitlab/_backends/requests_backend.py +12 -12
  4. gitlab/_version.py +1 -1
  5. gitlab/base.py +48 -48
  6. gitlab/cli.py +15 -25
  7. gitlab/client.py +114 -140
  8. gitlab/config.py +16 -17
  9. gitlab/const.py +2 -0
  10. gitlab/exceptions.py +7 -5
  11. gitlab/mixins.py +154 -238
  12. gitlab/types.py +13 -14
  13. gitlab/utils.py +32 -43
  14. gitlab/v4/cli.py +51 -54
  15. gitlab/v4/objects/__init__.py +1 -0
  16. gitlab/v4/objects/access_requests.py +11 -3
  17. gitlab/v4/objects/appearance.py +12 -14
  18. gitlab/v4/objects/applications.py +5 -6
  19. gitlab/v4/objects/artifacts.py +10 -17
  20. gitlab/v4/objects/audit_events.py +4 -19
  21. gitlab/v4/objects/award_emojis.py +13 -57
  22. gitlab/v4/objects/badges.py +4 -19
  23. gitlab/v4/objects/boards.py +7 -27
  24. gitlab/v4/objects/branches.py +26 -14
  25. gitlab/v4/objects/broadcast_messages.py +3 -13
  26. gitlab/v4/objects/bulk_imports.py +6 -14
  27. gitlab/v4/objects/ci_lint.py +7 -13
  28. gitlab/v4/objects/cluster_agents.py +3 -13
  29. gitlab/v4/objects/clusters.py +13 -23
  30. gitlab/v4/objects/commits.py +23 -28
  31. gitlab/v4/objects/container_registry.py +13 -19
  32. gitlab/v4/objects/custom_attributes.py +16 -21
  33. gitlab/v4/objects/deploy_keys.py +22 -19
  34. gitlab/v4/objects/deploy_tokens.py +14 -32
  35. gitlab/v4/objects/deployments.py +13 -15
  36. gitlab/v4/objects/discussions.py +13 -29
  37. gitlab/v4/objects/draft_notes.py +4 -14
  38. gitlab/v4/objects/environments.py +13 -21
  39. gitlab/v4/objects/epics.py +14 -17
  40. gitlab/v4/objects/events.py +27 -79
  41. gitlab/v4/objects/export_import.py +7 -19
  42. gitlab/v4/objects/features.py +11 -12
  43. gitlab/v4/objects/files.py +23 -38
  44. gitlab/v4/objects/geo_nodes.py +7 -11
  45. gitlab/v4/objects/group_access_tokens.py +6 -13
  46. gitlab/v4/objects/groups.py +42 -37
  47. gitlab/v4/objects/hooks.py +4 -17
  48. gitlab/v4/objects/integrations.py +7 -18
  49. gitlab/v4/objects/invitations.py +12 -23
  50. gitlab/v4/objects/issues.py +21 -27
  51. gitlab/v4/objects/iterations.py +4 -8
  52. gitlab/v4/objects/job_token_scope.py +18 -14
  53. gitlab/v4/objects/jobs.py +17 -32
  54. gitlab/v4/objects/keys.py +8 -11
  55. gitlab/v4/objects/labels.py +19 -30
  56. gitlab/v4/objects/ldap.py +25 -9
  57. gitlab/v4/objects/member_roles.py +102 -0
  58. gitlab/v4/objects/members.py +11 -29
  59. gitlab/v4/objects/merge_request_approvals.py +31 -44
  60. gitlab/v4/objects/merge_requests.py +30 -40
  61. gitlab/v4/objects/merge_trains.py +3 -6
  62. gitlab/v4/objects/milestones.py +23 -29
  63. gitlab/v4/objects/namespaces.py +4 -10
  64. gitlab/v4/objects/notes.py +26 -69
  65. gitlab/v4/objects/notification_settings.py +5 -14
  66. gitlab/v4/objects/package_protection_rules.py +8 -8
  67. gitlab/v4/objects/packages.py +22 -37
  68. gitlab/v4/objects/pages.py +8 -14
  69. gitlab/v4/objects/personal_access_tokens.py +7 -10
  70. gitlab/v4/objects/pipelines.py +38 -47
  71. gitlab/v4/objects/project_access_tokens.py +6 -13
  72. gitlab/v4/objects/projects.py +63 -77
  73. gitlab/v4/objects/push_rules.py +13 -15
  74. gitlab/v4/objects/registry_protection_repository_rules.py +6 -7
  75. gitlab/v4/objects/registry_protection_rules.py +7 -11
  76. gitlab/v4/objects/releases.py +6 -20
  77. gitlab/v4/objects/repositories.py +25 -34
  78. gitlab/v4/objects/resource_groups.py +10 -15
  79. gitlab/v4/objects/reviewers.py +4 -2
  80. gitlab/v4/objects/runners.py +14 -13
  81. gitlab/v4/objects/secure_files.py +8 -21
  82. gitlab/v4/objects/service_accounts.py +7 -5
  83. gitlab/v4/objects/settings.py +13 -14
  84. gitlab/v4/objects/sidekiq.py +17 -18
  85. gitlab/v4/objects/snippets.py +78 -66
  86. gitlab/v4/objects/statistics.py +8 -23
  87. gitlab/v4/objects/status_checks.py +6 -3
  88. gitlab/v4/objects/tags.py +4 -13
  89. gitlab/v4/objects/templates.py +11 -59
  90. gitlab/v4/objects/todos.py +3 -6
  91. gitlab/v4/objects/topics.py +10 -21
  92. gitlab/v4/objects/triggers.py +3 -13
  93. gitlab/v4/objects/users.py +101 -93
  94. gitlab/v4/objects/variables.py +4 -19
  95. gitlab/v4/objects/wikis.py +4 -19
  96. {python_gitlab-5.6.0.dist-info → python_gitlab-6.1.0.dist-info}/METADATA +3 -2
  97. python_gitlab-6.1.0.dist-info/RECORD +107 -0
  98. {python_gitlab-5.6.0.dist-info → python_gitlab-6.1.0.dist-info}/WHEEL +1 -1
  99. python_gitlab-5.6.0.dist-info/RECORD +0 -106
  100. {python_gitlab-5.6.0.dist-info → python_gitlab-6.1.0.dist-info}/entry_points.txt +0 -0
  101. {python_gitlab-5.6.0.dist-info → python_gitlab-6.1.0.dist-info/licenses}/AUTHORS +0 -0
  102. {python_gitlab-5.6.0.dist-info → python_gitlab-6.1.0.dist-info/licenses}/COPYING +0 -0
  103. {python_gitlab-5.6.0.dist-info → python_gitlab-6.1.0.dist-info}/top_level.txt +0 -0
gitlab/mixins.py CHANGED
@@ -1,19 +1,9 @@
1
+ from __future__ import annotations
2
+
1
3
  import enum
4
+ from collections.abc import Iterator
2
5
  from types import ModuleType
3
- from typing import (
4
- Any,
5
- Callable,
6
- Dict,
7
- Iterator,
8
- List,
9
- Literal,
10
- Optional,
11
- overload,
12
- Tuple,
13
- Type,
14
- TYPE_CHECKING,
15
- Union,
16
- )
6
+ from typing import Any, Callable, Literal, overload, TYPE_CHECKING
17
7
 
18
8
  import requests
19
9
 
@@ -48,18 +38,16 @@ __all__ = [
48
38
 
49
39
  if TYPE_CHECKING:
50
40
  # When running mypy we use these as the base classes
51
- _RestManagerBase = base.RESTManager
52
41
  _RestObjectBase = base.RESTObject
53
42
  else:
54
- _RestManagerBase = object
55
43
  _RestObjectBase = object
56
44
 
57
45
 
58
- class HeadMixin(_RestManagerBase):
46
+ class HeadMixin(base.RESTManager[base.TObjCls]):
59
47
  @exc.on_http_error(exc.GitlabHeadError)
60
48
  def head(
61
- self, id: Optional[Union[str, int]] = None, **kwargs: Any
62
- ) -> "requests.structures.CaseInsensitiveDict[Any]":
49
+ self, id: str | int | None = None, **kwargs: Any
50
+ ) -> requests.structures.CaseInsensitiveDict[Any]:
63
51
  """Retrieve headers from an endpoint.
64
52
 
65
53
  Args:
@@ -73,9 +61,6 @@ class HeadMixin(_RestManagerBase):
73
61
  GitlabAuthenticationError: If authentication is not correct
74
62
  GitlabHeadError: If the server cannot perform the request
75
63
  """
76
- if TYPE_CHECKING:
77
- assert self.path is not None
78
-
79
64
  path = self.path
80
65
  if id is not None:
81
66
  path = f"{path}/{utils.EncodedId(id)}"
@@ -83,20 +68,11 @@ class HeadMixin(_RestManagerBase):
83
68
  return self.gitlab.http_head(path, **kwargs)
84
69
 
85
70
 
86
- class GetMixin(HeadMixin, _RestManagerBase):
87
- _computed_path: Optional[str]
88
- _from_parent_attrs: Dict[str, Any]
89
- _obj_cls: Optional[Type[base.RESTObject]]
90
- _optional_get_attrs: Tuple[str, ...] = ()
91
- _parent: Optional[base.RESTObject]
92
- _parent_attrs: Dict[str, Any]
93
- _path: Optional[str]
94
- gitlab: gitlab.Gitlab
71
+ class GetMixin(HeadMixin[base.TObjCls]):
72
+ _optional_get_attrs: tuple[str, ...] = ()
95
73
 
96
74
  @exc.on_http_error(exc.GitlabGetError)
97
- def get(
98
- self, id: Union[str, int], lazy: bool = False, **kwargs: Any
99
- ) -> base.RESTObject:
75
+ def get(self, id: str | int, lazy: bool = False, **kwargs: Any) -> base.TObjCls:
100
76
  """Retrieve a single object.
101
77
 
102
78
  Args:
@@ -116,8 +92,6 @@ class GetMixin(HeadMixin, _RestManagerBase):
116
92
  if isinstance(id, str):
117
93
  id = utils.EncodedId(id)
118
94
  path = f"{self.path}/{id}"
119
- if TYPE_CHECKING:
120
- assert self._obj_cls is not None
121
95
  if lazy is True:
122
96
  if TYPE_CHECKING:
123
97
  assert self._obj_cls._id_attr is not None
@@ -128,18 +102,11 @@ class GetMixin(HeadMixin, _RestManagerBase):
128
102
  return self._obj_cls(self, server_data, lazy=lazy)
129
103
 
130
104
 
131
- class GetWithoutIdMixin(HeadMixin, _RestManagerBase):
132
- _computed_path: Optional[str]
133
- _from_parent_attrs: Dict[str, Any]
134
- _obj_cls: Optional[Type[base.RESTObject]]
135
- _optional_get_attrs: Tuple[str, ...] = ()
136
- _parent: Optional[base.RESTObject]
137
- _parent_attrs: Dict[str, Any]
138
- _path: Optional[str]
139
- gitlab: gitlab.Gitlab
105
+ class GetWithoutIdMixin(HeadMixin[base.TObjCls]):
106
+ _optional_get_attrs: tuple[str, ...] = ()
140
107
 
141
108
  @exc.on_http_error(exc.GitlabGetError)
142
- def get(self, **kwargs: Any) -> base.RESTObject:
109
+ def get(self, **kwargs: Any) -> base.TObjCls:
143
110
  """Retrieve a single object.
144
111
 
145
112
  Args:
@@ -152,22 +119,19 @@ class GetWithoutIdMixin(HeadMixin, _RestManagerBase):
152
119
  GitlabAuthenticationError: If authentication is not correct
153
120
  GitlabGetError: If the server cannot perform the request
154
121
  """
155
- if TYPE_CHECKING:
156
- assert self.path is not None
157
122
  server_data = self.gitlab.http_get(self.path, **kwargs)
158
123
  if TYPE_CHECKING:
159
124
  assert not isinstance(server_data, requests.Response)
160
- assert self._obj_cls is not None
161
125
  return self._obj_cls(self, server_data)
162
126
 
163
127
 
164
128
  class RefreshMixin(_RestObjectBase):
165
- _id_attr: Optional[str]
166
- _attrs: Dict[str, Any]
129
+ _id_attr: str | None
130
+ _attrs: dict[str, Any]
167
131
  _module: ModuleType
168
- _parent_attrs: Dict[str, Any]
169
- _updated_attrs: Dict[str, Any]
170
- manager: base.RESTManager
132
+ _parent_attrs: dict[str, Any]
133
+ _updated_attrs: dict[str, Any]
134
+ manager: base.RESTManager[Any]
171
135
 
172
136
  @exc.on_http_error(exc.GitlabGetError)
173
137
  def refresh(self, **kwargs: Any) -> None:
@@ -194,22 +158,32 @@ class RefreshMixin(_RestObjectBase):
194
158
  self._update_attrs(server_data)
195
159
 
196
160
 
197
- class ListMixin(HeadMixin, _RestManagerBase):
198
- _computed_path: Optional[str]
199
- _from_parent_attrs: Dict[str, Any]
200
- _list_filters: Tuple[str, ...] = ()
201
- _obj_cls: Optional[Type[base.RESTObject]]
202
- _parent: Optional[base.RESTObject]
203
- _parent_attrs: Dict[str, Any]
204
- _path: Optional[str]
205
- gitlab: gitlab.Gitlab
161
+ class ListMixin(HeadMixin[base.TObjCls]):
162
+ _list_filters: tuple[str, ...] = ()
163
+
164
+ @overload
165
+ def list(
166
+ self, *, iterator: Literal[False] = False, **kwargs: Any
167
+ ) -> list[base.TObjCls]: ...
168
+
169
+ @overload
170
+ def list(
171
+ self, *, iterator: Literal[True] = True, **kwargs: Any
172
+ ) -> base.RESTObjectList[base.TObjCls]: ...
173
+
174
+ @overload
175
+ def list(
176
+ self, *, iterator: bool = False, **kwargs: Any
177
+ ) -> base.RESTObjectList[base.TObjCls] | list[base.TObjCls]: ...
206
178
 
207
179
  @exc.on_http_error(exc.GitlabListError)
208
- def list(self, **kwargs: Any) -> Union[base.RESTObjectList, List[base.RESTObject]]:
180
+ def list(
181
+ self, *, iterator: bool = False, **kwargs: Any
182
+ ) -> base.RESTObjectList[base.TObjCls] | list[base.TObjCls]:
209
183
  """Retrieve a list of objects.
210
184
 
211
185
  Args:
212
- all: If True, return all the items, without pagination
186
+ get_all: If True, return all the items, without pagination
213
187
  per_page: Number of items to retrieve per request
214
188
  page: ID of the page to return (starts with page 1)
215
189
  iterator: If set to True and no pagination option is
@@ -244,37 +218,18 @@ class ListMixin(HeadMixin, _RestManagerBase):
244
218
  # Allow to overwrite the path, handy for custom listings
245
219
  path = data.pop("path", self.path)
246
220
 
247
- if TYPE_CHECKING:
248
- assert self._obj_cls is not None
249
- obj = self.gitlab.http_list(path, **data)
221
+ obj = self.gitlab.http_list(path, iterator=iterator, **data)
250
222
  if isinstance(obj, list):
251
223
  return [self._obj_cls(self, item, created_from_list=True) for item in obj]
252
224
  return base.RESTObjectList(self, self._obj_cls, obj)
253
225
 
254
226
 
255
- class RetrieveMixin(ListMixin, GetMixin):
256
- _computed_path: Optional[str]
257
- _from_parent_attrs: Dict[str, Any]
258
- _obj_cls: Optional[Type[base.RESTObject]]
259
- _parent: Optional[base.RESTObject]
260
- _parent_attrs: Dict[str, Any]
261
- _path: Optional[str]
262
- gitlab: gitlab.Gitlab
263
-
227
+ class RetrieveMixin(ListMixin[base.TObjCls], GetMixin[base.TObjCls]): ...
264
228
 
265
- class CreateMixin(_RestManagerBase):
266
- _computed_path: Optional[str]
267
- _from_parent_attrs: Dict[str, Any]
268
- _obj_cls: Optional[Type[base.RESTObject]]
269
- _parent: Optional[base.RESTObject]
270
- _parent_attrs: Dict[str, Any]
271
- _path: Optional[str]
272
- gitlab: gitlab.Gitlab
273
229
 
230
+ class CreateMixin(base.RESTManager[base.TObjCls]):
274
231
  @exc.on_http_error(exc.GitlabCreateError)
275
- def create(
276
- self, data: Optional[Dict[str, Any]] = None, **kwargs: Any
277
- ) -> base.RESTObject:
232
+ def create(self, data: dict[str, Any] | None = None, **kwargs: Any) -> base.TObjCls:
278
233
  """Create a new object.
279
234
 
280
235
  Args:
@@ -303,7 +258,6 @@ class CreateMixin(_RestManagerBase):
303
258
  server_data = self.gitlab.http_post(path, post_data=data, files=files, **kwargs)
304
259
  if TYPE_CHECKING:
305
260
  assert not isinstance(server_data, requests.Response)
306
- assert self._obj_cls is not None
307
261
  return self._obj_cls(self, server_data)
308
262
 
309
263
 
@@ -314,19 +268,11 @@ class UpdateMethod(enum.IntEnum):
314
268
  PATCH = 3
315
269
 
316
270
 
317
- class UpdateMixin(_RestManagerBase):
318
- _computed_path: Optional[str]
319
- _from_parent_attrs: Dict[str, Any]
320
- _obj_cls: Optional[Type[base.RESTObject]]
321
- _parent: Optional[base.RESTObject]
322
- _parent_attrs: Dict[str, Any]
323
- _path: Optional[str]
271
+ class UpdateMixin(base.RESTManager[base.TObjCls]):
272
+ # Update mixins attrs for easier implementation
324
273
  _update_method: UpdateMethod = UpdateMethod.PUT
325
- gitlab: gitlab.Gitlab
326
274
 
327
- def _get_update_method(
328
- self,
329
- ) -> Callable[..., Union[Dict[str, Any], requests.Response]]:
275
+ def _get_update_method(self) -> Callable[..., dict[str, Any] | requests.Response]:
330
276
  """Return the HTTP method to use.
331
277
 
332
278
  Returns:
@@ -344,10 +290,10 @@ class UpdateMixin(_RestManagerBase):
344
290
  @exc.on_http_error(exc.GitlabUpdateError)
345
291
  def update(
346
292
  self,
347
- id: Optional[Union[str, int]] = None,
348
- new_data: Optional[Dict[str, Any]] = None,
293
+ id: str | int | None = None,
294
+ new_data: dict[str, Any] | None = None,
349
295
  **kwargs: Any,
350
- ) -> Dict[str, Any]:
296
+ ) -> dict[str, Any]:
351
297
  """Update an object on the server.
352
298
 
353
299
  Args:
@@ -384,17 +330,9 @@ class UpdateMixin(_RestManagerBase):
384
330
  return result
385
331
 
386
332
 
387
- class SetMixin(_RestManagerBase):
388
- _computed_path: Optional[str]
389
- _from_parent_attrs: Dict[str, Any]
390
- _obj_cls: Optional[Type[base.RESTObject]]
391
- _parent: Optional[base.RESTObject]
392
- _parent_attrs: Dict[str, Any]
393
- _path: Optional[str]
394
- gitlab: gitlab.Gitlab
395
-
333
+ class SetMixin(base.RESTManager[base.TObjCls]):
396
334
  @exc.on_http_error(exc.GitlabSetError)
397
- def set(self, key: str, value: str, **kwargs: Any) -> base.RESTObject:
335
+ def set(self, key: str, value: str, **kwargs: Any) -> base.TObjCls:
398
336
  """Create or update the object.
399
337
 
400
338
  Args:
@@ -414,21 +352,12 @@ class SetMixin(_RestManagerBase):
414
352
  server_data = self.gitlab.http_put(path, post_data=data, **kwargs)
415
353
  if TYPE_CHECKING:
416
354
  assert not isinstance(server_data, requests.Response)
417
- assert self._obj_cls is not None
418
355
  return self._obj_cls(self, server_data)
419
356
 
420
357
 
421
- class DeleteMixin(_RestManagerBase):
422
- _computed_path: Optional[str]
423
- _from_parent_attrs: Dict[str, Any]
424
- _obj_cls: Optional[Type[base.RESTObject]]
425
- _parent: Optional[base.RESTObject]
426
- _parent_attrs: Dict[str, Any]
427
- _path: Optional[str]
428
- gitlab: gitlab.Gitlab
429
-
358
+ class DeleteMixin(base.RESTManager[base.TObjCls]):
430
359
  @exc.on_http_error(exc.GitlabDeleteError)
431
- def delete(self, id: Optional[Union[str, int]] = None, **kwargs: Any) -> None:
360
+ def delete(self, id: str | int | None = None, **kwargs: Any) -> None:
432
361
  """Delete an object on the server.
433
362
 
434
363
  Args:
@@ -444,42 +373,37 @@ class DeleteMixin(_RestManagerBase):
444
373
  else:
445
374
  path = f"{self.path}/{utils.EncodedId(id)}"
446
375
 
447
- if TYPE_CHECKING:
448
- assert path is not None
449
376
  self.gitlab.http_delete(path, **kwargs)
450
377
 
451
378
 
452
- class CRUDMixin(GetMixin, ListMixin, CreateMixin, UpdateMixin, DeleteMixin):
453
- _computed_path: Optional[str]
454
- _from_parent_attrs: Dict[str, Any]
455
- _obj_cls: Optional[Type[base.RESTObject]]
456
- _parent: Optional[base.RESTObject]
457
- _parent_attrs: Dict[str, Any]
458
- _path: Optional[str]
459
- gitlab: gitlab.Gitlab
379
+ class CRUDMixin(
380
+ GetMixin[base.TObjCls],
381
+ ListMixin[base.TObjCls],
382
+ CreateMixin[base.TObjCls],
383
+ UpdateMixin[base.TObjCls],
384
+ DeleteMixin[base.TObjCls],
385
+ ): ...
460
386
 
461
387
 
462
- class NoUpdateMixin(GetMixin, ListMixin, CreateMixin, DeleteMixin):
463
- _computed_path: Optional[str]
464
- _from_parent_attrs: Dict[str, Any]
465
- _obj_cls: Optional[Type[base.RESTObject]]
466
- _parent: Optional[base.RESTObject]
467
- _parent_attrs: Dict[str, Any]
468
- _path: Optional[str]
469
- gitlab: gitlab.Gitlab
388
+ class NoUpdateMixin(
389
+ GetMixin[base.TObjCls],
390
+ ListMixin[base.TObjCls],
391
+ CreateMixin[base.TObjCls],
392
+ DeleteMixin[base.TObjCls],
393
+ ): ...
470
394
 
471
395
 
472
396
  class SaveMixin(_RestObjectBase):
473
397
  """Mixin for RESTObject's that can be updated."""
474
398
 
475
- _id_attr: Optional[str]
476
- _attrs: Dict[str, Any]
399
+ _id_attr: str | None
400
+ _attrs: dict[str, Any]
477
401
  _module: ModuleType
478
- _parent_attrs: Dict[str, Any]
479
- _updated_attrs: Dict[str, Any]
480
- manager: base.RESTManager
402
+ _parent_attrs: dict[str, Any]
403
+ _updated_attrs: dict[str, Any]
404
+ manager: base.RESTManager[Any]
481
405
 
482
- def _get_updated_data(self) -> Dict[str, Any]:
406
+ def _get_updated_data(self) -> dict[str, Any]:
483
407
  updated_data = {}
484
408
  for attr in self.manager._update_attrs.required:
485
409
  # Get everything required, no matter if it's been updated
@@ -489,7 +413,7 @@ class SaveMixin(_RestObjectBase):
489
413
 
490
414
  return updated_data
491
415
 
492
- def save(self, **kwargs: Any) -> Optional[Dict[str, Any]]:
416
+ def save(self, **kwargs: Any) -> dict[str, Any] | None:
493
417
  """Save the changes made to the object to the server.
494
418
 
495
419
  The object is updated to match what the server returns.
@@ -521,12 +445,12 @@ class SaveMixin(_RestObjectBase):
521
445
  class ObjectDeleteMixin(_RestObjectBase):
522
446
  """Mixin for RESTObject's that can be deleted."""
523
447
 
524
- _id_attr: Optional[str]
525
- _attrs: Dict[str, Any]
448
+ _id_attr: str | None
449
+ _attrs: dict[str, Any]
526
450
  _module: ModuleType
527
- _parent_attrs: Dict[str, Any]
528
- _updated_attrs: Dict[str, Any]
529
- manager: base.RESTManager
451
+ _parent_attrs: dict[str, Any]
452
+ _updated_attrs: dict[str, Any]
453
+ manager: base.RESTManager[Any]
530
454
 
531
455
  def delete(self, **kwargs: Any) -> None:
532
456
  """Delete the object from the server.
@@ -545,16 +469,16 @@ class ObjectDeleteMixin(_RestObjectBase):
545
469
 
546
470
 
547
471
  class UserAgentDetailMixin(_RestObjectBase):
548
- _id_attr: Optional[str]
549
- _attrs: Dict[str, Any]
472
+ _id_attr: str | None
473
+ _attrs: dict[str, Any]
550
474
  _module: ModuleType
551
- _parent_attrs: Dict[str, Any]
552
- _updated_attrs: Dict[str, Any]
553
- manager: base.RESTManager
475
+ _parent_attrs: dict[str, Any]
476
+ _updated_attrs: dict[str, Any]
477
+ manager: base.RESTManager[Any]
554
478
 
555
479
  @cli.register_custom_action(cls_names=("Snippet", "ProjectSnippet", "ProjectIssue"))
556
480
  @exc.on_http_error(exc.GitlabGetError)
557
- def user_agent_detail(self, **kwargs: Any) -> Dict[str, Any]:
481
+ def user_agent_detail(self, **kwargs: Any) -> dict[str, Any]:
558
482
  """Get the user agent detail.
559
483
 
560
484
  Args:
@@ -572,12 +496,12 @@ class UserAgentDetailMixin(_RestObjectBase):
572
496
 
573
497
 
574
498
  class AccessRequestMixin(_RestObjectBase):
575
- _id_attr: Optional[str]
576
- _attrs: Dict[str, Any]
499
+ _id_attr: str | None
500
+ _attrs: dict[str, Any]
577
501
  _module: ModuleType
578
- _parent_attrs: Dict[str, Any]
579
- _updated_attrs: Dict[str, Any]
580
- manager: base.RESTManager
502
+ _parent_attrs: dict[str, Any]
503
+ _updated_attrs: dict[str, Any]
504
+ manager: base.RESTManager[Any]
581
505
 
582
506
  @cli.register_custom_action(
583
507
  cls_names=("ProjectAccessRequest", "GroupAccessRequest"),
@@ -607,12 +531,12 @@ class AccessRequestMixin(_RestObjectBase):
607
531
 
608
532
 
609
533
  class DownloadMixin(_RestObjectBase):
610
- _id_attr: Optional[str]
611
- _attrs: Dict[str, Any]
534
+ _id_attr: str | None
535
+ _attrs: dict[str, Any]
612
536
  _module: ModuleType
613
- _parent_attrs: Dict[str, Any]
614
- _updated_attrs: Dict[str, Any]
615
- manager: base.RESTManager
537
+ _parent_attrs: dict[str, Any]
538
+ _updated_attrs: dict[str, Any]
539
+ manager: base.RESTManager[Any]
616
540
 
617
541
  @overload
618
542
  def download(
@@ -640,7 +564,7 @@ class DownloadMixin(_RestObjectBase):
640
564
  def download(
641
565
  self,
642
566
  streamed: Literal[True] = True,
643
- action: Optional[Callable[[bytes], Any]] = None,
567
+ action: Callable[[bytes], Any] | None = None,
644
568
  chunk_size: int = 1024,
645
569
  *,
646
570
  iterator: Literal[False] = False,
@@ -652,12 +576,12 @@ class DownloadMixin(_RestObjectBase):
652
576
  def download(
653
577
  self,
654
578
  streamed: bool = False,
655
- action: Optional[Callable[[bytes], Any]] = None,
579
+ action: Callable[[bytes], Any] | None = None,
656
580
  chunk_size: int = 1024,
657
581
  *,
658
582
  iterator: bool = False,
659
583
  **kwargs: Any,
660
- ) -> Optional[Union[bytes, Iterator[Any]]]:
584
+ ) -> bytes | Iterator[Any] | None:
661
585
  """Download the archive of a resource export.
662
586
 
663
587
  Args:
@@ -689,15 +613,7 @@ class DownloadMixin(_RestObjectBase):
689
613
  )
690
614
 
691
615
 
692
- class RotateMixin(_RestManagerBase):
693
- _computed_path: Optional[str]
694
- _from_parent_attrs: Dict[str, Any]
695
- _obj_cls: Optional[Type[base.RESTObject]]
696
- _parent: Optional[base.RESTObject]
697
- _parent_attrs: Dict[str, Any]
698
- _path: Optional[str]
699
- gitlab: gitlab.Gitlab
700
-
616
+ class RotateMixin(base.RESTManager[base.TObjCls]):
701
617
  @cli.register_custom_action(
702
618
  cls_names=(
703
619
  "PersonalAccessTokenManager",
@@ -708,8 +624,8 @@ class RotateMixin(_RestManagerBase):
708
624
  )
709
625
  @exc.on_http_error(exc.GitlabRotateError)
710
626
  def rotate(
711
- self, id: Union[str, int], expires_at: Optional[str] = None, **kwargs: Any
712
- ) -> Dict[str, Any]:
627
+ self, id: str | int, expires_at: str | None = None, **kwargs: Any
628
+ ) -> dict[str, Any]:
713
629
  """Rotate an access token.
714
630
 
715
631
  Args:
@@ -721,7 +637,7 @@ class RotateMixin(_RestManagerBase):
721
637
  GitlabRotateError: If the server cannot perform the request
722
638
  """
723
639
  path = f"{self.path}/{utils.EncodedId(id)}/rotate"
724
- data: Dict[str, Any] = {}
640
+ data: dict[str, Any] = {}
725
641
  if expires_at is not None:
726
642
  data = {"expires_at": expires_at}
727
643
 
@@ -732,22 +648,23 @@ class RotateMixin(_RestManagerBase):
732
648
 
733
649
 
734
650
  class ObjectRotateMixin(_RestObjectBase):
735
- _id_attr: Optional[str]
736
- _attrs: Dict[str, Any]
651
+ _id_attr: str | None
652
+ _attrs: dict[str, Any]
737
653
  _module: ModuleType
738
- _parent_attrs: Dict[str, Any]
739
- _updated_attrs: Dict[str, Any]
740
- manager: base.RESTManager
654
+ _parent_attrs: dict[str, Any]
655
+ _updated_attrs: dict[str, Any]
656
+ manager: base.RESTManager[Any]
741
657
 
742
658
  @cli.register_custom_action(
743
659
  cls_names=("PersonalAccessToken", "GroupAccessToken", "ProjectAccessToken"),
744
660
  optional=("expires_at",),
745
661
  )
746
662
  @exc.on_http_error(exc.GitlabRotateError)
747
- def rotate(self, **kwargs: Any) -> Dict[str, Any]:
663
+ def rotate(self, *, self_rotate: bool = False, **kwargs: Any) -> dict[str, Any]:
748
664
  """Rotate the current access token object.
749
665
 
750
666
  Args:
667
+ self_rotate: If True, the current access token object will be rotated.
751
668
  **kwargs: Extra options to send to the server (e.g. sudo)
752
669
 
753
670
  Raises:
@@ -757,18 +674,19 @@ class ObjectRotateMixin(_RestObjectBase):
757
674
  if TYPE_CHECKING:
758
675
  assert isinstance(self.manager, RotateMixin)
759
676
  assert self.encoded_id is not None
760
- server_data = self.manager.rotate(self.encoded_id, **kwargs)
677
+ token_id = "self" if self_rotate else self.encoded_id
678
+ server_data = self.manager.rotate(token_id, **kwargs)
761
679
  self._update_attrs(server_data)
762
680
  return server_data
763
681
 
764
682
 
765
683
  class SubscribableMixin(_RestObjectBase):
766
- _id_attr: Optional[str]
767
- _attrs: Dict[str, Any]
684
+ _id_attr: str | None
685
+ _attrs: dict[str, Any]
768
686
  _module: ModuleType
769
- _parent_attrs: Dict[str, Any]
770
- _updated_attrs: Dict[str, Any]
771
- manager: base.RESTManager
687
+ _parent_attrs: dict[str, Any]
688
+ _updated_attrs: dict[str, Any]
689
+ manager: base.RESTManager[Any]
772
690
 
773
691
  @cli.register_custom_action(
774
692
  cls_names=("ProjectIssue", "ProjectMergeRequest", "ProjectLabel", "GroupLabel")
@@ -812,12 +730,12 @@ class SubscribableMixin(_RestObjectBase):
812
730
 
813
731
 
814
732
  class TodoMixin(_RestObjectBase):
815
- _id_attr: Optional[str]
816
- _attrs: Dict[str, Any]
733
+ _id_attr: str | None
734
+ _attrs: dict[str, Any]
817
735
  _module: ModuleType
818
- _parent_attrs: Dict[str, Any]
819
- _updated_attrs: Dict[str, Any]
820
- manager: base.RESTManager
736
+ _parent_attrs: dict[str, Any]
737
+ _updated_attrs: dict[str, Any]
738
+ manager: base.RESTManager[Any]
821
739
 
822
740
  @cli.register_custom_action(cls_names=("ProjectIssue", "ProjectMergeRequest"))
823
741
  @exc.on_http_error(exc.GitlabTodoError)
@@ -836,16 +754,16 @@ class TodoMixin(_RestObjectBase):
836
754
 
837
755
 
838
756
  class TimeTrackingMixin(_RestObjectBase):
839
- _id_attr: Optional[str]
840
- _attrs: Dict[str, Any]
757
+ _id_attr: str | None
758
+ _attrs: dict[str, Any]
841
759
  _module: ModuleType
842
- _parent_attrs: Dict[str, Any]
843
- _updated_attrs: Dict[str, Any]
844
- manager: base.RESTManager
760
+ _parent_attrs: dict[str, Any]
761
+ _updated_attrs: dict[str, Any]
762
+ manager: base.RESTManager[Any]
845
763
 
846
764
  @cli.register_custom_action(cls_names=("ProjectIssue", "ProjectMergeRequest"))
847
765
  @exc.on_http_error(exc.GitlabTimeTrackingError)
848
- def time_stats(self, **kwargs: Any) -> Dict[str, Any]:
766
+ def time_stats(self, **kwargs: Any) -> dict[str, Any]:
849
767
  """Get time stats for the object.
850
768
 
851
769
  Args:
@@ -873,7 +791,7 @@ class TimeTrackingMixin(_RestObjectBase):
873
791
  cls_names=("ProjectIssue", "ProjectMergeRequest"), required=("duration",)
874
792
  )
875
793
  @exc.on_http_error(exc.GitlabTimeTrackingError)
876
- def time_estimate(self, duration: str, **kwargs: Any) -> Dict[str, Any]:
794
+ def time_estimate(self, duration: str, **kwargs: Any) -> dict[str, Any]:
877
795
  """Set an estimated time of work for the object.
878
796
 
879
797
  Args:
@@ -893,7 +811,7 @@ class TimeTrackingMixin(_RestObjectBase):
893
811
 
894
812
  @cli.register_custom_action(cls_names=("ProjectIssue", "ProjectMergeRequest"))
895
813
  @exc.on_http_error(exc.GitlabTimeTrackingError)
896
- def reset_time_estimate(self, **kwargs: Any) -> Dict[str, Any]:
814
+ def reset_time_estimate(self, **kwargs: Any) -> dict[str, Any]:
897
815
  """Resets estimated time for the object to 0 seconds.
898
816
 
899
817
  Args:
@@ -913,7 +831,7 @@ class TimeTrackingMixin(_RestObjectBase):
913
831
  cls_names=("ProjectIssue", "ProjectMergeRequest"), required=("duration",)
914
832
  )
915
833
  @exc.on_http_error(exc.GitlabTimeTrackingError)
916
- def add_spent_time(self, duration: str, **kwargs: Any) -> Dict[str, Any]:
834
+ def add_spent_time(self, duration: str, **kwargs: Any) -> dict[str, Any]:
917
835
  """Add time spent working on the object.
918
836
 
919
837
  Args:
@@ -933,7 +851,7 @@ class TimeTrackingMixin(_RestObjectBase):
933
851
 
934
852
  @cli.register_custom_action(cls_names=("ProjectIssue", "ProjectMergeRequest"))
935
853
  @exc.on_http_error(exc.GitlabTimeTrackingError)
936
- def reset_spent_time(self, **kwargs: Any) -> Dict[str, Any]:
854
+ def reset_spent_time(self, **kwargs: Any) -> dict[str, Any]:
937
855
  """Resets the time spent working on the object.
938
856
 
939
857
  Args:
@@ -951,22 +869,22 @@ class TimeTrackingMixin(_RestObjectBase):
951
869
 
952
870
 
953
871
  class ParticipantsMixin(_RestObjectBase):
954
- _id_attr: Optional[str]
955
- _attrs: Dict[str, Any]
872
+ _id_attr: str | None
873
+ _attrs: dict[str, Any]
956
874
  _module: ModuleType
957
- _parent_attrs: Dict[str, Any]
958
- _updated_attrs: Dict[str, Any]
959
- manager: base.RESTManager
875
+ _parent_attrs: dict[str, Any]
876
+ _updated_attrs: dict[str, Any]
877
+ manager: base.RESTManager[Any]
960
878
 
961
879
  @cli.register_custom_action(cls_names=("ProjectMergeRequest", "ProjectIssue"))
962
880
  @exc.on_http_error(exc.GitlabListError)
963
881
  def participants(
964
882
  self, **kwargs: Any
965
- ) -> Union[gitlab.client.GitlabList, List[Dict[str, Any]]]:
883
+ ) -> gitlab.client.GitlabList | list[dict[str, Any]]:
966
884
  """List the participants.
967
885
 
968
886
  Args:
969
- all: If True, return all the items, without pagination
887
+ get_all: If True, return all the items, without pagination
970
888
  per_page: Number of items to retrieve per request
971
889
  page: ID of the page to return (starts with page 1)
972
890
  **kwargs: Extra options to send to the server (e.g. sudo)
@@ -986,13 +904,13 @@ class ParticipantsMixin(_RestObjectBase):
986
904
  return result
987
905
 
988
906
 
989
- class BadgeRenderMixin(_RestManagerBase):
907
+ class BadgeRenderMixin(base.RESTManager[base.TObjCls]):
990
908
  @cli.register_custom_action(
991
909
  cls_names=("GroupBadgeManager", "ProjectBadgeManager"),
992
910
  required=("link_url", "image_url"),
993
911
  )
994
912
  @exc.on_http_error(exc.GitlabRenderError)
995
- def render(self, link_url: str, image_url: str, **kwargs: Any) -> Dict[str, Any]:
913
+ def render(self, link_url: str, image_url: str, **kwargs: Any) -> dict[str, Any]:
996
914
  """Preview link_url and image_url after interpolation.
997
915
 
998
916
  Args:
@@ -1016,17 +934,15 @@ class BadgeRenderMixin(_RestManagerBase):
1016
934
 
1017
935
 
1018
936
  class PromoteMixin(_RestObjectBase):
1019
- _id_attr: Optional[str]
1020
- _attrs: Dict[str, Any]
937
+ _id_attr: str | None
938
+ _attrs: dict[str, Any]
1021
939
  _module: ModuleType
1022
- _parent_attrs: Dict[str, Any]
1023
- _updated_attrs: Dict[str, Any]
940
+ _parent_attrs: dict[str, Any]
941
+ _updated_attrs: dict[str, Any]
1024
942
  _update_method: UpdateMethod = UpdateMethod.PUT
1025
- manager: base.RESTManager
943
+ manager: base.RESTManager[Any]
1026
944
 
1027
- def _get_update_method(
1028
- self,
1029
- ) -> Callable[..., Union[Dict[str, Any], requests.Response]]:
945
+ def _get_update_method(self) -> Callable[..., dict[str, Any] | requests.Response]:
1030
946
  """Return the HTTP method to use.
1031
947
 
1032
948
  Returns:
@@ -1039,7 +955,7 @@ class PromoteMixin(_RestObjectBase):
1039
955
  return http_method
1040
956
 
1041
957
  @exc.on_http_error(exc.GitlabPromoteError)
1042
- def promote(self, **kwargs: Any) -> Dict[str, Any]:
958
+ def promote(self, **kwargs: Any) -> dict[str, Any]:
1043
959
  """Promote the item.
1044
960
 
1045
961
  Args:
@@ -1063,13 +979,13 @@ class PromoteMixin(_RestObjectBase):
1063
979
 
1064
980
 
1065
981
  class UploadMixin(_RestObjectBase):
1066
- _id_attr: Optional[str]
1067
- _attrs: Dict[str, Any]
982
+ _id_attr: str | None
983
+ _attrs: dict[str, Any]
1068
984
  _module: ModuleType
1069
- _parent_attrs: Dict[str, Any]
1070
- _updated_attrs: Dict[str, Any]
985
+ _parent_attrs: dict[str, Any]
986
+ _updated_attrs: dict[str, Any]
1071
987
  _upload_path: str
1072
- manager: base.RESTManager
988
+ manager: base.RESTManager[Any]
1073
989
 
1074
990
  def _get_upload_path(self) -> str:
1075
991
  """Formats _upload_path with object attributes.
@@ -1089,10 +1005,10 @@ class UploadMixin(_RestObjectBase):
1089
1005
  def upload(
1090
1006
  self,
1091
1007
  filename: str,
1092
- filedata: Optional[bytes] = None,
1093
- filepath: Optional[str] = None,
1008
+ filedata: bytes | None = None,
1009
+ filepath: str | None = None,
1094
1010
  **kwargs: Any,
1095
- ) -> Dict[str, Any]:
1011
+ ) -> dict[str, Any]:
1096
1012
  """Upload the specified file.
1097
1013
 
1098
1014
  .. note::