ghapi 2.0.3__tar.gz → 2.0.4__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.
- {ghapi-2.0.3/ghapi.egg-info → ghapi-2.0.4}/PKG-INFO +2 -2
- ghapi-2.0.4/ghapi/__init__.py +1 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi/_modidx.py +24 -21
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi/core.py +46 -23
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi/skill.py +12 -3
- {ghapi-2.0.3 → ghapi-2.0.4/ghapi.egg-info}/PKG-INFO +2 -2
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi.egg-info/requires.txt +1 -1
- {ghapi-2.0.3 → ghapi-2.0.4}/pyproject.toml +1 -1
- ghapi-2.0.3/ghapi/__init__.py +0 -1
- {ghapi-2.0.3 → ghapi-2.0.4}/CONTRIBUTING.md +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/LICENSE +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/MANIFEST.in +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/README.md +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi/actions.py +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi/all.py +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi/auth.py +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi/build_lib.py +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi/cli.py +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi/event.py +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi/gh_spec.py +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi/graphql.py +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi/page.py +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi/py.typed +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi/templates.py +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi.egg-info/SOURCES.txt +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi.egg-info/dependency_links.txt +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi.egg-info/entry_points.txt +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/ghapi.egg-info/top_level.txt +0 -0
- {ghapi-2.0.3 → ghapi-2.0.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ghapi
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.4
|
|
4
4
|
Summary: A python client for the GitHub API
|
|
5
5
|
Author-email: Jeremy Howard <info@fast.ai>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -16,7 +16,7 @@ Requires-Python: >=3.10
|
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
Requires-Dist: fastcore>=1.14.5
|
|
19
|
-
Requires-Dist: fastspec>=0.1.
|
|
19
|
+
Requires-Dist: fastspec>=0.1.2
|
|
20
20
|
Provides-Extra: dev
|
|
21
21
|
Requires-Dist: fastgit; extra == "dev"
|
|
22
22
|
Requires-Dist: matplotlib; extra == "dev"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.0.4"
|
|
@@ -48,35 +48,15 @@ d = { 'settings': { 'branch': 'main',
|
|
|
48
48
|
'ghapi.core.GhApi.__init__': ('core.html#ghapi.__init__', 'ghapi/core.py'),
|
|
49
49
|
'ghapi.core.GhApi._get_repo_files': ('core.html#ghapi._get_repo_files', 'ghapi/core.py'),
|
|
50
50
|
'ghapi.core.GhApi._repr_markdown_': ('core.html#ghapi._repr_markdown_', 'ghapi/core.py'),
|
|
51
|
-
'ghapi.core.GhApi.check_status': ('core.html#ghapi.check_status', 'ghapi/core.py'),
|
|
52
|
-
'ghapi.core.GhApi.commit_tree': ('core.html#ghapi.commit_tree', 'ghapi/core.py'),
|
|
53
|
-
'ghapi.core.GhApi.create_branch_empty': ('core.html#ghapi.create_branch_empty', 'ghapi/core.py'),
|
|
54
|
-
'ghapi.core.GhApi.create_file': ('core.html#ghapi.create_file', 'ghapi/core.py'),
|
|
55
51
|
'ghapi.core.GhApi.create_gist': ('core.html#ghapi.create_gist', 'ghapi/core.py'),
|
|
56
|
-
'ghapi.core.GhApi.create_or_update_file': ('core.html#ghapi.create_or_update_file', 'ghapi/core.py'),
|
|
57
|
-
'ghapi.core.GhApi.create_release': ('core.html#ghapi.create_release', 'ghapi/core.py'),
|
|
58
52
|
'ghapi.core.GhApi.debug': ('core.html#ghapi.debug', 'ghapi/core.py'),
|
|
59
|
-
'ghapi.core.GhApi.delete_branch': ('core.html#ghapi.delete_branch', 'ghapi/core.py'),
|
|
60
|
-
'ghapi.core.GhApi.delete_file': ('core.html#ghapi.delete_file', 'ghapi/core.py'),
|
|
61
|
-
'ghapi.core.GhApi.delete_release': ('core.html#ghapi.delete_release', 'ghapi/core.py'),
|
|
62
|
-
'ghapi.core.GhApi.delete_tag': ('core.html#ghapi.delete_tag', 'ghapi/core.py'),
|
|
63
|
-
'ghapi.core.GhApi.enable_pages': ('core.html#ghapi.enable_pages', 'ghapi/core.py'),
|
|
64
|
-
'ghapi.core.GhApi.get_branch': ('core.html#ghapi.get_branch', 'ghapi/core.py'),
|
|
65
|
-
'ghapi.core.GhApi.get_content': ('core.html#ghapi.get_content', 'ghapi/core.py'),
|
|
66
53
|
'ghapi.core.GhApi.get_file_content': ('core.html#ghapi.get_file_content', 'ghapi/core.py'),
|
|
67
54
|
'ghapi.core.GhApi.get_repo_contents': ('core.html#ghapi.get_repo_contents', 'ghapi/core.py'),
|
|
68
55
|
'ghapi.core.GhApi.get_repo_files': ('core.html#ghapi.get_repo_files', 'ghapi/core.py'),
|
|
69
56
|
'ghapi.core.GhApi.gist_file': ('core.html#ghapi.gist_file', 'ghapi/core.py'),
|
|
70
|
-
'ghapi.core.GhApi.issue_template': ('core.html#ghapi.issue_template', 'ghapi/core.py'),
|
|
71
57
|
'ghapi.core.GhApi.limit_rem': ('core.html#ghapi.limit_rem', 'ghapi/core.py'),
|
|
72
|
-
'ghapi.core.GhApi.list_branches': ('core.html#ghapi.list_branches', 'ghapi/core.py'),
|
|
73
|
-
'ghapi.core.GhApi.list_files': ('core.html#ghapi.list_files', 'ghapi/core.py'),
|
|
74
|
-
'ghapi.core.GhApi.list_tags': ('core.html#ghapi.list_tags', 'ghapi/core.py'),
|
|
75
58
|
'ghapi.core.GhApi.load_gist': ('core.html#ghapi.load_gist', 'ghapi/core.py'),
|
|
76
|
-
'ghapi.core.GhApi.pr_status': ('core.html#ghapi.pr_status', 'ghapi/core.py'),
|
|
77
|
-
'ghapi.core.GhApi.read_issue': ('core.html#ghapi.read_issue', 'ghapi/core.py'),
|
|
78
59
|
'ghapi.core.GhApi.recv_hdrs': ('core.html#ghapi.recv_hdrs', 'ghapi/core.py'),
|
|
79
|
-
'ghapi.core.GhApi.update_contents': ('core.html#ghapi.update_contents', 'ghapi/core.py'),
|
|
80
60
|
'ghapi.core.GhApi.update_gist': ('core.html#ghapi.update_gist', 'ghapi/core.py'),
|
|
81
61
|
'ghapi.core.GhApi.upload_file': ('core.html#ghapi.upload_file', 'ghapi/core.py'),
|
|
82
62
|
'ghapi.core.GhSyncTransport': ('core.html#ghsynctransport', 'ghapi/core.py'),
|
|
@@ -91,6 +71,8 @@ d = { 'settings': { 'branch': 'main',
|
|
|
91
71
|
'ghapi.core._CheckStatus._repr_markdown_': ('core.html#_checkstatus._repr_markdown_', 'ghapi/core.py'),
|
|
92
72
|
'ghapi.core._IssueInfo': ('core.html#_issueinfo', 'ghapi/core.py'),
|
|
93
73
|
'ghapi.core._IssueInfo._repr_markdown_': ('core.html#_issueinfo._repr_markdown_', 'ghapi/core.py'),
|
|
74
|
+
'ghapi.core._LiveDefaults': ('core.html#_livedefaults', 'ghapi/core.py'),
|
|
75
|
+
'ghapi.core._LiveDefaults.items': ('core.html#_livedefaults.items', 'ghapi/core.py'),
|
|
94
76
|
'ghapi.core._filter_diff': ('core.html#_filter_diff', 'ghapi/core.py'),
|
|
95
77
|
'ghapi.core._find_matches': ('core.html#_find_matches', 'ghapi/core.py'),
|
|
96
78
|
'ghapi.core._fmt_replies': ('core.html#_fmt_replies', 'ghapi/core.py'),
|
|
@@ -99,12 +81,33 @@ d = { 'settings': { 'branch': 'main',
|
|
|
99
81
|
'ghapi.core._reduce_ctx': ('core.html#_reduce_ctx', 'ghapi/core.py'),
|
|
100
82
|
'ghapi.core._run_subp': ('core.html#_run_subp', 'ghapi/core.py'),
|
|
101
83
|
'ghapi.core.call_gh': ('core.html#call_gh', 'ghapi/core.py'),
|
|
84
|
+
'ghapi.core.check_status': ('core.html#check_status', 'ghapi/core.py'),
|
|
85
|
+
'ghapi.core.commit_tree': ('core.html#commit_tree', 'ghapi/core.py'),
|
|
86
|
+
'ghapi.core.create_branch_empty': ('core.html#create_branch_empty', 'ghapi/core.py'),
|
|
87
|
+
'ghapi.core.create_file': ('core.html#create_file', 'ghapi/core.py'),
|
|
88
|
+
'ghapi.core.create_or_update_file': ('core.html#create_or_update_file', 'ghapi/core.py'),
|
|
89
|
+
'ghapi.core.create_release': ('core.html#create_release', 'ghapi/core.py'),
|
|
102
90
|
'ghapi.core.date2gh': ('core.html#date2gh', 'ghapi/core.py'),
|
|
91
|
+
'ghapi.core.delete_branch': ('core.html#delete_branch', 'ghapi/core.py'),
|
|
92
|
+
'ghapi.core.delete_file': ('core.html#delete_file', 'ghapi/core.py'),
|
|
93
|
+
'ghapi.core.delete_release': ('core.html#delete_release', 'ghapi/core.py'),
|
|
94
|
+
'ghapi.core.delete_tag': ('core.html#delete_tag', 'ghapi/core.py'),
|
|
95
|
+
'ghapi.core.enable_pages': ('core.html#enable_pages', 'ghapi/core.py'),
|
|
96
|
+
'ghapi.core.get_branch': ('core.html#get_branch', 'ghapi/core.py'),
|
|
97
|
+
'ghapi.core.get_content': ('core.html#get_content', 'ghapi/core.py'),
|
|
103
98
|
'ghapi.core.gh2date': ('core.html#gh2date', 'ghapi/core.py'),
|
|
99
|
+
'ghapi.core.gh_patch': ('core.html#gh_patch', 'ghapi/core.py'),
|
|
104
100
|
'ghapi.core.issue_body': ('core.html#issue_body', 'ghapi/core.py'),
|
|
101
|
+
'ghapi.core.issue_template': ('core.html#issue_template', 'ghapi/core.py'),
|
|
102
|
+
'ghapi.core.list_branches': ('core.html#list_branches', 'ghapi/core.py'),
|
|
103
|
+
'ghapi.core.list_files': ('core.html#list_files', 'ghapi/core.py'),
|
|
104
|
+
'ghapi.core.list_tags': ('core.html#list_tags', 'ghapi/core.py'),
|
|
105
105
|
'ghapi.core.pr_file_diff': ('core.html#pr_file_diff', 'ghapi/core.py'),
|
|
106
|
+
'ghapi.core.pr_status': ('core.html#pr_status', 'ghapi/core.py'),
|
|
106
107
|
'ghapi.core.print_summary': ('core.html#print_summary', 'ghapi/core.py'),
|
|
107
|
-
'ghapi.core.
|
|
108
|
+
'ghapi.core.read_issue': ('core.html#read_issue', 'ghapi/core.py'),
|
|
109
|
+
'ghapi.core.read_pr': ('core.html#read_pr', 'ghapi/core.py'),
|
|
110
|
+
'ghapi.core.update_contents': ('core.html#update_contents', 'ghapi/core.py')},
|
|
108
111
|
'ghapi.event': { 'ghapi.event.GhApi.fetch_events': ('event.html#ghapi.fetch_events', 'ghapi/event.py'),
|
|
109
112
|
'ghapi.event.GhApi.list_events': ('event.html#ghapi.list_events', 'ghapi/event.py'),
|
|
110
113
|
'ghapi.event.GhApi.list_events_parallel': ('event.html#ghapi.list_events_parallel', 'ghapi/event.py'),
|
|
@@ -5,10 +5,16 @@ Docs: https://ghapi.fast.ai/core.html.md"""
|
|
|
5
5
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_core.ipynb.
|
|
6
6
|
|
|
7
7
|
# %% auto #0
|
|
8
|
-
__all__ = ['GH_HOST', 'pspec', 'img_md_pat', 'EMPTY_TREE_SHA', 'GhTransport', 'GhSyncTransport', 'print_summary', '
|
|
9
|
-
'call_gh', 'date2gh', 'gh2date', '
|
|
8
|
+
__all__ = ['GH_HOST', 'pspec', 'img_md_pat', 'EMPTY_TREE_SHA', 'GhTransport', 'GhSyncTransport', 'print_summary', 'gh_patch',
|
|
9
|
+
'GhApi', 'call_gh', 'date2gh', 'gh2date', 'delete_release', 'create_release', 'list_tags', 'list_branches',
|
|
10
|
+
'create_branch_empty', 'delete_tag', 'delete_branch', 'get_branch', 'commit_tree', 'list_files',
|
|
11
|
+
'get_content', 'create_or_update_file', 'create_file', 'delete_file', 'update_contents', 'enable_pages',
|
|
12
|
+
'read_issue', 'read_pr', 'pr_file_diff', 'issue_template', 'issue_body', 'check_status', 'pr_status',
|
|
13
|
+
'APIError']
|
|
10
14
|
|
|
11
15
|
# %% ../nbs/00_core.ipynb #5b5cba7b
|
|
16
|
+
from contextvars import ContextVar
|
|
17
|
+
|
|
12
18
|
from fastcore.all import *
|
|
13
19
|
from fastspec.spec import SpecParser
|
|
14
20
|
from fastspec.oapi import OpenAPIClient, OpFunc, SyncOpFunc, _build_groups, UNSET
|
|
@@ -85,6 +91,23 @@ def print_summary(method, url, kwargs):
|
|
|
85
91
|
hdrs = {k:v for k,v in (kwargs.get('headers') or {}).items() if k.lower()!='authorization'}
|
|
86
92
|
print(method, url, {**kwargs, 'headers':hdrs})
|
|
87
93
|
|
|
94
|
+
# %% ../nbs/00_core.ipynb #cecd0177
|
|
95
|
+
_gh_override = ContextVar('_gh_override', default={})
|
|
96
|
+
|
|
97
|
+
class _LiveDefaults(dict):
|
|
98
|
+
"Endpoint defaults, superseded at call time by any active `gh_patch` override"
|
|
99
|
+
def items(self): return {**self, **_gh_override.get()}.items()
|
|
100
|
+
|
|
101
|
+
def gh_patch(fn):
|
|
102
|
+
"`patch` `fn` into `GhApi`, adding `owner`/`repo` params that override the client defaults for this call and its internal calls"
|
|
103
|
+
async def _f(self, *args, owner=UNSET, repo=UNSET, **kwargs):
|
|
104
|
+
over = {k:v for k,v in zip(('owner','repo'),(owner,repo)) if v is not UNSET}
|
|
105
|
+
if not over: return await fn(self, *args, **kwargs)
|
|
106
|
+
token = _gh_override.set({**_gh_override.get(), **over})
|
|
107
|
+
try: return await fn(self, *args, **kwargs)
|
|
108
|
+
finally: _gh_override.reset(token)
|
|
109
|
+
return patch(splice_sig(_f, fn, 'self'))
|
|
110
|
+
|
|
88
111
|
# %% ../nbs/00_core.ipynb #83e8a9ce
|
|
89
112
|
class GhApi(OpenAPIClient):
|
|
90
113
|
"GitHub API client. Endpoint groups (`issues`, `pulls`, ...) are generated per-instance from GitHub's OpenAPI metadata, so the class shows only convenience methods -- inspect a live instance, e.g. `doc(GhApi())`, to see the full API."
|
|
@@ -102,7 +125,7 @@ class GhApi(OpenAPIClient):
|
|
|
102
125
|
self.token,self.gh_host = token,gh_host or GH_HOST
|
|
103
126
|
tcls,fcls = (GhSyncTransport,SyncOpFunc) if sync else (GhTransport,OpFunc)
|
|
104
127
|
self.transport = tcls(debug=debug, limit_cb=limit_cb, timeout=timeout, base_headers=self.headers)
|
|
105
|
-
self.ops = [fcls(o, self.transport, self.gh_host, defaults=kwargs) for o in pspec.ops]
|
|
128
|
+
self.ops = [fcls(o, self.transport, self.gh_host, defaults=_LiveDefaults(kwargs)) for o in pspec.ops]
|
|
106
129
|
self.func_dict = {f'{o.path}:{o.verb.upper()}':o for o in self.ops}
|
|
107
130
|
self.groups = _build_groups(self.ops)
|
|
108
131
|
for k,v in self.groups.items(): setattr(self, k, v)
|
|
@@ -195,7 +218,7 @@ async def update_gist(self:GhApi, gist_id:str, content:str):
|
|
|
195
218
|
return (await self.gists.update(gist_id, files={fname: {'content': content}})).html_url
|
|
196
219
|
|
|
197
220
|
# %% ../nbs/00_core.ipynb #4b7a278c
|
|
198
|
-
@
|
|
221
|
+
@gh_patch
|
|
199
222
|
async def delete_release(self:GhApi, release):
|
|
200
223
|
"Delete a release and its associated tag"
|
|
201
224
|
await self.repos.delete_release(release.id)
|
|
@@ -211,7 +234,7 @@ async def upload_file(self:GhApi, rel, fn):
|
|
|
211
234
|
return await self(url, 'POST', headers={'Content-Type':mime}, query = {'name':fn.name}, data=fn.read_bytes())
|
|
212
235
|
|
|
213
236
|
# %% ../nbs/00_core.ipynb #3cad71a4
|
|
214
|
-
@
|
|
237
|
+
@gh_patch
|
|
215
238
|
async def create_release(self:GhApi, tag_name, branch='master', name=None, body='',
|
|
216
239
|
draft=False, prerelease=False, files=None, make_latest=UNSET):
|
|
217
240
|
"Wrapper for `GhApi.repos.create_release` which also uploads `files`"
|
|
@@ -222,13 +245,13 @@ async def create_release(self:GhApi, tag_name, branch='master', name=None, body=
|
|
|
222
245
|
return rel
|
|
223
246
|
|
|
224
247
|
# %% ../nbs/00_core.ipynb #2be73ae0
|
|
225
|
-
@
|
|
248
|
+
@gh_patch
|
|
226
249
|
async def list_tags(self:GhApi, prefix:str=''):
|
|
227
250
|
"List all tags, optionally filtered to those starting with `prefix`"
|
|
228
251
|
return await self.git.list_matching_refs(f'tags/{prefix}')
|
|
229
252
|
|
|
230
253
|
# %% ../nbs/00_core.ipynb #303eeec6
|
|
231
|
-
@
|
|
254
|
+
@gh_patch
|
|
232
255
|
async def list_branches(self:GhApi, prefix:str=''):
|
|
233
256
|
"List all branches, optionally filtered to those starting with `prefix`"
|
|
234
257
|
return await self.git.list_matching_refs(f'heads/{prefix}')
|
|
@@ -238,7 +261,7 @@ async def list_branches(self:GhApi, prefix:str=''):
|
|
|
238
261
|
EMPTY_TREE_SHA = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'
|
|
239
262
|
|
|
240
263
|
# %% ../nbs/00_core.ipynb #ba6ab941
|
|
241
|
-
@
|
|
264
|
+
@gh_patch
|
|
242
265
|
async def create_branch_empty(self:GhApi, branch):
|
|
243
266
|
t = await self.git.create_tree(base_tree=EMPTY_TREE_SHA, tree = [dict(
|
|
244
267
|
path='.dummy', content='ignore me', mode='100644', type='blob')])
|
|
@@ -246,25 +269,25 @@ async def create_branch_empty(self:GhApi, branch):
|
|
|
246
269
|
return await self.git.create_ref(f'refs/heads/{branch}', c.sha)
|
|
247
270
|
|
|
248
271
|
# %% ../nbs/00_core.ipynb #68b150fc
|
|
249
|
-
@
|
|
272
|
+
@gh_patch
|
|
250
273
|
async def delete_tag(self:GhApi, tag:str):
|
|
251
274
|
"Delete a tag"
|
|
252
275
|
return await self.git.delete_ref(f'tags/{tag}')
|
|
253
276
|
|
|
254
277
|
# %% ../nbs/00_core.ipynb #75c168a1
|
|
255
|
-
@
|
|
278
|
+
@gh_patch
|
|
256
279
|
async def delete_branch(self:GhApi, branch:str):
|
|
257
280
|
"Delete a branch"
|
|
258
281
|
return await self.git.delete_ref(f'heads/{branch}')
|
|
259
282
|
|
|
260
283
|
# %% ../nbs/00_core.ipynb #96053795
|
|
261
|
-
@
|
|
284
|
+
@gh_patch
|
|
262
285
|
async def get_branch(self:GhApi, branch=None):
|
|
263
286
|
branch = branch or (await self.repos.get()).default_branch
|
|
264
287
|
return (await self.list_branches(branch))[0]
|
|
265
288
|
|
|
266
289
|
# %% ../nbs/00_core.ipynb #9f70ac44
|
|
267
|
-
@
|
|
290
|
+
@gh_patch
|
|
268
291
|
async def commit_tree(self:GhApi, branch, message, tree, base=None):
|
|
269
292
|
"Commit `tree` entries to `branch`, creating it from `base` when needed"
|
|
270
293
|
try:
|
|
@@ -282,20 +305,20 @@ async def commit_tree(self:GhApi, branch, message, tree, base=None):
|
|
|
282
305
|
return commit
|
|
283
306
|
|
|
284
307
|
# %% ../nbs/00_core.ipynb #93b24881
|
|
285
|
-
@
|
|
308
|
+
@gh_patch
|
|
286
309
|
async def list_files(self:GhApi, branch=None):
|
|
287
310
|
ref = await self.get_branch(branch)
|
|
288
311
|
res = (await self.git.get_tree(ref.object.sha)).tree
|
|
289
312
|
return {o.path:o for o in res}
|
|
290
313
|
|
|
291
314
|
# %% ../nbs/00_core.ipynb #ffc347b2
|
|
292
|
-
@
|
|
315
|
+
@gh_patch
|
|
293
316
|
async def get_content(self:GhApi, path):
|
|
294
317
|
res = await self.repos.get_content(path)
|
|
295
318
|
return base64.b64decode(res.content)
|
|
296
319
|
|
|
297
320
|
# %% ../nbs/00_core.ipynb #9582ee1f
|
|
298
|
-
@
|
|
321
|
+
@gh_patch
|
|
299
322
|
async def create_or_update_file(self:GhApi, path, message, committer, author, content=None, sha=None, branch=''):
|
|
300
323
|
if not branch: branch = (await self.repos.get())['default_branch']
|
|
301
324
|
if not isinstance(content,bytes): content = content.encode()
|
|
@@ -305,20 +328,20 @@ async def create_or_update_file(self:GhApi, path, message, committer, author, co
|
|
|
305
328
|
branch=branch, committer=committer or {}, author=author or {}, **kwargs)
|
|
306
329
|
|
|
307
330
|
# %% ../nbs/00_core.ipynb #5044445d
|
|
308
|
-
@
|
|
331
|
+
@gh_patch
|
|
309
332
|
async def create_file(self:GhApi, path, message, committer, author, content=None, branch=None):
|
|
310
333
|
if not branch: branch = (await self.repos.get())['default_branch']
|
|
311
334
|
return await self.create_or_update_file(path, message, branch=branch, committer=committer, content=content, author=author)
|
|
312
335
|
|
|
313
336
|
# %% ../nbs/00_core.ipynb #4e74c337
|
|
314
|
-
@
|
|
337
|
+
@gh_patch
|
|
315
338
|
async def delete_file(self:GhApi, path, message, committer, author, sha=None, branch=None):
|
|
316
339
|
if not branch: branch = (await self.repos.get())['default_branch']
|
|
317
340
|
if sha is None: sha = (await self.list_files())[path].sha
|
|
318
341
|
return await self.repos.delete_file(path, message=message, sha=sha, branch=branch, committer=committer, author=author)
|
|
319
342
|
|
|
320
343
|
# %% ../nbs/00_core.ipynb #93f6b559
|
|
321
|
-
@
|
|
344
|
+
@gh_patch
|
|
322
345
|
async def update_contents(self:GhApi, path, message, committer, author, content, sha=None, branch=None):
|
|
323
346
|
if not branch: branch = (await self.repos.get())['default_branch']
|
|
324
347
|
if sha is None: sha = (await self.list_files())[path].sha
|
|
@@ -366,7 +389,7 @@ async def get_repo_contents(self:GhApi, owner, repo, branch='main',
|
|
|
366
389
|
return L(await parallel_async(self.get_file_content, paths, owner=owner, repo=repo, branch=branch, n_workers=n_workers))
|
|
367
390
|
|
|
368
391
|
# %% ../nbs/00_core.ipynb #ac4ab4e0
|
|
369
|
-
@
|
|
392
|
+
@gh_patch
|
|
370
393
|
async def enable_pages(self:GhApi, branch=None, path="/"):
|
|
371
394
|
"Enable or update pages for a repo to point to a `branch` and `path`."
|
|
372
395
|
if path not in ('/docs','/'): raise Exception("path not in ('/docs','/')")
|
|
@@ -388,7 +411,7 @@ class _IssueInfo(AttrDict):
|
|
|
388
411
|
return '\n\n'.join(res)
|
|
389
412
|
__repr__ = _repr_markdown_
|
|
390
413
|
|
|
391
|
-
@
|
|
414
|
+
@gh_patch
|
|
392
415
|
async def read_issue(self:GhApi, issue_number:int):
|
|
393
416
|
"Fetch an issue or PR: title, body, comments, and (for PRs) diff, review comments, and reviews"
|
|
394
417
|
try: pr = await self.pulls.get(issue_number)
|
|
@@ -481,7 +504,7 @@ def _parse_tmpl(name, txt):
|
|
|
481
504
|
for b in d.get('body', []) if b['type']!='markdown']
|
|
482
505
|
return dict2obj(dict(name=name, title=d.get('name'), description=d.get('description'), sections=secs))
|
|
483
506
|
|
|
484
|
-
@
|
|
507
|
+
@gh_patch
|
|
485
508
|
async def issue_template(self:GhApi):
|
|
486
509
|
"Parsed issue templates for the repo (or the owner's `.github` repo as fallback); pass one to `issue_body`"
|
|
487
510
|
for repo,path in ((None,'.github/ISSUE_TEMPLATE'), ('.github','ISSUE_TEMPLATE'), ('.github','.github/ISSUE_TEMPLATE')):
|
|
@@ -519,14 +542,14 @@ class _CheckStatus(AttrDict):
|
|
|
519
542
|
return '\n'.join([f'**{verdict}**', ''] + [f"- {r.name}: {r.conclusion or r.status}" for r in runs])
|
|
520
543
|
__repr__ = _repr_markdown_
|
|
521
544
|
|
|
522
|
-
@
|
|
545
|
+
@gh_patch
|
|
523
546
|
async def check_status(self:GhApi, ref:str):
|
|
524
547
|
"Combined commit status and check-run results for `ref` (a SHA, branch, or tag)"
|
|
525
548
|
combined = await self.repos.get_combined_status_for_ref(ref)
|
|
526
549
|
checks = await self.checks.list_for_ref(ref)
|
|
527
550
|
return _CheckStatus(dict2obj(dict(state=combined.state, statuses=combined.statuses, check_runs=checks.check_runs)))
|
|
528
551
|
|
|
529
|
-
@
|
|
552
|
+
@gh_patch
|
|
530
553
|
async def pr_status(self:GhApi, pull_number:int):
|
|
531
554
|
"Combined status and check-run results for a PR's head commit"
|
|
532
555
|
return await self.check_status((await self.pulls.get(pull_number)).head.sha)
|
|
@@ -9,9 +9,17 @@
|
|
|
9
9
|
api = GhApi(owner='fastai', repo='ghapi')
|
|
10
10
|
await api.issues.list_for_repo(state='open') # owner/repo already bound
|
|
11
11
|
|
|
12
|
+
These are only defaults: any endpoint or convenience method accepts `owner=`/`repo=` to override per call (e.g. `await api.check_status('v1.0', repo='other')`), so one client can serve a whole org; convenience methods propagate the override to everything they call internally.
|
|
13
|
+
|
|
12
14
|
# Discovering the API
|
|
13
15
|
|
|
14
|
-
`api
|
|
16
|
+
Displaying `api` lists every available group with a docs link. Endpoint names follow `<verb>_<noun>`, e.g. `issues.list_for_repo`, `issues.create`, `pulls.merge`. Groups can contain hundreds of endpoints, so search their names with `pyskills.xdir()` instead of rendering the whole group, then inspect the matching endpoint:
|
|
17
|
+
|
|
18
|
+
import pyskills
|
|
19
|
+
pyskills.xdir(api.actions, 'log')
|
|
20
|
+
doc(api.actions.download_job_logs_for_workflow_run)
|
|
21
|
+
|
|
22
|
+
The optional `pyskills.xdir` query is a case-insensitive regex. `doc()` works at every level of a live instance, but `doc(api.actions)` renders that group's complete endpoint list; use it only when the group is small enough to read. The instance must be live because the API is generated at construction time: inspecting the `GhApi` *class* shows only convenience methods, not generated groups. `api['/path/{owner}/{repo}', 'GET']` looks up an endpoint directly by path and verb.
|
|
15
23
|
|
|
16
24
|
# Reading an issue or PR (including comments and reviews)
|
|
17
25
|
|
|
@@ -68,6 +76,8 @@ For a PR to another owner's repo, use normal local Git through the commit, ensur
|
|
|
68
76
|
await api.commit_tree(branch, message, entries)
|
|
69
77
|
await api.pulls.create(owner='upstream', head=f'me:{branch}', base='main', title=title, body=body)
|
|
70
78
|
|
|
79
|
+
Once the PR exists, the fork branch is the source of truth, and the local clone is residue: reset it (`git restore .` and switch back to the main branch) rather than leaving the edits behind, where a later session will find an unexplained diff. In particular, never build a second PR's edits on a tree still dirty from the first: each PR gets its own branch from main with a clean tree, since layered uncommitted edits ship a merged diff to `commit_tree` and block `git switch` once shared files diverge.
|
|
80
|
+
|
|
71
81
|
# Gotchas
|
|
72
82
|
|
|
73
83
|
- A PR *is* an issue for general comments (`issues.list_comments`, not `pulls.*`) -- inline code-review comments are the separate `pulls.list_review_comments`.
|
|
@@ -93,8 +103,7 @@ class ReadOnlyGhPolicy(AllowPolicy):
|
|
|
93
103
|
allow({OpFunc: [('__call__', ReadOnlyGhPolicy())]})
|
|
94
104
|
class GitPolicy(AllowPolicy):
|
|
95
105
|
"Allow only safecmd-default git subcommands"
|
|
96
|
-
cmds = {
|
|
97
|
-
'blame','branch','cat-file','config','describe','diff','log','ls-files','ls-tree','merge-base',
|
|
106
|
+
cmds = {'blame','branch','cat-file','config','describe','diff','log','ls-files','ls-tree','merge-base',
|
|
98
107
|
'remote','rev-parse','shortlog','show','stash','status','tag','fetch','add','commit','switch','checkout'}
|
|
99
108
|
|
|
100
109
|
def __call__(self, obj, args, kwargs, data):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ghapi
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.4
|
|
4
4
|
Summary: A python client for the GitHub API
|
|
5
5
|
Author-email: Jeremy Howard <info@fast.ai>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -16,7 +16,7 @@ Requires-Python: >=3.10
|
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
Requires-Dist: fastcore>=1.14.5
|
|
19
|
-
Requires-Dist: fastspec>=0.1.
|
|
19
|
+
Requires-Dist: fastspec>=0.1.2
|
|
20
20
|
Provides-Extra: dev
|
|
21
21
|
Requires-Dist: fastgit; extra == "dev"
|
|
22
22
|
Requires-Dist: matplotlib; extra == "dev"
|
|
@@ -12,7 +12,7 @@ license = {text = "Apache-2.0"}
|
|
|
12
12
|
authors = [{name = "Jeremy Howard", email = "info@fast.ai"}]
|
|
13
13
|
keywords = ['github', 'api']
|
|
14
14
|
classifiers = ["Natural Language :: English", "Intended Audience :: Developers", "Development Status :: 5 - Production/Stable", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only"]
|
|
15
|
-
dependencies = ['fastcore>=1.14.5', 'fastspec>=0.1.
|
|
15
|
+
dependencies = ['fastcore>=1.14.5', 'fastspec>=0.1.2']
|
|
16
16
|
|
|
17
17
|
[project.urls]
|
|
18
18
|
Repository = "https://github.com/fastai/ghapi"
|
ghapi-2.0.3/ghapi/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.0.3"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|