gitcode-api 1.0.0__tar.gz → 1.1.0__tar.gz
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.
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/PKG-INFO +39 -16
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/README.md +32 -13
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api/_base_client.py +38 -38
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api/_client.py +12 -10
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api/_exceptions.py +2 -2
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api/_models.py +4 -4
- gitcode_api-1.1.0/gitcode_api/resources/_shared.py +128 -0
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api/resources/account.py +296 -59
- gitcode_api-1.1.0/gitcode_api/resources/collaboration.py +2451 -0
- gitcode_api-1.1.0/gitcode_api/resources/misc.py +580 -0
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api/resources/repositories.py +650 -221
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api.egg-info/PKG-INFO +39 -16
- gitcode_api-1.1.0/gitcode_api.egg-info/requires.txt +1 -0
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/pyproject.toml +9 -4
- gitcode_api-1.1.0/tests/test_client.py +89 -0
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/tests/test_resources_account.py +3 -3
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/tests/test_resources_misc.py +3 -1
- gitcode_api-1.0.0/gitcode_api/resources/_shared.py +0 -75
- gitcode_api-1.0.0/gitcode_api/resources/collaboration.py +0 -1451
- gitcode_api-1.0.0/gitcode_api/resources/misc.py +0 -301
- gitcode_api-1.0.0/gitcode_api.egg-info/requires.txt +0 -2
- gitcode_api-1.0.0/tests/test_client.py +0 -35
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/LICENSE +0 -0
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api/__init__.py +0 -0
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api/resources/__init__.py +0 -0
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api.egg-info/SOURCES.txt +0 -0
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api.egg-info/dependency_links.txt +0 -0
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api.egg-info/top_level.txt +0 -0
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/setup.cfg +0 -0
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/tests/test_base_client.py +0 -0
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/tests/test_models.py +0 -0
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/tests/test_resources_collaboration.py +0 -0
- {gitcode_api-1.0.0 → gitcode_api-1.1.0}/tests/test_resources_repositories.py +0 -0
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitcode-api
|
|
3
|
-
Version: 1.
|
|
4
|
-
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Easy to use Python SDK for the GitCode REST API, community-maintained.
|
|
5
|
+
Author-email: Hugo Huang <hugo@hugohuang.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: documentation, https://gitcode-api.readthedocs.io
|
|
5
8
|
Project-URL: gitcode, https://gitcode.com/SushiNinja/GitCode-API
|
|
9
|
+
Project-URL: github, https://github.com/Trenza1ore/GitCode-API
|
|
10
|
+
Keywords: gitcode,git,devops,api,sdk,python,httpx,client
|
|
6
11
|
Classifier: Development Status :: 4 - Beta
|
|
7
12
|
Classifier: Programming Language :: Python
|
|
8
13
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
@@ -18,12 +23,13 @@ Requires-Python: <4,>=3.9
|
|
|
18
23
|
Description-Content-Type: text/markdown
|
|
19
24
|
License-File: LICENSE
|
|
20
25
|
Requires-Dist: httpx
|
|
21
|
-
Requires-Dist: python-dotenv
|
|
22
26
|
Dynamic: license-file
|
|
23
27
|
|
|
24
28
|
# GitCode-API
|
|
25
29
|
|
|
26
|
-
[
|
|
30
|
+
   
|
|
31
|
+
|
|
32
|
+
  
|
|
27
33
|
|
|
28
34
|
`gitcode-api` is a community-maintained Python SDK for the GitCode REST API. It provides easy-to-use synchronous and asynchronous clients, repository-scoped helpers, and lightweight response models so you can work with GitCode from Python without hand-writing raw HTTP requests.
|
|
29
35
|
|
|
@@ -43,18 +49,6 @@ Install from PyPI:
|
|
|
43
49
|
pip install gitcode-api
|
|
44
50
|
```
|
|
45
51
|
|
|
46
|
-
For local development from source:
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
uv sync
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
Install documentation dependencies:
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
uv sync --group docs
|
|
56
|
-
```
|
|
57
|
-
|
|
58
52
|
## Authentication
|
|
59
53
|
|
|
60
54
|
Pass `api_key=` directly, or set `GITCODE_ACCESS_TOKEN` in your environment:
|
|
@@ -106,6 +100,35 @@ async def main() -> None:
|
|
|
106
100
|
asyncio.run(main())
|
|
107
101
|
```
|
|
108
102
|
|
|
103
|
+
### Context managers
|
|
104
|
+
|
|
105
|
+
`GitCode` and `AsyncGitCode` (and the lower-level `SyncAPIClient` / `AsyncAPIClient`) support `with` / `async with`. When the SDK creates the underlying httpx client for you, leaving the block calls `close()` / `await close()` on that client automatically.
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from gitcode_api import GitCode
|
|
109
|
+
|
|
110
|
+
with GitCode(owner="SushiNinja", repo="GitCode-API") as client:
|
|
111
|
+
repo = client.repos.get()
|
|
112
|
+
print(repo.full_name)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
import asyncio
|
|
117
|
+
|
|
118
|
+
from gitcode_api import AsyncGitCode
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
async def main() -> None:
|
|
122
|
+
async with AsyncGitCode(owner="SushiNinja", repo="GitCode-API") as client:
|
|
123
|
+
pulls = await client.pulls.list(state="open", per_page=20)
|
|
124
|
+
print(len(pulls))
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
asyncio.run(main())
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
If you pass a custom `http_client=`, the SDK does not close it; you still own that client’s lifecycle (for example `async with httpx.AsyncClient(...) as http:` plus `AsyncGitCode(http_client=http)`).
|
|
131
|
+
|
|
109
132
|
## Common Workflows
|
|
110
133
|
|
|
111
134
|
Create a pull request:
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# GitCode-API
|
|
2
2
|
|
|
3
|
-
[
|
|
3
|
+
   
|
|
4
|
+
|
|
5
|
+
  
|
|
4
6
|
|
|
5
7
|
`gitcode-api` is a community-maintained Python SDK for the GitCode REST API. It provides easy-to-use synchronous and asynchronous clients, repository-scoped helpers, and lightweight response models so you can work with GitCode from Python without hand-writing raw HTTP requests.
|
|
6
8
|
|
|
@@ -20,18 +22,6 @@ Install from PyPI:
|
|
|
20
22
|
pip install gitcode-api
|
|
21
23
|
```
|
|
22
24
|
|
|
23
|
-
For local development from source:
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
uv sync
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Install documentation dependencies:
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
uv sync --group docs
|
|
33
|
-
```
|
|
34
|
-
|
|
35
25
|
## Authentication
|
|
36
26
|
|
|
37
27
|
Pass `api_key=` directly, or set `GITCODE_ACCESS_TOKEN` in your environment:
|
|
@@ -83,6 +73,35 @@ async def main() -> None:
|
|
|
83
73
|
asyncio.run(main())
|
|
84
74
|
```
|
|
85
75
|
|
|
76
|
+
### Context managers
|
|
77
|
+
|
|
78
|
+
`GitCode` and `AsyncGitCode` (and the lower-level `SyncAPIClient` / `AsyncAPIClient`) support `with` / `async with`. When the SDK creates the underlying httpx client for you, leaving the block calls `close()` / `await close()` on that client automatically.
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from gitcode_api import GitCode
|
|
82
|
+
|
|
83
|
+
with GitCode(owner="SushiNinja", repo="GitCode-API") as client:
|
|
84
|
+
repo = client.repos.get()
|
|
85
|
+
print(repo.full_name)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
import asyncio
|
|
90
|
+
|
|
91
|
+
from gitcode_api import AsyncGitCode
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
async def main() -> None:
|
|
95
|
+
async with AsyncGitCode(owner="SushiNinja", repo="GitCode-API") as client:
|
|
96
|
+
pulls = await client.pulls.list(state="open", per_page=20)
|
|
97
|
+
print(len(pulls))
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
asyncio.run(main())
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
If you pass a custom `http_client=`, the SDK does not close it; you still own that client’s lifecycle (for example `async with httpx.AsyncClient(...) as http:` plus `AsyncGitCode(http_client=http)`).
|
|
104
|
+
|
|
86
105
|
## Common Workflows
|
|
87
106
|
|
|
88
107
|
Create a pull request:
|
|
@@ -5,7 +5,7 @@ payload cleanup, and response parsing for both sync and async clients.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import os
|
|
8
|
-
from typing import Any
|
|
8
|
+
from typing import Any, Dict, Optional, Tuple, Union
|
|
9
9
|
from urllib.parse import quote
|
|
10
10
|
|
|
11
11
|
import httpx
|
|
@@ -17,7 +17,7 @@ DEFAULT_TIMEOUT = 30.0
|
|
|
17
17
|
DEFAULT_TOKEN_ENV = "GITCODE_ACCESS_TOKEN"
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
def _drop_none_values(mapping:
|
|
20
|
+
def _drop_none_values(mapping: Dict[str, Any]) -> Dict[str, Any]:
|
|
21
21
|
"""Return a copy of ``mapping`` without keys that have ``None`` values."""
|
|
22
22
|
return {key: value for key, value in mapping.items() if value is not None}
|
|
23
23
|
|
|
@@ -35,11 +35,11 @@ class BaseGitCodeClient:
|
|
|
35
35
|
def __init__(
|
|
36
36
|
self,
|
|
37
37
|
*,
|
|
38
|
-
api_key: str
|
|
39
|
-
owner: str
|
|
40
|
-
repo: str
|
|
38
|
+
api_key: Optional[str] = None,
|
|
39
|
+
owner: Optional[str] = None,
|
|
40
|
+
repo: Optional[str] = None,
|
|
41
41
|
base_url: str = DEFAULT_BASE_URL,
|
|
42
|
-
timeout: float
|
|
42
|
+
timeout: Optional[float] = None,
|
|
43
43
|
) -> None:
|
|
44
44
|
"""Store client configuration and resolve authentication."""
|
|
45
45
|
self.api_key = self._resolve_api_key(api_key)
|
|
@@ -48,7 +48,7 @@ class BaseGitCodeClient:
|
|
|
48
48
|
self.base_url = base_url.rstrip("/")
|
|
49
49
|
self.timeout = timeout if timeout is not None else DEFAULT_TIMEOUT
|
|
50
50
|
|
|
51
|
-
def _resolve_api_key(self, api_key: str
|
|
51
|
+
def _resolve_api_key(self, api_key: Optional[str]) -> str:
|
|
52
52
|
"""Resolve the access token from an argument or environment variable."""
|
|
53
53
|
token = api_key or os.getenv(DEFAULT_TOKEN_ENV)
|
|
54
54
|
if not token:
|
|
@@ -57,9 +57,9 @@ class BaseGitCodeClient:
|
|
|
57
57
|
|
|
58
58
|
def _resolve_repo_context(
|
|
59
59
|
self,
|
|
60
|
-
owner: str
|
|
61
|
-
repo: str
|
|
62
|
-
) ->
|
|
60
|
+
owner: Optional[str] = None,
|
|
61
|
+
repo: Optional[str] = None,
|
|
62
|
+
) -> Tuple[str, str]:
|
|
63
63
|
"""Return the effective repository owner and name for a request."""
|
|
64
64
|
resolved_owner = owner or self.owner
|
|
65
65
|
resolved_repo = repo or self.repo
|
|
@@ -69,7 +69,7 @@ class BaseGitCodeClient:
|
|
|
69
69
|
)
|
|
70
70
|
return resolved_owner, resolved_repo
|
|
71
71
|
|
|
72
|
-
def _headers(self, extra_headers:
|
|
72
|
+
def _headers(self, extra_headers: Optional[Dict[str, str]] = None) -> Dict[str, str]:
|
|
73
73
|
"""Build request headers for authenticated JSON API calls."""
|
|
74
74
|
headers = {
|
|
75
75
|
"Accept": "application/json",
|
|
@@ -79,7 +79,7 @@ class BaseGitCodeClient:
|
|
|
79
79
|
headers.update(extra_headers)
|
|
80
80
|
return headers
|
|
81
81
|
|
|
82
|
-
def _encode_segment(self, value: str
|
|
82
|
+
def _encode_segment(self, value: Union[int, str]) -> str:
|
|
83
83
|
"""Percent-encode a single URL path segment."""
|
|
84
84
|
return quote(str(value), safe="")
|
|
85
85
|
|
|
@@ -87,15 +87,15 @@ class BaseGitCodeClient:
|
|
|
87
87
|
"""Percent-encode a repository path while preserving slashes."""
|
|
88
88
|
return quote(value, safe="/")
|
|
89
89
|
|
|
90
|
-
def _join_path(self, *segments: str
|
|
90
|
+
def _join_path(self, *segments: Union[int, str]) -> str:
|
|
91
91
|
"""Join URL path segments into an API path."""
|
|
92
92
|
return "/" + "/".join(self._encode_segment(segment) for segment in segments)
|
|
93
93
|
|
|
94
94
|
def _repo_path(
|
|
95
95
|
self,
|
|
96
|
-
*segments:
|
|
97
|
-
owner: str
|
|
98
|
-
repo: str
|
|
96
|
+
*segments: Union[int, str],
|
|
97
|
+
owner: Optional[str] = None,
|
|
98
|
+
repo: Optional[str] = None,
|
|
99
99
|
) -> str:
|
|
100
100
|
"""Build a path under ``/repos/{owner}/{repo}``."""
|
|
101
101
|
resolved_owner, resolved_repo = self._resolve_repo_context(owner, repo)
|
|
@@ -106,8 +106,8 @@ class BaseGitCodeClient:
|
|
|
106
106
|
prefix: str,
|
|
107
107
|
file_path: str,
|
|
108
108
|
*,
|
|
109
|
-
owner: str
|
|
110
|
-
repo: str
|
|
109
|
+
owner: Optional[str] = None,
|
|
110
|
+
repo: Optional[str] = None,
|
|
111
111
|
) -> str:
|
|
112
112
|
"""Build a file-oriented repository path such as ``contents`` or ``raw``."""
|
|
113
113
|
resolved_owner, resolved_repo = self._resolve_repo_context(owner, repo)
|
|
@@ -117,7 +117,7 @@ class BaseGitCodeClient:
|
|
|
117
117
|
f"{self._encode_path_value(file_path)}"
|
|
118
118
|
)
|
|
119
119
|
|
|
120
|
-
def _path(self, *segments: str
|
|
120
|
+
def _path(self, *segments: Union[int, str]) -> str:
|
|
121
121
|
"""Build a non-repository API path."""
|
|
122
122
|
return self._join_path(*segments)
|
|
123
123
|
|
|
@@ -130,10 +130,10 @@ class BaseGitCodeClient:
|
|
|
130
130
|
def _coerce_payload(
|
|
131
131
|
self,
|
|
132
132
|
*,
|
|
133
|
-
params:
|
|
133
|
+
params: Optional[Dict[str, Any]] = None,
|
|
134
134
|
json: Any = None,
|
|
135
|
-
data:
|
|
136
|
-
) ->
|
|
135
|
+
data: Optional[Dict[str, Any]] = None,
|
|
136
|
+
) -> Tuple[Optional[Dict[str, Any]], Any, Optional[Dict[str, Any]]]:
|
|
137
137
|
"""Drop ``None`` values from params and form payloads before sending."""
|
|
138
138
|
clean_params = _drop_none_values(params or {}) or None
|
|
139
139
|
clean_data = _drop_none_values(data or {}) or None
|
|
@@ -192,12 +192,12 @@ class SyncAPIClient(BaseGitCodeClient):
|
|
|
192
192
|
def __init__(
|
|
193
193
|
self,
|
|
194
194
|
*,
|
|
195
|
-
api_key: str
|
|
196
|
-
owner: str
|
|
197
|
-
repo: str
|
|
195
|
+
api_key: Optional[str] = None,
|
|
196
|
+
owner: Optional[str] = None,
|
|
197
|
+
repo: Optional[str] = None,
|
|
198
198
|
base_url: str = DEFAULT_BASE_URL,
|
|
199
|
-
timeout: float
|
|
200
|
-
http_client: httpx.Client
|
|
199
|
+
timeout: Optional[float] = None,
|
|
200
|
+
http_client: Optional[httpx.Client] = None,
|
|
201
201
|
) -> None:
|
|
202
202
|
"""Create or reuse an ``httpx.Client`` for synchronous requests."""
|
|
203
203
|
super().__init__(api_key=api_key, owner=owner, repo=repo, base_url=base_url, timeout=timeout)
|
|
@@ -209,10 +209,10 @@ class SyncAPIClient(BaseGitCodeClient):
|
|
|
209
209
|
method: str,
|
|
210
210
|
path: str,
|
|
211
211
|
*,
|
|
212
|
-
params:
|
|
212
|
+
params: Optional[Dict[str, Any]] = None,
|
|
213
213
|
json: Any = None,
|
|
214
|
-
data:
|
|
215
|
-
headers:
|
|
214
|
+
data: Optional[Dict[str, Any]] = None,
|
|
215
|
+
headers: Optional[Dict[str, str]] = None,
|
|
216
216
|
raw: bool = False,
|
|
217
217
|
) -> Any:
|
|
218
218
|
"""Send an HTTP request to the GitCode API and parse the response.
|
|
@@ -265,12 +265,12 @@ class AsyncAPIClient(BaseGitCodeClient):
|
|
|
265
265
|
def __init__(
|
|
266
266
|
self,
|
|
267
267
|
*,
|
|
268
|
-
api_key: str
|
|
269
|
-
owner: str
|
|
270
|
-
repo: str
|
|
268
|
+
api_key: Optional[str] = None,
|
|
269
|
+
owner: Optional[str] = None,
|
|
270
|
+
repo: Optional[str] = None,
|
|
271
271
|
base_url: str = DEFAULT_BASE_URL,
|
|
272
|
-
timeout: float
|
|
273
|
-
http_client: httpx.AsyncClient
|
|
272
|
+
timeout: Optional[float] = None,
|
|
273
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
|
274
274
|
) -> None:
|
|
275
275
|
"""Create or reuse an ``httpx.AsyncClient`` for asynchronous requests."""
|
|
276
276
|
super().__init__(api_key=api_key, owner=owner, repo=repo, base_url=base_url, timeout=timeout)
|
|
@@ -282,10 +282,10 @@ class AsyncAPIClient(BaseGitCodeClient):
|
|
|
282
282
|
method: str,
|
|
283
283
|
path: str,
|
|
284
284
|
*,
|
|
285
|
-
params:
|
|
285
|
+
params: Optional[Dict[str, Any]] = None,
|
|
286
286
|
json: Any = None,
|
|
287
|
-
data:
|
|
288
|
-
headers:
|
|
287
|
+
data: Optional[Dict[str, Any]] = None,
|
|
288
|
+
headers: Optional[Dict[str, str]] = None,
|
|
289
289
|
raw: bool = False,
|
|
290
290
|
) -> Any:
|
|
291
291
|
"""Send an asynchronous HTTP request to the GitCode API.
|
|
@@ -4,6 +4,8 @@ These client classes expose grouped resource helpers that mirror the
|
|
|
4
4
|
published GitCode REST API documentation.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
7
9
|
import httpx
|
|
8
10
|
|
|
9
11
|
from ._base_client import DEFAULT_BASE_URL, AsyncAPIClient, SyncAPIClient
|
|
@@ -105,12 +107,12 @@ class GitCode(SyncAPIClient):
|
|
|
105
107
|
def __init__(
|
|
106
108
|
self,
|
|
107
109
|
*,
|
|
108
|
-
api_key: str
|
|
109
|
-
owner: str
|
|
110
|
-
repo: str
|
|
110
|
+
api_key: Optional[str] = None,
|
|
111
|
+
owner: Optional[str] = None,
|
|
112
|
+
repo: Optional[str] = None,
|
|
111
113
|
base_url: str = DEFAULT_BASE_URL,
|
|
112
|
-
timeout: float
|
|
113
|
-
http_client: httpx.Client
|
|
114
|
+
timeout: Optional[float] = None,
|
|
115
|
+
http_client: Optional[httpx.Client] = None,
|
|
114
116
|
) -> None:
|
|
115
117
|
"""Create a synchronous client and attach resource groups."""
|
|
116
118
|
super().__init__(
|
|
@@ -201,12 +203,12 @@ class AsyncGitCode(AsyncAPIClient):
|
|
|
201
203
|
def __init__(
|
|
202
204
|
self,
|
|
203
205
|
*,
|
|
204
|
-
api_key: str
|
|
205
|
-
owner: str
|
|
206
|
-
repo: str
|
|
206
|
+
api_key: Optional[str] = None,
|
|
207
|
+
owner: Optional[str] = None,
|
|
208
|
+
repo: Optional[str] = None,
|
|
207
209
|
base_url: str = DEFAULT_BASE_URL,
|
|
208
|
-
timeout: float
|
|
209
|
-
http_client: httpx.AsyncClient
|
|
210
|
+
timeout: Optional[float] = None,
|
|
211
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
|
210
212
|
) -> None:
|
|
211
213
|
"""Create an asynchronous client and attach resource groups."""
|
|
212
214
|
super().__init__(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Custom exceptions raised by the GitCode SDK."""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any, Optional
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class GitCodeError(Exception):
|
|
@@ -25,7 +25,7 @@ class GitCodeAPIError(GitCodeError):
|
|
|
25
25
|
message: str,
|
|
26
26
|
*,
|
|
27
27
|
status_code: int,
|
|
28
|
-
request_id: str
|
|
28
|
+
request_id: Optional[str] = None,
|
|
29
29
|
payload: Any = None,
|
|
30
30
|
) -> None:
|
|
31
31
|
"""Store structured error metadata from a failed API response."""
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Lightweight wrappers and typed payload hints for GitCode API responses."""
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import Any, Iterator, List, Mapping, MutableMapping, TypedDict, TypeVar
|
|
4
|
+
from typing import Any, Dict, Iterator, List, Mapping, MutableMapping, TypedDict, TypeVar, Union
|
|
5
5
|
|
|
6
6
|
JsonValue = Any
|
|
7
7
|
|
|
@@ -15,7 +15,7 @@ def _wrap_value(value: Any) -> Any:
|
|
|
15
15
|
return value
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
@dataclass
|
|
18
|
+
@dataclass
|
|
19
19
|
class APIObject(Mapping[str, Any]):
|
|
20
20
|
"""Dictionary-backed wrapper around GitCode JSON objects.
|
|
21
21
|
|
|
@@ -48,7 +48,7 @@ class APIObject(Mapping[str, Any]):
|
|
|
48
48
|
"""Return a wrapped value with an optional default."""
|
|
49
49
|
return _wrap_value(self.data.get(key, default))
|
|
50
50
|
|
|
51
|
-
def to_dict(self) ->
|
|
51
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
52
52
|
"""Return a shallow ``dict`` copy of the underlying payload."""
|
|
53
53
|
return dict(self.data)
|
|
54
54
|
|
|
@@ -91,7 +91,7 @@ class IssueCreateParams(TypedDict, total=False):
|
|
|
91
91
|
body: str
|
|
92
92
|
assignee: str
|
|
93
93
|
labels: List[str]
|
|
94
|
-
milestone: int
|
|
94
|
+
milestone: Union[int, str]
|
|
95
95
|
|
|
96
96
|
|
|
97
97
|
class PullRequestCreateParams(TypedDict, total=False):
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Shared resource base classes for the GitCode SDK."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Optional, Union
|
|
4
|
+
|
|
5
|
+
from .._base_client import AsyncAPIClient, SyncAPIClient
|
|
6
|
+
from .._models import APIObject, ModelT, as_model, as_model_list
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SyncResource:
|
|
10
|
+
"""Base class for synchronous resource groups."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, client: SyncAPIClient) -> None:
|
|
13
|
+
"""Bind the resource to a synchronous API client."""
|
|
14
|
+
self._client = client
|
|
15
|
+
|
|
16
|
+
def _request(
|
|
17
|
+
self,
|
|
18
|
+
method: str,
|
|
19
|
+
path: str,
|
|
20
|
+
*,
|
|
21
|
+
params: Optional[Dict[str, Any]] = None,
|
|
22
|
+
json: Any = None,
|
|
23
|
+
data: Optional[Dict[str, Any]] = None,
|
|
24
|
+
raw: bool = False,
|
|
25
|
+
) -> Any:
|
|
26
|
+
"""Dispatch a low-level request through the owning client.
|
|
27
|
+
|
|
28
|
+
:param method: HTTP verb (for example ``GET`` or ``POST``).
|
|
29
|
+
:param path: Absolute or root-relative URL path.
|
|
30
|
+
:param params: Optional query string parameters.
|
|
31
|
+
:param json: Optional JSON-serializable request body.
|
|
32
|
+
:param data: Optional form or body fields.
|
|
33
|
+
:param raw: When ``True``, return the raw response body (for example bytes).
|
|
34
|
+
:returns: Parsed JSON, raw body, or other value from the client.
|
|
35
|
+
"""
|
|
36
|
+
return self._client.request(method, path, params=params, json=json, data=data, raw=raw)
|
|
37
|
+
|
|
38
|
+
def _model(self, method: str, path: str, model_type: type[ModelT], **kwargs: Any) -> ModelT:
|
|
39
|
+
"""Send a request and wrap a JSON object in ``model_type``.
|
|
40
|
+
|
|
41
|
+
:param method: HTTP verb.
|
|
42
|
+
:param path: Request path.
|
|
43
|
+
:param model_type: Model class for the top-level JSON object.
|
|
44
|
+
:param kwargs: Forwarded to :meth:`_request` (``params``, ``json``, ``data``, ``raw``, etc.).
|
|
45
|
+
:returns: An instance of ``model_type``.
|
|
46
|
+
"""
|
|
47
|
+
data = self._request(method, path, **kwargs)
|
|
48
|
+
return as_model(data, model_type)
|
|
49
|
+
|
|
50
|
+
def _models(self, method: str, path: str, model_type: type[ModelT], **kwargs: Any) -> List[ModelT]:
|
|
51
|
+
"""Send a request and wrap a JSON array in ``model_type`` instances.
|
|
52
|
+
|
|
53
|
+
:param method: HTTP verb.
|
|
54
|
+
:param path: Request path.
|
|
55
|
+
:param model_type: Model class for each element of the JSON array.
|
|
56
|
+
:param kwargs: Forwarded to :meth:`_request`.
|
|
57
|
+
:returns: A list of ``model_type`` instances.
|
|
58
|
+
"""
|
|
59
|
+
data = self._request(method, path, **kwargs)
|
|
60
|
+
return as_model_list(data, model_type)
|
|
61
|
+
|
|
62
|
+
def _maybe_model(self, method: str, path: str, model_type: type[ModelT], **kwargs: Any) -> Union[ModelT, APIObject]:
|
|
63
|
+
"""Wrap dict responses as models and scalar responses as ``APIObject``.
|
|
64
|
+
|
|
65
|
+
:param method: HTTP verb.
|
|
66
|
+
:param path: Request path.
|
|
67
|
+
:param model_type: Model class when the response is a JSON object.
|
|
68
|
+
:param kwargs: Forwarded to :meth:`_request`.
|
|
69
|
+
:returns: ``model_type`` for dict bodies; otherwise ``APIObject`` wrapping a ``value`` field.
|
|
70
|
+
"""
|
|
71
|
+
data = self._request(method, path, **kwargs)
|
|
72
|
+
if isinstance(data, dict):
|
|
73
|
+
return as_model(data, model_type)
|
|
74
|
+
return APIObject({"value": data})
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class AsyncResource:
|
|
78
|
+
"""Base class for asynchronous resource groups."""
|
|
79
|
+
|
|
80
|
+
def __init__(self, client: AsyncAPIClient) -> None:
|
|
81
|
+
"""Bind the resource to an asynchronous API client."""
|
|
82
|
+
self._client = client
|
|
83
|
+
|
|
84
|
+
async def _request(
|
|
85
|
+
self,
|
|
86
|
+
method: str,
|
|
87
|
+
path: str,
|
|
88
|
+
*,
|
|
89
|
+
params: Optional[Dict[str, Any]] = None,
|
|
90
|
+
json: Any = None,
|
|
91
|
+
data: Optional[Dict[str, Any]] = None,
|
|
92
|
+
raw: bool = False,
|
|
93
|
+
) -> Any:
|
|
94
|
+
"""Dispatch a low-level async request through the owning client.
|
|
95
|
+
|
|
96
|
+
:param method: HTTP verb (for example ``GET`` or ``POST``).
|
|
97
|
+
:param path: Absolute or root-relative URL path.
|
|
98
|
+
:param params: Optional query string parameters.
|
|
99
|
+
:param json: Optional JSON-serializable request body.
|
|
100
|
+
:param data: Optional form or body fields.
|
|
101
|
+
:param raw: When ``True``, return the raw response body (for example bytes).
|
|
102
|
+
:returns: Parsed JSON, raw body, or other value from the client.
|
|
103
|
+
"""
|
|
104
|
+
return await self._client.request(method, path, params=params, json=json, data=data, raw=raw)
|
|
105
|
+
|
|
106
|
+
async def _model(self, method: str, path: str, model_type: type[ModelT], **kwargs: Any) -> ModelT:
|
|
107
|
+
"""Send a request and wrap a JSON object in ``model_type``.
|
|
108
|
+
|
|
109
|
+
:param method: HTTP verb.
|
|
110
|
+
:param path: Request path.
|
|
111
|
+
:param model_type: Model class for the top-level JSON object.
|
|
112
|
+
:param kwargs: Forwarded to :meth:`_request`.
|
|
113
|
+
:returns: An instance of ``model_type``.
|
|
114
|
+
"""
|
|
115
|
+
data = await self._request(method, path, **kwargs)
|
|
116
|
+
return as_model(data, model_type)
|
|
117
|
+
|
|
118
|
+
async def _models(self, method: str, path: str, model_type: type[ModelT], **kwargs: Any) -> List[ModelT]:
|
|
119
|
+
"""Send a request and wrap a JSON array in ``model_type`` instances.
|
|
120
|
+
|
|
121
|
+
:param method: HTTP verb.
|
|
122
|
+
:param path: Request path.
|
|
123
|
+
:param model_type: Model class for each element of the JSON array.
|
|
124
|
+
:param kwargs: Forwarded to :meth:`_request`.
|
|
125
|
+
:returns: A list of ``model_type`` instances.
|
|
126
|
+
"""
|
|
127
|
+
data = await self._request(method, path, **kwargs)
|
|
128
|
+
return as_model_list(data, model_type)
|