python-gitlab 5.6.0__py3-none-any.whl → 6.0.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 (102) 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 +14 -24
  7. gitlab/client.py +114 -140
  8. gitlab/config.py +16 -17
  9. gitlab/exceptions.py +7 -5
  10. gitlab/mixins.py +154 -238
  11. gitlab/types.py +13 -14
  12. gitlab/utils.py +32 -43
  13. gitlab/v4/cli.py +50 -53
  14. gitlab/v4/objects/__init__.py +1 -0
  15. gitlab/v4/objects/access_requests.py +11 -3
  16. gitlab/v4/objects/appearance.py +12 -14
  17. gitlab/v4/objects/applications.py +5 -6
  18. gitlab/v4/objects/artifacts.py +10 -17
  19. gitlab/v4/objects/audit_events.py +4 -19
  20. gitlab/v4/objects/award_emojis.py +13 -57
  21. gitlab/v4/objects/badges.py +4 -19
  22. gitlab/v4/objects/boards.py +7 -27
  23. gitlab/v4/objects/branches.py +3 -15
  24. gitlab/v4/objects/broadcast_messages.py +3 -13
  25. gitlab/v4/objects/bulk_imports.py +6 -14
  26. gitlab/v4/objects/ci_lint.py +7 -13
  27. gitlab/v4/objects/cluster_agents.py +3 -13
  28. gitlab/v4/objects/clusters.py +13 -23
  29. gitlab/v4/objects/commits.py +23 -28
  30. gitlab/v4/objects/container_registry.py +13 -19
  31. gitlab/v4/objects/custom_attributes.py +16 -21
  32. gitlab/v4/objects/deploy_keys.py +22 -19
  33. gitlab/v4/objects/deploy_tokens.py +14 -32
  34. gitlab/v4/objects/deployments.py +13 -15
  35. gitlab/v4/objects/discussions.py +13 -29
  36. gitlab/v4/objects/draft_notes.py +4 -14
  37. gitlab/v4/objects/environments.py +13 -21
  38. gitlab/v4/objects/epics.py +14 -17
  39. gitlab/v4/objects/events.py +27 -79
  40. gitlab/v4/objects/export_import.py +7 -19
  41. gitlab/v4/objects/features.py +11 -12
  42. gitlab/v4/objects/files.py +23 -38
  43. gitlab/v4/objects/geo_nodes.py +7 -11
  44. gitlab/v4/objects/group_access_tokens.py +6 -13
  45. gitlab/v4/objects/groups.py +40 -37
  46. gitlab/v4/objects/hooks.py +4 -17
  47. gitlab/v4/objects/integrations.py +7 -18
  48. gitlab/v4/objects/invitations.py +12 -23
  49. gitlab/v4/objects/issues.py +21 -27
  50. gitlab/v4/objects/iterations.py +4 -8
  51. gitlab/v4/objects/job_token_scope.py +18 -14
  52. gitlab/v4/objects/jobs.py +17 -32
  53. gitlab/v4/objects/keys.py +8 -11
  54. gitlab/v4/objects/labels.py +19 -30
  55. gitlab/v4/objects/ldap.py +25 -9
  56. gitlab/v4/objects/member_roles.py +102 -0
  57. gitlab/v4/objects/members.py +11 -29
  58. gitlab/v4/objects/merge_request_approvals.py +31 -44
  59. gitlab/v4/objects/merge_requests.py +30 -40
  60. gitlab/v4/objects/merge_trains.py +3 -6
  61. gitlab/v4/objects/milestones.py +23 -29
  62. gitlab/v4/objects/namespaces.py +4 -10
  63. gitlab/v4/objects/notes.py +26 -69
  64. gitlab/v4/objects/notification_settings.py +5 -14
  65. gitlab/v4/objects/package_protection_rules.py +8 -8
  66. gitlab/v4/objects/packages.py +22 -37
  67. gitlab/v4/objects/pages.py +8 -14
  68. gitlab/v4/objects/personal_access_tokens.py +7 -10
  69. gitlab/v4/objects/pipelines.py +38 -47
  70. gitlab/v4/objects/project_access_tokens.py +6 -13
  71. gitlab/v4/objects/projects.py +54 -76
  72. gitlab/v4/objects/push_rules.py +13 -15
  73. gitlab/v4/objects/registry_protection_repository_rules.py +6 -7
  74. gitlab/v4/objects/registry_protection_rules.py +7 -11
  75. gitlab/v4/objects/releases.py +6 -20
  76. gitlab/v4/objects/repositories.py +25 -34
  77. gitlab/v4/objects/resource_groups.py +10 -15
  78. gitlab/v4/objects/reviewers.py +4 -2
  79. gitlab/v4/objects/runners.py +14 -13
  80. gitlab/v4/objects/secure_files.py +8 -21
  81. gitlab/v4/objects/service_accounts.py +7 -5
  82. gitlab/v4/objects/settings.py +13 -14
  83. gitlab/v4/objects/sidekiq.py +17 -18
  84. gitlab/v4/objects/snippets.py +78 -66
  85. gitlab/v4/objects/statistics.py +8 -23
  86. gitlab/v4/objects/status_checks.py +6 -3
  87. gitlab/v4/objects/tags.py +3 -13
  88. gitlab/v4/objects/templates.py +11 -59
  89. gitlab/v4/objects/todos.py +3 -6
  90. gitlab/v4/objects/topics.py +10 -21
  91. gitlab/v4/objects/triggers.py +3 -13
  92. gitlab/v4/objects/users.py +87 -93
  93. gitlab/v4/objects/variables.py +4 -19
  94. gitlab/v4/objects/wikis.py +4 -19
  95. {python_gitlab-5.6.0.dist-info → python_gitlab-6.0.0.dist-info}/METADATA +3 -2
  96. python_gitlab-6.0.0.dist-info/RECORD +107 -0
  97. {python_gitlab-5.6.0.dist-info → python_gitlab-6.0.0.dist-info}/WHEEL +1 -1
  98. python_gitlab-5.6.0.dist-info/RECORD +0 -106
  99. {python_gitlab-5.6.0.dist-info → python_gitlab-6.0.0.dist-info}/entry_points.txt +0 -0
  100. {python_gitlab-5.6.0.dist-info → python_gitlab-6.0.0.dist-info/licenses}/AUTHORS +0 -0
  101. {python_gitlab-5.6.0.dist-info → python_gitlab-6.0.0.dist-info/licenses}/COPYING +0 -0
  102. {python_gitlab-5.6.0.dist-info → python_gitlab-6.0.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,
@@ -34,6 +36,7 @@ from .invitations import GroupInvitationManager # noqa: F401
34
36
  from .issues import GroupIssueManager # noqa: F401
35
37
  from .iterations import GroupIterationManager # noqa: F401
36
38
  from .labels import GroupLabelManager # noqa: F401
39
+ from .member_roles import GroupMemberRoleManager # noqa: F401
37
40
  from .members import ( # noqa: F401
38
41
  GroupBillableMemberManager,
39
42
  GroupMemberAllManager,
@@ -79,7 +82,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
79
82
  clusters: GroupClusterManager
80
83
  customattributes: GroupCustomAttributeManager
81
84
  deploytokens: GroupDeployTokenManager
82
- descendant_groups: "GroupDescendantGroupManager"
85
+ descendant_groups: GroupDescendantGroupManager
83
86
  epics: GroupEpicManager
84
87
  exports: GroupExportManager
85
88
  hooks: GroupHookManager
@@ -89,7 +92,8 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
89
92
  issues_statistics: GroupIssuesStatisticsManager
90
93
  iterations: GroupIterationManager
91
94
  labels: GroupLabelManager
92
- ldap_group_links: "GroupLDAPGroupLinkManager"
95
+ ldap_group_links: GroupLDAPGroupLinkManager
96
+ member_roles: GroupMemberRoleManager
93
97
  members: GroupMemberManager
94
98
  members_all: GroupMemberAllManager
95
99
  mergerequests: GroupMergeRequestManager
@@ -101,11 +105,11 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
101
105
  pushrules: GroupPushRulesManager
102
106
  registry_repositories: GroupRegistryRepositoryManager
103
107
  runners: GroupRunnerManager
104
- subgroups: "GroupSubgroupManager"
108
+ subgroups: GroupSubgroupManager
105
109
  variables: GroupVariableManager
106
110
  wikis: GroupWikiManager
107
- saml_group_links: "GroupSAMLGroupLinkManager"
108
- service_accounts: "GroupServiceAccountManager"
111
+ saml_group_links: GroupSAMLGroupLinkManager
112
+ service_accounts: GroupServiceAccountManager
109
113
 
110
114
  @cli.register_custom_action(cls_names="Group", required=("project_id",))
111
115
  @exc.on_http_error(exc.GitlabTransferProjectError)
@@ -125,7 +129,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
125
129
 
126
130
  @cli.register_custom_action(cls_names="Group", required=(), optional=("group_id",))
127
131
  @exc.on_http_error(exc.GitlabGroupTransferError)
128
- def transfer(self, group_id: Optional[int] = None, **kwargs: Any) -> None:
132
+ def transfer(self, group_id: int | None = None, **kwargs: Any) -> None:
129
133
  """Transfer the group to a new parent group or make it a top-level group.
130
134
 
131
135
  Requires GitLab ≥14.6.
@@ -149,7 +153,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
149
153
  @exc.on_http_error(exc.GitlabSearchError)
150
154
  def search(
151
155
  self, scope: str, search: str, **kwargs: Any
152
- ) -> Union[gitlab.GitlabList, List[Dict[str, Any]]]:
156
+ ) -> gitlab.GitlabList | list[dict[str, Any]]:
153
157
  """Search the group resources matching the provided string.
154
158
 
155
159
  Args:
@@ -193,7 +197,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
193
197
  self,
194
198
  group_id: int,
195
199
  group_access: int,
196
- expires_at: Optional[str] = None,
200
+ expires_at: str | None = None,
197
201
  **kwargs: Any,
198
202
  ) -> None:
199
203
  """Share the group with a group.
@@ -253,7 +257,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
253
257
  self.manager.gitlab.http_post(path, **kwargs)
254
258
 
255
259
 
256
- class GroupManager(CRUDMixin, RESTManager):
260
+ class GroupManager(CRUDMixin[Group]):
257
261
  _path = "/groups"
258
262
  _obj_cls = Group
259
263
  _list_filters = (
@@ -315,22 +319,19 @@ class GroupManager(CRUDMixin, RESTManager):
315
319
  "extra_shared_runners_minutes_limit",
316
320
  "prevent_forking_outside_group",
317
321
  "shared_runners_setting",
318
- ),
322
+ )
319
323
  )
320
324
  _types = {"avatar": types.ImageAttribute, "skip_groups": types.ArrayAttribute}
321
325
 
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
326
  @exc.on_http_error(exc.GitlabImportError)
326
327
  def import_group(
327
328
  self,
328
329
  file: BinaryIO,
329
330
  path: str,
330
331
  name: str,
331
- parent_id: Optional[Union[int, str]] = None,
332
+ parent_id: int | str | None = None,
332
333
  **kwargs: Any,
333
- ) -> Union[Dict[str, Any], requests.Response]:
334
+ ) -> dict[str, Any] | requests.Response:
334
335
  """Import a group from an archive file.
335
336
 
336
337
  Args:
@@ -349,7 +350,7 @@ class GroupManager(CRUDMixin, RESTManager):
349
350
  A representation of the import status.
350
351
  """
351
352
  files = {"file": ("file.tar.gz", file, "application/octet-stream")}
352
- data: Dict[str, Any] = {"path": path, "name": name}
353
+ data: dict[str, Any] = {"path": path, "name": name}
353
354
  if parent_id is not None:
354
355
  data["parent_id"] = parent_id
355
356
 
@@ -358,13 +359,7 @@ class GroupManager(CRUDMixin, RESTManager):
358
359
  )
359
360
 
360
361
 
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
362
+ class SubgroupBaseManager(ListMixin[TObjCls]):
368
363
  _from_parent_attrs = {"group_id": "id"}
369
364
  _list_filters = (
370
365
  "skip_groups",
@@ -380,24 +375,33 @@ class GroupSubgroupManager(ListMixin, RESTManager):
380
375
  _types = {"skip_groups": types.ArrayAttribute}
381
376
 
382
377
 
378
+ class GroupSubgroup(RESTObject):
379
+ pass
380
+
381
+
382
+ class GroupSubgroupManager(SubgroupBaseManager[GroupSubgroup]):
383
+ _path = "/groups/{group_id}/subgroups"
384
+ _obj_cls = GroupSubgroup
385
+
386
+
383
387
  class GroupDescendantGroup(RESTObject):
384
388
  pass
385
389
 
386
390
 
387
- class GroupDescendantGroupManager(GroupSubgroupManager):
391
+ class GroupDescendantGroupManager(SubgroupBaseManager[GroupDescendantGroup]):
388
392
  """
389
393
  This manager inherits from GroupSubgroupManager as descendant groups
390
394
  share all attributes with subgroups, except the path and object class.
391
395
  """
392
396
 
393
397
  _path = "/groups/{group_id}/descendant_groups"
394
- _obj_cls: Type[GroupDescendantGroup] = GroupDescendantGroup
398
+ _obj_cls = GroupDescendantGroup
395
399
 
396
400
 
397
401
  class GroupLDAPGroupLink(RESTObject):
398
402
  _repr_attr = "provider"
399
403
 
400
- def _get_link_attrs(self) -> Dict[str, str]:
404
+ def _get_link_attrs(self) -> dict[str, str]:
401
405
  # https://docs.gitlab.com/ee/api/groups.html#add-ldap-group-link-with-cn-or-filter
402
406
  # https://docs.gitlab.com/ee/api/groups.html#delete-ldap-group-link-with-cn-or-filter
403
407
  # We can tell what attribute to use based on the data returned
@@ -426,9 +430,13 @@ class GroupLDAPGroupLink(RESTObject):
426
430
  )
427
431
 
428
432
 
429
- class GroupLDAPGroupLinkManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
433
+ class GroupLDAPGroupLinkManager(
434
+ ListMixin[GroupLDAPGroupLink],
435
+ CreateMixin[GroupLDAPGroupLink],
436
+ DeleteMixin[GroupLDAPGroupLink],
437
+ ):
430
438
  _path = "/groups/{group_id}/ldap_group_links"
431
- _obj_cls: Type[GroupLDAPGroupLink] = GroupLDAPGroupLink
439
+ _obj_cls = GroupLDAPGroupLink
432
440
  _from_parent_attrs = {"group_id": "id"}
433
441
  _create_attrs = RequiredOptional(
434
442
  required=("provider", "group_access"), exclusive=("cn", "filter")
@@ -440,13 +448,8 @@ class GroupSAMLGroupLink(ObjectDeleteMixin, RESTObject):
440
448
  _repr_attr = "name"
441
449
 
442
450
 
443
- class GroupSAMLGroupLinkManager(NoUpdateMixin, RESTManager):
451
+ class GroupSAMLGroupLinkManager(NoUpdateMixin[GroupSAMLGroupLink]):
444
452
  _path = "/groups/{group_id}/saml_group_links"
445
- _obj_cls: Type[GroupSAMLGroupLink] = GroupSAMLGroupLink
453
+ _obj_cls = GroupSAMLGroupLink
446
454
  _from_parent_attrs = {"group_id": "id"}
447
455
  _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"}