From de3a1b67f0f149853e8d91eeb537c08a46cf37f3 Mon Sep 17 00:00:00 2001 From: Ian Turner Date: Sat, 11 Jun 2022 10:56:39 -0700 Subject: [PATCH 1/4] Previous patch --- gitlab/base.py | 13 +++--- gitlab/client.py | 2 +- gitlab/mixins.py | 70 ++++++++++++++++++--------------- gitlab/v4/cli.py | 4 +- gitlab/v4/objects/milestones.py | 9 +++-- gitlab/v4/objects/snippets.py | 4 +- gitlab/v4/objects/users.py | 4 +- 7 files changed, 57 insertions(+), 49 deletions(-) mode change 100644 => 100755 gitlab/v4/cli.py diff --git a/gitlab/base.py b/gitlab/base.py index 6e7d5c584..bd2c6d132 100644 --- a/gitlab/base.py +++ b/gitlab/base.py @@ -19,7 +19,7 @@ import pprint import textwrap from types import ModuleType -from typing import Any, Dict, Iterable, Optional, Type, Union +from typing import Any, Dict, Generic, Iterable, Iterator, Optional, Collection, Type, TypeVar, Union import gitlab from gitlab import types as g_types @@ -245,8 +245,9 @@ def attributes(self) -> Dict[str, Any]: d.update(self.__dict__["_parent_attrs"]) return d +T = TypeVar("T", bound=RESTObject) -class RESTObjectList: +class RESTObjectList(Generic[T], Iterable[T]): """Generator object representing a list of RESTObject's. This generator uses the Gitlab pagination system to fetch new data when @@ -262,7 +263,7 @@ class RESTObjectList: """ def __init__( - self, manager: "RESTManager", obj_cls: Type[RESTObject], _list: GitlabList + self, manager: "RESTManager", obj_cls: Type[T], _list: GitlabList ) -> None: """Creates an objects list from a GitlabList. @@ -278,16 +279,16 @@ def __init__( self._obj_cls = obj_cls self._list = _list - def __iter__(self) -> "RESTObjectList": + def __iter__(self) -> Iterator[T]: return self def __len__(self) -> int: return len(self._list) - def __next__(self) -> RESTObject: + def __next__(self) -> T: return self.next() - def next(self) -> RESTObject: + def next(self) -> T: data = self._list.next() return self._obj_cls(self.manager, data, created_from_list=True) diff --git a/gitlab/client.py b/gitlab/client.py index f5d12dfc1..258770d86 100644 --- a/gitlab/client.py +++ b/gitlab/client.py @@ -815,7 +815,7 @@ def http_list( as_list: Optional[bool] = None, # Deprecated in favor of `iterator` iterator: Optional[bool] = None, **kwargs: Any, - ) -> Union["GitlabList", List[Dict[str, Any]]]: + ) -> Union[GitlabList, List[Dict[str, int]]]: """Make a GET request to the Gitlab server for list-oriented queries. Args: diff --git a/gitlab/mixins.py b/gitlab/mixins.py index 4dee7106a..14b7cbb18 100644 --- a/gitlab/mixins.py +++ b/gitlab/mixins.py @@ -20,10 +20,13 @@ Any, Callable, Dict, + Generic, List, Optional, + Iterable, Tuple, Type, + TypeVar, TYPE_CHECKING, Union, ) @@ -67,13 +70,16 @@ _RestManagerBase = object _RestObjectBase = object +T1 = TypeVar("T1", bound=base.RESTObject) +T2 = TypeVar("T2", bound=base.RESTObject) -class GetMixin(_RestManagerBase): + +class GetMixin(_RestManagerBase, Generic[T1, T2]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Optional[Type[base.RESTObject]] + _obj_cls: Type[T1] _optional_get_attrs: Tuple[str, ...] = () - _parent: Optional[base.RESTObject] + _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab @@ -81,7 +87,7 @@ class GetMixin(_RestManagerBase): @exc.on_http_error(exc.GitlabGetError) def get( self, id: Union[str, int], lazy: bool = False, **kwargs: Any - ) -> base.RESTObject: + ) -> T1: """Retrieve a single object. Args: @@ -113,12 +119,12 @@ def get( return self._obj_cls(self, server_data) -class GetWithoutIdMixin(_RestManagerBase): +class GetWithoutIdMixin(_RestManagerBase, Generic[T1, T2]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Optional[Type[base.RESTObject]] + _obj_cls: Type[T1] _optional_get_attrs: Tuple[str, ...] = () - _parent: Optional[base.RESTObject] + _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab @@ -181,18 +187,18 @@ def refresh(self, **kwargs: Any) -> None: self._update_attrs(server_data) -class ListMixin(_RestManagerBase): +class ListMixin(_RestManagerBase, Generic[T1, T2]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] _list_filters: Tuple[str, ...] = () - _obj_cls: Optional[Type[base.RESTObject]] - _parent: Optional[base.RESTObject] + _obj_cls: Type[T1] + _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab @exc.on_http_error(exc.GitlabListError) - def list(self, **kwargs: Any) -> Union[base.RESTObjectList, List[base.RESTObject]]: + def list(self, **kwargs: Any) -> Iterable[T1]: """Retrieve a list of objects. Args: @@ -234,21 +240,21 @@ def list(self, **kwargs: Any) -> Union[base.RESTObjectList, List[base.RESTObject return base.RESTObjectList(self, self._obj_cls, obj) -class RetrieveMixin(ListMixin, GetMixin): +class RetrieveMixin(ListMixin, GetMixin, Generic[T1, T2]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Optional[Type[base.RESTObject]] - _parent: Optional[base.RESTObject] + _obj_cls: Type[T1] + _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab -class CreateMixin(_RestManagerBase): +class CreateMixin(_RestManagerBase, Generic[T1, T2]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Optional[Type[base.RESTObject]] - _parent: Optional[base.RESTObject] + _obj_cls: Type[T1] + _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab @@ -287,11 +293,11 @@ def create( return self._obj_cls(self, server_data) -class UpdateMixin(_RestManagerBase): +class UpdateMixin(_RestManagerBase, Generic[T1, T2]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Optional[Type[base.RESTObject]] - _parent: Optional[base.RESTObject] + _obj_cls: Type[T1] + _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] _update_uses_post: bool = False @@ -352,11 +358,11 @@ def update( return result -class SetMixin(_RestManagerBase): +class SetMixin(_RestManagerBase, Generic[T1, T2]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Optional[Type[base.RESTObject]] - _parent: Optional[base.RESTObject] + _obj_cls: Type[T1] + _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab @@ -386,11 +392,11 @@ def set(self, key: str, value: str, **kwargs: Any) -> base.RESTObject: return self._obj_cls(self, server_data) -class DeleteMixin(_RestManagerBase): +class DeleteMixin(_RestManagerBase, Generic[T1, T2]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Optional[Type[base.RESTObject]] - _parent: Optional[base.RESTObject] + _obj_cls: Type[T1] + _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab @@ -417,21 +423,21 @@ def delete(self, id: Optional[Union[str, int]] = None, **kwargs: Any) -> None: self.gitlab.http_delete(path, **kwargs) -class CRUDMixin(GetMixin, ListMixin, CreateMixin, UpdateMixin, DeleteMixin): +class CRUDMixin(GetMixin, ListMixin, CreateMixin, UpdateMixin, DeleteMixin, Generic[T1, T2]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Optional[Type[base.RESTObject]] - _parent: Optional[base.RESTObject] + _obj_cls: Type[T1] + _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab -class NoUpdateMixin(GetMixin, ListMixin, CreateMixin, DeleteMixin): +class NoUpdateMixin(GetMixin, ListMixin, CreateMixin, DeleteMixin, Generic[T1, T2]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Optional[Type[base.RESTObject]] - _parent: Optional[base.RESTObject] + _obj_cls: Type[T1] + _parent: T2 _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab diff --git a/gitlab/v4/cli.py b/gitlab/v4/cli.py old mode 100644 new mode 100755 index 2b0d4ce72..026898b56 --- a/gitlab/v4/cli.py +++ b/gitlab/v4/cli.py @@ -19,7 +19,7 @@ import argparse import operator import sys -from typing import Any, Dict, List, Optional, Type, TYPE_CHECKING, Union +from typing import Any, Dict, Iterable, List, Optional, Type, TYPE_CHECKING, Union import gitlab import gitlab.base @@ -143,7 +143,7 @@ def do_create(self) -> gitlab.base.RESTObject: def do_list( self, - ) -> Union[gitlab.base.RESTObjectList, List[gitlab.base.RESTObject]]: + ) -> Iterable[gitlab.base.RESTObjectList]: if TYPE_CHECKING: assert isinstance(self.mgr, gitlab.mixins.ListMixin) try: diff --git a/gitlab/v4/objects/milestones.py b/gitlab/v4/objects/milestones.py index 2d82a59c7..3b6578ce4 100644 --- a/gitlab/v4/objects/milestones.py +++ b/gitlab/v4/objects/milestones.py @@ -3,6 +3,7 @@ from gitlab import cli from gitlab import exceptions as exc from gitlab import types +from gitlab.client import GitlabList from gitlab.base import RESTManager, RESTObject, RESTObjectList from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, PromoteMixin, SaveMixin from gitlab.types import RequiredOptional @@ -47,7 +48,7 @@ def issues(self, **kwargs: Any) -> RESTObjectList: path = f"{self.manager.path}/{self.encoded_id}/issues" data_list = self.manager.gitlab.http_list(path, iterator=True, **kwargs) if TYPE_CHECKING: - assert isinstance(data_list, RESTObjectList) + assert isinstance(data_list, GitlabList) manager = GroupIssueManager(self.manager.gitlab, parent=self.manager._parent) # FIXME(gpocentek): the computed manager path is not correct return RESTObjectList(manager, GroupIssue, data_list) @@ -73,7 +74,7 @@ def merge_requests(self, **kwargs: Any) -> RESTObjectList: path = f"{self.manager.path}/{self.encoded_id}/merge_requests" data_list = self.manager.gitlab.http_list(path, iterator=True, **kwargs) if TYPE_CHECKING: - assert isinstance(data_list, RESTObjectList) + assert isinstance(data_list, GitlabList) manager = GroupIssueManager(self.manager.gitlab, parent=self.manager._parent) # FIXME(gpocentek): the computed manager path is not correct return RESTObjectList(manager, GroupMergeRequest, data_list) @@ -124,7 +125,7 @@ def issues(self, **kwargs: Any) -> RESTObjectList: path = f"{self.manager.path}/{self.encoded_id}/issues" data_list = self.manager.gitlab.http_list(path, iterator=True, **kwargs) if TYPE_CHECKING: - assert isinstance(data_list, RESTObjectList) + assert isinstance(data_list, GitlabList) manager = ProjectIssueManager(self.manager.gitlab, parent=self.manager._parent) # FIXME(gpocentek): the computed manager path is not correct return RESTObjectList(manager, ProjectIssue, data_list) @@ -150,7 +151,7 @@ def merge_requests(self, **kwargs: Any) -> RESTObjectList: path = f"{self.manager.path}/{self.encoded_id}/merge_requests" data_list = self.manager.gitlab.http_list(path, iterator=True, **kwargs) if TYPE_CHECKING: - assert isinstance(data_list, RESTObjectList) + assert isinstance(data_list, GitlabList) manager = ProjectMergeRequestManager( self.manager.gitlab, parent=self.manager._parent ) diff --git a/gitlab/v4/objects/snippets.py b/gitlab/v4/objects/snippets.py index 597a3aaf0..5cea9fd52 100644 --- a/gitlab/v4/objects/snippets.py +++ b/gitlab/v4/objects/snippets.py @@ -1,4 +1,4 @@ -from typing import Any, Callable, cast, List, Optional, TYPE_CHECKING, Union +from typing import Any, Callable, cast, Iterable, List, Optional, TYPE_CHECKING, Union import requests @@ -71,7 +71,7 @@ class SnippetManager(CRUDMixin, RESTManager): ) @cli.register_custom_action("SnippetManager") - def public(self, **kwargs: Any) -> Union[RESTObjectList, List[RESTObject]]: + def public(self, **kwargs: Any) -> Iterable[Snippet]: """List all the public snippets. Args: diff --git a/gitlab/v4/objects/users.py b/gitlab/v4/objects/users.py index acd3b2f76..97c073f11 100644 --- a/gitlab/v4/objects/users.py +++ b/gitlab/v4/objects/users.py @@ -3,7 +3,7 @@ https://docs.gitlab.com/ee/api/users.html https://docs.gitlab.com/ee/api/projects.html#list-projects-starred-by-a-user """ -from typing import Any, cast, Dict, List, Optional, Union +from typing import Any, cast, Dict, Iterable, List, Optional, Union import requests @@ -532,7 +532,7 @@ class UserProjectManager(ListMixin, CreateMixin, RESTManager): "id_before", ) - def list(self, **kwargs: Any) -> Union[RESTObjectList, List[RESTObject]]: + def list(self, **kwargs: Any) -> Iterable[UserProject]: """Retrieve a list of objects. Args: From bd64572c5cf2cd219b1dfa70276b96fbf5b5f8c4 Mon Sep 17 00:00:00 2001 From: Ian Turner Date: Sat, 11 Jun 2022 17:34:50 -0700 Subject: [PATCH 2/4] Remove duplicate type definitions in middle-level abstract classes --- gitlab/base.py | 10 ++++++---- gitlab/mixins.py | 50 +++++++++++++++--------------------------------- 2 files changed, 21 insertions(+), 39 deletions(-) diff --git a/gitlab/base.py b/gitlab/base.py index bd2c6d132..cfdbe4204 100644 --- a/gitlab/base.py +++ b/gitlab/base.py @@ -328,8 +328,10 @@ def total(self) -> Optional[int]: """The total number of items.""" return self._list.total +T_obj = TypeVar("T_obj", bound=RESTObject) +T_parent = TypeVar("T_parent", bound=Optional[RESTObject]) -class RESTManager: +class RESTManager(Generic[T_obj, T_parent]): """Base class for CRUD operations on objects. Derived class must define ``_path`` and ``_obj_cls``. @@ -341,16 +343,16 @@ class RESTManager: _create_attrs: g_types.RequiredOptional = g_types.RequiredOptional() _update_attrs: g_types.RequiredOptional = g_types.RequiredOptional() _path: Optional[str] = None - _obj_cls: Optional[Type[RESTObject]] = None + _obj_cls: Type[T_obj] _from_parent_attrs: Dict[str, Any] = {} _types: Dict[str, Type[g_types.GitlabAttribute]] = {} _computed_path: Optional[str] - _parent: Optional[RESTObject] + _parent: Optional[T_parent] _parent_attrs: Dict[str, Any] gitlab: Gitlab - def __init__(self, gl: Gitlab, parent: Optional[RESTObject] = None) -> None: + def __init__(self, gl: Gitlab, parent: Optional[T_parent] = None) -> None: """REST manager constructor. Args: diff --git a/gitlab/mixins.py b/gitlab/mixins.py index 14b7cbb18..3bdf6fef4 100644 --- a/gitlab/mixins.py +++ b/gitlab/mixins.py @@ -70,16 +70,14 @@ _RestManagerBase = object _RestObjectBase = object -T1 = TypeVar("T1", bound=base.RESTObject) -T2 = TypeVar("T2", bound=base.RESTObject) +T_obj = TypeVar("T_obj", bound=base.RESTObject) +T_parent = TypeVar("T_parent", bound=Optional[base.RESTObject]) -class GetMixin(_RestManagerBase, Generic[T1, T2]): +class GetMixin(_RestManagerBase[T_obj, T_parent], Generic[T_obj, T_parent]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Type[T1] _optional_get_attrs: Tuple[str, ...] = () - _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab @@ -87,7 +85,7 @@ class GetMixin(_RestManagerBase, Generic[T1, T2]): @exc.on_http_error(exc.GitlabGetError) def get( self, id: Union[str, int], lazy: bool = False, **kwargs: Any - ) -> T1: + ) -> T_obj: """Retrieve a single object. Args: @@ -119,12 +117,10 @@ def get( return self._obj_cls(self, server_data) -class GetWithoutIdMixin(_RestManagerBase, Generic[T1, T2]): +class GetWithoutIdMixin(_RestManagerBase[T_obj, T_parent], Generic[T_obj, T_parent]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Type[T1] _optional_get_attrs: Tuple[str, ...] = () - _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab @@ -187,18 +183,16 @@ def refresh(self, **kwargs: Any) -> None: self._update_attrs(server_data) -class ListMixin(_RestManagerBase, Generic[T1, T2]): +class ListMixin(_RestManagerBase[T_obj, T_parent], Generic[T_obj, T_parent]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] _list_filters: Tuple[str, ...] = () - _obj_cls: Type[T1] - _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab @exc.on_http_error(exc.GitlabListError) - def list(self, **kwargs: Any) -> Iterable[T1]: + def list(self, **kwargs: Any) -> Iterable[T_obj]: """Retrieve a list of objects. Args: @@ -240,21 +234,17 @@ def list(self, **kwargs: Any) -> Iterable[T1]: return base.RESTObjectList(self, self._obj_cls, obj) -class RetrieveMixin(ListMixin, GetMixin, Generic[T1, T2]): +class RetrieveMixin(ListMixin[T_obj, T_parent], GetMixin[T_obj, T_parent], Generic[T_obj, T_parent]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Type[T1] - _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab -class CreateMixin(_RestManagerBase, Generic[T1, T2]): +class CreateMixin(_RestManagerBase[T_obj, T_parent], Generic[T_obj, T_parent]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Type[T1] - _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab @@ -293,11 +283,9 @@ def create( return self._obj_cls(self, server_data) -class UpdateMixin(_RestManagerBase, Generic[T1, T2]): +class UpdateMixin(_RestManagerBase[T_obj, T_parent], Generic[T_obj, T_parent]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Type[T1] - _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] _update_uses_post: bool = False @@ -358,11 +346,9 @@ def update( return result -class SetMixin(_RestManagerBase, Generic[T1, T2]): +class SetMixin(_RestManagerBase[T_obj, T_parent], Generic[T_obj, T_parent]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Type[T1] - _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab @@ -392,11 +378,9 @@ def set(self, key: str, value: str, **kwargs: Any) -> base.RESTObject: return self._obj_cls(self, server_data) -class DeleteMixin(_RestManagerBase, Generic[T1, T2]): +class DeleteMixin(_RestManagerBase[T_obj, T_parent], Generic[T_obj, T_parent]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Type[T1] - _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab @@ -423,21 +407,17 @@ def delete(self, id: Optional[Union[str, int]] = None, **kwargs: Any) -> None: self.gitlab.http_delete(path, **kwargs) -class CRUDMixin(GetMixin, ListMixin, CreateMixin, UpdateMixin, DeleteMixin, Generic[T1, T2]): +class CRUDMixin(GetMixin[T_obj, T_parent], ListMixin[T_obj, T_parent], CreateMixin[T_obj, T_parent], UpdateMixin[T_obj, T_parent], DeleteMixin[T_obj, T_parent], Generic[T_obj, T_parent]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Type[T1] - _parent: Optional[T2] _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab -class NoUpdateMixin(GetMixin, ListMixin, CreateMixin, DeleteMixin, Generic[T1, T2]): +class NoUpdateMixin(GetMixin[T_obj, T_parent], ListMixin[T_obj, T_parent], CreateMixin[T_obj, T_parent], DeleteMixin[T_obj, T_parent], Generic[T_obj, T_parent]): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] - _obj_cls: Type[T1] - _parent: T2 _parent_attrs: Dict[str, Any] _path: Optional[str] gitlab: gitlab.Gitlab @@ -838,7 +818,7 @@ def participants(self, **kwargs: Any) -> Dict[str, Any]: return result -class BadgeRenderMixin(_RestManagerBase): +class BadgeRenderMixin(_RestManagerBase[T_obj, T_parent]): @cli.register_custom_action( ("GroupBadgeManager", "ProjectBadgeManager"), ("link_url", "image_url") ) From 68e8c0d023a840723b057f46898d059780820e1d Mon Sep 17 00:00:00 2001 From: Ian Turner Date: Mon, 13 Jun 2022 19:45:21 -0700 Subject: [PATCH 3/4] Minor fixes to make pre-commit happy --- .pre-commit-config.yaml | 40 ------------------------------ gitlab/base.py | 16 +++++++++++- gitlab/client.py | 2 +- gitlab/mixins.py | 44 +++++++++++++++++++++++---------- gitlab/v4/objects/milestones.py | 2 +- gitlab/v4/objects/snippets.py | 4 +-- gitlab/v4/objects/users.py | 4 +-- 7 files changed, 52 insertions(+), 60 deletions(-) delete mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index 0cb694464..000000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,40 +0,0 @@ -default_language_version: - python: python3 - -repos: - - repo: https://github.com/psf/black - rev: 22.3.0 - hooks: - - id: black - - repo: https://github.com/commitizen-tools/commitizen - rev: v2.24.0 - hooks: - - id: commitizen - stages: [commit-msg] - - repo: https://github.com/pycqa/flake8 - rev: 4.0.1 - hooks: - - id: flake8 - - repo: https://github.com/pycqa/isort - rev: 5.10.1 - hooks: - - id: isort - - repo: https://github.com/pycqa/pylint - rev: v2.13.9 - hooks: - - id: pylint - additional_dependencies: - - argcomplete==2.0.0 - - pytest==7.1.2 - - requests==2.27.1 - - requests-toolbelt==0.9.1 - files: 'gitlab/' - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.930 - hooks: - - id: mypy - args: [] - additional_dependencies: - - types-PyYAML==6.0.8 - - types-requests==2.27.30 - - types-setuptools==57.4.17 diff --git a/gitlab/base.py b/gitlab/base.py index cfdbe4204..01f8c8264 100644 --- a/gitlab/base.py +++ b/gitlab/base.py @@ -19,7 +19,17 @@ import pprint import textwrap from types import ModuleType -from typing import Any, Dict, Generic, Iterable, Iterator, Optional, Collection, Type, TypeVar, Union +from typing import ( + Any, + Dict, + Generic, + Iterable, + Iterator, + Optional, + Type, + TypeVar, + Union, +) import gitlab from gitlab import types as g_types @@ -245,8 +255,10 @@ def attributes(self) -> Dict[str, Any]: d.update(self.__dict__["_parent_attrs"]) return d + T = TypeVar("T", bound=RESTObject) + class RESTObjectList(Generic[T], Iterable[T]): """Generator object representing a list of RESTObject's. @@ -328,9 +340,11 @@ def total(self) -> Optional[int]: """The total number of items.""" return self._list.total + T_obj = TypeVar("T_obj", bound=RESTObject) T_parent = TypeVar("T_parent", bound=Optional[RESTObject]) + class RESTManager(Generic[T_obj, T_parent]): """Base class for CRUD operations on objects. diff --git a/gitlab/client.py b/gitlab/client.py index 258770d86..b3e6b97e7 100644 --- a/gitlab/client.py +++ b/gitlab/client.py @@ -815,7 +815,7 @@ def http_list( as_list: Optional[bool] = None, # Deprecated in favor of `iterator` iterator: Optional[bool] = None, **kwargs: Any, - ) -> Union[GitlabList, List[Dict[str, int]]]: + ) -> Union["GitlabList", List[Dict[str, int]]]: """Make a GET request to the Gitlab server for list-oriented queries. Args: diff --git a/gitlab/mixins.py b/gitlab/mixins.py index 3bdf6fef4..e3586ed1e 100644 --- a/gitlab/mixins.py +++ b/gitlab/mixins.py @@ -21,13 +21,12 @@ Callable, Dict, Generic, - List, - Optional, Iterable, + Optional, Tuple, Type, - TypeVar, TYPE_CHECKING, + TypeVar, Union, ) @@ -62,16 +61,22 @@ "BadgeRenderMixin", ] +T_obj = TypeVar("T_obj", bound=base.RESTObject) +T_parent = TypeVar("T_parent", bound=Optional[base.RESTObject]) + if TYPE_CHECKING: # When running mypy we use these as the base classes _RestManagerBase = base.RESTManager _RestObjectBase = base.RESTObject else: - _RestManagerBase = object - _RestObjectBase = object -T_obj = TypeVar("T_obj", bound=base.RESTObject) -T_parent = TypeVar("T_parent", bound=Optional[base.RESTObject]) + class _RestManagerBase(Generic[T_obj, T_parent]): + _obj_cls: Type[T_obj] + _parent: Optional[T_parent] + + class _RestObjectBase(Generic[T_obj, T_parent]): + _obj_cls: Type[T_obj] + _parent: Optional[T_parent] class GetMixin(_RestManagerBase[T_obj, T_parent], Generic[T_obj, T_parent]): @@ -83,9 +88,7 @@ class GetMixin(_RestManagerBase[T_obj, T_parent], Generic[T_obj, T_parent]): gitlab: gitlab.Gitlab @exc.on_http_error(exc.GitlabGetError) - def get( - self, id: Union[str, int], lazy: bool = False, **kwargs: Any - ) -> T_obj: + def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> T_obj: """Retrieve a single object. Args: @@ -234,7 +237,9 @@ def list(self, **kwargs: Any) -> Iterable[T_obj]: return base.RESTObjectList(self, self._obj_cls, obj) -class RetrieveMixin(ListMixin[T_obj, T_parent], GetMixin[T_obj, T_parent], Generic[T_obj, T_parent]): +class RetrieveMixin( + ListMixin[T_obj, T_parent], GetMixin[T_obj, T_parent], Generic[T_obj, T_parent] +): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] _parent_attrs: Dict[str, Any] @@ -407,7 +412,14 @@ def delete(self, id: Optional[Union[str, int]] = None, **kwargs: Any) -> None: self.gitlab.http_delete(path, **kwargs) -class CRUDMixin(GetMixin[T_obj, T_parent], ListMixin[T_obj, T_parent], CreateMixin[T_obj, T_parent], UpdateMixin[T_obj, T_parent], DeleteMixin[T_obj, T_parent], Generic[T_obj, T_parent]): +class CRUDMixin( + GetMixin[T_obj, T_parent], + ListMixin[T_obj, T_parent], + CreateMixin[T_obj, T_parent], + UpdateMixin[T_obj, T_parent], + DeleteMixin[T_obj, T_parent], + Generic[T_obj, T_parent], +): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] _parent_attrs: Dict[str, Any] @@ -415,7 +427,13 @@ class CRUDMixin(GetMixin[T_obj, T_parent], ListMixin[T_obj, T_parent], CreateMix gitlab: gitlab.Gitlab -class NoUpdateMixin(GetMixin[T_obj, T_parent], ListMixin[T_obj, T_parent], CreateMixin[T_obj, T_parent], DeleteMixin[T_obj, T_parent], Generic[T_obj, T_parent]): +class NoUpdateMixin( + GetMixin[T_obj, T_parent], + ListMixin[T_obj, T_parent], + CreateMixin[T_obj, T_parent], + DeleteMixin[T_obj, T_parent], + Generic[T_obj, T_parent], +): _computed_path: Optional[str] _from_parent_attrs: Dict[str, Any] _parent_attrs: Dict[str, Any] diff --git a/gitlab/v4/objects/milestones.py b/gitlab/v4/objects/milestones.py index 3b6578ce4..43adddcde 100644 --- a/gitlab/v4/objects/milestones.py +++ b/gitlab/v4/objects/milestones.py @@ -3,8 +3,8 @@ from gitlab import cli from gitlab import exceptions as exc from gitlab import types -from gitlab.client import GitlabList from gitlab.base import RESTManager, RESTObject, RESTObjectList +from gitlab.client import GitlabList from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, PromoteMixin, SaveMixin from gitlab.types import RequiredOptional diff --git a/gitlab/v4/objects/snippets.py b/gitlab/v4/objects/snippets.py index 5cea9fd52..512b97f0c 100644 --- a/gitlab/v4/objects/snippets.py +++ b/gitlab/v4/objects/snippets.py @@ -1,11 +1,11 @@ -from typing import Any, Callable, cast, Iterable, List, Optional, TYPE_CHECKING, Union +from typing import Any, Callable, cast, Iterable, Optional, TYPE_CHECKING, Union import requests from gitlab import cli from gitlab import exceptions as exc from gitlab import utils -from gitlab.base import RESTManager, RESTObject, RESTObjectList +from gitlab.base import RESTManager, RESTObject from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin, UserAgentDetailMixin from gitlab.types import RequiredOptional diff --git a/gitlab/v4/objects/users.py b/gitlab/v4/objects/users.py index 97c073f11..70eb40ede 100644 --- a/gitlab/v4/objects/users.py +++ b/gitlab/v4/objects/users.py @@ -3,14 +3,14 @@ https://docs.gitlab.com/ee/api/users.html https://docs.gitlab.com/ee/api/projects.html#list-projects-starred-by-a-user """ -from typing import Any, cast, Dict, Iterable, List, Optional, Union +from typing import Any, cast, Dict, Iterable, Optional, Union import requests from gitlab import cli from gitlab import exceptions as exc from gitlab import types -from gitlab.base import RESTManager, RESTObject, RESTObjectList +from gitlab.base import RESTManager, RESTObject from gitlab.mixins import ( CreateMixin, CRUDMixin, From bd81ee01365d796928922dad7b3606cce600a8ad Mon Sep 17 00:00:00 2001 From: Ian Turner Date: Mon, 13 Jun 2022 19:47:08 -0700 Subject: [PATCH 4/4] Restore this erroneously deleted file --- .pre-commit-config.yaml | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..0cb694464 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,40 @@ +default_language_version: + python: python3 + +repos: + - repo: https://github.com/psf/black + rev: 22.3.0 + hooks: + - id: black + - repo: https://github.com/commitizen-tools/commitizen + rev: v2.24.0 + hooks: + - id: commitizen + stages: [commit-msg] + - repo: https://github.com/pycqa/flake8 + rev: 4.0.1 + hooks: + - id: flake8 + - repo: https://github.com/pycqa/isort + rev: 5.10.1 + hooks: + - id: isort + - repo: https://github.com/pycqa/pylint + rev: v2.13.9 + hooks: + - id: pylint + additional_dependencies: + - argcomplete==2.0.0 + - pytest==7.1.2 + - requests==2.27.1 + - requests-toolbelt==0.9.1 + files: 'gitlab/' + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v0.930 + hooks: + - id: mypy + args: [] + additional_dependencies: + - types-PyYAML==6.0.8 + - types-requests==2.27.30 + - types-setuptools==57.4.17