ghapi 2.0.7__tar.gz → 2.1.1__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.7/ghapi.egg-info → ghapi-2.1.1}/PKG-INFO +3 -2
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi/__init__.py +1 -1
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi/_modidx.py +14 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi/core.py +52 -13
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi/page.py +38 -12
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi/skill.py +2 -1
- {ghapi-2.0.7 → ghapi-2.1.1/ghapi.egg-info}/PKG-INFO +3 -2
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi.egg-info/requires.txt +2 -1
- {ghapi-2.0.7 → ghapi-2.1.1}/pyproject.toml +2 -2
- {ghapi-2.0.7 → ghapi-2.1.1}/CONTRIBUTING.md +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/LICENSE +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/MANIFEST.in +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/README.md +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi/actions.py +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi/all.py +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi/auth.py +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi/build_lib.py +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi/cli.py +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi/event.py +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi/gh_spec.py +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi/gql_spec.py +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi/graphql.py +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi/py.typed +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi/templates.py +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi.egg-info/SOURCES.txt +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi.egg-info/dependency_links.txt +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi.egg-info/entry_points.txt +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/ghapi.egg-info/top_level.txt +0 -0
- {ghapi-2.0.7 → ghapi-2.1.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ghapi
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.1
|
|
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>=2.2.6
|
|
19
|
-
Requires-Dist: fastspec>=0.
|
|
19
|
+
Requires-Dist: fastspec>=0.2.0
|
|
20
20
|
Requires-Dist: tomli; python_version < "3.11"
|
|
21
21
|
Provides-Extra: dev
|
|
22
22
|
Requires-Dist: fastgit; extra == "dev"
|
|
@@ -26,6 +26,7 @@ Requires-Dist: nbdev; extra == "dev"
|
|
|
26
26
|
Requires-Dist: pyyaml; extra == "dev"
|
|
27
27
|
Requires-Dist: pysym2md>=0.0.6; extra == "dev"
|
|
28
28
|
Requires-Dist: llms-txt>=0.0.6; extra == "dev"
|
|
29
|
+
Requires-Dist: pyskills; extra == "dev"
|
|
29
30
|
Dynamic: license-file
|
|
30
31
|
|
|
31
32
|
# ghapi
|
|
@@ -5,4 +5,4 @@ Modules:
|
|
|
5
5
|
- `ghapi.graphql`: Query GitHub's GraphQL API: schema-aware chained queries, one-request batching, and raw GraphQL
|
|
6
6
|
- `ghapi.skill`: GitHub REST API access via `GhApi`, plus local git operations via `fastgit.Git`. Use this for day-to-day GitHub work: reading/creating issues and PRs, checking CI status, managing releases/branches/gists, and repo-local git operations -- all from Python, no shelling out to `gh`/`git` needed."""
|
|
7
7
|
|
|
8
|
-
__version__ = "2.
|
|
8
|
+
__version__ = "2.1.1"
|
|
@@ -74,6 +74,11 @@ d = { 'settings': { 'branch': 'main',
|
|
|
74
74
|
'ghapi.core.GhTransport._post': ('core.html#ghtransport._post', 'ghapi/core.py'),
|
|
75
75
|
'ghapi.core.GhTransport._pre': ('core.html#ghtransport._pre', 'ghapi/core.py'),
|
|
76
76
|
'ghapi.core.GhTransport.request': ('core.html#ghtransport.request', 'ghapi/core.py'),
|
|
77
|
+
'ghapi.core.PullRow': ('core.html#pullrow', 'ghapi/core.py'),
|
|
78
|
+
'ghapi.core.PullRow.line': ('core.html#pullrow.line', 'ghapi/core.py'),
|
|
79
|
+
'ghapi.core.PullRows': ('core.html#pullrows', 'ghapi/core.py'),
|
|
80
|
+
'ghapi.core.PullRows.__init__': ('core.html#pullrows.__init__', 'ghapi/core.py'),
|
|
81
|
+
'ghapi.core.PullRows.__repr__': ('core.html#pullrows.__repr__', 'ghapi/core.py'),
|
|
77
82
|
'ghapi.core._CheckStatus': ('core.html#_checkstatus', 'ghapi/core.py'),
|
|
78
83
|
'ghapi.core._CheckStatus._repr_markdown_': ('core.html#_checkstatus._repr_markdown_', 'ghapi/core.py'),
|
|
79
84
|
'ghapi.core._IssueInfo': ('core.html#_issueinfo', 'ghapi/core.py'),
|
|
@@ -82,6 +87,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
82
87
|
'ghapi.core._LiveDefaults.__contains__': ('core.html#_livedefaults.__contains__', 'ghapi/core.py'),
|
|
83
88
|
'ghapi.core._LiveDefaults.get': ('core.html#_livedefaults.get', 'ghapi/core.py'),
|
|
84
89
|
'ghapi.core._LiveDefaults.items': ('core.html#_livedefaults.items', 'ghapi/core.py'),
|
|
90
|
+
'ghapi.core._age': ('core.html#_age', 'ghapi/core.py'),
|
|
85
91
|
'ghapi.core._dur': ('core.html#_dur', 'ghapi/core.py'),
|
|
86
92
|
'ghapi.core._filter_diff': ('core.html#_filter_diff', 'ghapi/core.py'),
|
|
87
93
|
'ghapi.core._find_matches': ('core.html#_find_matches', 'ghapi/core.py'),
|
|
@@ -89,6 +95,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
89
95
|
'ghapi.core._include': ('core.html#_include', 'ghapi/core.py'),
|
|
90
96
|
'ghapi.core._listed': ('core.html#_listed', 'ghapi/core.py'),
|
|
91
97
|
'ghapi.core._parse_tmpl': ('core.html#_parse_tmpl', 'ghapi/core.py'),
|
|
98
|
+
'ghapi.core._pr_mark': ('core.html#_pr_mark', 'ghapi/core.py'),
|
|
92
99
|
'ghapi.core._reduce_ctx': ('core.html#_reduce_ctx', 'ghapi/core.py'),
|
|
93
100
|
'ghapi.core._run_subp': ('core.html#_run_subp', 'ghapi/core.py'),
|
|
94
101
|
'ghapi.core._transdeps': ('core.html#_transdeps', 'ghapi/core.py'),
|
|
@@ -119,6 +126,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
119
126
|
'ghapi.core.issue_template': ('core.html#issue_template', 'ghapi/core.py'),
|
|
120
127
|
'ghapi.core.list_branches': ('core.html#list_branches', 'ghapi/core.py'),
|
|
121
128
|
'ghapi.core.list_files': ('core.html#list_files', 'ghapi/core.py'),
|
|
129
|
+
'ghapi.core.list_prs': ('core.html#list_prs', 'ghapi/core.py'),
|
|
122
130
|
'ghapi.core.list_tags': ('core.html#list_tags', 'ghapi/core.py'),
|
|
123
131
|
'ghapi.core.local_dep_graph': ('core.html#local_dep_graph', 'ghapi/core.py'),
|
|
124
132
|
'ghapi.core.pr_file_diff': ('core.html#pr_file_diff', 'ghapi/core.py'),
|
|
@@ -150,10 +158,16 @@ d = { 'settings': { 'branch': 'main',
|
|
|
150
158
|
'ghapi.graphql.GhGql.__init__': ('graphql.html#ghgql.__init__', 'ghapi/graphql.py'),
|
|
151
159
|
'ghapi.graphql.GhGql.repo': ('graphql.html#ghgql.repo', 'ghapi/graphql.py')},
|
|
152
160
|
'ghapi.page': { 'ghapi.page.GhApi.last_page': ('page.html#ghapi.last_page', 'ghapi/page.py'),
|
|
161
|
+
'ghapi.page.GhApi.mark_done': ('page.html#ghapi.mark_done', 'ghapi/page.py'),
|
|
162
|
+
'ghapi.page.NotifRow': ('page.html#notifrow', 'ghapi/page.py'),
|
|
163
|
+
'ghapi.page.NotifRow.__repr__': ('page.html#notifrow.__repr__', 'ghapi/page.py'),
|
|
164
|
+
'ghapi.page.NotifRows': ('page.html#notifrows', 'ghapi/page.py'),
|
|
165
|
+
'ghapi.page.NotifRows.__repr__': ('page.html#notifrows.__repr__', 'ghapi/page.py'),
|
|
153
166
|
'ghapi.page._Scanner': ('page.html#_scanner', 'ghapi/page.py'),
|
|
154
167
|
'ghapi.page._Scanner.__getitem__': ('page.html#_scanner.__getitem__', 'ghapi/page.py'),
|
|
155
168
|
'ghapi.page._Scanner.__init__': ('page.html#_scanner.__init__', 'ghapi/page.py'),
|
|
156
169
|
'ghapi.page._Scanner.scan': ('page.html#_scanner.scan', 'ghapi/page.py'),
|
|
170
|
+
'ghapi.page._notif_mark': ('page.html#_notif_mark', 'ghapi/page.py'),
|
|
157
171
|
'ghapi.page._parse_link_hdr': ('page.html#_parse_link_hdr', 'ghapi/page.py'),
|
|
158
172
|
'ghapi.page.gh_notifs': ('page.html#gh_notifs', 'ghapi/page.py'),
|
|
159
173
|
'ghapi.page.paged': ('page.html#paged', 'ghapi/page.py'),
|
|
@@ -6,12 +6,12 @@ Docs: https://ghapi.fast.ai/core.html.md"""
|
|
|
6
6
|
|
|
7
7
|
# %% auto #0
|
|
8
8
|
__all__ = ['GH_HOST', 'pspec', 'img_md_pat', 'EMPTY_TREE_SHA', 'GhTransport', 'GhSyncTransport', 'print_summary', 'gh_patch',
|
|
9
|
-
'GhApi', 'call_gh', 'date2gh', 'gh2date', '
|
|
10
|
-
'create_branch_empty', 'delete_tag', 'delete_branch', 'get_branch', 'commit_tree',
|
|
11
|
-
'get_content', 'create_or_update_file', 'create_file', 'delete_file', 'update_contents',
|
|
12
|
-
'read_issue', 'read_pr', 'pr_file_diff', '
|
|
13
|
-
'
|
|
14
|
-
'dep_order', 'dep_dependents', 'dep_graph', 'APIError']
|
|
9
|
+
'GhApi', 'call_gh', 'date2gh', 'gh2date', 'GhRows', 'delete_release', 'create_release', 'list_tags',
|
|
10
|
+
'list_branches', 'create_branch_empty', 'delete_tag', 'delete_branch', 'get_branch', 'commit_tree',
|
|
11
|
+
'list_files', 'get_content', 'create_or_update_file', 'create_file', 'delete_file', 'update_contents',
|
|
12
|
+
'enable_pages', 'read_issue', 'read_pr', 'pr_file_diff', 'PullRow', 'PullRows', 'list_prs', 'issue_template',
|
|
13
|
+
'issue_body', 'CheckRun', 'CommitStatus', 'check_status', 'pr_status', 'failed_step_log', 'dep_key',
|
|
14
|
+
'local_dep_graph', 'dep_closure', 'dep_order', 'dep_dependents', 'dep_graph', 'APIError']
|
|
15
15
|
|
|
16
16
|
# %% ../nbs/00_core.ipynb #5b5cba7b
|
|
17
17
|
from contextvars import ContextVar
|
|
@@ -26,7 +26,7 @@ from fastspec.errors import APIError
|
|
|
26
26
|
import mimetypes,base64
|
|
27
27
|
from collections import Counter
|
|
28
28
|
from urllib.parse import quote
|
|
29
|
-
from datetime import datetime
|
|
29
|
+
from datetime import datetime, timezone
|
|
30
30
|
import os, shutil, tempfile, subprocess, fnmatch, html
|
|
31
31
|
import asyncio
|
|
32
32
|
|
|
@@ -178,6 +178,11 @@ def gh2date(dtstr:str)->datetime:
|
|
|
178
178
|
"Convert date string `dtstr` received from a GitHub API operation to a UTC `datetime`"
|
|
179
179
|
return datetime.fromisoformat(dtstr.replace('Z', ''))
|
|
180
180
|
|
|
181
|
+
# %% ../nbs/00_core.ipynb #7876622c
|
|
182
|
+
class GhRows(L):
|
|
183
|
+
"Result rows whose bare display is one actionable line each"
|
|
184
|
+
def __repr__(self): return '\n'.join(map(repr, self))
|
|
185
|
+
|
|
181
186
|
# %% ../nbs/00_core.ipynb #16068542
|
|
182
187
|
img_md_pat = re.compile(r'!\[(?P<alt>.*?)\]\((?P<url>[^\s]+)\)')
|
|
183
188
|
|
|
@@ -427,13 +432,13 @@ async def read_issue(self:GhApi, issue_number:int):
|
|
|
427
432
|
res = dict(title=iss.title, body=iss.body or '', is_pr=pr is not None, # chkstyle: ignore-node
|
|
428
433
|
comments=await self.issues.list_comments(issue_number))
|
|
429
434
|
if pr is not None:
|
|
430
|
-
res['diff'] = await self.pulls.get(issue_number,
|
|
435
|
+
res['diff'] = await self.pulls.get(issue_number, headers_={'Accept': 'application/vnd.github.v3.diff'})
|
|
431
436
|
res['review_comments'] = await self.pulls.list_review_comments(issue_number)
|
|
432
437
|
res['reviews'] = await self.pulls.list_reviews(issue_number)
|
|
433
438
|
else:
|
|
434
439
|
evts = await self.issues.list_events(issue_number)
|
|
435
440
|
sha = first(e.commit_id for e in evts if e.commit_id)
|
|
436
|
-
if sha: res['diff'] = await self.repos.get_commit(sha,
|
|
441
|
+
if sha: res['diff'] = await self.repos.get_commit(sha, headers_={'Accept': 'application/vnd.github.v3.diff'})
|
|
437
442
|
return _IssueInfo(dict2obj(res))
|
|
438
443
|
|
|
439
444
|
# %% ../nbs/00_core.ipynb #ccf9d458
|
|
@@ -499,6 +504,44 @@ async def pr_file_diff(
|
|
|
499
504
|
if f.filename == filename: return f"## {f.filename} (+{f.additions} -{f.deletions})\n\n```diff\n{html.unescape(f.patch)}\n```"
|
|
500
505
|
return f"File `{filename}` not found in PR #{pr_number}"
|
|
501
506
|
|
|
507
|
+
# %% ../nbs/00_core.ipynb #2c94c2c8
|
|
508
|
+
def _age(s):
|
|
509
|
+
"Compact age of ISO-8601 timestamp `s`"
|
|
510
|
+
secs = (datetime.now(timezone.utc) - datetime.fromisoformat(s.replace('Z', '+00:00'))).total_seconds()
|
|
511
|
+
for n,u in ((31536000,'y'), (2592000,'mo'), (604800,'w'), (86400,'d'), (3600,'h')):
|
|
512
|
+
if secs >= n: return f'{int(secs//n)}{u}'
|
|
513
|
+
return f'{int(secs//60)}m'
|
|
514
|
+
|
|
515
|
+
def _pr_mark(r):
|
|
516
|
+
if r.get('merged_at'): return 'merged '
|
|
517
|
+
if r.state == 'closed': return 'closed '
|
|
518
|
+
if r.get('draft'): return 'draft '
|
|
519
|
+
return ''
|
|
520
|
+
|
|
521
|
+
class PullRow(AttrDict):
|
|
522
|
+
"One pull request as an actionable line"
|
|
523
|
+
def line(self, maxlen=120): return f'#{self.number} {_age(self.updated_at)} {_pr_mark(self)}{truncstr(self.title, maxlen)}'
|
|
524
|
+
__repr__ = line
|
|
525
|
+
|
|
526
|
+
class PullRows(GhRows):
|
|
527
|
+
"Pull request rows, displayed with titles truncated to `maxlen`"
|
|
528
|
+
def __init__(self, items, maxlen=120):
|
|
529
|
+
super().__init__(items)
|
|
530
|
+
self.maxlen = maxlen
|
|
531
|
+
def __repr__(self): return '\n'.join(o.line(self.maxlen) for o in self)
|
|
532
|
+
|
|
533
|
+
@gh_patch
|
|
534
|
+
async def list_prs(
|
|
535
|
+
self:GhApi,
|
|
536
|
+
state='open', # 'open', 'closed', or 'all'
|
|
537
|
+
maxlen=120, # Title characters shown per row
|
|
538
|
+
per_page=100, # PRs per request (100 max)
|
|
539
|
+
page=1, # Page of results, for repos with more than `per_page` PRs
|
|
540
|
+
)->PullRows:
|
|
541
|
+
"Pull requests as one-line rows, most recently updated first"
|
|
542
|
+
prs = await self.pulls.list(state=state, sort='updated', direction='desc', per_page=per_page, page=page)
|
|
543
|
+
return PullRows([PullRow(o) for o in prs], maxlen=maxlen)
|
|
544
|
+
|
|
502
545
|
# %% ../nbs/00_core.ipynb #55a976ea
|
|
503
546
|
def _parse_tmpl(name, txt):
|
|
504
547
|
"Parse issue template `txt`: yml forms into section labels for `issue_body`; md templates kept as `raw`"
|
|
@@ -551,10 +594,6 @@ class CheckRun(AttrDict):
|
|
|
551
594
|
class CommitStatus(AttrDict):
|
|
552
595
|
def __repr__(self): return f'{self.context}: {self.state}'
|
|
553
596
|
|
|
554
|
-
class GhRows(L):
|
|
555
|
-
"Result rows whose bare display is one actionable line each"
|
|
556
|
-
def __repr__(self): return '\n'.join(map(repr, self))
|
|
557
|
-
|
|
558
597
|
class _CheckStatus(AttrDict):
|
|
559
598
|
def _repr_markdown_(self):
|
|
560
599
|
runs = self.check_runs
|
|
@@ -5,11 +5,13 @@ Docs: https://ghapi.fast.ai/page.html.md"""
|
|
|
5
5
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/03_page.ipynb.
|
|
6
6
|
|
|
7
7
|
# %% auto #0
|
|
8
|
-
__all__ = ['paged', 'parse_link_hdr', 'pages', 'sync_paged', 'sync_pages', 'gh_notifs']
|
|
8
|
+
__all__ = ['paged', 'parse_link_hdr', 'pages', 'sync_paged', 'sync_pages', 'NotifRow', 'NotifRows', 'gh_notifs']
|
|
9
9
|
|
|
10
10
|
# %% ../nbs/03_page.ipynb #6a0bf465
|
|
11
11
|
from fastcore.all import *
|
|
12
12
|
from .core import *
|
|
13
|
+
from .core import _age
|
|
14
|
+
from fastspec.errors import APIError
|
|
13
15
|
|
|
14
16
|
import re, asyncio
|
|
15
17
|
from datetime import datetime, timedelta, timezone
|
|
@@ -87,17 +89,41 @@ def sync_pages(oper, n_pages, *args, per_page=100, n_workers=16, **kwargs):
|
|
|
87
89
|
range(n_pages), threadpool=True, n_workers=n_workers))
|
|
88
90
|
|
|
89
91
|
# %% ../nbs/03_page.ipynb #58141083
|
|
92
|
+
class NotifRow(AttrDict):
|
|
93
|
+
"One notification thread as an actionable line"
|
|
94
|
+
def __repr__(self):
|
|
95
|
+
s = self.subject
|
|
96
|
+
num = s.url.rpartition('/')[2] if s.url else ''
|
|
97
|
+
kind = ('PR' if s.type=='PullRequest' else s.type) + (f' #{num}' if num.isdigit() else '')
|
|
98
|
+
return f'{self.id} {_age(self.updated_at)} {self.reason} {self.repository.full_name} {self.get("mark","")}{kind}: {s.title}'
|
|
99
|
+
|
|
100
|
+
class NotifRows(GhRows):
|
|
101
|
+
"Notification rows; displays 'no notifications' when empty"
|
|
102
|
+
def __repr__(self): return super().__repr__() or 'no notifications'
|
|
103
|
+
|
|
104
|
+
def _notif_mark(sub):
|
|
105
|
+
"`[merged] `/`[closed] ` marker for a fetched subject; empty for open or unfetchable subjects"
|
|
106
|
+
if not sub: return ''
|
|
107
|
+
if sub.get('merged_at'): return '[merged] '
|
|
108
|
+
if sub.get('state') == 'closed': return '[closed] '
|
|
109
|
+
return ''
|
|
110
|
+
|
|
90
111
|
async def gh_notifs(days=5, reasons=('mention', 'review_requested', 'author', 'assign'), include_read=False, per_page=100):
|
|
91
|
-
"
|
|
112
|
+
"Notifications from the past `days` (all available if None) as `NotifRows`, marked `[closed]`/`[merged]` when the subject is no longer open; `reasons=None` includes every reason"
|
|
113
|
+
api = GhApi()
|
|
92
114
|
since = (datetime.now(timezone.utc) - timedelta(days=days)).isoformat() if days else None
|
|
93
|
-
notifs = L([o async for o in paged(
|
|
115
|
+
notifs = L([o async for o in paged(api.activity.list_notifications_for_authenticated_user, all=include_read, since=since, per_page=per_page)]).concat()
|
|
94
116
|
if reasons: notifs = notifs.filter(lambda n: n.reason in reasons)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
117
|
+
async def _get(u):
|
|
118
|
+
if not u: return None
|
|
119
|
+
try: return await api(u, 'GET')
|
|
120
|
+
except APIError: return None # deleted or inaccessible subjects lose their mark, not the whole listing
|
|
121
|
+
subs = await asyncio.gather(*(_get(n.subject.url) for n in notifs))
|
|
122
|
+
for n, s in zip(notifs, subs): n['mark'] = _notif_mark(s)
|
|
123
|
+
return NotifRows(NotifRow(o) for o in notifs)
|
|
124
|
+
|
|
125
|
+
# %% ../nbs/03_page.ipynb #abc91f9e
|
|
126
|
+
@patch
|
|
127
|
+
async def mark_done(self:GhApi, *ids):
|
|
128
|
+
"Mark notification threads as done, removing them from the inbox (the API cannot list done threads back)"
|
|
129
|
+
await asyncio.gather(*(self.activity.mark_thread_as_done(i) for i in ids))
|
|
@@ -87,11 +87,12 @@ Once the PR exists, the fork branch is the source of truth, and the local clone
|
|
|
87
87
|
|
|
88
88
|
# Gotchas
|
|
89
89
|
|
|
90
|
+
- Results with tuned reprs (`read_issue`, `check_status`, `list_prs`, `gh_notifs`) display bare. Raw endpoint results don't, so when only confirmation matters (a `create_comment`, a `pulls.update`), assign the result to a variable and show one key (`r.html_url`, `r.state`) instead of displaying the whole payload.
|
|
90
91
|
- A PR *is* an issue for general comments (`issues.list_comments`, not `pulls.*`) -- inline code-review comments are the separate `pulls.list_review_comments`.
|
|
91
92
|
- Rate limits: register `limit_cb` on `GhApi(...)` to get called back whenever the remaining quota changes, or check `api.limit_rem` any time.
|
|
92
93
|
- HTTP errors raise `fastspec.errors.APIError`, which carries `.status_code` and the endpoint called.
|
|
93
94
|
- `per_page` maxes out at 100; beyond that, use `paged`/`pages` rather than manually looping `page=`.
|
|
94
|
-
- Per-call headers on named endpoints use `
|
|
95
|
+
- Per-call headers on named endpoints use `headers_=` (e.g. `await api.pulls.get(n, headers_={'Accept': 'application/vnd.github.v3.diff'})` for the raw diff); plain `headers=` is a kwarg only for direct `api(path, verb, ...)` calls.
|
|
95
96
|
- `GITHUB_TOKEN` unset means unauthenticated (heavily rate-limited, no write access) -- `GhApi()` warns but doesn't raise.
|
|
96
97
|
"""
|
|
97
98
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ghapi
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.1
|
|
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>=2.2.6
|
|
19
|
-
Requires-Dist: fastspec>=0.
|
|
19
|
+
Requires-Dist: fastspec>=0.2.0
|
|
20
20
|
Requires-Dist: tomli; python_version < "3.11"
|
|
21
21
|
Provides-Extra: dev
|
|
22
22
|
Requires-Dist: fastgit; extra == "dev"
|
|
@@ -26,6 +26,7 @@ Requires-Dist: nbdev; extra == "dev"
|
|
|
26
26
|
Requires-Dist: pyyaml; extra == "dev"
|
|
27
27
|
Requires-Dist: pysym2md>=0.0.6; extra == "dev"
|
|
28
28
|
Requires-Dist: llms-txt>=0.0.6; extra == "dev"
|
|
29
|
+
Requires-Dist: pyskills; extra == "dev"
|
|
29
30
|
Dynamic: license-file
|
|
30
31
|
|
|
31
32
|
# ghapi
|
|
@@ -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>=2.2.6', 'fastspec>=0.
|
|
15
|
+
dependencies = ['fastcore>=2.2.6', 'fastspec>=0.2.0', 'tomli; python_version < "3.11"']
|
|
16
16
|
|
|
17
17
|
[project.urls]
|
|
18
18
|
Repository = "https://github.com/fastai/ghapi"
|
|
@@ -25,7 +25,7 @@ ghapi = "ghapi._modidx:d"
|
|
|
25
25
|
ghapi = "ghapi.skill"
|
|
26
26
|
|
|
27
27
|
[project.optional-dependencies]
|
|
28
|
-
dev = ['fastgit', 'matplotlib', 'nbclassic', 'nbdev', 'pyyaml', 'pysym2md>=0.0.6', 'llms-txt>=0.0.6']
|
|
28
|
+
dev = ['fastgit', 'matplotlib', 'nbclassic', 'nbdev', 'pyyaml', 'pysym2md>=0.0.6', 'llms-txt>=0.0.6', 'pyskills']
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
[project.scripts]
|
|
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
|