summaryrefslogtreecommitdiff
path: root/gitlab
diff options
context:
space:
mode:
Diffstat (limited to 'gitlab')
-rw-r--r--gitlab/base.py21
-rw-r--r--gitlab/client.py63
-rw-r--r--gitlab/const.py9
-rw-r--r--gitlab/mixins.py52
-rw-r--r--gitlab/v4/cli.py5
-rw-r--r--gitlab/v4/objects/__init__.py11
-rw-r--r--gitlab/v4/objects/boards.py4
-rw-r--r--gitlab/v4/objects/branches.py46
-rw-r--r--gitlab/v4/objects/commits.py9
-rw-r--r--gitlab/v4/objects/container_registry.py26
-rw-r--r--gitlab/v4/objects/deployments.py2
-rw-r--r--gitlab/v4/objects/discussions.py8
-rw-r--r--gitlab/v4/objects/epics.py7
-rw-r--r--gitlab/v4/objects/features.py2
-rw-r--r--gitlab/v4/objects/groups.py65
-rw-r--r--gitlab/v4/objects/issues.py17
-rw-r--r--gitlab/v4/objects/members.py8
-rw-r--r--gitlab/v4/objects/merge_requests.py22
-rw-r--r--gitlab/v4/objects/notes.py6
-rw-r--r--gitlab/v4/objects/packages.py4
-rw-r--r--gitlab/v4/objects/personal_access_tokens.py23
-rw-r--r--gitlab/v4/objects/pipelines.py41
-rw-r--r--gitlab/v4/objects/projects.py108
-rw-r--r--gitlab/v4/objects/releases.py3
-rw-r--r--gitlab/v4/objects/runners.py2
-rw-r--r--gitlab/v4/objects/sidekiq.py6
-rw-r--r--gitlab/v4/objects/snippets.py9
-rw-r--r--gitlab/v4/objects/tags.py37
-rw-r--r--gitlab/v4/objects/todos.py2
-rw-r--r--gitlab/v4/objects/users.py40
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)