python-gitea 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
gitea/client/gitea.py ADDED
@@ -0,0 +1,93 @@
1
+ """Gitea API client implementation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import requests
8
+ from requests import Response
9
+
10
+ from gitea.client.base import Client
11
+ from gitea.user.user import User
12
+
13
+
14
+ class Gitea(Client): # pylint: disable=too-few-public-methods
15
+ """Synchronous Gitea API client."""
16
+
17
+ def __init__(self, token: str | None = None, base_url: str = "https://gitea.com") -> None:
18
+ """Initialize the Gitea client.
19
+
20
+ Args:
21
+ token: The API token for authentication.
22
+ base_url: The base URL of the Gitea instance.
23
+
24
+ """
25
+ super().__init__(token=token, base_url=base_url)
26
+ self.session: requests.Session | None = None
27
+ self.user = User(client=self)
28
+
29
+ def __str__(self) -> str:
30
+ """Return a string representation of the Gitea client.
31
+
32
+ Returns:
33
+ A string representing the Gitea client.
34
+
35
+ """
36
+ return f"Gitea Client(base_url={self.base_url})"
37
+
38
+ def __enter__(self) -> Gitea:
39
+ """Enter the context manager.
40
+
41
+ Returns:
42
+ The Gitea client instance.
43
+
44
+ """
45
+ if self.session is not None:
46
+ raise RuntimeError("Gitea session already open; do not re-enter context manager.")
47
+ self.session = requests.Session()
48
+ return self
49
+
50
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
51
+ """Exit the context manager.
52
+
53
+ Args:
54
+ exc_type: The exception type.
55
+ exc_val: The exception value.
56
+ exc_tb: The traceback.
57
+
58
+ """
59
+ if self.session:
60
+ self.session.close()
61
+ self.session = None
62
+
63
+ def _request(
64
+ self, method: str, endpoint: str, headers: dict | None = None, timeout: int = 30, **kwargs: Any
65
+ ) -> Response:
66
+ """Make an HTTP request to the Gitea API.
67
+
68
+ Args:
69
+ method: The HTTP method (GET, POST, etc.).
70
+ endpoint: The API endpoint.
71
+ headers: Additional headers for the request.
72
+ timeout: Timeout for the request in seconds.
73
+ **kwargs: Additional arguments for the request.
74
+
75
+ Returns:
76
+ The HTTP response object.
77
+
78
+ """
79
+ if self.session is None:
80
+ raise RuntimeError(
81
+ "Gitea must be used as a context manager. "
82
+ + "Use 'with Gitea(...) as client:' to ensure proper resource cleanup."
83
+ )
84
+ url = self._build_url(endpoint=endpoint)
85
+ response = self.session.request(
86
+ method, url, headers={**self.headers, **(headers or {})}, timeout=timeout, **kwargs
87
+ )
88
+ try:
89
+ response.raise_for_status()
90
+ except Exception:
91
+ response.close()
92
+ raise
93
+ return response
@@ -0,0 +1,8 @@
1
+ """Gitea Issue resource."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from gitea.issue.async_issue import AsyncIssue
6
+ from gitea.issue.issue import Issue
7
+
8
+ __all__ = ["AsyncIssue", "Issue"]
@@ -0,0 +1,277 @@
1
+ """Asynchronous Gitea Issue resource."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime
6
+ from typing import Any, Literal, cast
7
+
8
+ from aiohttp import ClientResponse
9
+
10
+ from gitea.issue.base import BaseIssue
11
+ from gitea.resource.async_resource import AsyncResource
12
+ from gitea.utils.response import process_async_response
13
+
14
+
15
+ class AsyncIssue(BaseIssue, AsyncResource):
16
+ """Asynchronous Gitea Issue resource."""
17
+
18
+ async def _list_issues( # noqa: PLR0913
19
+ self,
20
+ owner: str,
21
+ repository: str,
22
+ state: Literal["closed", "open", "all"] | None = None,
23
+ labels: list[str] | None = None,
24
+ search_string: str | None = None,
25
+ issue_type: Literal["issues", "pulls"] | None = None,
26
+ milestones: list[str] | list[int] | None = None,
27
+ since: datetime | None = None,
28
+ before: datetime | None = None,
29
+ created_by: str | None = None,
30
+ assigned_by: str | None = None,
31
+ mentioned_by: str | None = None,
32
+ page: int | None = None,
33
+ limit: int | None = None,
34
+ **kwargs: Any,
35
+ ) -> ClientResponse:
36
+ """List issues in a repository.
37
+
38
+ Args:
39
+ owner: The owner of the repository.
40
+ repository: The name of the repository.
41
+ state: Filter issues by state.
42
+ labels: Filter issues by labels.
43
+ search_string: Filter issues by search string.
44
+ issue_type: Filter by issue type.
45
+ milestones: Filter issues by milestones.
46
+ since: Filter issues updated since this time.
47
+ before: Filter issues updated before this time.
48
+ created_by: Filter issues created by this user.
49
+ assigned_by: Filter issues assigned to this user.
50
+ mentioned_by: Filter issues mentioning this user.
51
+ page: The page number for pagination.
52
+ limit: The number of issues per page.
53
+ **kwargs: Additional arguments for the request.
54
+
55
+ Returns:
56
+ The HTTP response object.
57
+
58
+ """
59
+ endpoint, params = self._list_issues_helper(
60
+ owner=owner,
61
+ repository=repository,
62
+ state=state,
63
+ labels=labels,
64
+ search_string=search_string,
65
+ issue_type=issue_type,
66
+ milestones=milestones,
67
+ since=since,
68
+ before=before,
69
+ created_by=created_by,
70
+ assigned_by=assigned_by,
71
+ mentioned_by=mentioned_by,
72
+ page=page,
73
+ limit=limit,
74
+ )
75
+ return await self._get(endpoint=endpoint, params=params, **kwargs)
76
+
77
+ async def list_issues( # noqa: PLR0913
78
+ self,
79
+ owner: str,
80
+ repository: str,
81
+ state: Literal["closed", "open", "all"] | None = None,
82
+ labels: list[str] | None = None,
83
+ search_string: str | None = None,
84
+ issue_type: Literal["issues", "pulls"] | None = None,
85
+ milestones: list[str] | list[int] | None = None,
86
+ since: datetime | None = None,
87
+ before: datetime | None = None,
88
+ created_by: str | None = None,
89
+ assigned_by: str | None = None,
90
+ mentioned_by: str | None = None,
91
+ page: int | None = None,
92
+ limit: int | None = None,
93
+ **kwargs: Any,
94
+ ) -> tuple[list[dict[str, Any]], int]:
95
+ """List issues in a repository.
96
+
97
+ Args:
98
+ owner: The owner of the repository.
99
+ repository: The name of the repository.
100
+ state: Filter issues by state.
101
+ labels: Filter issues by labels.
102
+ search_string: Filter issues by search string.
103
+ issue_type: Filter by issue type.
104
+ milestones: Filter issues by milestones.
105
+ since: Filter issues updated since this time.
106
+ before: Filter issues updated before this time.
107
+ created_by: Filter issues created by this user.
108
+ assigned_by: Filter issues assigned to this user.
109
+ mentioned_by: Filter issues mentioning this user.
110
+ page: The page number for pagination.
111
+ limit: The number of issues per page.
112
+ **kwargs: Additional arguments for the request.
113
+
114
+ Returns:
115
+ A tuple containing the list of issues as a list of dictionaries and the status code.
116
+
117
+ """
118
+ response = await self._list_issues(
119
+ owner=owner,
120
+ repository=repository,
121
+ state=state,
122
+ labels=labels,
123
+ search_string=search_string,
124
+ issue_type=issue_type,
125
+ milestones=milestones,
126
+ since=since,
127
+ before=before,
128
+ created_by=created_by,
129
+ assigned_by=assigned_by,
130
+ mentioned_by=mentioned_by,
131
+ page=page,
132
+ limit=limit,
133
+ **kwargs,
134
+ )
135
+ data, status_code = await process_async_response(response)
136
+ return cast(list[dict[str, Any]], data), status_code
137
+
138
+ async def _get_issue(self, owner: str, repository: str, index: int, **kwargs: Any) -> ClientResponse:
139
+ """Get a single issue by its index.
140
+
141
+ Args:
142
+ owner: The owner of the repository.
143
+ repository: The name of the repository.
144
+ index: The index of the issue.
145
+ **kwargs: Additional arguments for the request.
146
+
147
+ Returns:
148
+ The HTTP response object.
149
+
150
+ """
151
+ endpoint = self._get_issue_helper(owner=owner, repository=repository, index=index)
152
+ return await self._get(endpoint=endpoint, **kwargs)
153
+
154
+ async def get_issue(self, owner: str, repository: str, index: int, **kwargs: Any) -> tuple[dict[str, Any], int]:
155
+ """Get a single issue by its index.
156
+
157
+ Args:
158
+ owner: The owner of the repository.
159
+ repository: The name of the repository.
160
+ index: The index of the issue.
161
+ **kwargs: Additional arguments for the request.
162
+
163
+ Returns:
164
+ A tuple containing the issue as a dictionary and the status code.
165
+
166
+ """
167
+ response = await self._get_issue(owner=owner, repository=repository, index=index, **kwargs)
168
+ data, status_code = await process_async_response(response)
169
+ return cast(dict[str, Any], data), status_code
170
+
171
+ async def _edit_issue( # noqa: PLR0913
172
+ self,
173
+ owner: str,
174
+ repository: str,
175
+ index: int,
176
+ assignee: str | None = None,
177
+ assignees: list[str] | None = None,
178
+ body: str | None = None,
179
+ due_date: datetime | None = None,
180
+ milestone: int | None = None,
181
+ ref: str | None = None,
182
+ state: Literal["closed", "open"] | None = None,
183
+ title: str | None = None,
184
+ unset_due_date: bool | None = None,
185
+ **kwargs: Any,
186
+ ) -> ClientResponse:
187
+ """Edit a specific issue in a repository.
188
+
189
+ Args:
190
+ owner: The owner of the repository.
191
+ repository: The name of the repository.
192
+ index: The index of the issue.
193
+ assignee: The new assignee of the issue.
194
+ assignees: The new assignees of the issue.
195
+ body: The new body of the issue.
196
+ due_date: The new due date of the issue.
197
+ milestone: The new milestone of the issue.
198
+ ref: The new reference of the issue.
199
+ state: The new state of the issue.
200
+ title: The new title of the issue.
201
+ unset_due_date: Whether to unset the due date of the issue.
202
+ **kwargs: Additional arguments for the request.
203
+
204
+ Returns:
205
+ The HTTP response object.
206
+
207
+ """
208
+ endpoint, payload = self._edit_issue_helper(
209
+ owner=owner,
210
+ repository=repository,
211
+ index=index,
212
+ assignee=assignee,
213
+ assignees=assignees,
214
+ body=body,
215
+ due_date=due_date,
216
+ milestone=milestone,
217
+ ref=ref,
218
+ state=state,
219
+ title=title,
220
+ unset_due_date=unset_due_date,
221
+ )
222
+ return await self._patch(endpoint=endpoint, json=payload, **kwargs)
223
+
224
+ async def edit_issue( # noqa: PLR0913
225
+ self,
226
+ owner: str,
227
+ repository: str,
228
+ index: int,
229
+ assignee: str | None = None,
230
+ assignees: list[str] | None = None,
231
+ body: str | None = None,
232
+ due_date: datetime | None = None,
233
+ milestone: int | None = None,
234
+ ref: str | None = None,
235
+ state: Literal["closed", "open"] | None = None,
236
+ title: str | None = None,
237
+ unset_due_date: bool | None = None,
238
+ **kwargs: Any,
239
+ ) -> tuple[dict[str, Any], int]:
240
+ """Edit a specific issue in a repository.
241
+
242
+ Args:
243
+ owner: The owner of the repository.
244
+ repository: The name of the repository.
245
+ index: The index of the issue.
246
+ assignee: The new assignee of the issue.
247
+ assignees: The new assignees of the issue.
248
+ body: The new body of the issue.
249
+ due_date: The new due date of the issue.
250
+ milestone: The new milestone of the issue.
251
+ ref: The new reference of the issue.
252
+ state: The new state of the issue.
253
+ title: The new title of the issue.
254
+ unset_due_date: Whether to unset the due date of the issue.
255
+ **kwargs: Additional arguments for the request.
256
+
257
+ Returns:
258
+ A tuple containing the edited issue as a dictionary and the status code.
259
+
260
+ """
261
+ response = await self._edit_issue(
262
+ owner=owner,
263
+ repository=repository,
264
+ index=index,
265
+ assignee=assignee,
266
+ assignees=assignees,
267
+ body=body,
268
+ due_date=due_date,
269
+ milestone=milestone,
270
+ ref=ref,
271
+ state=state,
272
+ title=title,
273
+ unset_due_date=unset_due_date,
274
+ **kwargs,
275
+ )
276
+ data, status_code = await process_async_response(response)
277
+ return cast(dict[str, Any], data), status_code
gitea/issue/base.py ADDED
@@ -0,0 +1,201 @@
1
+ """Base class for Gitea Issue resource."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime
6
+ from typing import Any, Literal
7
+
8
+
9
+ class BaseIssue:
10
+ """Base class for Gitea Issue resource."""
11
+
12
+ def _list_issues_endpoint(self, owner: str, repository: str) -> str:
13
+ """Construct the endpoint URL for listing issues in a repository.
14
+
15
+ Args:
16
+ owner: The owner of the repository.
17
+ repository: The name of the repository.
18
+
19
+ Returns:
20
+ The endpoint URL for listing issues.
21
+
22
+ """
23
+ return f"/repos/{owner}/{repository}/issues"
24
+
25
+ def _list_issues_helper( # noqa: PLR0913
26
+ self,
27
+ owner: str,
28
+ repository: str,
29
+ state: Literal["closed", "open", "all"] | None = None,
30
+ labels: list[str] | None = None,
31
+ search_string: str | None = None,
32
+ issue_type: Literal["issues", "pulls"] | None = None,
33
+ milestones: list[str] | list[int] | None = None,
34
+ since: datetime | None = None,
35
+ before: datetime | None = None,
36
+ created_by: str | None = None,
37
+ assigned_by: str | None = None,
38
+ mentioned_by: str | None = None,
39
+ page: int | None = None,
40
+ limit: int | None = None,
41
+ ) -> tuple[str, dict[str, Any]]:
42
+ """Get the endpoint and parameters for listing issues in a repository.
43
+
44
+ Args:
45
+ owner: The owner of the repository.
46
+ repository: The name of the repository.
47
+ state: Filter issues by state.
48
+ labels: Filter issues by labels.
49
+ search_string: Filter issues by search string.
50
+ issue_type: Filter by issue type.
51
+ milestones: Filter issues by milestones.
52
+ since: Filter issues updated since this time.
53
+ before: Filter issues updated before this time.
54
+ created_by: Filter issues created by this user.
55
+ assigned_by: Filter issues assigned to this user.
56
+ mentioned_by: Filter issues mentioning this user.
57
+ page: The page number for pagination.
58
+ limit: The number of issues per page.
59
+
60
+ Returns:
61
+ A tuple containing the endpoint and the request arguments.
62
+
63
+ - The API endpoint for listing issues.
64
+ - A dictionary of request arguments.
65
+
66
+ """
67
+ endpoint = self._list_issues_endpoint(owner=owner, repository=repository)
68
+
69
+ params = {}
70
+ if state is not None:
71
+ params["state"] = state
72
+ if labels is not None:
73
+ params["labels"] = ",".join(labels)
74
+ if search_string is not None:
75
+ params["q"] = search_string
76
+ if issue_type is not None:
77
+ params["type"] = issue_type
78
+ if milestones is not None:
79
+ params["milestone"] = ",".join(str(m) for m in milestones)
80
+ if since is not None:
81
+ params["since"] = since.isoformat()
82
+ if before is not None:
83
+ params["before"] = before.isoformat()
84
+ if created_by is not None:
85
+ params["created_by"] = created_by
86
+ if assigned_by is not None:
87
+ params["assigned_by"] = assigned_by
88
+ if mentioned_by is not None:
89
+ params["mentioned_by"] = mentioned_by
90
+ if page is not None:
91
+ params["page"] = page
92
+ if limit is not None:
93
+ params["limit"] = limit
94
+
95
+ return endpoint, params
96
+
97
+ def _get_issue_endpoint(self, owner: str, repository: str, index: int) -> str:
98
+ """Construct the endpoint URL for a specific issue in a repository.
99
+
100
+ Args:
101
+ owner: The owner of the repository.
102
+ repository: The name of the repository.
103
+ index: The index of the issue.
104
+
105
+ Returns:
106
+ The endpoint URL for the specific issue.
107
+
108
+ """
109
+ return f"/repos/{owner}/{repository}/issues/{index}"
110
+
111
+ def _get_issue_helper(self, owner: str, repository: str, index: int) -> str:
112
+ """Get the endpoint and parameters for retrieving a specific issue in a repository.
113
+
114
+ Args:
115
+ owner: The owner of the repository.
116
+ repository: The name of the repository.
117
+ index: The index of the issue.
118
+
119
+ Returns:
120
+ The API endpoint for retrieving the specific issue.
121
+
122
+ """
123
+ endpoint = self._get_issue_endpoint(owner=owner, repository=repository, index=index)
124
+ return endpoint
125
+
126
+ def _edit_issue_endpoint(self, owner: str, repository: str, index: int) -> str:
127
+ """Construct the endpoint URL for editing a specific issue in a repository.
128
+
129
+ Args:
130
+ owner: The owner of the repository.
131
+ repository: The name of the repository.
132
+ index: The index of the issue.
133
+
134
+ Returns:
135
+ The endpoint URL for editing the specific issue.
136
+
137
+ """
138
+ return f"/repos/{owner}/{repository}/issues/{index}"
139
+
140
+ def _edit_issue_helper( # noqa: PLR0913
141
+ self,
142
+ owner: str,
143
+ repository: str,
144
+ index: int,
145
+ assignee: str | None = None,
146
+ assignees: list[str] | None = None,
147
+ body: str | None = None,
148
+ due_date: datetime | None = None,
149
+ milestone: int | None = None,
150
+ ref: str | None = None,
151
+ state: Literal["closed", "open"] | None = None,
152
+ title: str | None = None,
153
+ unset_due_date: bool | None = None,
154
+ ) -> tuple[str, dict[str, Any]]:
155
+ """Get the endpoint and parameters for editing a specific issue in a repository.
156
+
157
+ Args:
158
+ owner: The owner of the repository.
159
+ repository: The name of the repository.
160
+ index: The index of the issue.
161
+ assignee: The new assignee of the issue.
162
+ assignees: The new assignees of the issue.
163
+ body: The new body of the issue.
164
+ due_date: The new due date of the issue.
165
+ milestone: The new milestone of the issue.
166
+ ref: The new reference of the issue.
167
+ state: The new state of the issue.
168
+ title: The new title of the issue.
169
+ unset_due_date: Whether to unset the due date of the issue.
170
+
171
+ Returns:
172
+ A tuple containing the endpoint and the request arguments.
173
+
174
+ - The API endpoint for editing the specific issue.
175
+ - A dictionary of request arguments.
176
+
177
+ """
178
+ endpoint = self._edit_issue_endpoint(owner=owner, repository=repository, index=index)
179
+
180
+ payload = {}
181
+
182
+ if assignee is not None:
183
+ payload["assignee"] = assignee
184
+ if assignees is not None:
185
+ payload["assignees"] = assignees
186
+ if body is not None:
187
+ payload["body"] = body
188
+ if due_date is not None:
189
+ payload["due_date"] = due_date.isoformat()
190
+ if milestone is not None:
191
+ payload["milestone"] = milestone
192
+ if ref is not None:
193
+ payload["ref"] = ref
194
+ if state is not None:
195
+ payload["state"] = state
196
+ if title is not None:
197
+ payload["title"] = title
198
+ if unset_due_date is not None:
199
+ payload["unset_due_date"] = unset_due_date
200
+
201
+ return endpoint, payload