= GhApi() api
GhApi details
You can set an environment variable named GH_HOST
to override the default of https://api.github.com
incase you are running GitHub Enterprise(GHE). However, this library has not been tested on GHE, so proceed at your own risk.
print_summary
print_summary (req:urllib.request.Request)
Print Request.summary
with the token (if any) removed
GhApi
GhApi (owner=None, repo=None, token=None, jwt_token=None, debug=None, limit_cb=None, gh_host=None, authenticate=True, **kwargs)
Initialize self. See help(type(self)) for accurate signature.
Access by path
GhApi.__call__
GhApi.__call__ (path:str, verb:str=None, headers:dict=None, route:dict=None, query:dict=None, data=None, timeout=None, decode=True)
Call a fully specified path
using HTTP verb
, passing arguments to fastcore.core.urlsend
You can call a GhApi
object as a function, passing in the path to the endpoint, the HTTP verb, and any route, query parameter, or post data parameters as required.
'/repos/{owner}/{repo}/git/ref/{ref}', 'GET', route=dict(
api(='fastai', repo='ghapi-test', ref='heads/master')) owner
{ 'node_id': 'MDM6UmVmMzE1NzEyNTg4OnJlZnMvaGVhZHMvbWFzdGVy',
'object': { 'sha': '17efbb7eb346f0f9161c227af9c8db93597321e2',
'type': 'commit',
'url': 'https://api.github.com/repos/fastai/ghapi-test/git/commits/17efbb7eb346f0f9161c227af9c8db93597321e2'},
'ref': 'refs/heads/master',
'url': 'https://api.github.com/repos/fastai/ghapi-test/git/refs/heads/master'}
GhApi.__getitem__
GhApi.__getitem__ (k)
Lookup and call an endpoint by path and verb (which defaults to ‘GET’)
You can access endpoints by indexing into the object. When using the API this way, you do not need to specify what type of parameter (route, query, or post data) is being used. This is, therefore, the same call as above:
'/repos/{owner}/{repo}/git/ref/{ref}'](owner='fastai', repo='ghapi-test', ref='heads/master') api[
{ 'node_id': 'MDM6UmVmMzE1NzEyNTg4OnJlZnMvaGVhZHMvbWFzdGVy',
'object': { 'sha': '17efbb7eb346f0f9161c227af9c8db93597321e2',
'type': 'commit',
'url': 'https://api.github.com/repos/fastai/ghapi-test/git/commits/17efbb7eb346f0f9161c227af9c8db93597321e2'},
'ref': 'refs/heads/master',
'url': 'https://api.github.com/repos/fastai/ghapi-test/git/refs/heads/master'}
Media types
For some endpoints GitHub lets you specify a media type the for response data, using the Accept
header. If you choose a media type that is not JSON formatted (for instance application/vnd.github.v3.sha
) then the call to the GhApi
object will return a string instead of an object.
'/repos/{owner}/{repo}/commits/{ref}', 'GET', route=dict(
api(='fastai', repo='ghapi-test', ref='refs/heads/master'),
owner={'Accept': 'application/vnd.github.VERSION.sha'}) headers
'17efbb7eb346f0f9161c227af9c8db93597321e2'
Rate limits
GitHub has various rate limits for their API. After each call, the response includes information about how many requests are remaining in the hourly quota. If you’d like to add alerts, or indications showing current quota usage, you can register a callback with GhApi
by passing a callable to the limit_cb
parameter. This callback will be called whenever the amount of quota used changes. It will be called with two arguments: the new quota remaining, and the total hourly quota.
def _f(rem,quota): print(f"Quota remaining: {rem} of {quota}")
= GhApi(limit_cb=_f)
api '/repos/{owner}/{repo}/git/ref/{ref}'](owner='fastai', repo='ghapi-test', ref='heads/master').ref api[
Quota remaining: 4897 of 5000
'refs/heads/master'
You can always get the remaining quota from the limit_rem
attribute:
api.limit_rem
'4897'
Operations
Instead of passing a path to GhApi
, you will more often use the operation methods provided in the API’s operation groups, which include documentation, signatures, and auto-complete.
If you provide owner
and/or repo
to the constructor, they will be automatically inserted into any calls which use them (except when calling GhApi
as a function). You can also pass any other arbitrary keyword arguments you like to have them used as defaults for any relevant calls.
You must include a GitHub API token if you need to access any authenticated endpoints. If don’t pass the token
param, then your GITHUB_TOKEN
environment variable will be used, if available.
= GhApi(owner='fastai', repo='ghapi-test', token=token) api
Operation groups
The following groups of endpoints are provided, which you can list at any time along with a link to documentation for all endpoints in that group, by displaying the GhApi
object:
api
- actions
- activity
- apps
- billing
- checks
- classroom
- code_scanning
- code_security
- codes_of_conduct
- codespaces
- copilot
- dependabot
- dependency_graph
- emojis
- gists
- git
- gitignore
- interactions
- issues
- licenses
- markdown
- meta
- migrations
- oidc
- orgs
- packages
- projects
- pulls
- rate_limit
- reactions
- repos
- search
- secret_scanning
- security_advisories
- teams
- users
api.codes_of_conduct
- codes-of-conduct.get_all_codes_of_conduct(): Get all codes of conduct
- codes-of-conduct.get_conduct_code(key): Get a code of conduct
Calling endpoints
The GitHub API’s endpoint names generally start with a verb like “get”, “list”, “delete”, “create”, etc, followed _
, then by a noun such as “ref”, “webhook”, “issue”, etc.
Each endpoint has a different signature, which you can see by using Shift-Tab in Jupyter, or by just printing the endpoint object (which also shows a link to the GitHub docs):
print(api.repos.create_webhook)
repos.create_webhook(name: str = None, config: dict = None, events: list = ['push'], active: bool = True)
https://docs.github.com/rest/repos/webhooks#create-a-repository-webhook
Displaying an endpoint object in Jupyter also provides a formatted summary and link to the official GitHub documentation:
api.repos.create_webhook
repos.create_webhook(name, config, events, active): Create a repository webhook
Endpoint objects are called using standard Python method syntax:
= api.git.get_ref('heads/master')
ref object.type, 'commit') test_eq(ref.
Information about the endpoint are available as attributes:
api.git.get_ref.path,api.git.get_ref.verb
('/repos/fastai/ghapi-test/git/ref/{ref}', 'get')
You can get a list of all endpoints available in a group, along with a link to documentation for each, by viewing the group:
api.git
- git.create_blob(content, encoding): Create a blob
- git.get_blob(file_sha): Get a blob
- git.create_commit(message, tree, parents, author, committer, signature): Create a commit
- git.get_commit(commit_sha): Get a commit object
- git.list_matching_refs(ref): List matching references
- git.get_ref(ref): Get a reference
- git.create_ref(ref, sha): Create a reference
- git.update_ref(ref, sha, force): Update a reference
- git.delete_ref(ref): Delete a reference
- git.create_tag(tag, message, object, type, tagger): Create a tag object
- git.get_tag(tag_sha): Get a tag
- git.create_tree(tree, base_tree): Create a tree
- git.get_tree(tree_sha, recursive): Get a tree
For “list” endpoints, the noun will be a plural form, e.g.:
= api.repos.list_webhooks()
hooks len(hooks), 0) test_eq(
You can pass dicts, lists, etc. directly, where they are required for GitHub API endpoints:
= 'https://example.com'
url = dict(url=url, content_type='json', secret='XXX')
cfg = api.repos.create_webhook(config=cfg, events=['ping'])
hook test_eq(hook.config.url, url)
Let’s confirm that our new webhook has been created:
= api.repos.list_webhooks()
hooks len(hooks), 1)
test_eq(0].events, ['ping']) test_eq(hooks[
Finally, we can delete our new webhook:
0].id) api.repos.delete_webhook(hooks[
{}
Convenience functions
date2gh
date2gh (dt:datetime.datetime)
Convert dt
(which is assumed to be in UTC time zone) to a format suitable for GitHub API operations
The GitHub API assumes that dates will be in a specific string format. date2gh
converts Python standard datetime
objects to that format. For instance, to find issues opened in the ‘fastcore’ repo in the last 4 weeks:
= date2gh(datetime.utcnow() - timedelta(weeks=4))
dt = GhApi('fastai').issues.list_for_repo(repo='fastcore', since=dt)
issues len(issues)
3
gh2date
gh2date (dtstr:str)
Convert date string dtstr
received from a GitHub API operation to a UTC datetime
= issues[0].created_at
created print(created, '->', gh2date(created))
2024-08-27T06:49:29Z -> 2024-08-27 06:49:29
You can set the debug
attribute to any callable to intercept all requests, for instance to print Request.summary
. print_summary
is provided for this purpose. Using this, we can see the preview header that is added for preview functionality, e.g.
=print_summary
api.debug0]
api.codes_of_conduct.get_all_codes_of_conduct()[=None api.debug
{'data': None,
'full_url': 'https://api.github.com/codes_of_conduct',
'headers': {'Accept': 'application/vnd.github.v3+json'},
'method': 'GET'}
Preview endpoints
GitHub’s preview API functionality requires a special header to be passed to enable it. This is added automatically for you.
Convenience methods
Some methods in the GitHub API are a bit clunky or unintuitive. In these situations we add convenience methods to GhApi
to make things simpler. There are also some multi-step processes in the GitHub API that GhApi
provide convenient wrappers for. The methods currently available are shown below; do not hesitate to create an issue or pull request if there are other processes that you’d like to see supported better.
GhApi.create_gist
GhApi.create_gist (description, content, filename='gist.txt', public=False)
Create a gist containing a single file
= api.create_gist("some description", "some content")
gist 'gist.txt'].content gist.html_url, gist.files[
('https://gist.github.com/jph00/d11f49a2c4515491f09d520e402ede75',
'some content')
id) api.gists.delete(gist.
{}
Note that if you want to create a gist with multiple files, call the GitHub API directly, e.g.:
"some description", files={"f1.txt": {"content": "my content"}, ...}) api.gists.create(
Releases
GhApi.delete_release
GhApi.delete_release (release)
Delete a release and its associated tag
GhApi.upload_file
GhApi.upload_file (rel, fn)
Upload fn
to endpoint for release rel
GhApi.create_release
GhApi.create_release (tag_name, branch='master', name=None, body='', draft=False, prerelease=False, files=None)
Wrapper for GhApi.repos.create_release
which also uploads files
Creating a release and attaching files to it is normally a multi-stage process, so create_release
wraps this up for you. It takes the same arguments as repos.create_release
, along with files
, which can contain a single file name, or a list of file names to upload to your release:
= api.create_release('0.0.1', files=['README.md'])
rel 'v0.0.1') test_eq(rel.name,
0.2)
sleep(= api.repos.list_releases()
rels len(rels), 1) test_eq(
We can check that our file has been uploaded; GitHub refers to them as “assets”:
= api.repos.list_release_assets(rels[0].id)
assets 0].name, 'README.md') test_eq(assets[
GhApi.delete_release
GhApi.delete_release (release)
Delete a release and its associated tag
GhApi.list_branches
GhApi.list_branches (prefix:str='')
List all branches, optionally filtered to those starting with prefix
Branches can be listed in the exactly the same way as tags.
len(api.list_branches('master')), 1) test_eq(
We can delete our release and confirm that it is removed:
0])
api.delete_release(rels[len(api.repos.list_releases()), 0) test_eq(
# #|hide
# #not working
# #|export
# @patch
# def create_branch_empty(self:GhApi, branch):
# c = self.git.create_commit(f'create {branch}', EMPTY_TREE_SHA)
# return self.git.create_ref(f'refs/heads/{branch}', c.sha)
GhApi.create_branch_empty
GhApi.create_branch_empty (branch)
= api.create_branch_empty("testme")
ref len(api.list_branches('testme')), 1) test_eq(
GhApi.delete_tag
GhApi.delete_tag (tag:str)
Delete a tag
GhApi.delete_branch
GhApi.delete_branch (branch:str)
Delete a branch
'testme')
api.delete_branch(len(api.list_branches('testme')), 0) test_eq(
GhApi.get_branch
GhApi.get_branch (branch=None)
Content (git files)
GhApi.list_files
GhApi.list_files (branch=None)
= api.list_files()
files 'README.md'] files[
{ 'mode': '100644',
'path': 'README.md',
'sha': 'eaea0f2698e76c75602058bf4e2e9fd7940ac4e3',
'size': 72,
'type': 'blob',
'url': 'https://api.github.com/repos/fastai/ghapi-test/git/blobs/eaea0f2698e76c75602058bf4e2e9fd7940ac4e3'}
GhApi.get_content
GhApi.get_content (path)
= api.get_content('README.md').decode()
readme assert 'ghapi' in readme
GhApi.create_or_update_file
GhApi.create_or_update_file (path, message, committer, author, content=None, sha=None, branch='')
GhApi.create_file
GhApi.create_file (path, message, committer, author, content=None, branch=None)
= dict(name="Monalisa Octocat", email="[email protected]")
person = api.create_file(
res ='foo',
path="Create foo",
message="foobar",
content=person, author=person
committer
)'foobar', api.get_content('foo').decode()) test_eq(
GhApi.delete_file
GhApi.delete_file (path, message, committer, author, sha=None, branch=None)
'foo', 'delete foo', committer=person, author=person)
api.delete_file(assert 'foo' not in api.list_files()
GhApi.update_contents
GhApi.update_contents (path, message, committer, author, content, sha=None, branch=None)
= api.update_contents(
res ='README.md',
path="Update README",
message=person, author=person,
committer=readme+"foobar"
content
) res.content.size
78
= api.get_content('README.md').decode()
readme assert 'foobar' in readme
'README.md', "Revert README", committer=person, author=person, content=readme[:-6]); api.update_contents(
GitHub Pages
GhApi.enable_pages
GhApi.enable_pages (branch=None, path='/')
Enable or update pages for a repo to point to a branch
and path
.
branch
is set to the default branch if None
. path
must be /docs
or /
.
= api.enable_pages(branch='new-branch', path='/')
res
'new-branch')
test_eq(res.source.branch, '/')
test_eq(res.source.path,
api.repos.delete_pages_site()'new-branch') api.delete_branch(