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.
Files changed (33) hide show
  1. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/PKG-INFO +39 -16
  2. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/README.md +32 -13
  3. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api/_base_client.py +38 -38
  4. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api/_client.py +12 -10
  5. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api/_exceptions.py +2 -2
  6. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api/_models.py +4 -4
  7. gitcode_api-1.1.0/gitcode_api/resources/_shared.py +128 -0
  8. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api/resources/account.py +296 -59
  9. gitcode_api-1.1.0/gitcode_api/resources/collaboration.py +2451 -0
  10. gitcode_api-1.1.0/gitcode_api/resources/misc.py +580 -0
  11. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api/resources/repositories.py +650 -221
  12. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api.egg-info/PKG-INFO +39 -16
  13. gitcode_api-1.1.0/gitcode_api.egg-info/requires.txt +1 -0
  14. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/pyproject.toml +9 -4
  15. gitcode_api-1.1.0/tests/test_client.py +89 -0
  16. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/tests/test_resources_account.py +3 -3
  17. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/tests/test_resources_misc.py +3 -1
  18. gitcode_api-1.0.0/gitcode_api/resources/_shared.py +0 -75
  19. gitcode_api-1.0.0/gitcode_api/resources/collaboration.py +0 -1451
  20. gitcode_api-1.0.0/gitcode_api/resources/misc.py +0 -301
  21. gitcode_api-1.0.0/gitcode_api.egg-info/requires.txt +0 -2
  22. gitcode_api-1.0.0/tests/test_client.py +0 -35
  23. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/LICENSE +0 -0
  24. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api/__init__.py +0 -0
  25. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api/resources/__init__.py +0 -0
  26. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api.egg-info/SOURCES.txt +0 -0
  27. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api.egg-info/dependency_links.txt +0 -0
  28. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/gitcode_api.egg-info/top_level.txt +0 -0
  29. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/setup.cfg +0 -0
  30. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/tests/test_base_client.py +0 -0
  31. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/tests/test_models.py +0 -0
  32. {gitcode_api-1.0.0 → gitcode_api-1.1.0}/tests/test_resources_collaboration.py +0 -0
  33. {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.0.0
4
- Project-URL: github, https://github.com/Trenza1ore/GitCode-API
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
- [中文说明](README.zh.md)
30
+ ![PyPI - Version](https://img.shields.io/pypi/v/gitcode-api?link=https%3A%2F%2Fpypi.org%2Fproject%2Fgitcode-api%2F) ![GitHub Badge](https://img.shields.io/badge/github-repo-blue?logo=github&link=https%3A%2F%2Fgithub.com%2FTrenza1ore%2FGitCode-API) ![GitCode Badge](https://img.shields.io/badge/gitcode-repo-brown?logo=gitcode&link=https%3A%2F%2Fgitcode.com%2FSushiNinja%2FGitCode-API) ![PyPI - License](https://img.shields.io/pypi/l/gitcode-api)
31
+
32
+ ![Docs](https://img.shields.io/badge/%E6%96%87%E6%A1%A3-Docs-cyan?style=for-the-badge&logo=readthedocs&link=https%3A%2F%2Fgitcode-api.readthedocs.io%2Fen%2Flatest%2Findex.html) ![中文README](https://img.shields.io/badge/%E4%B8%AD%E6%96%87-README-brown?style=for-the-badge&logo=googledocs&link=README.zh.md) ![English README](https://img.shields.io/badge/English-README-blue?style=for-the-badge&logo=googledocs&link=README.md)
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
- [中文说明](README.zh.md)
3
+ ![PyPI - Version](https://img.shields.io/pypi/v/gitcode-api?link=https%3A%2F%2Fpypi.org%2Fproject%2Fgitcode-api%2F) ![GitHub Badge](https://img.shields.io/badge/github-repo-blue?logo=github&link=https%3A%2F%2Fgithub.com%2FTrenza1ore%2FGitCode-API) ![GitCode Badge](https://img.shields.io/badge/gitcode-repo-brown?logo=gitcode&link=https%3A%2F%2Fgitcode.com%2FSushiNinja%2FGitCode-API) ![PyPI - License](https://img.shields.io/pypi/l/gitcode-api)
4
+
5
+ ![Docs](https://img.shields.io/badge/%E6%96%87%E6%A1%A3-Docs-cyan?style=for-the-badge&logo=readthedocs&link=https%3A%2F%2Fgitcode-api.readthedocs.io%2Fen%2Flatest%2Findex.html) ![中文README](https://img.shields.io/badge/%E4%B8%AD%E6%96%87-README-brown?style=for-the-badge&logo=googledocs&link=README.zh.md) ![English README](https://img.shields.io/badge/English-README-blue?style=for-the-badge&logo=googledocs&link=README.md)
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: dict[str, Any]) -> dict[str, Any]:
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 | None = None,
39
- owner: str | None = None,
40
- repo: str | None = None,
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 | None = None,
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 | None) -> 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 | None = None,
61
- repo: str | None = None,
62
- ) -> tuple[str, str]:
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: dict[str, str] | None = None) -> dict[str, str]:
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 | int) -> 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 | int) -> 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: str | int,
97
- owner: str | None = None,
98
- repo: str | None = None,
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 | None = None,
110
- repo: str | None = None,
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 | int) -> 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: dict[str, Any] | None = None,
133
+ params: Optional[Dict[str, Any]] = None,
134
134
  json: Any = None,
135
- data: dict[str, Any] | None = None,
136
- ) -> tuple[dict[str, Any] | None, Any, dict[str, Any] | None]:
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 | None = None,
196
- owner: str | None = None,
197
- repo: str | None = None,
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 | None = None,
200
- http_client: httpx.Client | None = None,
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: dict[str, Any] | None = None,
212
+ params: Optional[Dict[str, Any]] = None,
213
213
  json: Any = None,
214
- data: dict[str, Any] | None = None,
215
- headers: dict[str, str] | None = None,
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 | None = None,
269
- owner: str | None = None,
270
- repo: str | None = None,
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 | None = None,
273
- http_client: httpx.AsyncClient | None = None,
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: dict[str, Any] | None = None,
285
+ params: Optional[Dict[str, Any]] = None,
286
286
  json: Any = None,
287
- data: dict[str, Any] | None = None,
288
- headers: dict[str, str] | None = None,
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 | None = None,
109
- owner: str | None = None,
110
- repo: str | None = None,
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 | None = None,
113
- http_client: httpx.Client | None = None,
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 | None = None,
205
- owner: str | None = None,
206
- repo: str | None = None,
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 | None = None,
209
- http_client: httpx.AsyncClient | None = None,
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 | None = None,
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(slots=True)
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) -> dict[str, Any]:
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 | str
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)