Skip to content

Commit 7ba5995

Browse files
authored
Merge pull request #1695 from python-gitlab/jlvillal/mypy_epics
chore: add type-hints to remaining gitlab/v4/objects/*.py files
2 parents a390ec3 + 0c22bd9 commit 7ba5995

File tree

13 files changed

+353
-90
lines changed

13 files changed

+353
-90
lines changed

gitlab/base.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ def _create_managers(self) -> None:
150150
# annotations. If an attribute is annotated as being a *Manager type
151151
# then we create the manager and assign it to the attribute.
152152
for attr, annotation in sorted(self.__annotations__.items()):
153+
# We ignore creating a manager for the 'manager' attribute as that
154+
# is done in the self.__init__() method
155+
if attr in ("manager",):
156+
continue
153157
if not isinstance(annotation, (type, str)):
154158
continue
155159
if isinstance(annotation, type):

gitlab/v4/objects/epics.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Any, cast, Dict, Optional, TYPE_CHECKING, Union
2+
13
from gitlab import exceptions as exc
24
from gitlab import types
35
from gitlab.base import RequiredOptional, RESTManager, RESTObject
@@ -42,11 +44,17 @@ class GroupEpicManager(CRUDMixin, RESTManager):
4244
)
4345
_types = {"labels": types.ListAttribute}
4446

47+
def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> GroupEpic:
48+
return cast(GroupEpic, super().get(id=id, lazy=lazy, **kwargs))
49+
4550

4651
class GroupEpicIssue(ObjectDeleteMixin, SaveMixin, RESTObject):
4752
_id_attr = "epic_issue_id"
53+
# Define type for 'manager' here So mypy won't complain about
54+
# 'self.manager.update()' call in the 'save' method.
55+
manager: "GroupEpicIssueManager"
4856

49-
def save(self, **kwargs):
57+
def save(self, **kwargs: Any) -> None:
5058
"""Save the changes made to the object to the server.
5159
5260
The object is updated to match what the server returns.
@@ -78,7 +86,9 @@ class GroupEpicIssueManager(
7886
_update_attrs = RequiredOptional(optional=("move_before_id", "move_after_id"))
7987

8088
@exc.on_http_error(exc.GitlabCreateError)
81-
def create(self, data, **kwargs):
89+
def create(
90+
self, data: Optional[Dict[str, Any]] = None, **kwargs: Any
91+
) -> GroupEpicIssue:
8292
"""Create a new object.
8393
8494
Args:
@@ -94,9 +104,13 @@ def create(self, data, **kwargs):
94104
RESTObject: A new instance of the manage object class build with
95105
the data sent by the server
96106
"""
107+
if TYPE_CHECKING:
108+
assert data is not None
97109
CreateMixin._check_missing_create_attrs(self, data)
98110
path = f"{self.path}/{data.pop('issue_id')}"
99111
server_data = self.gitlab.http_post(path, **kwargs)
112+
if TYPE_CHECKING:
113+
assert isinstance(server_data, dict)
100114
# The epic_issue_id attribute doesn't exist when creating the resource,
101115
# but is used everywhere elese. Let's create it to be consistent client
102116
# side

gitlab/v4/objects/files.py

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import base64
2+
from typing import Any, Callable, cast, Dict, List, Optional, TYPE_CHECKING
3+
4+
import requests
25

36
from gitlab import cli
47
from gitlab import exceptions as exc
@@ -22,6 +25,8 @@
2225
class ProjectFile(SaveMixin, ObjectDeleteMixin, RESTObject):
2326
_id_attr = "file_path"
2427
_short_print_attr = "file_path"
28+
file_path: str
29+
manager: "ProjectFileManager"
2530

2631
def decode(self) -> bytes:
2732
"""Returns the decoded content of the file.
@@ -31,7 +36,11 @@ def decode(self) -> bytes:
3136
"""
3237
return base64.b64decode(self.content)
3338

34-
def save(self, branch, commit_message, **kwargs):
39+
# NOTE(jlvillal): Signature doesn't match SaveMixin.save() so ignore
40+
# type error
41+
def save( # type: ignore
42+
self, branch: str, commit_message: str, **kwargs: Any
43+
) -> None:
3544
"""Save the changes made to the file to the server.
3645
3746
The object is updated to match what the server returns.
@@ -50,7 +59,12 @@ def save(self, branch, commit_message, **kwargs):
5059
self.file_path = self.file_path.replace("/", "%2F")
5160
super(ProjectFile, self).save(**kwargs)
5261

53-
def delete(self, branch, commit_message, **kwargs):
62+
@exc.on_http_error(exc.GitlabDeleteError)
63+
# NOTE(jlvillal): Signature doesn't match DeleteMixin.delete() so ignore
64+
# type error
65+
def delete( # type: ignore
66+
self, branch: str, commit_message: str, **kwargs: Any
67+
) -> None:
5468
"""Delete the file from the server.
5569
5670
Args:
@@ -80,7 +94,11 @@ class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTMa
8094
)
8195

8296
@cli.register_custom_action("ProjectFileManager", ("file_path", "ref"))
83-
def get(self, file_path, ref, **kwargs):
97+
# NOTE(jlvillal): Signature doesn't match UpdateMixin.update() so ignore
98+
# type error
99+
def get( # type: ignore
100+
self, file_path: str, ref: str, **kwargs: Any
101+
) -> ProjectFile:
84102
"""Retrieve a single file.
85103
86104
Args:
@@ -95,15 +113,17 @@ def get(self, file_path, ref, **kwargs):
95113
Returns:
96114
object: The generated RESTObject
97115
"""
98-
return GetMixin.get(self, file_path, ref=ref, **kwargs)
116+
return cast(ProjectFile, GetMixin.get(self, file_path, ref=ref, **kwargs))
99117

100118
@cli.register_custom_action(
101119
"ProjectFileManager",
102120
("file_path", "branch", "content", "commit_message"),
103121
("encoding", "author_email", "author_name"),
104122
)
105123
@exc.on_http_error(exc.GitlabCreateError)
106-
def create(self, data, **kwargs):
124+
def create(
125+
self, data: Optional[Dict[str, Any]] = None, **kwargs: Any
126+
) -> ProjectFile:
107127
"""Create a new object.
108128
109129
Args:
@@ -120,15 +140,23 @@ def create(self, data, **kwargs):
120140
GitlabCreateError: If the server cannot perform the request
121141
"""
122142

143+
if TYPE_CHECKING:
144+
assert data is not None
123145
self._check_missing_create_attrs(data)
124146
new_data = data.copy()
125147
file_path = new_data.pop("file_path").replace("/", "%2F")
126148
path = f"{self.path}/{file_path}"
127149
server_data = self.gitlab.http_post(path, post_data=new_data, **kwargs)
150+
if TYPE_CHECKING:
151+
assert isinstance(server_data, dict)
128152
return self._obj_cls(self, server_data)
129153

130154
@exc.on_http_error(exc.GitlabUpdateError)
131-
def update(self, file_path, new_data=None, **kwargs):
155+
# NOTE(jlvillal): Signature doesn't match UpdateMixin.update() so ignore
156+
# type error
157+
def update( # type: ignore
158+
self, file_path: str, new_data: Optional[Dict[str, Any]] = None, **kwargs: Any
159+
) -> Dict[str, Any]:
132160
"""Update an object on the server.
133161
134162
Args:
@@ -149,13 +177,20 @@ def update(self, file_path, new_data=None, **kwargs):
149177
data["file_path"] = file_path
150178
path = f"{self.path}/{file_path}"
151179
self._check_missing_update_attrs(data)
152-
return self.gitlab.http_put(path, post_data=data, **kwargs)
180+
result = self.gitlab.http_put(path, post_data=data, **kwargs)
181+
if TYPE_CHECKING:
182+
assert isinstance(result, dict)
183+
return result
153184

154185
@cli.register_custom_action(
155186
"ProjectFileManager", ("file_path", "branch", "commit_message")
156187
)
157188
@exc.on_http_error(exc.GitlabDeleteError)
158-
def delete(self, file_path, branch, commit_message, **kwargs):
189+
# NOTE(jlvillal): Signature doesn't match DeleteMixin.delete() so ignore
190+
# type error
191+
def delete( # type: ignore
192+
self, file_path: str, branch: str, commit_message: str, **kwargs: Any
193+
) -> None:
159194
"""Delete a file on the server.
160195
161196
Args:
@@ -175,8 +210,14 @@ def delete(self, file_path, branch, commit_message, **kwargs):
175210
@cli.register_custom_action("ProjectFileManager", ("file_path", "ref"))
176211
@exc.on_http_error(exc.GitlabGetError)
177212
def raw(
178-
self, file_path, ref, streamed=False, action=None, chunk_size=1024, **kwargs
179-
):
213+
self,
214+
file_path: str,
215+
ref: str,
216+
streamed: bool = False,
217+
action: Optional[Callable[..., Any]] = None,
218+
chunk_size: int = 1024,
219+
**kwargs: Any,
220+
) -> Optional[bytes]:
180221
"""Return the content of a file for a commit.
181222
182223
Args:
@@ -203,11 +244,13 @@ def raw(
203244
result = self.gitlab.http_get(
204245
path, query_data=query_data, streamed=streamed, raw=True, **kwargs
205246
)
247+
if TYPE_CHECKING:
248+
assert isinstance(result, requests.Response)
206249
return utils.response_content(result, streamed, action, chunk_size)
207250

208251
@cli.register_custom_action("ProjectFileManager", ("file_path", "ref"))
209252
@exc.on_http_error(exc.GitlabListError)
210-
def blame(self, file_path, ref, **kwargs):
253+
def blame(self, file_path: str, ref: str, **kwargs: Any) -> List[Dict[str, Any]]:
211254
"""Return the content of a file for a commit.
212255
213256
Args:
@@ -225,4 +268,7 @@ def blame(self, file_path, ref, **kwargs):
225268
file_path = file_path.replace("/", "%2F").replace(".", "%2E")
226269
path = f"{self.path}/{file_path}/blame"
227270
query_data = {"ref": ref}
228-
return self.gitlab.http_list(path, query_data, **kwargs)
271+
result = self.gitlab.http_list(path, query_data, **kwargs)
272+
if TYPE_CHECKING:
273+
assert isinstance(result, list)
274+
return result

gitlab/v4/objects/geo_nodes.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Any, cast, Dict, List, TYPE_CHECKING, Union
2+
13
from gitlab import cli
24
from gitlab import exceptions as exc
35
from gitlab.base import RequiredOptional, RESTManager, RESTObject
@@ -18,7 +20,7 @@
1820
class GeoNode(SaveMixin, ObjectDeleteMixin, RESTObject):
1921
@cli.register_custom_action("GeoNode")
2022
@exc.on_http_error(exc.GitlabRepairError)
21-
def repair(self, **kwargs):
23+
def repair(self, **kwargs: Any) -> None:
2224
"""Repair the OAuth authentication of the geo node.
2325
2426
Args:
@@ -30,11 +32,13 @@ def repair(self, **kwargs):
3032
"""
3133
path = f"/geo_nodes/{self.get_id()}/repair"
3234
server_data = self.manager.gitlab.http_post(path, **kwargs)
35+
if TYPE_CHECKING:
36+
assert isinstance(server_data, dict)
3337
self._update_attrs(server_data)
3438

3539
@cli.register_custom_action("GeoNode")
3640
@exc.on_http_error(exc.GitlabGetError)
37-
def status(self, **kwargs):
41+
def status(self, **kwargs: Any) -> Dict[str, Any]:
3842
"""Get the status of the geo node.
3943
4044
Args:
@@ -48,7 +52,10 @@ def status(self, **kwargs):
4852
dict: The status of the geo node
4953
"""
5054
path = f"/geo_nodes/{self.get_id()}/status"
51-
return self.manager.gitlab.http_get(path, **kwargs)
55+
result = self.manager.gitlab.http_get(path, **kwargs)
56+
if TYPE_CHECKING:
57+
assert isinstance(result, dict)
58+
return result
5259

5360

5461
class GeoNodeManager(RetrieveMixin, UpdateMixin, DeleteMixin, RESTManager):
@@ -58,9 +65,12 @@ class GeoNodeManager(RetrieveMixin, UpdateMixin, DeleteMixin, RESTManager):
5865
optional=("enabled", "url", "files_max_capacity", "repos_max_capacity"),
5966
)
6067

68+
def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> GeoNode:
69+
return cast(GeoNode, super().get(id=id, lazy=lazy, **kwargs))
70+
6171
@cli.register_custom_action("GeoNodeManager")
6272
@exc.on_http_error(exc.GitlabGetError)
63-
def status(self, **kwargs):
73+
def status(self, **kwargs: Any) -> List[Dict[str, Any]]:
6474
"""Get the status of all the geo nodes.
6575
6676
Args:
@@ -73,11 +83,14 @@ def status(self, **kwargs):
7383
Returns:
7484
list: The status of all the geo nodes
7585
"""
76-
return self.gitlab.http_list("/geo_nodes/status", **kwargs)
86+
result = self.gitlab.http_list("/geo_nodes/status", **kwargs)
87+
if TYPE_CHECKING:
88+
assert isinstance(result, list)
89+
return result
7790

7891
@cli.register_custom_action("GeoNodeManager")
7992
@exc.on_http_error(exc.GitlabGetError)
80-
def current_failures(self, **kwargs):
93+
def current_failures(self, **kwargs: Any) -> List[Dict[str, Any]]:
8194
"""Get the list of failures on the current geo node.
8295
8396
Args:
@@ -90,4 +103,7 @@ def current_failures(self, **kwargs):
90103
Returns:
91104
list: The list of failures
92105
"""
93-
return self.gitlab.http_list("/geo_nodes/current/failures", **kwargs)
106+
result = self.gitlab.http_list("/geo_nodes/current/failures", **kwargs)
107+
if TYPE_CHECKING:
108+
assert isinstance(result, list)
109+
return result

0 commit comments

Comments
 (0)