diff options
Diffstat (limited to 'gitlab')
30 files changed, 263 insertions, 395 deletions
diff --git a/gitlab/base.py b/gitlab/base.py index bea1901..a4a1ef9 100644 --- a/gitlab/base.py +++ b/gitlab/base.py @@ -150,13 +150,22 @@ class RESTObject(object): return hash(self.get_id()) def _create_managers(self) -> None: - managers = getattr(self, "_managers", None) - if managers is None: - return - - for attr, cls_name in self._managers: + # NOTE(jlvillal): We are creating our managers by looking at the class + # annotations. If an attribute is annotated as being a *Manager type + # then we create the manager and assign it to the attribute. + for attr, annotation in sorted(self.__annotations__.items()): + if not isinstance(annotation, (type, str)): + continue + if isinstance(annotation, type): + cls_name = annotation.__name__ + else: + cls_name = annotation + # All *Manager classes are used except for the base "RESTManager" class + if cls_name == "RESTManager" or not cls_name.endswith("Manager"): + continue cls = getattr(self._module, cls_name) manager = cls(self.manager.gitlab, parent=self) + # Since we have our own __setattr__ method, we can't use setattr() self.__dict__[attr] = manager def _update_attrs(self, new_attrs: Dict[str, Any]) -> None: @@ -183,7 +192,7 @@ class RESTObjectList(object): This generator uses the Gitlab pagination system to fetch new data when required. - Note: you should not instanciate such objects, they are returned by calls + Note: you should not instantiate such objects, they are returned by calls to RESTManager.list() Args: diff --git a/gitlab/client.py b/gitlab/client.py index 47fae81..8bec64f 100644 --- a/gitlab/client.py +++ b/gitlab/client.py @@ -29,8 +29,9 @@ import gitlab.exceptions from gitlab import utils REDIRECT_MSG = ( - "python-gitlab detected an http to https redirection. You " - "must update your GitLab URL to use https:// to avoid issues." + "python-gitlab detected a {status_code} ({reason!r}) redirection. You must update " + "your GitLab URL to the correct URL to avoid issues. The redirection was from: " + "{source!r} to {target!r}" ) @@ -38,7 +39,7 @@ class Gitlab(object): """Represents a GitLab server connection. Args: - url (str): The URL of the GitLab server. + url (str): The URL of the GitLab server (defaults to https://gitlab.com). private_token (str): The user private token oauth_token (str): An oauth token job_token (str): A CI job token @@ -58,7 +59,7 @@ class Gitlab(object): def __init__( self, - url: str, + url: Optional[str] = None, private_token: Optional[str] = None, oauth_token: Optional[str] = None, job_token: Optional[str] = None, @@ -78,7 +79,7 @@ class Gitlab(object): self._api_version = str(api_version) self._server_version: Optional[str] = None self._server_revision: Optional[str] = None - self._base_url = url.rstrip("/") + self._base_url = self._get_base_url(url) self._url = "%s/api/v%s" % (self._base_url, api_version) #: Timeout to use for requests to gitlab server self.timeout = timeout @@ -441,6 +442,17 @@ class Gitlab(object): "verify": self.ssl_verify, } + def _get_base_url(self, url: Optional[str] = None) -> str: + """Return the base URL with the trailing slash stripped. + If the URL is a Falsy value, return the default URL. + Returns: + str: The base URL + """ + if not url: + return gitlab.const.DEFAULT_URL + + return url.rstrip("/") + def _build_url(self, path: str) -> str: """Returns the full url from path. @@ -456,24 +468,29 @@ class Gitlab(object): return "%s%s" % (self._url, path) def _check_redirects(self, result: requests.Response) -> None: - # Check the requests history to detect http to https redirections. - # If the initial verb is POST, the next request will use a GET request, - # leading to an unwanted behaviour. - # If the initial verb is PUT, the data will not be send with the next - # request. - # If we detect a redirection to https with a POST or a PUT request, we + # Check the requests history to detect 301/302 redirections. + # If the initial verb is POST or PUT, the redirected request will use a + # GET request, leading to unwanted behaviour. + # If we detect a redirection with a POST or a PUT request, we # raise an exception with a useful error message. - if result.history and self._base_url.startswith("http:"): - for item in result.history: - if item.status_code not in (301, 302): - continue - # GET methods can be redirected without issue - if item.request.method == "GET": - continue - # Did we end-up with an https:// URL? - location = item.headers.get("Location", None) - if location and location.startswith("https://"): - raise gitlab.exceptions.RedirectError(REDIRECT_MSG) + if not result.history: + return + + for item in result.history: + if item.status_code not in (301, 302): + continue + # GET methods can be redirected without issue + if item.request.method == "GET": + continue + target = item.headers.get("location") + raise gitlab.exceptions.RedirectError( + REDIRECT_MSG.format( + status_code=item.status_code, + reason=item.reason, + source=item.url, + target=target, + ) + ) def _prepare_send_data( self, @@ -585,6 +602,8 @@ class Gitlab(object): # gitlab installation) req = requests.Request(verb, url, json=json, data=data, params=params, **opts) prepped = self.session.prepare_request(req) + if TYPE_CHECKING: + assert prepped.url is not None prepped.url = utils.sanitized_url(prepped.url) settings = self.session.merge_environment_settings( prepped.url, {}, streamed, verify, None diff --git a/gitlab/const.py b/gitlab/const.py index 33687c1..c57423e 100644 --- a/gitlab/const.py +++ b/gitlab/const.py @@ -17,18 +17,19 @@ from gitlab.__version__ import __title__, __version__ +DEFAULT_URL: str = "https://gitlab.com" + NO_ACCESS: int = 0 MINIMAL_ACCESS: int = 5 GUEST_ACCESS: int = 10 REPORTER_ACCESS: int = 20 DEVELOPER_ACCESS: int = 30 MAINTAINER_ACCESS: int = 40 -MASTER_ACCESS: int = MAINTAINER_ACCESS OWNER_ACCESS: int = 50 -VISIBILITY_PRIVATE: int = 0 -VISIBILITY_INTERNAL: int = 10 -VISIBILITY_PUBLIC: int = 20 +VISIBILITY_PRIVATE: str = "private" +VISIBILITY_INTERNAL: str = "internal" +VISIBILITY_PUBLIC: str = "public" NOTIFICATION_LEVEL_DISABLED: str = "disabled" NOTIFICATION_LEVEL_PARTICIPATING: str = "participating" diff --git a/gitlab/mixins.py b/gitlab/mixins.py index f35c134..0c2cd94 100644 --- a/gitlab/mixins.py +++ b/gitlab/mixins.py @@ -15,7 +15,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import warnings from types import ModuleType from typing import ( Any, @@ -440,7 +439,7 @@ class SetMixin(_RestManagerBase): Raises: GitlabAuthenticationError: If authentication is not correct - GitlabSetError: If an error occured + GitlabSetError: If an error occurred Returns: obj: The created/updated attribute @@ -662,7 +661,7 @@ class DownloadMixin(_RestObjectBase): Args: streamed (bool): If True the data will be processed by chunks of `chunk_size` and each chunk is passed to `action` for - reatment + treatment action (callable): Callable responsible of dealing with chunk of data chunk_size (int): Size of each chunk @@ -927,50 +926,3 @@ class BadgeRenderMixin(_RestManagerBase): if TYPE_CHECKING: assert not isinstance(result, requests.Response) return result - - -class MemberAllMixin(_RestManagerBase): - """This mixin is deprecated.""" - - _computed_path: Optional[str] - _from_parent_attrs: Dict[str, Any] - _obj_cls: Optional[Type[base.RESTObject]] - _parent: Optional[base.RESTObject] - _parent_attrs: Dict[str, Any] - _path: Optional[str] - gitlab: gitlab.Gitlab - - @cli.register_custom_action(("GroupMemberManager", "ProjectMemberManager")) - @exc.on_http_error(exc.GitlabListError) - def all(self, **kwargs: Any) -> List[base.RESTObject]: - """List all the members, included inherited ones. - - This Method is deprecated. - - Args: - all (bool): If True, return all the items, without pagination - per_page (int): Number of items to retrieve per request - page (int): ID of the page to return (starts with page 1) - as_list (bool): If set to False and no pagination option is - defined, return a generator instead of a list - **kwargs: Extra options to send to the server (e.g. sudo) - - Raises: - GitlabAuthenticationError: If authentication is not correct - GitlabListError: If the list could not be retrieved - - Returns: - RESTObjectList: The list of members - """ - - warnings.warn( - "The all() method for this object is deprecated " - "and will be removed in a future version. Use .members_all.list(all=True), instead.", - DeprecationWarning, - ) - path = "%s/all" % self.path - - if TYPE_CHECKING: - assert self._obj_cls is not None - obj = self.gitlab.http_list(path, **kwargs) - return [self._obj_cls(self, item) for item in obj] diff --git a/gitlab/v4/cli.py b/gitlab/v4/cli.py index 2fc1986..6986552 100644 --- a/gitlab/v4/cli.py +++ b/gitlab/v4/cli.py @@ -99,7 +99,10 @@ class GitlabCLI(object): def do_project_export_download(self) -> None: try: project = self.gl.projects.get(int(self.args["project_id"]), lazy=True) - data = project.exports.get().download() + export_status = project.exports.get() + if TYPE_CHECKING: + assert export_status is not None + data = export_status.download() sys.stdout.buffer.write(data) except Exception as e: diff --git a/gitlab/v4/objects/__init__.py b/gitlab/v4/objects/__init__.py index 1b95410..c2ff4fb 100644 --- a/gitlab/v4/objects/__init__.py +++ b/gitlab/v4/objects/__init__.py @@ -74,15 +74,4 @@ from .users import * from .variables import * from .wikis import * -# TODO: deprecate these in favor of gitlab.const.* -VISIBILITY_PRIVATE = "private" -VISIBILITY_INTERNAL = "internal" -VISIBILITY_PUBLIC = "public" - -ACCESS_GUEST = 10 -ACCESS_REPORTER = 20 -ACCESS_DEVELOPER = 30 -ACCESS_MASTER = 40 -ACCESS_OWNER = 50 - __all__ = [name for name in dir() if not name.startswith("_")] diff --git a/gitlab/v4/objects/boards.py b/gitlab/v4/objects/boards.py index b517fde..8b2959d 100644 --- a/gitlab/v4/objects/boards.py +++ b/gitlab/v4/objects/boards.py @@ -26,7 +26,7 @@ class GroupBoardListManager(CRUDMixin, RESTManager): class GroupBoard(SaveMixin, ObjectDeleteMixin, RESTObject): - _managers = (("lists", "GroupBoardListManager"),) + lists: GroupBoardListManager class GroupBoardManager(CRUDMixin, RESTManager): @@ -49,7 +49,7 @@ class ProjectBoardListManager(CRUDMixin, RESTManager): class ProjectBoard(SaveMixin, ObjectDeleteMixin, RESTObject): - _managers = (("lists", "ProjectBoardListManager"),) + lists: ProjectBoardListManager class ProjectBoardManager(CRUDMixin, RESTManager): diff --git a/gitlab/v4/objects/branches.py b/gitlab/v4/objects/branches.py index 3738657..5bd8442 100644 --- a/gitlab/v4/objects/branches.py +++ b/gitlab/v4/objects/branches.py @@ -1,5 +1,3 @@ -from gitlab import cli -from gitlab import exceptions as exc from gitlab.base import RequiredOptional, RESTManager, RESTObject from gitlab.mixins import NoUpdateMixin, ObjectDeleteMixin @@ -14,50 +12,6 @@ __all__ = [ class ProjectBranch(ObjectDeleteMixin, RESTObject): _id_attr = "name" - @cli.register_custom_action( - "ProjectBranch", tuple(), ("developers_can_push", "developers_can_merge") - ) - @exc.on_http_error(exc.GitlabProtectError) - def protect(self, developers_can_push=False, developers_can_merge=False, **kwargs): - """Protect the branch. - - Args: - developers_can_push (bool): Set to True if developers are allowed - to push to the branch - developers_can_merge (bool): Set to True if developers are allowed - to merge to the branch - **kwargs: Extra options to send to the server (e.g. sudo) - - Raises: - GitlabAuthenticationError: If authentication is not correct - GitlabProtectError: If the branch could not be protected - """ - id = self.get_id().replace("/", "%2F") - path = "%s/%s/protect" % (self.manager.path, id) - post_data = { - "developers_can_push": developers_can_push, - "developers_can_merge": developers_can_merge, - } - self.manager.gitlab.http_put(path, post_data=post_data, **kwargs) - self._attrs["protected"] = True - - @cli.register_custom_action("ProjectBranch") - @exc.on_http_error(exc.GitlabProtectError) - def unprotect(self, **kwargs): - """Unprotect the branch. - - Args: - **kwargs: Extra options to send to the server (e.g. sudo) - - Raises: - GitlabAuthenticationError: If authentication is not correct - GitlabProtectError: If the branch could not be unprotected - """ - id = self.get_id().replace("/", "%2F") - path = "%s/%s/unprotect" % (self.manager.path, id) - self.manager.gitlab.http_put(path, **kwargs) - self._attrs["protected"] = False - class ProjectBranchManager(NoUpdateMixin, RESTManager): _path = "/projects/%(project_id)s/repository/branches" diff --git a/gitlab/v4/objects/commits.py b/gitlab/v4/objects/commits.py index 76e582b..05b55b0 100644 --- a/gitlab/v4/objects/commits.py +++ b/gitlab/v4/objects/commits.py @@ -17,11 +17,10 @@ __all__ = [ class ProjectCommit(RESTObject): _short_print_attr = "title" - _managers = ( - ("comments", "ProjectCommitCommentManager"), - ("discussions", "ProjectCommitDiscussionManager"), - ("statuses", "ProjectCommitStatusManager"), - ) + + comments: "ProjectCommitCommentManager" + discussions: ProjectCommitDiscussionManager + statuses: "ProjectCommitStatusManager" @cli.register_custom_action("ProjectCommit") @exc.on_http_error(exc.GitlabGetError) diff --git a/gitlab/v4/objects/container_registry.py b/gitlab/v4/objects/container_registry.py index 432cb7f..ce03d35 100644 --- a/gitlab/v4/objects/container_registry.py +++ b/gitlab/v4/objects/container_registry.py @@ -12,7 +12,7 @@ __all__ = [ class ProjectRegistryRepository(ObjectDeleteMixin, RESTObject): - _managers = (("tags", "ProjectRegistryTagManager"),) + tags: "ProjectRegistryTagManager" class ProjectRegistryRepositoryManager(DeleteMixin, ListMixin, RESTManager): @@ -31,26 +31,28 @@ class ProjectRegistryTagManager(DeleteMixin, RetrieveMixin, RESTManager): _path = "/projects/%(project_id)s/registry/repositories/%(repository_id)s/tags" @cli.register_custom_action( - "ProjectRegistryTagManager", optional=("name_regex", "keep_n", "older_than") + "ProjectRegistryTagManager", + ("name_regex_delete",), + optional=("keep_n", "name_regex_keep", "older_than"), ) @exc.on_http_error(exc.GitlabDeleteError) - def delete_in_bulk(self, name_regex=".*", **kwargs): + def delete_in_bulk(self, name_regex_delete, **kwargs): """Delete Tag in bulk Args: - name_regex (string): The regex of the name to delete. To delete all - tags specify .*. - keep_n (integer): The amount of latest tags of given name to keep. - name_regex_keep (string): The regex of the name to keep. This value - overrides any matches from name_regex. - older_than (string): Tags to delete that are older than the given time, - written in human readable form 1h, 1d, 1month. - **kwargs: Extra options to send to the server (e.g. sudo) + name_regex_delete (string): The regex of the name to delete. To delete all + tags specify .*. + keep_n (integer): The amount of latest tags of given name to keep. + name_regex_keep (string): The regex of the name to keep. This value + overrides any matches from name_regex. + older_than (string): Tags to delete that are older than the given time, + written in human readable form 1h, 1d, 1month. + **kwargs: Extra options to send to the server (e.g. sudo) Raises: GitlabAuthenticationError: If authentication is not correct GitlabDeleteError: If the server cannot perform the request """ valid_attrs = ["keep_n", "name_regex_keep", "older_than"] - data = {"name_regex": name_regex} + data = {"name_regex_delete": name_regex_delete} data.update({k: v for k, v in kwargs.items() if k in valid_attrs}) self.gitlab.http_delete(self.path, query_data=data, **kwargs) diff --git a/gitlab/v4/objects/deployments.py b/gitlab/v4/objects/deployments.py index 8cf0fd9..11c60d1 100644 --- a/gitlab/v4/objects/deployments.py +++ b/gitlab/v4/objects/deployments.py @@ -10,7 +10,7 @@ __all__ = [ class ProjectDeployment(SaveMixin, RESTObject): - _managers = (("mergerequests", "ProjectDeploymentMergeRequestManager"),) + mergerequests: ProjectDeploymentMergeRequestManager class ProjectDeploymentManager(RetrieveMixin, CreateMixin, UpdateMixin, RESTManager): diff --git a/gitlab/v4/objects/discussions.py b/gitlab/v4/objects/discussions.py index f91d8fb..ae7a4d5 100644 --- a/gitlab/v4/objects/discussions.py +++ b/gitlab/v4/objects/discussions.py @@ -21,7 +21,7 @@ __all__ = [ class ProjectCommitDiscussion(RESTObject): - _managers = (("notes", "ProjectCommitDiscussionNoteManager"),) + notes: ProjectCommitDiscussionNoteManager class ProjectCommitDiscussionManager(RetrieveMixin, CreateMixin, RESTManager): @@ -32,7 +32,7 @@ class ProjectCommitDiscussionManager(RetrieveMixin, CreateMixin, RESTManager): class ProjectIssueDiscussion(RESTObject): - _managers = (("notes", "ProjectIssueDiscussionNoteManager"),) + notes: ProjectIssueDiscussionNoteManager class ProjectIssueDiscussionManager(RetrieveMixin, CreateMixin, RESTManager): @@ -43,7 +43,7 @@ class ProjectIssueDiscussionManager(RetrieveMixin, CreateMixin, RESTManager): class ProjectMergeRequestDiscussion(SaveMixin, RESTObject): - _managers = (("notes", "ProjectMergeRequestDiscussionNoteManager"),) + notes: ProjectMergeRequestDiscussionNoteManager class ProjectMergeRequestDiscussionManager( @@ -59,7 +59,7 @@ class ProjectMergeRequestDiscussionManager( class ProjectSnippetDiscussion(RESTObject): - _managers = (("notes", "ProjectSnippetDiscussionNoteManager"),) + notes: ProjectSnippetDiscussionNoteManager class ProjectSnippetDiscussionManager(RetrieveMixin, CreateMixin, RESTManager): diff --git a/gitlab/v4/objects/epics.py b/gitlab/v4/objects/epics.py index 4311aa7..90dc6ad 100644 --- a/gitlab/v4/objects/epics.py +++ b/gitlab/v4/objects/epics.py @@ -23,10 +23,9 @@ __all__ = [ class GroupEpic(ObjectDeleteMixin, SaveMixin, RESTObject): _id_attr = "iid" - _managers = ( - ("issues", "GroupEpicIssueManager"), - ("resourcelabelevents", "GroupEpicResourceLabelEventManager"), - ) + + issues: "GroupEpicIssueManager" + resourcelabelevents: GroupEpicResourceLabelEventManager class GroupEpicManager(CRUDMixin, RESTManager): diff --git a/gitlab/v4/objects/features.py b/gitlab/v4/objects/features.py index 93ac950..f4117c8 100644 --- a/gitlab/v4/objects/features.py +++ b/gitlab/v4/objects/features.py @@ -41,7 +41,7 @@ class FeatureManager(ListMixin, DeleteMixin, RESTManager): Raises: GitlabAuthenticationError: If authentication is not correct - GitlabSetError: If an error occured + GitlabSetError: If an error occurred Returns: obj: The created/updated attribute diff --git a/gitlab/v4/objects/groups.py b/gitlab/v4/objects/groups.py index ee82415..b675a39 100644 --- a/gitlab/v4/objects/groups.py +++ b/gitlab/v4/objects/groups.py @@ -43,39 +43,38 @@ __all__ = [ class Group(SaveMixin, ObjectDeleteMixin, RESTObject): _short_print_attr = "name" - _managers = ( - ("accessrequests", "GroupAccessRequestManager"), - ("audit_events", "GroupAuditEventManager"), - ("badges", "GroupBadgeManager"), - ("billable_members", "GroupBillableMemberManager"), - ("boards", "GroupBoardManager"), - ("customattributes", "GroupCustomAttributeManager"), - ("descendant_groups", "GroupDescendantGroupManager"), - ("exports", "GroupExportManager"), - ("epics", "GroupEpicManager"), - ("hooks", "GroupHookManager"), - ("imports", "GroupImportManager"), - ("issues", "GroupIssueManager"), - ("issues_statistics", "GroupIssuesStatisticsManager"), - ("labels", "GroupLabelManager"), - ("members", "GroupMemberManager"), - ("members_all", "GroupMemberAllManager"), - ("mergerequests", "GroupMergeRequestManager"), - ("milestones", "GroupMilestoneManager"), - ("notificationsettings", "GroupNotificationSettingsManager"), - ("packages", "GroupPackageManager"), - ("projects", "GroupProjectManager"), - ("runners", "GroupRunnerManager"), - ("subgroups", "GroupSubgroupManager"), - ("variables", "GroupVariableManager"), - ("clusters", "GroupClusterManager"), - ("deploytokens", "GroupDeployTokenManager"), - ("wikis", "GroupWikiManager"), - ) - @cli.register_custom_action("Group", ("to_project_id",)) + accessrequests: GroupAccessRequestManager + audit_events: GroupAuditEventManager + badges: GroupBadgeManager + billable_members: GroupBillableMemberManager + boards: GroupBoardManager + clusters: GroupClusterManager + customattributes: GroupCustomAttributeManager + deploytokens: GroupDeployTokenManager + descendant_groups: "GroupDescendantGroupManager" + epics: GroupEpicManager + exports: GroupExportManager + hooks: GroupHookManager + imports: GroupImportManager + issues: GroupIssueManager + issues_statistics: GroupIssuesStatisticsManager + labels: GroupLabelManager + members: GroupMemberManager + members_all: GroupMemberAllManager + mergerequests: GroupMergeRequestManager + milestones: GroupMilestoneManager + notificationsettings: GroupNotificationSettingsManager + packages: GroupPackageManager + projects: GroupProjectManager + runners: GroupRunnerManager + subgroups: "GroupSubgroupManager" + variables: GroupVariableManager + wikis: GroupWikiManager + + @cli.register_custom_action("Group", ("project_id",)) @exc.on_http_error(exc.GitlabTransferProjectError) - def transfer_project(self, to_project_id, **kwargs): + def transfer_project(self, project_id, **kwargs): """Transfer a project to this group. Args: @@ -84,9 +83,9 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject): Raises: GitlabAuthenticationError: If authentication is not correct - GitlabTransferProjectError: If the project could not be transfered + GitlabTransferProjectError: If the project could not be transferred """ - path = "/groups/%s/projects/%s" % (self.id, to_project_id) + path = "/groups/%s/projects/%s" % (self.id, project_id) self.manager.gitlab.http_post(path, **kwargs) @cli.register_custom_action("Group", ("scope", "search")) diff --git a/gitlab/v4/objects/issues.py b/gitlab/v4/objects/issues.py index c77a8d5..9272908 100644 --- a/gitlab/v4/objects/issues.py +++ b/gitlab/v4/objects/issues.py @@ -105,15 +105,14 @@ class ProjectIssue( ): _short_print_attr = "title" _id_attr = "iid" - _managers = ( - ("awardemojis", "ProjectIssueAwardEmojiManager"), - ("discussions", "ProjectIssueDiscussionManager"), - ("links", "ProjectIssueLinkManager"), - ("notes", "ProjectIssueNoteManager"), - ("resourcelabelevents", "ProjectIssueResourceLabelEventManager"), - ("resourcemilestoneevents", "ProjectIssueResourceMilestoneEventManager"), - ("resourcestateevents", "ProjectIssueResourceStateEventManager"), - ) + + awardemojis: ProjectIssueAwardEmojiManager + discussions: ProjectIssueDiscussionManager + links: "ProjectIssueLinkManager" + notes: ProjectIssueNoteManager + resourcelabelevents: ProjectIssueResourceLabelEventManager + resourcemilestoneevents: ProjectIssueResourceMilestoneEventManager + resourcestateevents: ProjectIssueResourceStateEventManager @cli.register_custom_action("ProjectIssue", ("to_project_id",)) @exc.on_http_error(exc.GitlabUpdateError) diff --git a/gitlab/v4/objects/members.py b/gitlab/v4/objects/members.py index 3ff8de5..0c92185 100644 --- a/gitlab/v4/objects/members.py +++ b/gitlab/v4/objects/members.py @@ -4,7 +4,6 @@ from gitlab.mixins import ( CRUDMixin, DeleteMixin, ListMixin, - MemberAllMixin, ObjectDeleteMixin, RetrieveMixin, SaveMixin, @@ -28,7 +27,7 @@ class GroupMember(SaveMixin, ObjectDeleteMixin, RESTObject): _short_print_attr = "username" -class GroupMemberManager(MemberAllMixin, CRUDMixin, RESTManager): +class GroupMemberManager(CRUDMixin, RESTManager): _path = "/groups/%(group_id)s/members" _obj_cls = GroupMember _from_parent_attrs = {"group_id": "id"} @@ -43,7 +42,8 @@ class GroupMemberManager(MemberAllMixin, CRUDMixin, RESTManager): class GroupBillableMember(ObjectDeleteMixin, RESTObject): _short_print_attr = "username" - _managers = (("memberships", "GroupBillableMemberMembershipManager"),) + + memberships: "GroupBillableMemberMembershipManager" class GroupBillableMemberManager(ListMixin, DeleteMixin, RESTManager): @@ -73,7 +73,7 @@ class ProjectMember(SaveMixin, ObjectDeleteMixin, RESTObject): _short_print_attr = "username" -class ProjectMemberManager(MemberAllMixin, CRUDMixin, RESTManager): +class ProjectMemberManager(CRUDMixin, RESTManager): _path = "/projects/%(project_id)s/members" _obj_cls = ProjectMember _from_parent_attrs = {"project_id": "id"} diff --git a/gitlab/v4/objects/merge_requests.py b/gitlab/v4/objects/merge_requests.py index 2559207..4def98c 100644 --- a/gitlab/v4/objects/merge_requests.py +++ b/gitlab/v4/objects/merge_requests.py @@ -139,18 +139,16 @@ class ProjectMergeRequest( ): _id_attr = "iid" - _managers = ( - ("approvals", "ProjectMergeRequestApprovalManager"), - ("approval_rules", "ProjectMergeRequestApprovalRuleManager"), - ("awardemojis", "ProjectMergeRequestAwardEmojiManager"), - ("diffs", "ProjectMergeRequestDiffManager"), - ("discussions", "ProjectMergeRequestDiscussionManager"), - ("notes", "ProjectMergeRequestNoteManager"), - ("pipelines", "ProjectMergeRequestPipelineManager"), - ("resourcelabelevents", "ProjectMergeRequestResourceLabelEventManager"), - ("resourcemilestoneevents", "ProjectMergeRequestResourceMilestoneEventManager"), - ("resourcestateevents", "ProjectMergeRequestResourceStateEventManager"), - ) + approval_rules: ProjectMergeRequestApprovalRuleManager + approvals: ProjectMergeRequestApprovalManager + awardemojis: ProjectMergeRequestAwardEmojiManager + diffs: "ProjectMergeRequestDiffManager" + discussions: ProjectMergeRequestDiscussionManager + notes: ProjectMergeRequestNoteManager + pipelines: ProjectMergeRequestPipelineManager + resourcelabelevents: ProjectMergeRequestResourceLabelEventManager + resourcemilestoneevents: ProjectMergeRequestResourceMilestoneEventManager + resourcestateevents: ProjectMergeRequestResourceStateEventManager @cli.register_custom_action("ProjectMergeRequest") @exc.on_http_error(exc.GitlabMROnBuildSuccessError) diff --git a/gitlab/v4/objects/notes.py b/gitlab/v4/objects/notes.py index d85fea7..cbd237e 100644 --- a/gitlab/v4/objects/notes.py +++ b/gitlab/v4/objects/notes.py @@ -71,7 +71,7 @@ class ProjectCommitDiscussionNoteManager( class ProjectIssueNote(SaveMixin, ObjectDeleteMixin, RESTObject): - _managers = (("awardemojis", "ProjectIssueNoteAwardEmojiManager"),) + awardemojis: ProjectIssueNoteAwardEmojiManager class ProjectIssueNoteManager(CRUDMixin, RESTManager): @@ -104,7 +104,7 @@ class ProjectIssueDiscussionNoteManager( class ProjectMergeRequestNote(SaveMixin, ObjectDeleteMixin, RESTObject): - _managers = (("awardemojis", "ProjectMergeRequestNoteAwardEmojiManager"),) + awardemojis: ProjectMergeRequestNoteAwardEmojiManager class ProjectMergeRequestNoteManager(CRUDMixin, RESTManager): @@ -137,7 +137,7 @@ class ProjectMergeRequestDiscussionNoteManager( class ProjectSnippetNote(SaveMixin, ObjectDeleteMixin, RESTObject): - _managers = (("awardemojis", "ProjectSnippetNoteAwardEmojiManager"),) + awardemojis: ProjectMergeRequestNoteAwardEmojiManager class ProjectSnippetNoteManager(CRUDMixin, RESTManager): diff --git a/gitlab/v4/objects/packages.py b/gitlab/v4/objects/packages.py index 3e9d9f2..e76a5c6 100644 --- a/gitlab/v4/objects/packages.py +++ b/gitlab/v4/objects/packages.py @@ -105,7 +105,7 @@ class GenericPackageManager(RESTManager): file_name (str): The name of the file in the registry streamed (bool): If True the data will be processed by chunks of `chunk_size` and each chunk is passed to `action` for - reatment + treatment action (callable): Callable responsible of dealing with chunk of data chunk_size (int): Size of each chunk @@ -143,7 +143,7 @@ class GroupPackageManager(ListMixin, RESTManager): class ProjectPackage(ObjectDeleteMixin, RESTObject): - _managers = (("package_files", "ProjectPackageFileManager"),) + package_files: "ProjectPackageFileManager" class ProjectPackageManager(ListMixin, GetMixin, DeleteMixin, RESTManager): diff --git a/gitlab/v4/objects/personal_access_tokens.py b/gitlab/v4/objects/personal_access_tokens.py index a326bd6..6cdb305 100644 --- a/gitlab/v4/objects/personal_access_tokens.py +++ b/gitlab/v4/objects/personal_access_tokens.py @@ -1,17 +1,32 @@ -from gitlab.base import RESTManager, RESTObject -from gitlab.mixins import ListMixin +from gitlab.base import RequiredOptional, RESTManager, RESTObject +from gitlab.mixins import CreateMixin, DeleteMixin, ListMixin, ObjectDeleteMixin __all__ = [ "PersonalAccessToken", "PersonalAccessTokenManager", + "UserPersonalAccessToken", + "UserPersonalAccessTokenManager", ] -class PersonalAccessToken(RESTObject): +class PersonalAccessToken(ObjectDeleteMixin, RESTObject): pass -class PersonalAccessTokenManager(ListMixin, RESTManager): +class PersonalAccessTokenManager(DeleteMixin, ListMixin, RESTManager): _path = "/personal_access_tokens" _obj_cls = PersonalAccessToken _list_filters = ("user_id",) + + +class UserPersonalAccessToken(RESTObject): + pass + + +class UserPersonalAccessTokenManager(CreateMixin, RESTManager): + _path = "/users/%(user_id)s/personal_access_tokens" + _obj_cls = UserPersonalAccessToken + _from_parent_attrs = {"user_id": "id"} + _create_attrs = RequiredOptional( + required=("name", "scopes"), optional=("expires_at",) + ) diff --git a/gitlab/v4/objects/pipelines.py b/gitlab/v4/objects/pipelines.py index 5118e78..2d212a6 100644 --- a/gitlab/v4/objects/pipelines.py +++ b/gitlab/v4/objects/pipelines.py @@ -1,5 +1,3 @@ -import warnings - from gitlab import cli from gitlab import exceptions as exc from gitlab.base import RequiredOptional, RESTManager, RESTObject @@ -45,41 +43,12 @@ class ProjectMergeRequestPipelineManager(CreateMixin, ListMixin, RESTManager): _obj_cls = ProjectMergeRequestPipeline _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"} - # If the manager was called directly as a callable via - # mr.pipelines(), execute the deprecated method for now. - # TODO: in python-gitlab 3.0.0, remove this method entirely. - - @cli.register_custom_action("ProjectMergeRequest", custom_action="pipelines") - @exc.on_http_error(exc.GitlabListError) - def __call__(self, **kwargs): - """List the merge request pipelines. - - Args: - **kwargs: Extra options to send to the server (e.g. sudo) - - Raises: - GitlabAuthenticationError: If authentication is not correct - GitlabListError: If the list could not be retrieved - - Returns: - RESTObjectList: List of changes - """ - warnings.warn( - "Calling the ProjectMergeRequest.pipelines() method on " - "merge request objects directly is deprecated and will be replaced " - "by ProjectMergeRequest.pipelines.list() in python-gitlab 3.0.0.\n", - DeprecationWarning, - ) - return self.list(**kwargs) - class ProjectPipeline(RefreshMixin, ObjectDeleteMixin, RESTObject): - _managers = ( - ("jobs", "ProjectPipelineJobManager"), - ("bridges", "ProjectPipelineBridgeManager"), - ("variables", "ProjectPipelineVariableManager"), - ("test_report", "ProjectPipelineTestReportManager"), - ) + bridges: "ProjectPipelineBridgeManager" + jobs: "ProjectPipelineJobManager" + test_report: "ProjectPipelineTestReportManager" + variables: "ProjectPipelineVariableManager" @cli.register_custom_action("ProjectPipeline") @exc.on_http_error(exc.GitlabPipelineCancelError) @@ -199,7 +168,7 @@ class ProjectPipelineScheduleVariableManager( class ProjectPipelineSchedule(SaveMixin, ObjectDeleteMixin, RESTObject): - _managers = (("variables", "ProjectPipelineScheduleVariableManager"),) + variables: ProjectPipelineScheduleVariableManager @cli.register_custom_action("ProjectPipelineSchedule") @exc.on_http_error(exc.GitlabOwnershipError) diff --git a/gitlab/v4/objects/projects.py b/gitlab/v4/objects/projects.py index ee7aca8..551079a 100644 --- a/gitlab/v4/objects/projects.py +++ b/gitlab/v4/objects/projects.py @@ -110,60 +110,58 @@ class GroupProjectManager(ListMixin, RESTManager): class Project(RefreshMixin, SaveMixin, ObjectDeleteMixin, RepositoryMixin, RESTObject): _short_print_attr = "path" - _managers = ( - ("access_tokens", "ProjectAccessTokenManager"), - ("accessrequests", "ProjectAccessRequestManager"), - ("approvals", "ProjectApprovalManager"), - ("approvalrules", "ProjectApprovalRuleManager"), - ("badges", "ProjectBadgeManager"), - ("boards", "ProjectBoardManager"), - ("branches", "ProjectBranchManager"), - ("jobs", "ProjectJobManager"), - ("commits", "ProjectCommitManager"), - ("customattributes", "ProjectCustomAttributeManager"), - ("deployments", "ProjectDeploymentManager"), - ("environments", "ProjectEnvironmentManager"), - ("events", "ProjectEventManager"), - ("audit_events", "ProjectAuditEventManager"), - ("exports", "ProjectExportManager"), - ("files", "ProjectFileManager"), - ("forks", "ProjectForkManager"), - ("generic_packages", "GenericPackageManager"), - ("hooks", "ProjectHookManager"), - ("keys", "ProjectKeyManager"), - ("imports", "ProjectImportManager"), - ("issues", "ProjectIssueManager"), - ("labels", "ProjectLabelManager"), - ("members", "ProjectMemberManager"), - ("members_all", "ProjectMemberAllManager"), - ("mergerequests", "ProjectMergeRequestManager"), - ("milestones", "ProjectMilestoneManager"), - ("notes", "ProjectNoteManager"), - ("notificationsettings", "ProjectNotificationSettingsManager"), - ("packages", "ProjectPackageManager"), - ("pagesdomains", "ProjectPagesDomainManager"), - ("pipelines", "ProjectPipelineManager"), - ("protectedbranches", "ProjectProtectedBranchManager"), - ("protectedtags", "ProjectProtectedTagManager"), - ("pipelineschedules", "ProjectPipelineScheduleManager"), - ("pushrules", "ProjectPushRulesManager"), - ("releases", "ProjectReleaseManager"), - ("remote_mirrors", "ProjectRemoteMirrorManager"), - ("repositories", "ProjectRegistryRepositoryManager"), - ("runners", "ProjectRunnerManager"), - ("services", "ProjectServiceManager"), - ("snippets", "ProjectSnippetManager"), - ("tags", "ProjectTagManager"), - ("users", "ProjectUserManager"), - ("triggers", "ProjectTriggerManager"), - ("variables", "ProjectVariableManager"), - ("wikis", "ProjectWikiManager"), - ("clusters", "ProjectClusterManager"), - ("additionalstatistics", "ProjectAdditionalStatisticsManager"), - ("issues_statistics", "ProjectIssuesStatisticsManager"), - ("issuesstatistics", "ProjectIssuesStatisticsManager"), # Deprecated - ("deploytokens", "ProjectDeployTokenManager"), - ) + + access_tokens: ProjectAccessTokenManager + accessrequests: ProjectAccessRequestManager + additionalstatistics: ProjectAdditionalStatisticsManager + approvalrules: ProjectApprovalRuleManager + approvals: ProjectApprovalManager + audit_events: ProjectAuditEventManager + badges: ProjectBadgeManager + boards: ProjectBoardManager + branches: ProjectBranchManager + clusters: ProjectClusterManager + commits: ProjectCommitManager + customattributes: ProjectCustomAttributeManager + deployments: ProjectDeploymentManager + deploytokens: ProjectDeployTokenManager + environments: ProjectEnvironmentManager + events: ProjectEventManager + exports: ProjectExportManager + files: ProjectFileManager + forks: "ProjectForkManager" + generic_packages: GenericPackageManager + hooks: ProjectHookManager + imports: ProjectImportManager + issues: ProjectIssueManager + issues_statistics: ProjectIssuesStatisticsManager + jobs: ProjectJobManager + keys: ProjectKeyManager + labels: ProjectLabelManager + members: ProjectMemberManager + members_all: ProjectMemberAllManager + mergerequests: ProjectMergeRequestManager + milestones: ProjectMilestoneManager + notes: ProjectNoteManager + notificationsettings: ProjectNotificationSettingsManager + packages: ProjectPackageManager + pagesdomains: ProjectPagesDomainManager + pipelines: ProjectPipelineManager + pipelineschedules: ProjectPipelineScheduleManager + protectedbranches: ProjectProtectedBranchManager + protectedtags: ProjectProtectedTagManager + pushrules: ProjectPushRulesManager + releases: ProjectReleaseManager + remote_mirrors: "ProjectRemoteMirrorManager" + repositories: ProjectRegistryRepositoryManager + runners: ProjectRunnerManager + services: ProjectServiceManager + snippets: ProjectSnippetManager + tags: ProjectTagManager + triggers: ProjectTriggerManager + users: ProjectUserManager + variables: ProjectVariableManager + wikis: ProjectWikiManager @cli.register_custom_action("Project", ("forked_from_id",)) @exc.on_http_error(exc.GitlabCreateError) @@ -517,7 +515,7 @@ class Project(RefreshMixin, SaveMixin, ObjectDeleteMixin, RepositoryMixin, RESTO Raises: GitlabAuthenticationError: If authentication is not correct - GitlabTransferProjectError: If the project could not be transfered + GitlabTransferProjectError: If the project could not be transferred """ path = "/projects/%s/transfer" % (self.id,) self.manager.gitlab.http_put( diff --git a/gitlab/v4/objects/releases.py b/gitlab/v4/objects/releases.py index e27052d..2af3248 100644 --- a/gitlab/v4/objects/releases.py +++ b/gitlab/v4/objects/releases.py @@ -11,7 +11,8 @@ __all__ = [ class ProjectRelease(SaveMixin, RESTObject): _id_attr = "tag_name" - _managers = (("links", "ProjectReleaseLinkManager"),) + + links: "ProjectReleaseLinkManager" class ProjectReleaseManager(CRUDMixin, RESTManager): diff --git a/gitlab/v4/objects/runners.py b/gitlab/v4/objects/runners.py index 8a18f9b..a32dc84 100644 --- a/gitlab/v4/objects/runners.py +++ b/gitlab/v4/objects/runners.py @@ -34,7 +34,7 @@ class RunnerJobManager(ListMixin, RESTManager): class Runner(SaveMixin, ObjectDeleteMixin, RESTObject): - _managers = (("jobs", "RunnerJobManager"),) + jobs: RunnerJobManager class RunnerManager(CRUDMixin, RESTManager): diff --git a/gitlab/v4/objects/sidekiq.py b/gitlab/v4/objects/sidekiq.py index 54238ab..dc1094a 100644 --- a/gitlab/v4/objects/sidekiq.py +++ b/gitlab/v4/objects/sidekiq.py @@ -10,14 +10,14 @@ __all__ = [ class SidekiqManager(RESTManager): """Manager for the Sidekiq methods. - This manager doesn't actually manage objects but provides helper fonction + This manager doesn't actually manage objects but provides helper function for the sidekiq metrics API. """ @cli.register_custom_action("SidekiqManager") @exc.on_http_error(exc.GitlabGetError) def queue_metrics(self, **kwargs): - """Return the registred queues information. + """Return the registered queues information. Args: **kwargs: Extra options to send to the server (e.g. sudo) @@ -34,7 +34,7 @@ class SidekiqManager(RESTManager): @cli.register_custom_action("SidekiqManager") @exc.on_http_error(exc.GitlabGetError) def process_metrics(self, **kwargs): - """Return the registred sidekiq workers. + """Return the registered sidekiq workers. Args: **kwargs: Extra options to send to the server (e.g. sudo) diff --git a/gitlab/v4/objects/snippets.py b/gitlab/v4/objects/snippets.py index b893eca..164b30c 100644 --- a/gitlab/v4/objects/snippets.py +++ b/gitlab/v4/objects/snippets.py @@ -77,11 +77,10 @@ class SnippetManager(CRUDMixin, RESTManager): class ProjectSnippet(UserAgentDetailMixin, SaveMixin, ObjectDeleteMixin, RESTObject): _url = "/projects/%(project_id)s/snippets" _short_print_attr = "title" - _managers = ( - ("awardemojis", "ProjectSnippetAwardEmojiManager"), - ("discussions", "ProjectSnippetDiscussionManager"), - ("notes", "ProjectSnippetNoteManager"), - ) + + awardemojis: ProjectSnippetAwardEmojiManager + discussions: ProjectSnippetDiscussionManager + notes: ProjectSnippetNoteManager @cli.register_custom_action("ProjectSnippet") @exc.on_http_error(exc.GitlabGetError) diff --git a/gitlab/v4/objects/tags.py b/gitlab/v4/objects/tags.py index cf37e21..44fc23c 100644 --- a/gitlab/v4/objects/tags.py +++ b/gitlab/v4/objects/tags.py @@ -1,5 +1,3 @@ -from gitlab import cli -from gitlab import exceptions as exc from gitlab.base import RequiredOptional, RESTManager, RESTObject from gitlab.mixins import NoUpdateMixin, ObjectDeleteMixin @@ -15,41 +13,6 @@ class ProjectTag(ObjectDeleteMixin, RESTObject): _id_attr = "name" _short_print_attr = "name" - @cli.register_custom_action("ProjectTag", ("description",)) - def set_release_description(self, description, **kwargs): - """Set the release notes on the tag. - - If the release doesn't exist yet, it will be created. If it already - exists, its description will be updated. - - Args: - description (str): Description of the release. - **kwargs: Extra options to send to the server (e.g. sudo) - - Raises: - GitlabAuthenticationError: If authentication is not correct - GitlabCreateError: If the server fails to create the release - GitlabUpdateError: If the server fails to update the release - """ - id = self.get_id().replace("/", "%2F") - path = "%s/%s/release" % (self.manager.path, id) - data = {"description": description} - if self.release is None: - try: - server_data = self.manager.gitlab.http_post( - path, post_data=data, **kwargs - ) - except exc.GitlabHttpError as e: - raise exc.GitlabCreateError(e.response_code, e.error_message) from e - else: - try: - server_data = self.manager.gitlab.http_put( - path, post_data=data, **kwargs - ) - except exc.GitlabHttpError as e: - raise exc.GitlabUpdateError(e.response_code, e.error_message) from e - self.release = server_data - class ProjectTagManager(NoUpdateMixin, RESTManager): _path = "/projects/%(project_id)s/repository/tags" diff --git a/gitlab/v4/objects/todos.py b/gitlab/v4/objects/todos.py index 23a0614..de82437 100644 --- a/gitlab/v4/objects/todos.py +++ b/gitlab/v4/objects/todos.py @@ -45,6 +45,6 @@ class TodoManager(ListMixin, DeleteMixin, RESTManager): GitlabTodoError: If the server failed to perform the request Returns: - int: The number of todos maked done + int: The number of todos marked done """ self.gitlab.http_post("/todos/mark_as_done", **kwargs) diff --git a/gitlab/v4/objects/users.py b/gitlab/v4/objects/users.py index 9e5fd09..e4bbcf1 100644 --- a/gitlab/v4/objects/users.py +++ b/gitlab/v4/objects/users.py @@ -21,6 +21,7 @@ from gitlab.mixins import ( from .custom_attributes import UserCustomAttributeManager # noqa: F401 from .events import UserEventManager # noqa: F401 +from .personal_access_tokens import UserPersonalAccessTokenManager # noqa: F401 __all__ = [ "CurrentUserEmail", @@ -101,12 +102,11 @@ class CurrentUserStatusManager(GetWithoutIdMixin, UpdateMixin, RESTManager): class CurrentUser(RESTObject): _id_attr = None _short_print_attr = "username" - _managers = ( - ("status", "CurrentUserStatusManager"), - ("emails", "CurrentUserEmailManager"), - ("gpgkeys", "CurrentUserGPGKeyManager"), - ("keys", "CurrentUserKeyManager"), - ) + + emails: CurrentUserEmailManager + gpgkeys: CurrentUserGPGKeyManager + keys: CurrentUserKeyManager + status: CurrentUserStatusManager class CurrentUserManager(GetWithoutIdMixin, RESTManager): @@ -116,20 +116,20 @@ class CurrentUserManager(GetWithoutIdMixin, RESTManager): class User(SaveMixin, ObjectDeleteMixin, RESTObject): _short_print_attr = "username" - _managers = ( - ("customattributes", "UserCustomAttributeManager"), - ("emails", "UserEmailManager"), - ("followers_users", "UserFollowersManager"), - ("following_users", "UserFollowingManager"), - ("events", "UserEventManager"), - ("gpgkeys", "UserGPGKeyManager"), - ("identityproviders", "UserIdentityProviderManager"), - ("impersonationtokens", "UserImpersonationTokenManager"), - ("keys", "UserKeyManager"), - ("memberships", "UserMembershipManager"), - ("projects", "UserProjectManager"), - ("status", "UserStatusManager"), - ) + + customattributes: UserCustomAttributeManager + emails: "UserEmailManager" + events: UserEventManager + followers_users: "UserFollowersManager" + following_users: "UserFollowingManager" + gpgkeys: "UserGPGKeyManager" + identityproviders: "UserIdentityProviderManager" + impersonationtokens: "UserImpersonationTokenManager" + keys: "UserKeyManager" + memberships: "UserMembershipManager" + personal_access_tokens: UserPersonalAccessTokenManager + projects: "UserProjectManager" + status: "UserStatusManager" @cli.register_custom_action("User") @exc.on_http_error(exc.GitlabBlockError) |
