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
@@ -1,8 +1,8 @@
1
- from typing import Any, cast, Dict, List, TYPE_CHECKING, Union
1
+ from typing import Any, Dict, List, TYPE_CHECKING
2
2
 
3
3
  from gitlab import cli
4
4
  from gitlab import exceptions as exc
5
- from gitlab.base import RESTManager, RESTObject
5
+ from gitlab.base import RESTObject
6
6
  from gitlab.mixins import (
7
7
  DeleteMixin,
8
8
  ObjectDeleteMixin,
@@ -12,10 +12,7 @@ from gitlab.mixins import (
12
12
  )
13
13
  from gitlab.types import RequiredOptional
14
14
 
15
- __all__ = [
16
- "GeoNode",
17
- "GeoNodeManager",
18
- ]
15
+ __all__ = ["GeoNode", "GeoNodeManager"]
19
16
 
20
17
 
21
18
  class GeoNode(SaveMixin, ObjectDeleteMixin, RESTObject):
@@ -59,16 +56,15 @@ class GeoNode(SaveMixin, ObjectDeleteMixin, RESTObject):
59
56
  return result
60
57
 
61
58
 
62
- class GeoNodeManager(RetrieveMixin, UpdateMixin, DeleteMixin, RESTManager):
59
+ class GeoNodeManager(
60
+ RetrieveMixin[GeoNode], UpdateMixin[GeoNode], DeleteMixin[GeoNode]
61
+ ):
63
62
  _path = "/geo_nodes"
64
63
  _obj_cls = GeoNode
65
64
  _update_attrs = RequiredOptional(
66
- optional=("enabled", "url", "files_max_capacity", "repos_max_capacity"),
65
+ optional=("enabled", "url", "files_max_capacity", "repos_max_capacity")
67
66
  )
68
67
 
69
- def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> GeoNode:
70
- return cast(GeoNode, super().get(id=id, lazy=lazy, **kwargs))
71
-
72
68
  @cli.register_custom_action(cls_names="GeoNodeManager")
73
69
  @exc.on_http_error(exc.GitlabGetError)
74
70
  def status(self, **kwargs: Any) -> List[Dict[str, Any]]:
@@ -1,6 +1,4 @@
1
- from typing import Any, cast, Union
2
-
3
- from gitlab.base import RESTManager, RESTObject
1
+ from gitlab.base import RESTObject
4
2
  from gitlab.mixins import (
5
3
  CreateMixin,
6
4
  DeleteMixin,
@@ -11,10 +9,7 @@ from gitlab.mixins import (
11
9
  )
12
10
  from gitlab.types import ArrayAttribute, RequiredOptional
13
11
 
14
- __all__ = [
15
- "GroupAccessToken",
16
- "GroupAccessTokenManager",
17
- ]
12
+ __all__ = ["GroupAccessToken", "GroupAccessTokenManager"]
18
13
 
19
14
 
20
15
  class GroupAccessToken(ObjectDeleteMixin, ObjectRotateMixin, RESTObject):
@@ -22,7 +17,10 @@ class GroupAccessToken(ObjectDeleteMixin, ObjectRotateMixin, RESTObject):
22
17
 
23
18
 
24
19
  class GroupAccessTokenManager(
25
- CreateMixin, DeleteMixin, RetrieveMixin, RotateMixin, RESTManager
20
+ CreateMixin[GroupAccessToken],
21
+ DeleteMixin[GroupAccessToken],
22
+ RetrieveMixin[GroupAccessToken],
23
+ RotateMixin[GroupAccessToken],
26
24
  ):
27
25
  _path = "/groups/{group_id}/access_tokens"
28
26
  _obj_cls = GroupAccessToken
@@ -31,8 +29,3 @@ class GroupAccessTokenManager(
31
29
  required=("name", "scopes"), optional=("access_level", "expires_at")
32
30
  )
33
31
  _types = {"scopes": ArrayAttribute}
34
-
35
- def get(
36
- self, id: Union[str, int], lazy: bool = False, **kwargs: Any
37
- ) -> GroupAccessToken:
38
- return cast(GroupAccessToken, super().get(id=id, lazy=lazy, **kwargs))
@@ -1,4 +1,6 @@
1
- from typing import Any, BinaryIO, cast, Dict, List, Optional, Type, TYPE_CHECKING, Union
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, BinaryIO, TYPE_CHECKING
2
4
 
3
5
  import requests
4
6
 
@@ -6,7 +8,7 @@ import gitlab
6
8
  from gitlab import cli
7
9
  from gitlab import exceptions as exc
8
10
  from gitlab import types
9
- from gitlab.base import RESTManager, RESTObject
11
+ from gitlab.base import RESTObject, TObjCls
10
12
  from gitlab.mixins import (
11
13
  CreateMixin,
12
14
  CRUDMixin,
@@ -22,6 +24,7 @@ from .access_requests import GroupAccessRequestManager # noqa: F401
22
24
  from .audit_events import GroupAuditEventManager # noqa: F401
23
25
  from .badges import GroupBadgeManager # noqa: F401
24
26
  from .boards import GroupBoardManager # noqa: F401
27
+ from .branches import GroupProtectedBranchManager # noqa: F401
25
28
  from .clusters import GroupClusterManager # noqa: F401
26
29
  from .container_registry import GroupRegistryRepositoryManager # noqa: F401
27
30
  from .custom_attributes import GroupCustomAttributeManager # noqa: F401
@@ -34,6 +37,7 @@ from .invitations import GroupInvitationManager # noqa: F401
34
37
  from .issues import GroupIssueManager # noqa: F401
35
38
  from .iterations import GroupIterationManager # noqa: F401
36
39
  from .labels import GroupLabelManager # noqa: F401
40
+ from .member_roles import GroupMemberRoleManager # noqa: F401
37
41
  from .members import ( # noqa: F401
38
42
  GroupBillableMemberManager,
39
43
  GroupMemberAllManager,
@@ -79,7 +83,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
79
83
  clusters: GroupClusterManager
80
84
  customattributes: GroupCustomAttributeManager
81
85
  deploytokens: GroupDeployTokenManager
82
- descendant_groups: "GroupDescendantGroupManager"
86
+ descendant_groups: GroupDescendantGroupManager
83
87
  epics: GroupEpicManager
84
88
  exports: GroupExportManager
85
89
  hooks: GroupHookManager
@@ -89,7 +93,8 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
89
93
  issues_statistics: GroupIssuesStatisticsManager
90
94
  iterations: GroupIterationManager
91
95
  labels: GroupLabelManager
92
- ldap_group_links: "GroupLDAPGroupLinkManager"
96
+ ldap_group_links: GroupLDAPGroupLinkManager
97
+ member_roles: GroupMemberRoleManager
93
98
  members: GroupMemberManager
94
99
  members_all: GroupMemberAllManager
95
100
  mergerequests: GroupMergeRequestManager
@@ -98,14 +103,15 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
98
103
  packages: GroupPackageManager
99
104
  projects: GroupProjectManager
100
105
  shared_projects: SharedProjectManager
106
+ protectedbranches: GroupProtectedBranchManager
101
107
  pushrules: GroupPushRulesManager
102
108
  registry_repositories: GroupRegistryRepositoryManager
103
109
  runners: GroupRunnerManager
104
- subgroups: "GroupSubgroupManager"
110
+ subgroups: GroupSubgroupManager
105
111
  variables: GroupVariableManager
106
112
  wikis: GroupWikiManager
107
- saml_group_links: "GroupSAMLGroupLinkManager"
108
- service_accounts: "GroupServiceAccountManager"
113
+ saml_group_links: GroupSAMLGroupLinkManager
114
+ service_accounts: GroupServiceAccountManager
109
115
 
110
116
  @cli.register_custom_action(cls_names="Group", required=("project_id",))
111
117
  @exc.on_http_error(exc.GitlabTransferProjectError)
@@ -125,7 +131,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
125
131
 
126
132
  @cli.register_custom_action(cls_names="Group", required=(), optional=("group_id",))
127
133
  @exc.on_http_error(exc.GitlabGroupTransferError)
128
- def transfer(self, group_id: Optional[int] = None, **kwargs: Any) -> None:
134
+ def transfer(self, group_id: int | None = None, **kwargs: Any) -> None:
129
135
  """Transfer the group to a new parent group or make it a top-level group.
130
136
 
131
137
  Requires GitLab ≥14.6.
@@ -149,7 +155,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
149
155
  @exc.on_http_error(exc.GitlabSearchError)
150
156
  def search(
151
157
  self, scope: str, search: str, **kwargs: Any
152
- ) -> Union[gitlab.GitlabList, List[Dict[str, Any]]]:
158
+ ) -> gitlab.GitlabList | list[dict[str, Any]]:
153
159
  """Search the group resources matching the provided string.
154
160
 
155
161
  Args:
@@ -193,7 +199,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
193
199
  self,
194
200
  group_id: int,
195
201
  group_access: int,
196
- expires_at: Optional[str] = None,
202
+ expires_at: str | None = None,
197
203
  **kwargs: Any,
198
204
  ) -> None:
199
205
  """Share the group with a group.
@@ -253,7 +259,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
253
259
  self.manager.gitlab.http_post(path, **kwargs)
254
260
 
255
261
 
256
- class GroupManager(CRUDMixin, RESTManager):
262
+ class GroupManager(CRUDMixin[Group]):
257
263
  _path = "/groups"
258
264
  _obj_cls = Group
259
265
  _list_filters = (
@@ -315,22 +321,19 @@ class GroupManager(CRUDMixin, RESTManager):
315
321
  "extra_shared_runners_minutes_limit",
316
322
  "prevent_forking_outside_group",
317
323
  "shared_runners_setting",
318
- ),
324
+ )
319
325
  )
320
326
  _types = {"avatar": types.ImageAttribute, "skip_groups": types.ArrayAttribute}
321
327
 
322
- def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> Group:
323
- return cast(Group, super().get(id=id, lazy=lazy, **kwargs))
324
-
325
328
  @exc.on_http_error(exc.GitlabImportError)
326
329
  def import_group(
327
330
  self,
328
331
  file: BinaryIO,
329
332
  path: str,
330
333
  name: str,
331
- parent_id: Optional[Union[int, str]] = None,
334
+ parent_id: int | str | None = None,
332
335
  **kwargs: Any,
333
- ) -> Union[Dict[str, Any], requests.Response]:
336
+ ) -> dict[str, Any] | requests.Response:
334
337
  """Import a group from an archive file.
335
338
 
336
339
  Args:
@@ -349,7 +352,7 @@ class GroupManager(CRUDMixin, RESTManager):
349
352
  A representation of the import status.
350
353
  """
351
354
  files = {"file": ("file.tar.gz", file, "application/octet-stream")}
352
- data: Dict[str, Any] = {"path": path, "name": name}
355
+ data: dict[str, Any] = {"path": path, "name": name}
353
356
  if parent_id is not None:
354
357
  data["parent_id"] = parent_id
355
358
 
@@ -358,13 +361,7 @@ class GroupManager(CRUDMixin, RESTManager):
358
361
  )
359
362
 
360
363
 
361
- class GroupSubgroup(RESTObject):
362
- pass
363
-
364
-
365
- class GroupSubgroupManager(ListMixin, RESTManager):
366
- _path = "/groups/{group_id}/subgroups"
367
- _obj_cls: Union[Type["GroupDescendantGroup"], Type[GroupSubgroup]] = GroupSubgroup
364
+ class SubgroupBaseManager(ListMixin[TObjCls]):
368
365
  _from_parent_attrs = {"group_id": "id"}
369
366
  _list_filters = (
370
367
  "skip_groups",
@@ -380,24 +377,33 @@ class GroupSubgroupManager(ListMixin, RESTManager):
380
377
  _types = {"skip_groups": types.ArrayAttribute}
381
378
 
382
379
 
380
+ class GroupSubgroup(RESTObject):
381
+ pass
382
+
383
+
384
+ class GroupSubgroupManager(SubgroupBaseManager[GroupSubgroup]):
385
+ _path = "/groups/{group_id}/subgroups"
386
+ _obj_cls = GroupSubgroup
387
+
388
+
383
389
  class GroupDescendantGroup(RESTObject):
384
390
  pass
385
391
 
386
392
 
387
- class GroupDescendantGroupManager(GroupSubgroupManager):
393
+ class GroupDescendantGroupManager(SubgroupBaseManager[GroupDescendantGroup]):
388
394
  """
389
395
  This manager inherits from GroupSubgroupManager as descendant groups
390
396
  share all attributes with subgroups, except the path and object class.
391
397
  """
392
398
 
393
399
  _path = "/groups/{group_id}/descendant_groups"
394
- _obj_cls: Type[GroupDescendantGroup] = GroupDescendantGroup
400
+ _obj_cls = GroupDescendantGroup
395
401
 
396
402
 
397
403
  class GroupLDAPGroupLink(RESTObject):
398
404
  _repr_attr = "provider"
399
405
 
400
- def _get_link_attrs(self) -> Dict[str, str]:
406
+ def _get_link_attrs(self) -> dict[str, str]:
401
407
  # https://docs.gitlab.com/ee/api/groups.html#add-ldap-group-link-with-cn-or-filter
402
408
  # https://docs.gitlab.com/ee/api/groups.html#delete-ldap-group-link-with-cn-or-filter
403
409
  # We can tell what attribute to use based on the data returned
@@ -426,9 +432,13 @@ class GroupLDAPGroupLink(RESTObject):
426
432
  )
427
433
 
428
434
 
429
- class GroupLDAPGroupLinkManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
435
+ class GroupLDAPGroupLinkManager(
436
+ ListMixin[GroupLDAPGroupLink],
437
+ CreateMixin[GroupLDAPGroupLink],
438
+ DeleteMixin[GroupLDAPGroupLink],
439
+ ):
430
440
  _path = "/groups/{group_id}/ldap_group_links"
431
- _obj_cls: Type[GroupLDAPGroupLink] = GroupLDAPGroupLink
441
+ _obj_cls = GroupLDAPGroupLink
432
442
  _from_parent_attrs = {"group_id": "id"}
433
443
  _create_attrs = RequiredOptional(
434
444
  required=("provider", "group_access"), exclusive=("cn", "filter")
@@ -440,13 +450,8 @@ class GroupSAMLGroupLink(ObjectDeleteMixin, RESTObject):
440
450
  _repr_attr = "name"
441
451
 
442
452
 
443
- class GroupSAMLGroupLinkManager(NoUpdateMixin, RESTManager):
453
+ class GroupSAMLGroupLinkManager(NoUpdateMixin[GroupSAMLGroupLink]):
444
454
  _path = "/groups/{group_id}/saml_group_links"
445
- _obj_cls: Type[GroupSAMLGroupLink] = GroupSAMLGroupLink
455
+ _obj_cls = GroupSAMLGroupLink
446
456
  _from_parent_attrs = {"group_id": "id"}
447
457
  _create_attrs = RequiredOptional(required=("saml_group_name", "access_level"))
448
-
449
- def get(
450
- self, id: Union[str, int], lazy: bool = False, **kwargs: Any
451
- ) -> GroupSAMLGroupLink:
452
- return cast(GroupSAMLGroupLink, super().get(id=id, lazy=lazy, **kwargs))
@@ -1,7 +1,5 @@
1
- from typing import Any, cast, Union
2
-
3
1
  from gitlab import exceptions as exc
4
- from gitlab.base import RESTManager, RESTObject
2
+ from gitlab.base import RESTObject
5
3
  from gitlab.mixins import CRUDMixin, NoUpdateMixin, ObjectDeleteMixin, SaveMixin
6
4
  from gitlab.types import RequiredOptional
7
5
 
@@ -20,14 +18,11 @@ class Hook(ObjectDeleteMixin, RESTObject):
20
18
  _repr_attr = "url"
21
19
 
22
20
 
23
- class HookManager(NoUpdateMixin, RESTManager):
21
+ class HookManager(NoUpdateMixin[Hook]):
24
22
  _path = "/hooks"
25
23
  _obj_cls = Hook
26
24
  _create_attrs = RequiredOptional(required=("url",))
27
25
 
28
- def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> Hook:
29
- return cast(Hook, super().get(id=id, lazy=lazy, **kwargs))
30
-
31
26
 
32
27
  class ProjectHook(SaveMixin, ObjectDeleteMixin, RESTObject):
33
28
  _repr_attr = "url"
@@ -47,7 +42,7 @@ class ProjectHook(SaveMixin, ObjectDeleteMixin, RESTObject):
47
42
  self.manager.gitlab.http_post(path)
48
43
 
49
44
 
50
- class ProjectHookManager(CRUDMixin, RESTManager):
45
+ class ProjectHookManager(CRUDMixin[ProjectHook]):
51
46
  _path = "/projects/{project_id}/hooks"
52
47
  _obj_cls = ProjectHook
53
48
  _from_parent_attrs = {"project_id": "id"}
@@ -84,11 +79,6 @@ class ProjectHookManager(CRUDMixin, RESTManager):
84
79
  ),
85
80
  )
86
81
 
87
- def get(
88
- self, id: Union[str, int], lazy: bool = False, **kwargs: Any
89
- ) -> ProjectHook:
90
- return cast(ProjectHook, super().get(id=id, lazy=lazy, **kwargs))
91
-
92
82
 
93
83
  class GroupHook(SaveMixin, ObjectDeleteMixin, RESTObject):
94
84
  _repr_attr = "url"
@@ -108,7 +98,7 @@ class GroupHook(SaveMixin, ObjectDeleteMixin, RESTObject):
108
98
  self.manager.gitlab.http_post(path)
109
99
 
110
100
 
111
- class GroupHookManager(CRUDMixin, RESTManager):
101
+ class GroupHookManager(CRUDMixin[GroupHook]):
112
102
  _path = "/groups/{group_id}/hooks"
113
103
  _obj_cls = GroupHook
114
104
  _from_parent_attrs = {"group_id": "id"}
@@ -152,6 +142,3 @@ class GroupHookManager(CRUDMixin, RESTManager):
152
142
  "token",
153
143
  ),
154
144
  )
155
-
156
- def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> GroupHook:
157
- return cast(GroupHook, super().get(id=id, lazy=lazy, **kwargs))
@@ -3,10 +3,10 @@ GitLab API:
3
3
  https://docs.gitlab.com/ee/api/integrations.html
4
4
  """
5
5
 
6
- from typing import Any, cast, List, Union
6
+ from typing import List
7
7
 
8
8
  from gitlab import cli
9
- from gitlab.base import RESTManager, RESTObject
9
+ from gitlab.base import RESTObject
10
10
  from gitlab.mixins import (
11
11
  DeleteMixin,
12
12
  GetMixin,
@@ -29,7 +29,10 @@ class ProjectIntegration(SaveMixin, ObjectDeleteMixin, RESTObject):
29
29
 
30
30
 
31
31
  class ProjectIntegrationManager(
32
- GetMixin, UpdateMixin, DeleteMixin, ListMixin, RESTManager
32
+ GetMixin[ProjectIntegration],
33
+ UpdateMixin[ProjectIntegration],
34
+ DeleteMixin[ProjectIntegration],
35
+ ListMixin[ProjectIntegration],
33
36
  ):
34
37
  _path = "/projects/{project_id}/integrations"
35
38
  _from_parent_attrs = {"project_id": "id"}
@@ -149,11 +152,7 @@ class ProjectIntegrationManager(
149
152
  ),
150
153
  ),
151
154
  "jira": (
152
- (
153
- "url",
154
- "username",
155
- "password",
156
- ),
155
+ ("url", "username", "password"),
157
156
  (
158
157
  "api_url",
159
158
  "active",
@@ -265,11 +264,6 @@ class ProjectIntegrationManager(
265
264
  "youtrack": (("issues_url", "project_url"), ("description", "push_events")),
266
265
  }
267
266
 
268
- def get(
269
- self, id: Union[str, int], lazy: bool = False, **kwargs: Any
270
- ) -> ProjectIntegration:
271
- return cast(ProjectIntegration, super().get(id=id, lazy=lazy, **kwargs))
272
-
273
267
  @cli.register_custom_action(
274
268
  cls_names=("ProjectIntegrationManager", "ProjectServiceManager")
275
269
  )
@@ -288,8 +282,3 @@ class ProjectService(ProjectIntegration):
288
282
 
289
283
  class ProjectServiceManager(ProjectIntegrationManager):
290
284
  _obj_cls = ProjectService
291
-
292
- def get(
293
- self, id: Union[str, int], lazy: bool = False, **kwargs: Any
294
- ) -> ProjectService:
295
- return cast(ProjectService, super().get(id=id, lazy=lazy, **kwargs))
@@ -1,6 +1,8 @@
1
- from typing import Any, cast, Union
1
+ from __future__ import annotations
2
2
 
3
- from gitlab.base import RESTManager, RESTObject
3
+ from typing import Any
4
+
5
+ from gitlab.base import RESTObject, TObjCls
4
6
  from gitlab.exceptions import GitlabInvitationError
5
7
  from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin
6
8
  from gitlab.types import ArrayAttribute, CommaSeparatedListAttribute, RequiredOptional
@@ -13,9 +15,10 @@ __all__ = [
13
15
  ]
14
16
 
15
17
 
16
- class InvitationMixin(CRUDMixin):
17
- def create(self, *args: Any, **kwargs: Any) -> RESTObject:
18
- invitation = super().create(*args, **kwargs)
18
+ class InvitationMixin(CRUDMixin[TObjCls]):
19
+ # pylint: disable=abstract-method
20
+ def create(self, data: dict[str, Any] | None = None, **kwargs: Any) -> TObjCls:
21
+ invitation = super().create(data, **kwargs)
19
22
 
20
23
  if invitation.status == "error":
21
24
  raise GitlabInvitationError(invitation.message)
@@ -27,7 +30,7 @@ class ProjectInvitation(SaveMixin, ObjectDeleteMixin, RESTObject):
27
30
  _id_attr = "email"
28
31
 
29
32
 
30
- class ProjectInvitationManager(InvitationMixin, RESTManager):
33
+ class ProjectInvitationManager(InvitationMixin[ProjectInvitation]):
31
34
  _path = "/projects/{project_id}/invitations"
32
35
  _obj_cls = ProjectInvitation
33
36
  _from_parent_attrs = {"project_id": "id"}
@@ -41,9 +44,7 @@ class ProjectInvitationManager(InvitationMixin, RESTManager):
41
44
  ),
42
45
  exclusive=("email", "user_id"),
43
46
  )
44
- _update_attrs = RequiredOptional(
45
- optional=("access_level", "expires_at"),
46
- )
47
+ _update_attrs = RequiredOptional(optional=("access_level", "expires_at"))
47
48
  _list_filters = ("query",)
48
49
  _types = {
49
50
  "email": CommaSeparatedListAttribute,
@@ -51,17 +52,12 @@ class ProjectInvitationManager(InvitationMixin, RESTManager):
51
52
  "tasks_to_be_done": ArrayAttribute,
52
53
  }
53
54
 
54
- def get(
55
- self, id: Union[str, int], lazy: bool = False, **kwargs: Any
56
- ) -> ProjectInvitation:
57
- return cast(ProjectInvitation, super().get(id=id, lazy=lazy, **kwargs))
58
-
59
55
 
60
56
  class GroupInvitation(SaveMixin, ObjectDeleteMixin, RESTObject):
61
57
  _id_attr = "email"
62
58
 
63
59
 
64
- class GroupInvitationManager(InvitationMixin, RESTManager):
60
+ class GroupInvitationManager(InvitationMixin[GroupInvitation]):
65
61
  _path = "/groups/{group_id}/invitations"
66
62
  _obj_cls = GroupInvitation
67
63
  _from_parent_attrs = {"group_id": "id"}
@@ -75,17 +71,10 @@ class GroupInvitationManager(InvitationMixin, RESTManager):
75
71
  ),
76
72
  exclusive=("email", "user_id"),
77
73
  )
78
- _update_attrs = RequiredOptional(
79
- optional=("access_level", "expires_at"),
80
- )
74
+ _update_attrs = RequiredOptional(optional=("access_level", "expires_at"))
81
75
  _list_filters = ("query",)
82
76
  _types = {
83
77
  "email": CommaSeparatedListAttribute,
84
78
  "user_id": CommaSeparatedListAttribute,
85
79
  "tasks_to_be_done": ArrayAttribute,
86
80
  }
87
-
88
- def get(
89
- self, id: Union[str, int], lazy: bool = False, **kwargs: Any
90
- ) -> GroupInvitation:
91
- return cast(GroupInvitation, super().get(id=id, lazy=lazy, **kwargs))
@@ -1,11 +1,13 @@
1
- from typing import Any, cast, Dict, List, Optional, Tuple, TYPE_CHECKING, Union
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, TYPE_CHECKING
2
4
 
3
5
  import requests
4
6
 
5
7
  from gitlab import cli, client
6
8
  from gitlab import exceptions as exc
7
9
  from gitlab import types
8
- from gitlab.base import RESTManager, RESTObject
10
+ from gitlab.base import RESTObject
9
11
  from gitlab.mixins import (
10
12
  CreateMixin,
11
13
  CRUDMixin,
@@ -50,7 +52,7 @@ class Issue(RESTObject):
50
52
  _repr_attr = "title"
51
53
 
52
54
 
53
- class IssueManager(RetrieveMixin, RESTManager):
55
+ class IssueManager(RetrieveMixin[Issue]):
54
56
  _path = "/issues"
55
57
  _obj_cls = Issue
56
58
  _list_filters = (
@@ -73,15 +75,12 @@ class IssueManager(RetrieveMixin, RESTManager):
73
75
  )
74
76
  _types = {"iids": types.ArrayAttribute, "labels": types.CommaSeparatedListAttribute}
75
77
 
76
- def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> Issue:
77
- return cast(Issue, super().get(id=id, lazy=lazy, **kwargs))
78
-
79
78
 
80
79
  class GroupIssue(RESTObject):
81
80
  pass
82
81
 
83
82
 
84
- class GroupIssueManager(ListMixin, RESTManager):
83
+ class GroupIssueManager(ListMixin[GroupIssue]):
85
84
  _path = "/groups/{group_id}/issues"
86
85
  _obj_cls = GroupIssue
87
86
  _from_parent_attrs = {"group_id": "id"}
@@ -120,7 +119,7 @@ class ProjectIssue(
120
119
 
121
120
  awardemojis: ProjectIssueAwardEmojiManager
122
121
  discussions: ProjectIssueDiscussionManager
123
- links: "ProjectIssueLinkManager"
122
+ links: ProjectIssueLinkManager
124
123
  notes: ProjectIssueNoteManager
125
124
  resourcelabelevents: ProjectIssueResourceLabelEventManager
126
125
  resourcemilestoneevents: ProjectIssueResourceMilestoneEventManager
@@ -154,8 +153,8 @@ class ProjectIssue(
154
153
  @exc.on_http_error(exc.GitlabUpdateError)
155
154
  def reorder(
156
155
  self,
157
- move_after_id: Optional[int] = None,
158
- move_before_id: Optional[int] = None,
156
+ move_after_id: int | None = None,
157
+ move_before_id: int | None = None,
159
158
  **kwargs: Any,
160
159
  ) -> None:
161
160
  """Reorder an issue on a board.
@@ -170,7 +169,7 @@ class ProjectIssue(
170
169
  GitlabUpdateError: If the issue could not be reordered
171
170
  """
172
171
  path = f"{self.manager.path}/{self.encoded_id}/reorder"
173
- data: Dict[str, Any] = {}
172
+ data: dict[str, Any] = {}
174
173
 
175
174
  if move_after_id is not None:
176
175
  data["move_after_id"] = move_after_id
@@ -186,7 +185,7 @@ class ProjectIssue(
186
185
  @exc.on_http_error(exc.GitlabGetError)
187
186
  def related_merge_requests(
188
187
  self, **kwargs: Any
189
- ) -> Union[client.GitlabList, List[Dict[str, Any]]]:
188
+ ) -> client.GitlabList | list[dict[str, Any]]:
190
189
  """List merge requests related to the issue.
191
190
 
192
191
  Args:
@@ -207,9 +206,7 @@ class ProjectIssue(
207
206
 
208
207
  @cli.register_custom_action(cls_names="ProjectIssue")
209
208
  @exc.on_http_error(exc.GitlabGetError)
210
- def closed_by(
211
- self, **kwargs: Any
212
- ) -> Union[client.GitlabList, List[Dict[str, Any]]]:
209
+ def closed_by(self, **kwargs: Any) -> client.GitlabList | list[dict[str, Any]]:
213
210
  """List merge requests that will close the issue when merged.
214
211
 
215
212
  Args:
@@ -229,7 +226,7 @@ class ProjectIssue(
229
226
  return result
230
227
 
231
228
 
232
- class ProjectIssueManager(CRUDMixin, RESTManager):
229
+ class ProjectIssueManager(CRUDMixin[ProjectIssue]):
233
230
  _path = "/projects/{project_id}/issues"
234
231
  _obj_cls = ProjectIssue
235
232
  _from_parent_attrs = {"project_id": "id"}
@@ -279,21 +276,20 @@ class ProjectIssueManager(CRUDMixin, RESTManager):
279
276
  "updated_at",
280
277
  "due_date",
281
278
  "discussion_locked",
282
- ),
279
+ )
283
280
  )
284
281
  _types = {"iids": types.ArrayAttribute, "labels": types.CommaSeparatedListAttribute}
285
282
 
286
- def get(
287
- self, id: Union[str, int], lazy: bool = False, **kwargs: Any
288
- ) -> ProjectIssue:
289
- return cast(ProjectIssue, super().get(id=id, lazy=lazy, **kwargs))
290
-
291
283
 
292
284
  class ProjectIssueLink(ObjectDeleteMixin, RESTObject):
293
285
  _id_attr = "issue_link_id"
294
286
 
295
287
 
296
- class ProjectIssueLinkManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
288
+ class ProjectIssueLinkManager(
289
+ ListMixin[ProjectIssueLink],
290
+ CreateMixin[ProjectIssueLink],
291
+ DeleteMixin[ProjectIssueLink],
292
+ ):
297
293
  _path = "/projects/{project_id}/issues/{issue_iid}/links"
298
294
  _obj_cls = ProjectIssueLink
299
295
  _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
@@ -303,8 +299,8 @@ class ProjectIssueLinkManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
303
299
  # NOTE(jlvillal): Signature doesn't match CreateMixin.create() so ignore
304
300
  # type error
305
301
  def create( # type: ignore[override]
306
- self, data: Dict[str, Any], **kwargs: Any
307
- ) -> Tuple[RESTObject, RESTObject]:
302
+ self, data: dict[str, Any], **kwargs: Any
303
+ ) -> tuple[ProjectIssue, ProjectIssue]:
308
304
  """Create a new object.
309
305
 
310
306
  Args:
@@ -320,8 +316,6 @@ class ProjectIssueLinkManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
320
316
  GitlabCreateError: If the server cannot perform the request
321
317
  """
322
318
  self._create_attrs.validate_attrs(data=data)
323
- if TYPE_CHECKING:
324
- assert self.path is not None
325
319
  server_data = self.gitlab.http_post(self.path, post_data=data, **kwargs)
326
320
  if TYPE_CHECKING:
327
321
  assert isinstance(server_data, dict)
@@ -1,19 +1,15 @@
1
1
  from gitlab import types
2
- from gitlab.base import RESTManager, RESTObject
2
+ from gitlab.base import RESTObject
3
3
  from gitlab.mixins import ListMixin
4
4
 
5
- __all__ = [
6
- "ProjectIterationManager",
7
- "GroupIteration",
8
- "GroupIterationManager",
9
- ]
5
+ __all__ = ["ProjectIterationManager", "GroupIteration", "GroupIterationManager"]
10
6
 
11
7
 
12
8
  class GroupIteration(RESTObject):
13
9
  _repr_attr = "title"
14
10
 
15
11
 
16
- class GroupIterationManager(ListMixin, RESTManager):
12
+ class GroupIterationManager(ListMixin[GroupIteration]):
17
13
  _path = "/groups/{group_id}/iterations"
18
14
  _obj_cls = GroupIteration
19
15
  _from_parent_attrs = {"group_id": "id"}
@@ -33,7 +29,7 @@ class GroupIterationManager(ListMixin, RESTManager):
33
29
  _types = {"in": types.ArrayAttribute}
34
30
 
35
31
 
36
- class ProjectIterationManager(ListMixin, RESTManager):
32
+ class ProjectIterationManager(ListMixin[GroupIteration]):
37
33
  _path = "/projects/{project_id}/iterations"
38
34
  _obj_cls = GroupIteration
39
35
  _from_parent_attrs = {"project_id": "id"}