gitcode-api 1.2.13__py3-none-any.whl → 1.2.15__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.
- gitcode_api/__init__.py +2 -0
- gitcode_api/_models.py +28 -55
- gitcode_api/llm/jiuwen.py +1 -1
- gitcode_api/llm/openai.py +1 -0
- gitcode_api/resources/_shared.py +307 -1
- gitcode_api/resources/collaboration.py +225 -1
- gitcode_api/utils.py +37 -0
- gitcode_api/version.txt +1 -1
- {gitcode_api-1.2.13.dist-info → gitcode_api-1.2.15.dist-info}/METADATA +22 -2
- {gitcode_api-1.2.13.dist-info → gitcode_api-1.2.15.dist-info}/RECORD +14 -13
- {gitcode_api-1.2.13.dist-info → gitcode_api-1.2.15.dist-info}/WHEEL +0 -0
- {gitcode_api-1.2.13.dist-info → gitcode_api-1.2.15.dist-info}/entry_points.txt +0 -0
- {gitcode_api-1.2.13.dist-info → gitcode_api-1.2.15.dist-info}/licenses/LICENSE +0 -0
- {gitcode_api-1.2.13.dist-info → gitcode_api-1.2.15.dist-info}/top_level.txt +0 -0
gitcode_api/__init__.py
CHANGED
|
@@ -9,6 +9,7 @@ from ._exceptions import (
|
|
|
9
9
|
GitCodeError,
|
|
10
10
|
GitCodeHTTPStatusError,
|
|
11
11
|
)
|
|
12
|
+
from .utils import as_dict
|
|
12
13
|
|
|
13
14
|
__version__ = (Path(__file__).parent / "version.txt").read_text().strip()
|
|
14
15
|
|
|
@@ -20,4 +21,5 @@ __all__ = [
|
|
|
20
21
|
"GitCodeConfigurationError",
|
|
21
22
|
"GitCodeError",
|
|
22
23
|
"GitCodeHTTPStatusError",
|
|
24
|
+
"as_dict",
|
|
23
25
|
]
|
gitcode_api/_models.py
CHANGED
|
@@ -9,7 +9,6 @@ from typing import (
|
|
|
9
9
|
Mapping,
|
|
10
10
|
MutableMapping,
|
|
11
11
|
Optional,
|
|
12
|
-
TypedDict,
|
|
13
12
|
TypeVar,
|
|
14
13
|
Union,
|
|
15
14
|
get_args,
|
|
@@ -136,60 +135,6 @@ def as_model_list(data: List[Mapping[str, Any]], model_type: type[ModelT]) -> Li
|
|
|
136
135
|
return [as_model(item, model_type) for item in data]
|
|
137
136
|
|
|
138
137
|
|
|
139
|
-
class RepositoryCreateParams(TypedDict, total=False):
|
|
140
|
-
"""Typed fields accepted by repository creation endpoints."""
|
|
141
|
-
|
|
142
|
-
name: str
|
|
143
|
-
description: str
|
|
144
|
-
has_issues: bool
|
|
145
|
-
has_wiki: bool
|
|
146
|
-
auto_init: bool
|
|
147
|
-
gitignore_template: str
|
|
148
|
-
license_template: str
|
|
149
|
-
path: str
|
|
150
|
-
private: bool
|
|
151
|
-
public: int
|
|
152
|
-
default_branch: str
|
|
153
|
-
homepage: str
|
|
154
|
-
can_comment: bool
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
class IssueCreateParams(TypedDict, total=False):
|
|
158
|
-
"""Typed fields accepted by issue creation endpoints."""
|
|
159
|
-
|
|
160
|
-
title: str
|
|
161
|
-
body: str
|
|
162
|
-
assignee: str
|
|
163
|
-
labels: List[str]
|
|
164
|
-
milestone: Union[int, str]
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
class PullRequestCreateParams(TypedDict, total=False):
|
|
168
|
-
"""Typed fields accepted by pull request creation endpoints."""
|
|
169
|
-
|
|
170
|
-
title: str
|
|
171
|
-
body: str
|
|
172
|
-
head: str
|
|
173
|
-
base: str
|
|
174
|
-
assignees: List[str]
|
|
175
|
-
testers: List[str]
|
|
176
|
-
labels: List[str]
|
|
177
|
-
draft: bool
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
class WebhookCreateParams(TypedDict, total=False):
|
|
181
|
-
"""Typed fields accepted by webhook creation endpoints."""
|
|
182
|
-
|
|
183
|
-
url: str
|
|
184
|
-
encryption_type: int
|
|
185
|
-
password: str
|
|
186
|
-
push_events: bool
|
|
187
|
-
tag_push_events: bool
|
|
188
|
-
issues_events: bool
|
|
189
|
-
note_events: bool
|
|
190
|
-
merge_requests_events: bool
|
|
191
|
-
|
|
192
|
-
|
|
193
138
|
@dataclass(init=False)
|
|
194
139
|
class UserRef(APIObject):
|
|
195
140
|
"""Compact user payload embedded in other responses.
|
|
@@ -513,6 +458,8 @@ class Repository(APIObject):
|
|
|
513
458
|
:vartype relation: Optional[str]
|
|
514
459
|
:ivar members: Members returned in the repository payload.
|
|
515
460
|
:vartype members: Optional[List[str]]
|
|
461
|
+
:ivar parent: Upstream source repository information
|
|
462
|
+
:vartype parent: Optional[Dict[str, Any]]
|
|
516
463
|
:ivar permission: Permission summary for this object.
|
|
517
464
|
:vartype permission: Optional[RepositoryPermission]
|
|
518
465
|
:ivar enterprise: Enterprise associated with the repository.
|
|
@@ -566,6 +513,7 @@ class Repository(APIObject):
|
|
|
566
513
|
status: Optional[str] = None
|
|
567
514
|
relation: Optional[str] = None
|
|
568
515
|
members: Optional[List[str]] = None
|
|
516
|
+
parent: Optional[Dict[str, Any]] = None
|
|
569
517
|
permission: Optional[RepositoryPermission] = None
|
|
570
518
|
enterprise: Optional[Union[EnterpriseRef, str]] = None
|
|
571
519
|
issue_template_source: Optional[str] = None
|
|
@@ -760,6 +708,31 @@ class ContentObject(APIObject):
|
|
|
760
708
|
_links: Optional[ContentLinks] = None
|
|
761
709
|
|
|
762
710
|
|
|
711
|
+
@dataclass(init=False)
|
|
712
|
+
class RepositoryGitCodeTemplate(APIObject):
|
|
713
|
+
"""Metadata for a ``.gitcode`` issue or pull request template file.
|
|
714
|
+
|
|
715
|
+
Returned by :meth:`~gitcode_api.resources.collaboration.IssuesResource.list_templates`
|
|
716
|
+
and :meth:`~gitcode_api.resources.collaboration.PullsResource.list_templates`. Use
|
|
717
|
+
:meth:`~gitcode_api.resources.collaboration.IssuesResource.get_template` or
|
|
718
|
+
:meth:`~gitcode_api.resources.collaboration.PullsResource.get_template` to load the body.
|
|
719
|
+
|
|
720
|
+
:ivar path: Repository-relative path (under ``.gitcode/``).
|
|
721
|
+
:vartype path: Optional[str]
|
|
722
|
+
:ivar sha: Object SHA for the file.
|
|
723
|
+
:vartype sha: Optional[str]
|
|
724
|
+
:ivar template_owner: Owner path of the repository the template was resolved from.
|
|
725
|
+
:vartype template_owner: Optional[str]
|
|
726
|
+
:ivar template_repo: Repository name the template was resolved from.
|
|
727
|
+
:vartype template_repo: Optional[str]
|
|
728
|
+
"""
|
|
729
|
+
|
|
730
|
+
path: Optional[str] = None
|
|
731
|
+
sha: Optional[str] = None
|
|
732
|
+
template_owner: Optional[str] = None
|
|
733
|
+
template_repo: Optional[str] = None
|
|
734
|
+
|
|
735
|
+
|
|
763
736
|
@dataclass(init=False)
|
|
764
737
|
class CommitIdentity(APIObject):
|
|
765
738
|
"""Commit author/committer identity.
|
gitcode_api/llm/jiuwen.py
CHANGED
|
@@ -9,7 +9,7 @@ from gitcode_api._base_client import DEFAULT_BASE_URL
|
|
|
9
9
|
from ._tool import TOOL_DESCRIPTION, TOOL_NAME, TOOL_PARAMETERS, GitCodeLLMTool
|
|
10
10
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
|
-
from openjiuwen.core.foundation.tool import LocalFunction
|
|
12
|
+
from openjiuwen.core.foundation.tool import LocalFunction # type: ignore[import-untyped]
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def _missing_openjiuwen_error() -> ImportError:
|
gitcode_api/llm/openai.py
CHANGED
|
@@ -75,6 +75,7 @@ class GitCodeOpenAITool(GitCodeLLMTool):
|
|
|
75
75
|
return self.tool
|
|
76
76
|
|
|
77
77
|
async def __async_call__(self, *args, **kwargs) -> str:
|
|
78
|
+
"""Invoke the configured async tool callable."""
|
|
78
79
|
result = await super().__async_call__(*args, **kwargs)
|
|
79
80
|
return json.dumps(result, ensure_ascii=False, indent=self.indent)
|
|
80
81
|
|
gitcode_api/resources/_shared.py
CHANGED
|
@@ -1,11 +1,317 @@
|
|
|
1
1
|
"""Shared resource base classes for the GitCode SDK."""
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import re
|
|
4
|
+
from typing import Any, Dict, List, Optional, Pattern, Tuple, Union
|
|
4
5
|
|
|
5
6
|
from .._base_client import AsyncAPIClient, SyncAPIClient
|
|
6
7
|
from .._base_resource import BaseResource
|
|
8
|
+
from .._exceptions import GitCodeHTTPStatusError
|
|
7
9
|
from .._models import APIObject, ModelT, as_model, as_model_list
|
|
8
10
|
|
|
11
|
+
GITCODE_ISSUE_TEMPLATE_PATH_RE: Pattern[str] = re.compile(
|
|
12
|
+
r"\.gitcode/ISSUE_TEMPLATE.*\.(md|markdown|ya?ml)$", re.IGNORECASE
|
|
13
|
+
)
|
|
14
|
+
GITCODE_PULL_REQUEST_TEMPLATE_PATH_RE: Pattern[str] = re.compile(
|
|
15
|
+
r"\.gitcode/PULL_REQUEST_TEMPLATE.*\.(md|markdown|ya?ml)$", re.IGNORECASE
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _parse_parent_owner_repo(repo_obj: Any) -> Optional[Tuple[str, str]]:
|
|
20
|
+
parent = getattr(repo_obj, "parent", None)
|
|
21
|
+
if parent is None and hasattr(repo_obj, "get"):
|
|
22
|
+
parent = repo_obj.get("parent")
|
|
23
|
+
if not isinstance(parent, dict):
|
|
24
|
+
return None
|
|
25
|
+
full = str(parent.get("full_name") or "").strip()
|
|
26
|
+
if "/" not in full:
|
|
27
|
+
return None
|
|
28
|
+
owner_path, name = full.rsplit("/", 1)
|
|
29
|
+
owner_path, name = owner_path.strip(), name.strip()
|
|
30
|
+
if not owner_path or not name:
|
|
31
|
+
return None
|
|
32
|
+
return owner_path, name
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _resolution_sources_sync(client: SyncAPIClient, owner: str, repo: str) -> List[Tuple[str, str]]:
|
|
36
|
+
"""Build ordered repo candidates.
|
|
37
|
+
|
|
38
|
+
Starts with ``(owner, repo)`` and ``(owner, ".gitcode")``. For every candidate that is a
|
|
39
|
+
fork (per ``GET /repos/{owner}/{repo}``), appends that repository's parent main repo and
|
|
40
|
+
``(parent_owner, ".gitcode")`` when not already listed (deduplicated, breadth-first).
|
|
41
|
+
"""
|
|
42
|
+
from .repositories import ReposResource
|
|
43
|
+
|
|
44
|
+
sources: List[Tuple[str, str]] = []
|
|
45
|
+
seen: set[Tuple[str, str]] = set()
|
|
46
|
+
|
|
47
|
+
def add(pair: Tuple[str, str]) -> None:
|
|
48
|
+
if pair not in seen:
|
|
49
|
+
seen.add(pair)
|
|
50
|
+
sources.append(pair)
|
|
51
|
+
|
|
52
|
+
add((owner, repo))
|
|
53
|
+
add((owner, ".gitcode"))
|
|
54
|
+
|
|
55
|
+
max_candidates = 64
|
|
56
|
+
index = 0
|
|
57
|
+
while index < len(sources) and len(sources) < max_candidates:
|
|
58
|
+
so, sr = sources[index]
|
|
59
|
+
index += 1
|
|
60
|
+
try:
|
|
61
|
+
meta = ReposResource(client).get(owner=so, repo=sr)
|
|
62
|
+
except GitCodeHTTPStatusError:
|
|
63
|
+
continue
|
|
64
|
+
if not getattr(meta, "fork", None):
|
|
65
|
+
continue
|
|
66
|
+
parsed = _parse_parent_owner_repo(meta)
|
|
67
|
+
if not parsed:
|
|
68
|
+
continue
|
|
69
|
+
po, pr = parsed
|
|
70
|
+
add((po, pr))
|
|
71
|
+
add((po, ".gitcode"))
|
|
72
|
+
|
|
73
|
+
return sources
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
async def _resolution_sources_async(client: AsyncAPIClient, owner: str, repo: str) -> List[Tuple[str, str]]:
|
|
77
|
+
"""Async counterpart of :func:`_resolution_sources_sync`."""
|
|
78
|
+
from .repositories import AsyncReposResource
|
|
79
|
+
|
|
80
|
+
sources: List[Tuple[str, str]] = []
|
|
81
|
+
seen: set[Tuple[str, str]] = set()
|
|
82
|
+
|
|
83
|
+
def add(pair: Tuple[str, str]) -> None:
|
|
84
|
+
if pair not in seen:
|
|
85
|
+
seen.add(pair)
|
|
86
|
+
sources.append(pair)
|
|
87
|
+
|
|
88
|
+
add((owner, repo))
|
|
89
|
+
add((owner, ".gitcode"))
|
|
90
|
+
|
|
91
|
+
max_candidates = 64
|
|
92
|
+
index = 0
|
|
93
|
+
while index < len(sources) and len(sources) < max_candidates:
|
|
94
|
+
so, sr = sources[index]
|
|
95
|
+
index += 1
|
|
96
|
+
try:
|
|
97
|
+
meta = await AsyncReposResource(client).get(owner=so, repo=sr)
|
|
98
|
+
except GitCodeHTTPStatusError:
|
|
99
|
+
continue
|
|
100
|
+
if not getattr(meta, "fork", None):
|
|
101
|
+
continue
|
|
102
|
+
parsed = _parse_parent_owner_repo(meta)
|
|
103
|
+
if not parsed:
|
|
104
|
+
continue
|
|
105
|
+
po, pr = parsed
|
|
106
|
+
add((po, pr))
|
|
107
|
+
add((po, ".gitcode"))
|
|
108
|
+
|
|
109
|
+
return sources
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _walk_dot_gitcode_contents_sync(
|
|
113
|
+
client: SyncAPIClient,
|
|
114
|
+
template_owner: str,
|
|
115
|
+
template_repo: str,
|
|
116
|
+
path: str,
|
|
117
|
+
acc: List[Tuple[str, str, str, str]],
|
|
118
|
+
) -> None:
|
|
119
|
+
try:
|
|
120
|
+
data = client.request(
|
|
121
|
+
"GET",
|
|
122
|
+
client._repo_file_path("contents", path, owner=template_owner, repo=template_repo),
|
|
123
|
+
)
|
|
124
|
+
except GitCodeHTTPStatusError as exc:
|
|
125
|
+
if exc.status_code == 404:
|
|
126
|
+
return
|
|
127
|
+
raise
|
|
128
|
+
|
|
129
|
+
if isinstance(data, dict):
|
|
130
|
+
t = (data.get("type") or "").lower()
|
|
131
|
+
if t == "file":
|
|
132
|
+
sha = data.get("sha") or ""
|
|
133
|
+
pth = data.get("path") or path
|
|
134
|
+
if sha and pth:
|
|
135
|
+
acc.append((template_owner, template_repo, str(pth), str(sha)))
|
|
136
|
+
return
|
|
137
|
+
if t in ("dir", "directory", "tree"):
|
|
138
|
+
pth = data.get("path") or path
|
|
139
|
+
_walk_dot_gitcode_contents_sync(client, template_owner, template_repo, pth, acc)
|
|
140
|
+
return
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
if isinstance(data, list):
|
|
144
|
+
for item in data:
|
|
145
|
+
if not isinstance(item, dict):
|
|
146
|
+
continue
|
|
147
|
+
t = (item.get("type") or "").lower()
|
|
148
|
+
pth = item.get("path") or ""
|
|
149
|
+
if t == "file":
|
|
150
|
+
sha = item.get("sha") or ""
|
|
151
|
+
if sha and pth:
|
|
152
|
+
acc.append((template_owner, template_repo, str(pth), str(sha)))
|
|
153
|
+
elif t in ("dir", "directory", "tree") and pth:
|
|
154
|
+
_walk_dot_gitcode_contents_sync(client, template_owner, template_repo, str(pth), acc)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
async def _walk_dot_gitcode_contents_async(
|
|
158
|
+
client: AsyncAPIClient,
|
|
159
|
+
template_owner: str,
|
|
160
|
+
template_repo: str,
|
|
161
|
+
path: str,
|
|
162
|
+
acc: List[Tuple[str, str, str, str]],
|
|
163
|
+
) -> None:
|
|
164
|
+
try:
|
|
165
|
+
data = await client.request(
|
|
166
|
+
"GET",
|
|
167
|
+
client._repo_file_path("contents", path, owner=template_owner, repo=template_repo),
|
|
168
|
+
)
|
|
169
|
+
except GitCodeHTTPStatusError as exc:
|
|
170
|
+
if exc.status_code == 404:
|
|
171
|
+
return
|
|
172
|
+
raise
|
|
173
|
+
|
|
174
|
+
if isinstance(data, dict):
|
|
175
|
+
t = (data.get("type") or "").lower()
|
|
176
|
+
if t == "file":
|
|
177
|
+
sha = data.get("sha") or ""
|
|
178
|
+
pth = data.get("path") or path
|
|
179
|
+
if sha and pth:
|
|
180
|
+
acc.append((template_owner, template_repo, str(pth), str(sha)))
|
|
181
|
+
return
|
|
182
|
+
if t in ("dir", "directory", "tree"):
|
|
183
|
+
pth = data.get("path") or path
|
|
184
|
+
await _walk_dot_gitcode_contents_async(client, template_owner, template_repo, pth, acc)
|
|
185
|
+
return
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
if isinstance(data, list):
|
|
189
|
+
for item in data:
|
|
190
|
+
if not isinstance(item, dict):
|
|
191
|
+
continue
|
|
192
|
+
t = (item.get("type") or "").lower()
|
|
193
|
+
pth = item.get("path") or ""
|
|
194
|
+
if t == "file":
|
|
195
|
+
sha = item.get("sha") or ""
|
|
196
|
+
if sha and pth:
|
|
197
|
+
acc.append((template_owner, template_repo, str(pth), str(sha)))
|
|
198
|
+
elif t in ("dir", "directory", "tree") and pth:
|
|
199
|
+
await _walk_dot_gitcode_contents_async(client, template_owner, template_repo, str(pth), acc)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def list_gitcode_template_rows_sync(
|
|
203
|
+
client: SyncAPIClient,
|
|
204
|
+
owner: str,
|
|
205
|
+
repo: str,
|
|
206
|
+
path_pattern: Pattern[str],
|
|
207
|
+
) -> List[Tuple[str, str, str, str]]:
|
|
208
|
+
"""Return ``(template_owner, template_repo, path, sha)`` from the first resolution source with matches."""
|
|
209
|
+
for so, sr in _resolution_sources_sync(client, owner, repo):
|
|
210
|
+
acc: List[Tuple[str, str, str, str]] = []
|
|
211
|
+
try:
|
|
212
|
+
_walk_dot_gitcode_contents_sync(client, so, sr, ".gitcode", acc)
|
|
213
|
+
except GitCodeHTTPStatusError:
|
|
214
|
+
continue
|
|
215
|
+
rows = [(t_o, t_r, p, s) for t_o, t_r, p, s in acc if path_pattern.match(p)]
|
|
216
|
+
if rows:
|
|
217
|
+
return rows
|
|
218
|
+
return []
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
async def list_gitcode_template_rows_async(
|
|
222
|
+
client: AsyncAPIClient,
|
|
223
|
+
owner: str,
|
|
224
|
+
repo: str,
|
|
225
|
+
path_pattern: Pattern[str],
|
|
226
|
+
) -> List[Tuple[str, str, str, str]]:
|
|
227
|
+
for so, sr in await _resolution_sources_async(client, owner, repo):
|
|
228
|
+
acc: List[Tuple[str, str, str, str]] = []
|
|
229
|
+
try:
|
|
230
|
+
await _walk_dot_gitcode_contents_async(client, so, sr, ".gitcode", acc)
|
|
231
|
+
except GitCodeHTTPStatusError:
|
|
232
|
+
continue
|
|
233
|
+
rows = [(t_o, t_r, p, s) for t_o, t_r, p, s in acc if path_pattern.match(p)]
|
|
234
|
+
if rows:
|
|
235
|
+
return rows
|
|
236
|
+
return []
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def get_gitcode_template_body_sync(
|
|
240
|
+
client: SyncAPIClient,
|
|
241
|
+
path: str,
|
|
242
|
+
path_pattern: Pattern[str],
|
|
243
|
+
owner: str,
|
|
244
|
+
repo: str,
|
|
245
|
+
) -> str:
|
|
246
|
+
"""Fetch raw template text from the first resolution source that serves ``path``."""
|
|
247
|
+
if not path_pattern.match(path):
|
|
248
|
+
raise GitCodeHTTPStatusError(
|
|
249
|
+
"Path does not match the expected GitCode template pattern for this resource.",
|
|
250
|
+
status_code=404,
|
|
251
|
+
payload=None,
|
|
252
|
+
)
|
|
253
|
+
last_error: Optional[GitCodeHTTPStatusError] = None
|
|
254
|
+
for so, sr in _resolution_sources_sync(client, owner, repo):
|
|
255
|
+
try:
|
|
256
|
+
raw = client.request(
|
|
257
|
+
"GET",
|
|
258
|
+
client._repo_file_path("raw", path, owner=so, repo=sr),
|
|
259
|
+
raw=True,
|
|
260
|
+
)
|
|
261
|
+
except GitCodeHTTPStatusError as exc:
|
|
262
|
+
last_error = exc
|
|
263
|
+
continue
|
|
264
|
+
if isinstance(raw, bytes):
|
|
265
|
+
return raw.decode("utf-8")
|
|
266
|
+
if isinstance(raw, str):
|
|
267
|
+
return raw
|
|
268
|
+
return str(raw)
|
|
269
|
+
if last_error is not None:
|
|
270
|
+
raise last_error
|
|
271
|
+
raise GitCodeHTTPStatusError(
|
|
272
|
+
"No template found for the given path.",
|
|
273
|
+
status_code=404,
|
|
274
|
+
payload=None,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
async def get_gitcode_template_body_async(
|
|
279
|
+
client: AsyncAPIClient,
|
|
280
|
+
path: str,
|
|
281
|
+
path_pattern: Pattern[str],
|
|
282
|
+
owner: str,
|
|
283
|
+
repo: str,
|
|
284
|
+
) -> str:
|
|
285
|
+
if not path_pattern.match(path):
|
|
286
|
+
raise GitCodeHTTPStatusError(
|
|
287
|
+
"Path does not match the expected GitCode template pattern for this resource.",
|
|
288
|
+
status_code=404,
|
|
289
|
+
payload=None,
|
|
290
|
+
)
|
|
291
|
+
last_error: Optional[GitCodeHTTPStatusError] = None
|
|
292
|
+
for so, sr in await _resolution_sources_async(client, owner, repo):
|
|
293
|
+
try:
|
|
294
|
+
raw = await client.request(
|
|
295
|
+
"GET",
|
|
296
|
+
client._repo_file_path("raw", path, owner=so, repo=sr),
|
|
297
|
+
raw=True,
|
|
298
|
+
)
|
|
299
|
+
except GitCodeHTTPStatusError as exc:
|
|
300
|
+
last_error = exc
|
|
301
|
+
continue
|
|
302
|
+
if isinstance(raw, bytes):
|
|
303
|
+
return raw.decode("utf-8")
|
|
304
|
+
if isinstance(raw, str):
|
|
305
|
+
return raw
|
|
306
|
+
return str(raw)
|
|
307
|
+
if last_error is not None:
|
|
308
|
+
raise last_error
|
|
309
|
+
raise GitCodeHTTPStatusError(
|
|
310
|
+
"No template found for the given path.",
|
|
311
|
+
status_code=404,
|
|
312
|
+
payload=None,
|
|
313
|
+
)
|
|
314
|
+
|
|
9
315
|
|
|
10
316
|
class SyncResource(BaseResource):
|
|
11
317
|
"""Base class for synchronous resource groups."""
|
|
@@ -21,10 +21,20 @@ from .._models import (
|
|
|
21
21
|
RepoMember,
|
|
22
22
|
RepoMemberPermission,
|
|
23
23
|
RepositoryCollaboratorCheck,
|
|
24
|
+
RepositoryGitCodeTemplate,
|
|
24
25
|
UserSummary,
|
|
25
26
|
as_model,
|
|
26
27
|
)
|
|
27
|
-
from ._shared import
|
|
28
|
+
from ._shared import (
|
|
29
|
+
GITCODE_ISSUE_TEMPLATE_PATH_RE,
|
|
30
|
+
GITCODE_PULL_REQUEST_TEMPLATE_PATH_RE,
|
|
31
|
+
AsyncResource,
|
|
32
|
+
SyncResource,
|
|
33
|
+
get_gitcode_template_body_async,
|
|
34
|
+
get_gitcode_template_body_sync,
|
|
35
|
+
list_gitcode_template_rows_async,
|
|
36
|
+
list_gitcode_template_rows_sync,
|
|
37
|
+
)
|
|
28
38
|
|
|
29
39
|
|
|
30
40
|
def _comma_join(values: Optional[List[str]]) -> Optional[str]:
|
|
@@ -392,6 +402,62 @@ class IssuesResource(SyncResource):
|
|
|
392
402
|
params={"page": page, "per_page": per_page},
|
|
393
403
|
)
|
|
394
404
|
|
|
405
|
+
def list_templates(
|
|
406
|
+
self,
|
|
407
|
+
*,
|
|
408
|
+
owner: Optional[str] = None,
|
|
409
|
+
repo: Optional[str] = None,
|
|
410
|
+
) -> List[RepositoryGitCodeTemplate]:
|
|
411
|
+
"""List active issue templates under ``.gitcode/`` for this repository.
|
|
412
|
+
|
|
413
|
+
Matches Markdown (``.md``, ``.markdown``) and YAML (``.yml`` / ``.yaml``) paths whose
|
|
414
|
+
names start with ``ISSUE_TEMPLATE`` under ``.gitcode/`` (case-insensitive), including
|
|
415
|
+
files inside localized directories such as ``.gitcode/ISSUE_TEMPLATE.en/``.
|
|
416
|
+
|
|
417
|
+
Resolution tries the repository, then the owner's ``.gitcode`` repository, then any
|
|
418
|
+
parent main and ``parent/.gitcode`` pairs discovered from forks (each candidate is
|
|
419
|
+
inspected with ``GET /repos/{owner}/{repo}``; when ``fork`` is true, its parent's repos
|
|
420
|
+
are appended, deduplicated, and visited in order). The first source with matching
|
|
421
|
+
templates wins.
|
|
422
|
+
|
|
423
|
+
:param owner: Repository owner path. Uses the client default when omitted.
|
|
424
|
+
:param repo: Repository name. Uses the client default when omitted.
|
|
425
|
+
:returns: Template metadata entries (paths and SHAs); empty when none match.
|
|
426
|
+
"""
|
|
427
|
+
resolved_owner, resolved_repo = self._client._resolve_repo_context(owner, repo)
|
|
428
|
+
rows = list_gitcode_template_rows_sync(
|
|
429
|
+
self._client, resolved_owner, resolved_repo, GITCODE_ISSUE_TEMPLATE_PATH_RE
|
|
430
|
+
)
|
|
431
|
+
return [
|
|
432
|
+
as_model(
|
|
433
|
+
{
|
|
434
|
+
"path": path,
|
|
435
|
+
"sha": sha,
|
|
436
|
+
"template_owner": template_owner,
|
|
437
|
+
"template_repo": template_repo,
|
|
438
|
+
},
|
|
439
|
+
RepositoryGitCodeTemplate,
|
|
440
|
+
)
|
|
441
|
+
for template_owner, template_repo, path, sha in rows
|
|
442
|
+
]
|
|
443
|
+
|
|
444
|
+
def get_template(self, *, path: str, owner: Optional[str] = None, repo: Optional[str] = None) -> str:
|
|
445
|
+
"""Load a single issue template file body from the default branch.
|
|
446
|
+
|
|
447
|
+
Uses the same resolution order as :meth:`list_templates`. The path must match the
|
|
448
|
+
active issue template naming convention (see GitCode ``.gitcode`` documentation).
|
|
449
|
+
|
|
450
|
+
:param path: Repository-relative path such as ``.gitcode/ISSUE_TEMPLATE_bug.md`` or
|
|
451
|
+
``.gitcode/ISSUE_TEMPLATE/config.yml``.
|
|
452
|
+
:param owner: Repository owner path. Uses the client default when omitted.
|
|
453
|
+
:param repo: Repository name. Uses the client default when omitted.
|
|
454
|
+
:returns: Decoded template file contents (UTF-8).
|
|
455
|
+
"""
|
|
456
|
+
resolved_owner, resolved_repo = self._client._resolve_repo_context(owner, repo)
|
|
457
|
+
return get_gitcode_template_body_sync(
|
|
458
|
+
self._client, path, GITCODE_ISSUE_TEMPLATE_PATH_RE, resolved_owner, resolved_repo
|
|
459
|
+
)
|
|
460
|
+
|
|
395
461
|
|
|
396
462
|
class PullsResource(SyncResource):
|
|
397
463
|
"""Synchronous pull request endpoints."""
|
|
@@ -987,6 +1053,57 @@ class PullsResource(SyncResource):
|
|
|
987
1053
|
PullRequest,
|
|
988
1054
|
)
|
|
989
1055
|
|
|
1056
|
+
def list_templates(
|
|
1057
|
+
self,
|
|
1058
|
+
*,
|
|
1059
|
+
owner: Optional[str] = None,
|
|
1060
|
+
repo: Optional[str] = None,
|
|
1061
|
+
) -> List[RepositoryGitCodeTemplate]:
|
|
1062
|
+
"""List active pull request templates under ``.gitcode/`` for this repository.
|
|
1063
|
+
|
|
1064
|
+
Resolution tries the repository, then the owner's ``.gitcode`` repository, then any
|
|
1065
|
+
parent main and ``parent/.gitcode`` pairs discovered from forks (each candidate is
|
|
1066
|
+
inspected with ``GET /repos/{owner}/{repo}``; when ``fork`` is true, its parent's repos
|
|
1067
|
+
are appended, deduplicated, and visited in order). The first source with matching
|
|
1068
|
+
templates wins.
|
|
1069
|
+
|
|
1070
|
+
:param owner: Repository owner path. Uses the client default when omitted.
|
|
1071
|
+
:param repo: Repository path. Uses the client default when omitted.
|
|
1072
|
+
:returns: Template metadata entries (paths and SHAs); empty when none match.
|
|
1073
|
+
"""
|
|
1074
|
+
resolved_owner, resolved_repo = self._client._resolve_repo_context(owner, repo)
|
|
1075
|
+
rows = list_gitcode_template_rows_sync(
|
|
1076
|
+
self._client, resolved_owner, resolved_repo, GITCODE_PULL_REQUEST_TEMPLATE_PATH_RE
|
|
1077
|
+
)
|
|
1078
|
+
return [
|
|
1079
|
+
as_model(
|
|
1080
|
+
{
|
|
1081
|
+
"path": path,
|
|
1082
|
+
"sha": sha,
|
|
1083
|
+
"template_owner": template_owner,
|
|
1084
|
+
"template_repo": template_repo,
|
|
1085
|
+
},
|
|
1086
|
+
RepositoryGitCodeTemplate,
|
|
1087
|
+
)
|
|
1088
|
+
for template_owner, template_repo, path, sha in rows
|
|
1089
|
+
]
|
|
1090
|
+
|
|
1091
|
+
def get_template(self, *, path: str, owner: Optional[str] = None, repo: Optional[str] = None) -> str:
|
|
1092
|
+
"""Load a single pull request template file body from the default branch.
|
|
1093
|
+
|
|
1094
|
+
Uses the same resolution order as :meth:`list_templates`. The path must match the
|
|
1095
|
+
active pull request template naming convention (see GitCode ``.gitcode`` documentation).
|
|
1096
|
+
|
|
1097
|
+
:param path: Repository-relative path such as ``.gitcode/PULL_REQUEST_TEMPLATE.md``.
|
|
1098
|
+
:param owner: Repository owner path. Uses the client default when omitted.
|
|
1099
|
+
:param repo: Repository path. Uses the client default when omitted.
|
|
1100
|
+
:returns: Decoded template file contents (UTF-8).
|
|
1101
|
+
"""
|
|
1102
|
+
resolved_owner, resolved_repo = self._client._resolve_repo_context(owner, repo)
|
|
1103
|
+
return get_gitcode_template_body_sync(
|
|
1104
|
+
self._client, path, GITCODE_PULL_REQUEST_TEMPLATE_PATH_RE, resolved_owner, resolved_repo
|
|
1105
|
+
)
|
|
1106
|
+
|
|
990
1107
|
|
|
991
1108
|
class LabelsResource(SyncResource):
|
|
992
1109
|
"""Synchronous label endpoints."""
|
|
@@ -1599,6 +1716,62 @@ class AsyncIssuesResource(AsyncResource):
|
|
|
1599
1716
|
params=params,
|
|
1600
1717
|
)
|
|
1601
1718
|
|
|
1719
|
+
async def list_templates(
|
|
1720
|
+
self,
|
|
1721
|
+
*,
|
|
1722
|
+
owner: Optional[str] = None,
|
|
1723
|
+
repo: Optional[str] = None,
|
|
1724
|
+
) -> List[RepositoryGitCodeTemplate]:
|
|
1725
|
+
"""List active issue templates under ``.gitcode/`` for this repository.
|
|
1726
|
+
|
|
1727
|
+
Matches Markdown (``.md``, ``.markdown``) and YAML (``.yml`` / ``.yaml``) paths whose
|
|
1728
|
+
names start with ``ISSUE_TEMPLATE`` under ``.gitcode/`` (case-insensitive), including
|
|
1729
|
+
files inside localized directories such as ``.gitcode/ISSUE_TEMPLATE.en/``.
|
|
1730
|
+
|
|
1731
|
+
Resolution tries the repository, then the owner's ``.gitcode`` repository, then any
|
|
1732
|
+
parent main and ``parent/.gitcode`` pairs discovered from forks (each candidate is
|
|
1733
|
+
inspected with ``GET /repos/{owner}/{repo}``; when ``fork`` is true, its parent's repos
|
|
1734
|
+
are appended, deduplicated, and visited in order). The first source with matching
|
|
1735
|
+
templates wins.
|
|
1736
|
+
|
|
1737
|
+
:param owner: Repository owner path. Uses the client default when omitted.
|
|
1738
|
+
:param repo: Repository name. Uses the client default when omitted.
|
|
1739
|
+
:returns: Template metadata entries (paths and SHAs); empty when none match.
|
|
1740
|
+
"""
|
|
1741
|
+
resolved_owner, resolved_repo = self._client._resolve_repo_context(owner, repo)
|
|
1742
|
+
rows = await list_gitcode_template_rows_async(
|
|
1743
|
+
self._client, resolved_owner, resolved_repo, GITCODE_ISSUE_TEMPLATE_PATH_RE
|
|
1744
|
+
)
|
|
1745
|
+
return [
|
|
1746
|
+
as_model(
|
|
1747
|
+
{
|
|
1748
|
+
"path": path,
|
|
1749
|
+
"sha": sha,
|
|
1750
|
+
"template_owner": template_owner,
|
|
1751
|
+
"template_repo": template_repo,
|
|
1752
|
+
},
|
|
1753
|
+
RepositoryGitCodeTemplate,
|
|
1754
|
+
)
|
|
1755
|
+
for template_owner, template_repo, path, sha in rows
|
|
1756
|
+
]
|
|
1757
|
+
|
|
1758
|
+
async def get_template(self, *, path: str, owner: Optional[str] = None, repo: Optional[str] = None) -> str:
|
|
1759
|
+
"""Load a single issue template file body from the default branch.
|
|
1760
|
+
|
|
1761
|
+
Uses the same resolution order as :meth:`list_templates`. The path must match the
|
|
1762
|
+
active issue template naming convention (see GitCode ``.gitcode`` documentation).
|
|
1763
|
+
|
|
1764
|
+
:param path: Repository-relative path such as ``.gitcode/ISSUE_TEMPLATE_bug.md`` or
|
|
1765
|
+
``.gitcode/ISSUE_TEMPLATE/config.yml``.
|
|
1766
|
+
:param owner: Repository owner path. Uses the client default when omitted.
|
|
1767
|
+
:param repo: Repository name. Uses the client default when omitted.
|
|
1768
|
+
:returns: Decoded template file contents (UTF-8).
|
|
1769
|
+
"""
|
|
1770
|
+
resolved_owner, resolved_repo = self._client._resolve_repo_context(owner, repo)
|
|
1771
|
+
return await get_gitcode_template_body_async(
|
|
1772
|
+
self._client, path, GITCODE_ISSUE_TEMPLATE_PATH_RE, resolved_owner, resolved_repo
|
|
1773
|
+
)
|
|
1774
|
+
|
|
1602
1775
|
|
|
1603
1776
|
class AsyncPullsResource(AsyncResource):
|
|
1604
1777
|
"""Asynchronous pull request endpoints.
|
|
@@ -2141,6 +2314,57 @@ class AsyncPullsResource(AsyncResource):
|
|
|
2141
2314
|
"GET", self._client._path("enterprises", enterprise, "issues", number, "pull_requests"), PullRequest
|
|
2142
2315
|
)
|
|
2143
2316
|
|
|
2317
|
+
async def list_templates(
|
|
2318
|
+
self,
|
|
2319
|
+
*,
|
|
2320
|
+
owner: Optional[str] = None,
|
|
2321
|
+
repo: Optional[str] = None,
|
|
2322
|
+
) -> List[RepositoryGitCodeTemplate]:
|
|
2323
|
+
"""List active pull request templates under ``.gitcode/`` for this repository.
|
|
2324
|
+
|
|
2325
|
+
Resolution tries the repository, then the owner's ``.gitcode`` repository, then any
|
|
2326
|
+
parent main and ``parent/.gitcode`` pairs discovered from forks (each candidate is
|
|
2327
|
+
inspected with ``GET /repos/{owner}/{repo}``; when ``fork`` is true, its parent's repos
|
|
2328
|
+
are appended, deduplicated, and visited in order). The first source with matching
|
|
2329
|
+
templates wins.
|
|
2330
|
+
|
|
2331
|
+
:param owner: Repository owner path. Uses the client default when omitted.
|
|
2332
|
+
:param repo: Repository path. Uses the client default when omitted.
|
|
2333
|
+
:returns: Template metadata entries (paths and SHAs); empty when none match.
|
|
2334
|
+
"""
|
|
2335
|
+
resolved_owner, resolved_repo = self._client._resolve_repo_context(owner, repo)
|
|
2336
|
+
rows = await list_gitcode_template_rows_async(
|
|
2337
|
+
self._client, resolved_owner, resolved_repo, GITCODE_PULL_REQUEST_TEMPLATE_PATH_RE
|
|
2338
|
+
)
|
|
2339
|
+
return [
|
|
2340
|
+
as_model(
|
|
2341
|
+
{
|
|
2342
|
+
"path": path,
|
|
2343
|
+
"sha": sha,
|
|
2344
|
+
"template_owner": template_owner,
|
|
2345
|
+
"template_repo": template_repo,
|
|
2346
|
+
},
|
|
2347
|
+
RepositoryGitCodeTemplate,
|
|
2348
|
+
)
|
|
2349
|
+
for template_owner, template_repo, path, sha in rows
|
|
2350
|
+
]
|
|
2351
|
+
|
|
2352
|
+
async def get_template(self, *, path: str, owner: Optional[str] = None, repo: Optional[str] = None) -> str:
|
|
2353
|
+
"""Load a single pull request template file body from the default branch.
|
|
2354
|
+
|
|
2355
|
+
Uses the same resolution order as :meth:`list_templates`. The path must match the
|
|
2356
|
+
active pull request template naming convention (see GitCode ``.gitcode`` documentation).
|
|
2357
|
+
|
|
2358
|
+
:param path: Repository-relative path such as ``.gitcode/PULL_REQUEST_TEMPLATE.md``.
|
|
2359
|
+
:param owner: Repository owner path. Uses the client default when omitted.
|
|
2360
|
+
:param repo: Repository path. Uses the client default when omitted.
|
|
2361
|
+
:returns: Decoded template file contents (UTF-8).
|
|
2362
|
+
"""
|
|
2363
|
+
resolved_owner, resolved_repo = self._client._resolve_repo_context(owner, repo)
|
|
2364
|
+
return await get_gitcode_template_body_async(
|
|
2365
|
+
self._client, path, GITCODE_PULL_REQUEST_TEMPLATE_PATH_RE, resolved_owner, resolved_repo
|
|
2366
|
+
)
|
|
2367
|
+
|
|
2144
2368
|
|
|
2145
2369
|
class AsyncLabelsResource(AsyncResource):
|
|
2146
2370
|
"""Asynchronous label endpoints.
|
gitcode_api/utils.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Utility helper functions."""
|
|
2
|
+
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
from typing import Any, Dict, List, Union, overload
|
|
5
|
+
|
|
6
|
+
from ._models import APIObject
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@overload
|
|
10
|
+
def as_dict(data: APIObject, deep_copy: bool = False) -> Dict[str, Any]: ...
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@overload
|
|
14
|
+
def as_dict(data: List[APIObject], deep_copy: bool = False) -> List[Dict[str, Any]]: ...
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def as_dict(data: Union[APIObject, List[APIObject]], deep_copy: bool = False) -> Union[Dict, List[Dict]]:
|
|
18
|
+
"""Convert one :class:`~gitcode_api._models.APIObject` to a plain ``dict``, or several to a list of dicts.
|
|
19
|
+
|
|
20
|
+
Similar to :func:`dataclasses.asdict`. Input is converted with :meth:`~gitcode_api._models.APIObject.to_dict`.
|
|
21
|
+
When ``deep_copy`` is true, each mapping is copied with :func:`copy.deepcopy`.
|
|
22
|
+
|
|
23
|
+
:param data: A single response model or a list of them.
|
|
24
|
+
:param deep_copy: When true, return deep-copied dicts.
|
|
25
|
+
:returns: A dict for a single input, or a list of dicts for a list input.
|
|
26
|
+
"""
|
|
27
|
+
if isinstance(data, list):
|
|
28
|
+
items = data
|
|
29
|
+
else:
|
|
30
|
+
items = [data]
|
|
31
|
+
if not all(hasattr(item, "to_dict") for item in items):
|
|
32
|
+
raise TypeError("Input is not APIObject or list[APIObject]")
|
|
33
|
+
if deep_copy:
|
|
34
|
+
out = [deepcopy(item.to_dict()) for item in items]
|
|
35
|
+
else:
|
|
36
|
+
out = [item.to_dict() for item in items]
|
|
37
|
+
return out[0] if len(out) == 1 else out
|
gitcode_api/version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.2.
|
|
1
|
+
1.2.15
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitcode-api
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.15
|
|
4
4
|
Summary: Easy to use Python SDK for the GitCode REST API. Providing builtin CLI tool, and optional LLM integration (MCP, OpenAI tool, and openJiuwen tool) for agents. Community-maintained.
|
|
5
5
|
Author-email: Hugo Huang <hugo@hugohuang.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -37,7 +37,7 @@ Dynamic: license-file
|
|
|
37
37
|
|
|
38
38
|
# GitCode-API
|
|
39
39
|
|
|
40
|
-
[](https://pypi.org/project/gitcode-api) [](https://pepy.tech/projects/gitcode-api) [](https://www.codefactor.io/repository/github/trenza1ore/gitcode-api)
|
|
41
41
|
[](https://cursor.com/en/install-mcp?name=GitCode%20API&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJnaXRjb2RlLWFwaVttY3BdIiwiZ2l0Y29kZS1hcGkiLCJzZXJ2ZSJdLCJlbnYiOnsiR0lUQ09ERV9BQ0NFU1NfVE9LRU4iOiIke2lucHV0OmdpdGNvZGVfYWNjZXNzX3Rva2VufSJ9LCJpbnB1dHMiOlt7ImlkIjoiZ2l0Y29kZV9hY2Nlc3NfdG9rZW4iLCJ0eXBlIjoicHJvbXB0U3RyaW5nIiwiZGVzY3JpcHRpb24iOiJFbnRlciBHSVRDT0RFX0FDQ0VTU19UT0tFTiIsInBhc3N3b3JkIjp0cnVlfV19) [](https://vscode.dev/redirect/mcp/install?name=GitCode%20API&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22--from%22%2C%22gitcode-api%5Bmcp%5D%22%2C%22gitcode-api%22%2C%22serve%22%5D%2C%22env%22%3A%7B%22GITCODE_ACCESS_TOKEN%22%3A%22%24%7Binput%3Agitcode_access_token%7D%22%7D%2C%22inputs%22%3A%5B%7B%22id%22%3A%22gitcode_access_token%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22Enter%20GITCODE_ACCESS_TOKEN%22%2C%22password%22%3Atrue%7D%5D%7D)
|
|
42
42
|
[](https://github.com/Trenza1ore/GitCode-API) [](https://gitcode.com/SushiNinja/GitCode-API)
|
|
43
43
|
|
|
@@ -49,6 +49,7 @@ Dynamic: license-file
|
|
|
49
49
|
|
|
50
50
|
- Community project for developers who want a practical GitCode Python library.
|
|
51
51
|
- Sync and async clients with a consistent API surface.
|
|
52
|
+
- Convenient methods not offered by REST API directly: such as fetching Issue and PR templates.
|
|
52
53
|
- Resource groups such as `client.repos`, `client.pulls`, and `client.users`.
|
|
53
54
|
- Repository defaults via `owner=` and `repo=` on the client.
|
|
54
55
|
- Sphinx docs plus a mirrored GitCode REST API reference in `docs/`.
|
|
@@ -153,6 +154,21 @@ async def main() -> None:
|
|
|
153
154
|
asyncio.run(main())
|
|
154
155
|
```
|
|
155
156
|
|
|
157
|
+
### Convert typed return objects to dicts with `as_dict`
|
|
158
|
+
|
|
159
|
+
Response values are typed `APIObject` subclasses. For serialization or code that expects built-in `dict`, import `as_dict` from the package root: pass a single model to get a `dict`, or pass a list (for example from `client.pulls.list(...)`) to get `list[dict]`. This mirrors the idea of `dataclasses.asdict` but uses each model’s `to_dict()` method. Use `deep_copy=True` when you need detached deep copies.
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from gitcode_api import GitCode, as_dict
|
|
163
|
+
|
|
164
|
+
client = GitCode(owner="SushiNinja")
|
|
165
|
+
pulls = client.pulls.list_templates(repo="GitCode-API")
|
|
166
|
+
payload = as_dict(pulls) # list[dict]
|
|
167
|
+
|
|
168
|
+
repo = client.repos.get(repo="GitCode-API")
|
|
169
|
+
meta = as_dict(repo) # dict
|
|
170
|
+
```
|
|
171
|
+
|
|
156
172
|
### Context managers
|
|
157
173
|
|
|
158
174
|
`GitCode` and `AsyncGitCode` (and the lower-level `SyncAPIClient` / `AsyncAPIClient`) support `with` / `async with`. Leaving the block calls `close()` / `await close()` on the underlying client automatically, including a custom `http_client=` you passed in. `close()` also clears the LRU cache used by each resource group's `method_signature(...)` helper (see the [Available Resources](#available-resources) section).
|
|
@@ -354,6 +370,8 @@ Runnable examples live in `examples/`:
|
|
|
354
370
|
|
|
355
371
|
- `get_current_user.py`
|
|
356
372
|
- `get_repository_overview.py`
|
|
373
|
+
- `get_issue_templates.py`
|
|
374
|
+
- `get_pull_request_templates.py`
|
|
357
375
|
- `list_pull_requests.py`
|
|
358
376
|
- `async_list_branches.py`
|
|
359
377
|
|
|
@@ -362,6 +380,8 @@ Example scripts load shared configuration from `examples/.env` using `python-dot
|
|
|
362
380
|
```bash
|
|
363
381
|
uv run python examples/get_current_user.py
|
|
364
382
|
uv run python examples/get_repository_overview.py
|
|
383
|
+
uv run python examples/get_issue_templates.py
|
|
384
|
+
uv run python examples/get_pull_request_templates.py
|
|
365
385
|
uv run python examples/list_pull_requests.py
|
|
366
386
|
uv run python examples/async_list_branches.py
|
|
367
387
|
```
|
|
@@ -1,29 +1,30 @@
|
|
|
1
|
-
gitcode_api/__init__.py,sha256=
|
|
1
|
+
gitcode_api/__init__.py,sha256=vqRBOTuk4UygSh2y4wZSTiK2FNY8DSqjcSGjC3W0ifg,539
|
|
2
2
|
gitcode_api/__main__.py,sha256=Yd8P4MSNcWFqUTKnUaNibbWX4Vd-MVVNmhPFaohzqcA,137
|
|
3
3
|
gitcode_api/_base_client.py,sha256=PFsTMZJI4p5INjubRa4H4JcybYLGt-B9VkSAfTgpxfI,13280
|
|
4
4
|
gitcode_api/_base_resource.py,sha256=mlKe1b_1AKcqxhptaCpP-AOjKkLNzCbYG-Pkp1HYWrA,2238
|
|
5
5
|
gitcode_api/_cli_banner.py,sha256=3DsoJ2qZ-mWWB4yD-cnxDN_osXzrUKabrA5tbV6752M,978
|
|
6
6
|
gitcode_api/_client.py,sha256=bmZxBHdfshM5Kv_EurHUVu8rsEj0k3Up3ATSIPaFrvc,8258
|
|
7
7
|
gitcode_api/_exceptions.py,sha256=T5N8gBGmPSktDkLP5P_hxbzOHw3W378TzxN1xja40pA,1140
|
|
8
|
-
gitcode_api/_models.py,sha256=
|
|
8
|
+
gitcode_api/_models.py,sha256=ip0xgdWao8Z3ATfSaPn3KzG81OXd25RVB1ansOaJaUM,110586
|
|
9
9
|
gitcode_api/cli.py,sha256=Z-X5gK8iI2wCEXqT-igVKpVzO4ZhaRYaxmTbqMbIUx8,18735
|
|
10
10
|
gitcode_api/py.typed,sha256=mDShSrm8qg9qjacQc2F-rI8ATllqP6EdgHuEYxuCXZ0,7
|
|
11
11
|
gitcode_api/run_mcp.py,sha256=3_JOrjg9_yL-0M-H-F8mPgxdVKh7K2ggipu7UHeNCg0,147
|
|
12
|
-
gitcode_api/
|
|
12
|
+
gitcode_api/utils.py,sha256=51QmPTQPeNsPSGf2IzhwmiEO1H2GkJrp2A7vkzhOOag,1351
|
|
13
|
+
gitcode_api/version.txt,sha256=CR1Vtk3iATNBumC-6iLKE5LhwljWj_KqLo-QHPlyPWM,7
|
|
13
14
|
gitcode_api/llm/__init__.py,sha256=rU75ZlJvTWNVxBLc3QzdfWmSjqVc9z6hfQ8z6jVVKOk,1693
|
|
14
15
|
gitcode_api/llm/_tool.py,sha256=b65iUiHo1H29uA6mFM3WlD0zZlISsENx1tpEqlkiUoA,16239
|
|
15
|
-
gitcode_api/llm/jiuwen.py,sha256=
|
|
16
|
+
gitcode_api/llm/jiuwen.py,sha256=qca2y4544xoRYFOCMbkjiUZZLpJGMcBkK4w5bqs60-4,4276
|
|
16
17
|
gitcode_api/llm/mcp.py,sha256=eeAuEETZ4THw31wbcnQaTlZQJ-9ZolvsejQY630OqHs,5702
|
|
17
|
-
gitcode_api/llm/openai.py,sha256=
|
|
18
|
+
gitcode_api/llm/openai.py,sha256=FSPA0Jv-k4n1Ud92TDfP1TWRlW4FB7smaLwY331nagk,3257
|
|
18
19
|
gitcode_api/resources/__init__.py,sha256=nsCKW0bFDZ5ombJZxLThmO82sOuF7o4OKUMRkAmwbwk,1725
|
|
19
|
-
gitcode_api/resources/_shared.py,sha256=
|
|
20
|
+
gitcode_api/resources/_shared.py,sha256=07f4Zk_bk6JKjEwubZ6iSd1Lq1rGi7CyWjvcMNMcYpo,15402
|
|
20
21
|
gitcode_api/resources/account.py,sha256=mnc2p7wI-nBnHFNdWPNiHfmZpT6d3RDQC777gewtm4M,38801
|
|
21
|
-
gitcode_api/resources/collaboration.py,sha256=
|
|
22
|
+
gitcode_api/resources/collaboration.py,sha256=TK0QXG_ymE4vbtHyP3Y-M39oG9zQE44UU5-UnPhF2WM,112388
|
|
22
23
|
gitcode_api/resources/misc.py,sha256=w7bq8rmgKr2ScBKeWZ3EZJmAdylDdPtSPrhi3AQre7w,34747
|
|
23
24
|
gitcode_api/resources/repositories.py,sha256=EAK2znZhEsgVUu-NDEQslSEEYJzvb-kHuh4mW57y6sc,78178
|
|
24
|
-
gitcode_api-1.2.
|
|
25
|
-
gitcode_api-1.2.
|
|
26
|
-
gitcode_api-1.2.
|
|
27
|
-
gitcode_api-1.2.
|
|
28
|
-
gitcode_api-1.2.
|
|
29
|
-
gitcode_api-1.2.
|
|
25
|
+
gitcode_api-1.2.15.dist-info/licenses/LICENSE,sha256=gOACXuWhMu6PJKVLr9RQbxX3HULnZIGNXCaMFJIXhoA,1067
|
|
26
|
+
gitcode_api-1.2.15.dist-info/METADATA,sha256=UpCCoL8teAQTmSRO5LDV7qx6dNV6EzgoIfeVnsoS81s,23323
|
|
27
|
+
gitcode_api-1.2.15.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
28
|
+
gitcode_api-1.2.15.dist-info/entry_points.txt,sha256=dIPylJcgohIE2RRIlt3In2WzcwDK8TOdkL_ReKuij4o,53
|
|
29
|
+
gitcode_api-1.2.15.dist-info/top_level.txt,sha256=gIlg0ptyOUHJT64ajOjWIhRPYgIQnMIvnhhnesw9fxU,12
|
|
30
|
+
gitcode_api-1.2.15.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|