summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gitlab/v4/objects/audit_events.py15
-rw-r--r--gitlab/v4/objects/award_emojis.py38
-rw-r--r--gitlab/v4/objects/badges.py3
-rw-r--r--gitlab/v4/objects/branches.py12
-rw-r--r--gitlab/v4/objects/clusters.py12
-rw-r--r--gitlab/v4/objects/commits.py5
-rw-r--r--gitlab/v4/objects/container_registry.py7
-rw-r--r--gitlab/v4/objects/custom_attributes.py17
-rw-r--r--gitlab/v4/objects/deployments.py7
-rw-r--r--gitlab/v4/objects/discussions.py24
-rw-r--r--gitlab/v4/objects/environments.py7
-rw-r--r--gitlab/v4/objects/events.py54
-rw-r--r--gitlab/v4/objects/hooks.py13
-rw-r--r--gitlab/v4/objects/members.py22
-rw-r--r--gitlab/v4/objects/merge_requests.py5
-rw-r--r--gitlab/v4/objects/notes.py48
-rw-r--r--gitlab/v4/objects/packages.py7
-rw-r--r--gitlab/v4/objects/tags.py10
-rw-r--r--gitlab/v4/objects/templates.py16
-rw-r--r--gitlab/v4/objects/users.py31
-rw-r--r--pyproject.toml1
-rw-r--r--tests/meta/__init__.py0
-rw-r--r--tests/meta/test_ensure_type_hints.py85
-rw-r--r--tests/meta/test_mro.py (renamed from tests/unit/objects/test_mro.py)0
-rw-r--r--tox.ini4
25 files changed, 437 insertions, 6 deletions
diff --git a/gitlab/v4/objects/audit_events.py b/gitlab/v4/objects/audit_events.py
index ab632bb..649dc9d 100644
--- a/gitlab/v4/objects/audit_events.py
+++ b/gitlab/v4/objects/audit_events.py
@@ -2,6 +2,8 @@
GitLab API:
https://docs.gitlab.com/ee/api/audit_events.html
"""
+from typing import Any, cast, Union
+
from gitlab.base import RESTManager, RESTObject
from gitlab.mixins import RetrieveMixin
@@ -26,6 +28,9 @@ class AuditEventManager(RetrieveMixin, RESTManager):
_obj_cls = AuditEvent
_list_filters = ("created_after", "created_before", "entity_type", "entity_id")
+ def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> AuditEvent:
+ return cast(AuditEvent, super().get(id=id, lazy=lazy, **kwargs))
+
class GroupAuditEvent(RESTObject):
_id_attr = "id"
@@ -37,6 +42,11 @@ class GroupAuditEventManager(RetrieveMixin, RESTManager):
_from_parent_attrs = {"group_id": "id"}
_list_filters = ("created_after", "created_before")
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> GroupAuditEvent:
+ return cast(GroupAuditEvent, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectAuditEvent(RESTObject):
_id_attr = "id"
@@ -48,6 +58,11 @@ class ProjectAuditEventManager(RetrieveMixin, RESTManager):
_from_parent_attrs = {"project_id": "id"}
_list_filters = ("created_after", "created_before")
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectAuditEvent:
+ return cast(ProjectAuditEvent, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectAudit(ProjectAuditEvent):
pass
diff --git a/gitlab/v4/objects/award_emojis.py b/gitlab/v4/objects/award_emojis.py
index 41b2d7d..e4ad370 100644
--- a/gitlab/v4/objects/award_emojis.py
+++ b/gitlab/v4/objects/award_emojis.py
@@ -1,3 +1,5 @@
+from typing import Any, cast, Union
+
from gitlab.base import RequiredOptional, RESTManager, RESTObject
from gitlab.mixins import NoUpdateMixin, ObjectDeleteMixin
@@ -27,6 +29,11 @@ class ProjectIssueAwardEmojiManager(NoUpdateMixin, RESTManager):
_from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
_create_attrs = RequiredOptional(required=("name",))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectIssueAwardEmoji:
+ return cast(ProjectIssueAwardEmoji, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectIssueNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
pass
@@ -42,6 +49,11 @@ class ProjectIssueNoteAwardEmojiManager(NoUpdateMixin, RESTManager):
}
_create_attrs = RequiredOptional(required=("name",))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectIssueNoteAwardEmoji:
+ return cast(ProjectIssueNoteAwardEmoji, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectMergeRequestAwardEmoji(ObjectDeleteMixin, RESTObject):
pass
@@ -53,6 +65,13 @@ class ProjectMergeRequestAwardEmojiManager(NoUpdateMixin, RESTManager):
_from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
_create_attrs = RequiredOptional(required=("name",))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectMergeRequestAwardEmoji:
+ return cast(
+ ProjectMergeRequestAwardEmoji, super().get(id=id, lazy=lazy, **kwargs)
+ )
+
class ProjectMergeRequestNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
pass
@@ -68,6 +87,13 @@ class ProjectMergeRequestNoteAwardEmojiManager(NoUpdateMixin, RESTManager):
}
_create_attrs = RequiredOptional(required=("name",))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectMergeRequestNoteAwardEmoji:
+ return cast(
+ ProjectMergeRequestNoteAwardEmoji, super().get(id=id, lazy=lazy, **kwargs)
+ )
+
class ProjectSnippetAwardEmoji(ObjectDeleteMixin, RESTObject):
pass
@@ -79,6 +105,11 @@ class ProjectSnippetAwardEmojiManager(NoUpdateMixin, RESTManager):
_from_parent_attrs = {"project_id": "project_id", "snippet_id": "id"}
_create_attrs = RequiredOptional(required=("name",))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectSnippetAwardEmoji:
+ return cast(ProjectSnippetAwardEmoji, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectSnippetNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
pass
@@ -93,3 +124,10 @@ class ProjectSnippetNoteAwardEmojiManager(NoUpdateMixin, RESTManager):
"note_id": "id",
}
_create_attrs = RequiredOptional(required=("name",))
+
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectSnippetNoteAwardEmoji:
+ return cast(
+ ProjectSnippetNoteAwardEmoji, super().get(id=id, lazy=lazy, **kwargs)
+ )
diff --git a/gitlab/v4/objects/badges.py b/gitlab/v4/objects/badges.py
index dd3ea49..4dee75a 100644
--- a/gitlab/v4/objects/badges.py
+++ b/gitlab/v4/objects/badges.py
@@ -22,6 +22,9 @@ class GroupBadgeManager(BadgeRenderMixin, CRUDMixin, RESTManager):
_create_attrs = RequiredOptional(required=("link_url", "image_url"))
_update_attrs = RequiredOptional(optional=("link_url", "image_url"))
+ def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> GroupBadge:
+ return cast(GroupBadge, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectBadge(SaveMixin, ObjectDeleteMixin, RESTObject):
pass
diff --git a/gitlab/v4/objects/branches.py b/gitlab/v4/objects/branches.py
index 407765c..d06d6b4 100644
--- a/gitlab/v4/objects/branches.py
+++ b/gitlab/v4/objects/branches.py
@@ -1,3 +1,5 @@
+from typing import Any, cast, Union
+
from gitlab.base import RequiredOptional, RESTManager, RESTObject
from gitlab.mixins import NoUpdateMixin, ObjectDeleteMixin
@@ -19,6 +21,11 @@ class ProjectBranchManager(NoUpdateMixin, RESTManager):
_from_parent_attrs = {"project_id": "id"}
_create_attrs = RequiredOptional(required=("branch", "ref"))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectBranch:
+ return cast(ProjectBranch, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectProtectedBranch(ObjectDeleteMixin, RESTObject):
_id_attr = "name"
@@ -40,3 +47,8 @@ class ProjectProtectedBranchManager(NoUpdateMixin, RESTManager):
"code_owner_approval_required",
),
)
+
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectProtectedBranch:
+ return cast(ProjectProtectedBranch, super().get(id=id, lazy=lazy, **kwargs))
diff --git a/gitlab/v4/objects/clusters.py b/gitlab/v4/objects/clusters.py
index 4821b70..5491654 100644
--- a/gitlab/v4/objects/clusters.py
+++ b/gitlab/v4/objects/clusters.py
@@ -1,4 +1,4 @@
-from typing import Any, cast, Dict, Optional
+from typing import Any, cast, Dict, Optional, Union
from gitlab import exceptions as exc
from gitlab.base import RequiredOptional, RESTManager, RESTObject
@@ -57,6 +57,11 @@ class GroupClusterManager(CRUDMixin, RESTManager):
path = f"{self.path}/user"
return cast(GroupCluster, CreateMixin.create(self, data, path=path, **kwargs))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> GroupCluster:
+ return cast(GroupCluster, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectCluster(SaveMixin, ObjectDeleteMixin, RESTObject):
pass
@@ -102,3 +107,8 @@ class ProjectClusterManager(CRUDMixin, RESTManager):
"""
path = f"{self.path}/user"
return cast(ProjectCluster, CreateMixin.create(self, data, path=path, **kwargs))
+
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectCluster:
+ return cast(ProjectCluster, super().get(id=id, lazy=lazy, **kwargs))
diff --git a/gitlab/v4/objects/commits.py b/gitlab/v4/objects/commits.py
index 3301824..b93dcdf 100644
--- a/gitlab/v4/objects/commits.py
+++ b/gitlab/v4/objects/commits.py
@@ -151,6 +151,11 @@ class ProjectCommitManager(RetrieveMixin, CreateMixin, RESTManager):
optional=("author_email", "author_name"),
)
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectCommit:
+ return cast(ProjectCommit, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectCommitComment(RESTObject):
_id_attr = None
diff --git a/gitlab/v4/objects/container_registry.py b/gitlab/v4/objects/container_registry.py
index caf8f52..892574a 100644
--- a/gitlab/v4/objects/container_registry.py
+++ b/gitlab/v4/objects/container_registry.py
@@ -1,4 +1,4 @@
-from typing import Any, TYPE_CHECKING
+from typing import Any, cast, TYPE_CHECKING, Union
from gitlab import cli
from gitlab import exceptions as exc
@@ -60,3 +60,8 @@ class ProjectRegistryTagManager(DeleteMixin, RetrieveMixin, RESTManager):
if TYPE_CHECKING:
assert self.path is not None
self.gitlab.http_delete(self.path, query_data=data, **kwargs)
+
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectRegistryTag:
+ return cast(ProjectRegistryTag, super().get(id=id, lazy=lazy, **kwargs))
diff --git a/gitlab/v4/objects/custom_attributes.py b/gitlab/v4/objects/custom_attributes.py
index aed1965..d061614 100644
--- a/gitlab/v4/objects/custom_attributes.py
+++ b/gitlab/v4/objects/custom_attributes.py
@@ -1,3 +1,5 @@
+from typing import Any, cast, Union
+
from gitlab.base import RESTManager, RESTObject
from gitlab.mixins import DeleteMixin, ObjectDeleteMixin, RetrieveMixin, SetMixin
@@ -20,6 +22,11 @@ class GroupCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin, RESTMana
_obj_cls = GroupCustomAttribute
_from_parent_attrs = {"group_id": "id"}
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> GroupCustomAttribute:
+ return cast(GroupCustomAttribute, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectCustomAttribute(ObjectDeleteMixin, RESTObject):
_id_attr = "key"
@@ -30,6 +37,11 @@ class ProjectCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin, RESTMa
_obj_cls = ProjectCustomAttribute
_from_parent_attrs = {"project_id": "id"}
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectCustomAttribute:
+ return cast(ProjectCustomAttribute, super().get(id=id, lazy=lazy, **kwargs))
+
class UserCustomAttribute(ObjectDeleteMixin, RESTObject):
_id_attr = "key"
@@ -39,3 +51,8 @@ class UserCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin, RESTManag
_path = "/users/{user_id}/custom_attributes"
_obj_cls = UserCustomAttribute
_from_parent_attrs = {"user_id": "id"}
+
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> UserCustomAttribute:
+ return cast(UserCustomAttribute, super().get(id=id, lazy=lazy, **kwargs))
diff --git a/gitlab/v4/objects/deployments.py b/gitlab/v4/objects/deployments.py
index 8b4a7be..9aee699 100644
--- a/gitlab/v4/objects/deployments.py
+++ b/gitlab/v4/objects/deployments.py
@@ -1,3 +1,5 @@
+from typing import Any, cast, Union
+
from gitlab.base import RequiredOptional, RESTManager, RESTObject
from gitlab.mixins import CreateMixin, RetrieveMixin, SaveMixin, UpdateMixin
@@ -28,3 +30,8 @@ class ProjectDeploymentManager(RetrieveMixin, CreateMixin, UpdateMixin, RESTMana
_create_attrs = RequiredOptional(
required=("sha", "ref", "tag", "status", "environment")
)
+
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectDeployment:
+ return cast(ProjectDeployment, super().get(id=id, lazy=lazy, **kwargs))
diff --git a/gitlab/v4/objects/discussions.py b/gitlab/v4/objects/discussions.py
index 94f0a39..fa874c4 100644
--- a/gitlab/v4/objects/discussions.py
+++ b/gitlab/v4/objects/discussions.py
@@ -1,3 +1,5 @@
+from typing import Any, cast, Union
+
from gitlab.base import RequiredOptional, RESTManager, RESTObject
from gitlab.mixins import CreateMixin, RetrieveMixin, SaveMixin, UpdateMixin
@@ -30,6 +32,11 @@ class ProjectCommitDiscussionManager(RetrieveMixin, CreateMixin, RESTManager):
_from_parent_attrs = {"project_id": "project_id", "commit_id": "id"}
_create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectCommitDiscussion:
+ return cast(ProjectCommitDiscussion, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectIssueDiscussion(RESTObject):
notes: ProjectIssueDiscussionNoteManager
@@ -41,6 +48,11 @@ class ProjectIssueDiscussionManager(RetrieveMixin, CreateMixin, RESTManager):
_from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
_create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectIssueDiscussion:
+ return cast(ProjectIssueDiscussion, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectMergeRequestDiscussion(SaveMixin, RESTObject):
notes: ProjectMergeRequestDiscussionNoteManager
@@ -57,6 +69,13 @@ class ProjectMergeRequestDiscussionManager(
)
_update_attrs = RequiredOptional(required=("resolved",))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectMergeRequestDiscussion:
+ return cast(
+ ProjectMergeRequestDiscussion, super().get(id=id, lazy=lazy, **kwargs)
+ )
+
class ProjectSnippetDiscussion(RESTObject):
notes: ProjectSnippetDiscussionNoteManager
@@ -67,3 +86,8 @@ class ProjectSnippetDiscussionManager(RetrieveMixin, CreateMixin, RESTManager):
_obj_cls = ProjectSnippetDiscussion
_from_parent_attrs = {"project_id": "project_id", "snippet_id": "id"}
_create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
+
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectSnippetDiscussion:
+ return cast(ProjectSnippetDiscussion, super().get(id=id, lazy=lazy, **kwargs))
diff --git a/gitlab/v4/objects/environments.py b/gitlab/v4/objects/environments.py
index 6eec069..35f2fb2 100644
--- a/gitlab/v4/objects/environments.py
+++ b/gitlab/v4/objects/environments.py
@@ -1,4 +1,4 @@
-from typing import Any, Dict, Union
+from typing import Any, cast, Dict, Union
import requests
@@ -48,3 +48,8 @@ class ProjectEnvironmentManager(
_from_parent_attrs = {"project_id": "id"}
_create_attrs = RequiredOptional(required=("name",), optional=("external_url",))
_update_attrs = RequiredOptional(optional=("name", "external_url"))
+
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectEnvironment:
+ return cast(ProjectEnvironment, super().get(id=id, lazy=lazy, **kwargs))
diff --git a/gitlab/v4/objects/events.py b/gitlab/v4/objects/events.py
index 7af488d..b7d8fd1 100644
--- a/gitlab/v4/objects/events.py
+++ b/gitlab/v4/objects/events.py
@@ -1,3 +1,5 @@
+from typing import Any, cast, Union
+
from gitlab.base import RESTManager, RESTObject
from gitlab.mixins import ListMixin, RetrieveMixin
@@ -45,6 +47,13 @@ class GroupEpicResourceLabelEventManager(RetrieveMixin, RESTManager):
_obj_cls = GroupEpicResourceLabelEvent
_from_parent_attrs = {"group_id": "group_id", "epic_id": "id"}
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> GroupEpicResourceLabelEvent:
+ return cast(
+ GroupEpicResourceLabelEvent, super().get(id=id, lazy=lazy, **kwargs)
+ )
+
class ProjectEvent(Event):
pass
@@ -65,6 +74,13 @@ class ProjectIssueResourceLabelEventManager(RetrieveMixin, RESTManager):
_obj_cls = ProjectIssueResourceLabelEvent
_from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectIssueResourceLabelEvent:
+ return cast(
+ ProjectIssueResourceLabelEvent, super().get(id=id, lazy=lazy, **kwargs)
+ )
+
class ProjectIssueResourceMilestoneEvent(RESTObject):
pass
@@ -75,6 +91,13 @@ class ProjectIssueResourceMilestoneEventManager(RetrieveMixin, RESTManager):
_obj_cls = ProjectIssueResourceMilestoneEvent
_from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectIssueResourceMilestoneEvent:
+ return cast(
+ ProjectIssueResourceMilestoneEvent, super().get(id=id, lazy=lazy, **kwargs)
+ )
+
class ProjectIssueResourceStateEvent(RESTObject):
pass
@@ -85,6 +108,13 @@ class ProjectIssueResourceStateEventManager(RetrieveMixin, RESTManager):
_obj_cls = ProjectIssueResourceStateEvent
_from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectIssueResourceStateEvent:
+ return cast(
+ ProjectIssueResourceStateEvent, super().get(id=id, lazy=lazy, **kwargs)
+ )
+
class ProjectMergeRequestResourceLabelEvent(RESTObject):
pass
@@ -95,6 +125,14 @@ class ProjectMergeRequestResourceLabelEventManager(RetrieveMixin, RESTManager):
_obj_cls = ProjectMergeRequestResourceLabelEvent
_from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectMergeRequestResourceLabelEvent:
+ return cast(
+ ProjectMergeRequestResourceLabelEvent,
+ super().get(id=id, lazy=lazy, **kwargs),
+ )
+
class ProjectMergeRequestResourceMilestoneEvent(RESTObject):
pass
@@ -105,6 +143,14 @@ class ProjectMergeRequestResourceMilestoneEventManager(RetrieveMixin, RESTManage
_obj_cls = ProjectMergeRequestResourceMilestoneEvent
_from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectMergeRequestResourceMilestoneEvent:
+ return cast(
+ ProjectMergeRequestResourceMilestoneEvent,
+ super().get(id=id, lazy=lazy, **kwargs),
+ )
+
class ProjectMergeRequestResourceStateEvent(RESTObject):
pass
@@ -115,6 +161,14 @@ class ProjectMergeRequestResourceStateEventManager(RetrieveMixin, RESTManager):
_obj_cls = ProjectMergeRequestResourceStateEvent
_from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectMergeRequestResourceStateEvent:
+ return cast(
+ ProjectMergeRequestResourceStateEvent,
+ super().get(id=id, lazy=lazy, **kwargs),
+ )
+
class UserEvent(Event):
pass
diff --git a/gitlab/v4/objects/hooks.py b/gitlab/v4/objects/hooks.py
index 00dcfee..0b0092e 100644
--- a/gitlab/v4/objects/hooks.py
+++ b/gitlab/v4/objects/hooks.py
@@ -1,3 +1,5 @@
+from typing import Any, cast, Union
+
from gitlab.base import RequiredOptional, RESTManager, RESTObject
from gitlab.mixins import CRUDMixin, NoUpdateMixin, ObjectDeleteMixin, SaveMixin
@@ -21,6 +23,9 @@ class HookManager(NoUpdateMixin, RESTManager):
_obj_cls = Hook
_create_attrs = RequiredOptional(required=("url",))
+ def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> Hook:
+ return cast(Hook, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectHook(SaveMixin, ObjectDeleteMixin, RESTObject):
_short_print_attr = "url"
@@ -63,6 +68,11 @@ class ProjectHookManager(CRUDMixin, RESTManager):
),
)
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectHook:
+ return cast(ProjectHook, super().get(id=id, lazy=lazy, **kwargs))
+
class GroupHook(SaveMixin, ObjectDeleteMixin, RESTObject):
_short_print_attr = "url"
@@ -112,3 +122,6 @@ class GroupHookManager(CRUDMixin, RESTManager):
"token",
),
)
+
+ def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> GroupHook:
+ return cast(GroupHook, super().get(id=id, lazy=lazy, **kwargs))
diff --git a/gitlab/v4/objects/members.py b/gitlab/v4/objects/members.py
index a0abb00..8fa2bb3 100644
--- a/gitlab/v4/objects/members.py
+++ b/gitlab/v4/objects/members.py
@@ -1,3 +1,5 @@
+from typing import Any, cast, Union
+
from gitlab import types
from gitlab.base import RequiredOptional, RESTManager, RESTObject
from gitlab.mixins import (
@@ -39,6 +41,11 @@ class GroupMemberManager(CRUDMixin, RESTManager):
)
_types = {"user_ids": types.ListAttribute}
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> GroupMember:
+ return cast(GroupMember, super().get(id=id, lazy=lazy, **kwargs))
+
class GroupBillableMember(ObjectDeleteMixin, RESTObject):
_short_print_attr = "username"
@@ -68,6 +75,11 @@ class GroupMemberAllManager(RetrieveMixin, RESTManager):
_obj_cls = GroupMember
_from_parent_attrs = {"group_id": "id"}
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> GroupMember:
+ return cast(GroupMember, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectMember(SaveMixin, ObjectDeleteMixin, RESTObject):
_short_print_attr = "username"
@@ -85,8 +97,18 @@ class ProjectMemberManager(CRUDMixin, RESTManager):
)
_types = {"user_ids": types.ListAttribute}
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectMember:
+ return cast(ProjectMember, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectMemberAllManager(RetrieveMixin, RESTManager):
_path = "/projects/{project_id}/members/all"
_obj_cls = ProjectMember
_from_parent_attrs = {"project_id": "id"}
+
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectMember:
+ return cast(ProjectMember, super().get(id=id, lazy=lazy, **kwargs))
diff --git a/gitlab/v4/objects/merge_requests.py b/gitlab/v4/objects/merge_requests.py
index 672d0b7..068f25d 100644
--- a/gitlab/v4/objects/merge_requests.py
+++ b/gitlab/v4/objects/merge_requests.py
@@ -480,3 +480,8 @@ class ProjectMergeRequestDiffManager(RetrieveMixin, RESTManager):
_path = "/projects/{project_id}/merge_requests/{mr_iid}/versions"
_obj_cls = ProjectMergeRequestDiff
_from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
+
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectMergeRequestDiff:
+ return cast(ProjectMergeRequestDiff, super().get(id=id, lazy=lazy, **kwargs))
diff --git a/gitlab/v4/objects/notes.py b/gitlab/v4/objects/notes.py
index 9dd05cc..c4055ad 100644
--- a/gitlab/v4/objects/notes.py
+++ b/gitlab/v4/objects/notes.py
@@ -1,3 +1,5 @@
+from typing import Any, cast, Union
+
from gitlab.base import RequiredOptional, RESTManager, RESTObject
from gitlab.mixins import (
CreateMixin,
@@ -46,6 +48,11 @@ class ProjectNoteManager(RetrieveMixin, RESTManager):
_from_parent_attrs = {"project_id": "id"}
_create_attrs = RequiredOptional(required=("body",))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectNote:
+ return cast(ProjectNote, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectCommitDiscussionNote(SaveMixin, ObjectDeleteMixin, RESTObject):
pass
@@ -69,6 +76,13 @@ class ProjectCommitDiscussionNoteManager(
)
_update_attrs = RequiredOptional(required=("body",))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectCommitDiscussionNote:
+ return cast(
+ ProjectCommitDiscussionNote, super().get(id=id, lazy=lazy, **kwargs)
+ )
+
class ProjectIssueNote(SaveMixin, ObjectDeleteMixin, RESTObject):
awardemojis: ProjectIssueNoteAwardEmojiManager
@@ -81,6 +95,11 @@ class ProjectIssueNoteManager(CRUDMixin, RESTManager):
_create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
_update_attrs = RequiredOptional(required=("body",))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectIssueNote:
+ return cast(ProjectIssueNote, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectIssueDiscussionNote(SaveMixin, ObjectDeleteMixin, RESTObject):
pass
@@ -101,6 +120,11 @@ class ProjectIssueDiscussionNoteManager(
_create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
_update_attrs = RequiredOptional(required=("body",))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectIssueDiscussionNote:
+ return cast(ProjectIssueDiscussionNote, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectMergeRequestNote(SaveMixin, ObjectDeleteMixin, RESTObject):
awardemojis: ProjectMergeRequestNoteAwardEmojiManager
@@ -113,6 +137,11 @@ class ProjectMergeRequestNoteManager(CRUDMixin, RESTManager):
_create_attrs = RequiredOptional(required=("body",))
_update_attrs = RequiredOptional(required=("body",))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectMergeRequestNote:
+ return cast(ProjectMergeRequestNote, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectMergeRequestDiscussionNote(SaveMixin, ObjectDeleteMixin, RESTObject):
pass
@@ -134,6 +163,13 @@ class ProjectMergeRequestDiscussionNoteManager(
_create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
_update_attrs = RequiredOptional(required=("body",))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectMergeRequestDiscussionNote:
+ return cast(
+ ProjectMergeRequestDiscussionNote, super().get(id=id, lazy=lazy, **kwargs)
+ )
+
class ProjectSnippetNote(SaveMixin, ObjectDeleteMixin, RESTObject):
awardemojis: ProjectMergeRequestNoteAwardEmojiManager
@@ -146,6 +182,11 @@ class ProjectSnippetNoteManager(CRUDMixin, RESTManager):
_create_attrs = RequiredOptional(required=("body",))
_update_attrs = RequiredOptional(required=("body",))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectSnippetNote:
+ return cast(ProjectSnippetNote, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectSnippetDiscussionNote(SaveMixin, ObjectDeleteMixin, RESTObject):
pass
@@ -166,3 +207,10 @@ class ProjectSnippetDiscussionNoteManager(
}
_create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
_update_attrs = RequiredOptional(required=("body",))
+
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectSnippetDiscussionNote:
+ return cast(
+ ProjectSnippetDiscussionNote, super().get(id=id, lazy=lazy, **kwargs)
+ )
diff --git a/gitlab/v4/objects/packages.py b/gitlab/v4/objects/packages.py
index d992303..0062067 100644
--- a/gitlab/v4/objects/packages.py
+++ b/gitlab/v4/objects/packages.py
@@ -5,7 +5,7 @@ https://docs.gitlab.com/ee/user/packages/generic_packages/
"""
from pathlib import Path
-from typing import Any, Callable, Optional, TYPE_CHECKING, Union
+from typing import Any, Callable, cast, Optional, TYPE_CHECKING, Union
import requests
@@ -167,6 +167,11 @@ class ProjectPackageManager(ListMixin, GetMixin, DeleteMixin, RESTManager):
"package_name",
)
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectPackage:
+ return cast(ProjectPackage, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectPackageFile(RESTObject):
pass
diff --git a/gitlab/v4/objects/tags.py b/gitlab/v4/objects/tags.py
index a85f0e3..c76799d 100644
--- a/gitlab/v4/objects/tags.py
+++ b/gitlab/v4/objects/tags.py
@@ -1,3 +1,5 @@
+from typing import Any, cast, Union
+
from gitlab.base import RequiredOptional, RESTManager, RESTObject
from gitlab.mixins import NoUpdateMixin, ObjectDeleteMixin
@@ -22,6 +24,9 @@ class ProjectTagManager(NoUpdateMixin, RESTManager):
required=("tag_name", "ref"), optional=("message",)
)
+ def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> ProjectTag:
+ return cast(ProjectTag, super().get(id=id, lazy=lazy, **kwargs))
+
class ProjectProtectedTag(ObjectDeleteMixin, RESTObject):
_id_attr = "name"
@@ -35,3 +40,8 @@ class ProjectProtectedTagManager(NoUpdateMixin, RESTManager):
_create_attrs = RequiredOptional(
required=("name",), optional=("create_access_level",)
)
+
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> ProjectProtectedTag:
+ return cast(ProjectProtectedTag, super().get(id=id, lazy=lazy, **kwargs))
diff --git a/gitlab/v4/objects/templates.py b/gitlab/v4/objects/templates.py
index 04de463..bbe2ae6 100644
--- a/gitlab/v4/objects/templates.py
+++ b/gitlab/v4/objects/templates.py
@@ -1,3 +1,5 @@
+from typing import Any, cast, Union
+
from gitlab.base import RESTManager, RESTObject
from gitlab.mixins import RetrieveMixin
@@ -21,6 +23,9 @@ class DockerfileManager(RetrieveMixin, RESTManager):
_path = "/templates/dockerfiles"
_obj_cls = Dockerfile
+ def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> Dockerfile:
+ return cast(Dockerfile, super().get(id=id, lazy=lazy, **kwargs))
+
class Gitignore(RESTObject):
_id_attr = "name"
@@ -30,6 +35,9 @@ class GitignoreManager(RetrieveMixin, RESTManager):
_path = "/templates/gitignores"
_obj_cls = Gitignore
+ def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> Gitignore:
+ return cast(Gitignore, super().get(id=id, lazy=lazy, **kwargs))
+
class Gitlabciyml(RESTObject):
_id_attr = "name"
@@ -39,6 +47,11 @@ class GitlabciymlManager(RetrieveMixin, RESTManager):
_path = "/templates/gitlab_ci_ymls"
_obj_cls = Gitlabciyml
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> Gitlabciyml:
+ return cast(Gitlabciyml, super().get(id=id, lazy=lazy, **kwargs))
+
class License(RESTObject):
_id_attr = "key"
@@ -49,3 +62,6 @@ class LicenseManager(RetrieveMixin, RESTManager):
_obj_cls = License
_list_filters = ("popular",)
_optional_get_attrs = ("project", "fullname")
+
+ def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> License:
+ return cast(License, super().get(id=id, lazy=lazy, **kwargs))
diff --git a/gitlab/v4/objects/users.py b/gitlab/v4/objects/users.py
index ac75284..8649cba 100644
--- a/gitlab/v4/objects/users.py
+++ b/gitlab/v4/objects/users.py
@@ -74,6 +74,11 @@ class CurrentUserEmailManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManag
_obj_cls = CurrentUserEmail
_create_attrs = RequiredOptional(required=("email",))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> CurrentUserEmail:
+ return cast(CurrentUserEmail, super().get(id=id, lazy=lazy, **kwargs))
+
class CurrentUserGPGKey(ObjectDeleteMixin, RESTObject):
pass
@@ -84,6 +89,11 @@ class CurrentUserGPGKeyManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTMana
_obj_cls = CurrentUserGPGKey
_create_attrs = RequiredOptional(required=("key",))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> CurrentUserGPGKey:
+ return cast(CurrentUserGPGKey, super().get(id=id, lazy=lazy, **kwargs))
+
class CurrentUserKey(ObjectDeleteMixin, RESTObject):
_short_print_attr = "title"
@@ -94,6 +104,11 @@ class CurrentUserKeyManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager
_obj_cls = CurrentUserKey
_create_attrs = RequiredOptional(required=("title", "key"))
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> CurrentUserKey:
+ return cast(CurrentUserKey, super().get(id=id, lazy=lazy, **kwargs))
+
class CurrentUserStatus(SaveMixin, RESTObject):
_id_attr = None
@@ -357,6 +372,9 @@ class UserEmailManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
_from_parent_attrs = {"user_id": "id"}
_create_attrs = RequiredOptional(required=("email",))
+ def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> UserEmail:
+ return cast(UserEmail, super().get(id=id, lazy=lazy, **kwargs))
+
class UserActivities(RESTObject):
_id_attr = "username"
@@ -388,6 +406,9 @@ class UserGPGKeyManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
_from_parent_attrs = {"user_id": "id"}
_create_attrs = RequiredOptional(required=("key",))
+ def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> UserGPGKey:
+ return cast(UserGPGKey, super().get(id=id, lazy=lazy, **kwargs))
+
class UserKey(ObjectDeleteMixin, RESTObject):
pass
@@ -424,6 +445,11 @@ class UserImpersonationTokenManager(NoUpdateMixin, RESTManager):
)
_list_filters = ("state",)
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> UserImpersonationToken:
+ return cast(UserImpersonationToken, super().get(id=id, lazy=lazy, **kwargs))
+
class UserMembership(RESTObject):
_id_attr = "source_id"
@@ -435,6 +461,11 @@ class UserMembershipManager(RetrieveMixin, RESTManager):
_from_parent_attrs = {"user_id": "id"}
_list_filters = ("type",)
+ def get(
+ self, id: Union[str, int], lazy: bool = False, **kwargs: Any
+ ) -> UserMembership:
+ return cast(UserMembership, super().get(id=id, lazy=lazy, **kwargs))
+
# Having this outside projects avoids circular imports due to ProjectUser
class UserProject(RESTObject):
diff --git a/pyproject.toml b/pyproject.toml
index 160232c..f48ed5f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -27,6 +27,7 @@ module = [
"setup",
"tests.functional.*",
"tests.functional.api.*",
+ "tests.meta.*",
"tests.unit.*",
"tests.smoke.*"
]
diff --git a/tests/meta/__init__.py b/tests/meta/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/meta/__init__.py
diff --git a/tests/meta/test_ensure_type_hints.py b/tests/meta/test_ensure_type_hints.py
new file mode 100644
index 0000000..f647b45
--- /dev/null
+++ b/tests/meta/test_ensure_type_hints.py
@@ -0,0 +1,85 @@
+"""
+Ensure type-hints are setup correctly and detect if missing functions.
+
+Original notes by John L. Villalovos
+
+"""
+import inspect
+from typing import Tuple, Type
+
+import toml
+
+import gitlab.mixins
+import gitlab.v4.objects
+
+
+def pytest_generate_tests(metafunc):
+ """Find all of the classes in gitlab.v4.objects and pass them to our test
+ function"""
+
+ # Ignore any modules that we are ignoring in our pyproject.toml
+ excluded_modules = set()
+ with open("pyproject.toml", "r") as in_file:
+ pyproject = toml.load(in_file)
+ overrides = pyproject.get("tool", {}).get("mypy", {}).get("overrides", [])
+ for override in overrides:
+ if not override.get("ignore_errors"):
+ continue
+ for module in override.get("module", []):
+ if module.startswith("gitlab.v4.objects"):
+ excluded_modules.add(module)
+
+ class_info_list = []
+ for module_name, module_value in inspect.getmembers(gitlab.v4.objects):
+ if not inspect.ismodule(module_value):
+ # We only care about the modules
+ continue
+ # Iterate through all the classes in our module
+ for class_name, class_value in inspect.getmembers(module_value):
+ if not inspect.isclass(class_value):
+ continue
+
+ module_name = class_value.__module__
+ # Ignore modules that mypy is ignoring
+ if module_name in excluded_modules:
+ continue
+
+ # Ignore imported classes from gitlab.base
+ if module_name == "gitlab.base":
+ continue
+
+ class_info_list.append((class_name, class_value))
+
+ metafunc.parametrize("class_info", class_info_list)
+
+
+class TestTypeHints:
+ def test_check_get_function_type_hints(self, class_info: Tuple[str, Type]):
+ """Ensure classes derived from GetMixin have defined a 'get()' method with
+ correct type-hints.
+ """
+ class_name, class_value = class_info
+ if not class_name.endswith("Manager"):
+ return
+
+ mro = class_value.mro()
+ # The class needs to be derived from GetMixin or we ignore it
+ if gitlab.mixins.GetMixin not in mro:
+ return
+
+ obj_cls = class_value._obj_cls
+ signature = inspect.signature(class_value.get)
+ filename = inspect.getfile(class_value)
+
+ fail_message = (
+ f"class definition for {class_name!r} in file {filename!r} "
+ f"must have defined a 'get' method with a return annotation of "
+ f"{obj_cls} but found {signature.return_annotation}\n"
+ f"Recommend adding the followinng method:\n"
+ f"def get(\n"
+ f" self, id: Union[str, int], lazy: bool = False, **kwargs: Any\n"
+ f" ) -> {obj_cls.__name__}:\n"
+ f" return cast({obj_cls.__name__}, super().get(id=id, lazy=lazy, "
+ f"**kwargs))\n"
+ )
+ assert obj_cls == signature.return_annotation, fail_message
diff --git a/tests/unit/objects/test_mro.py b/tests/meta/test_mro.py
index 8f67b77..8f67b77 100644
--- a/tests/unit/objects/test_mro.py
+++ b/tests/meta/test_mro.py
diff --git a/tox.ini b/tox.ini
index da1f1e8..32c6658 100644
--- a/tox.ini
+++ b/tox.ini
@@ -13,7 +13,7 @@ install_command = pip install {opts} {packages}
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/requirements-test.txt
commands =
- pytest tests/unit {posargs}
+ pytest tests/unit tests/meta {posargs}
[testenv:pep8]
basepython = python3
@@ -72,7 +72,7 @@ commands = python setup.py build_sphinx
[testenv:cover]
commands =
pytest --cov --cov-report term --cov-report html \
- --cov-report xml tests/unit {posargs}
+ --cov-report xml tests/unit tests/meta {posargs}
[coverage:run]
omit = *tests*