Using GitHub's API via github3 in Python

Here is some example code to help you understand how the github3 library works. (I wrote it to help me understand how the github3 library works. I hope it helps you!)

First, a nice function to show only the public methods available to an object

(kind of like dir(), but nicer, in that it removes functions with double-underscores - you don't have to care about the details of this)

In [122]:
# public() is from here: http://nbviewer.ipython.org/gist/jiffyclub/9235955 
def public(thing):
    """Print 'public' attributes of thing."""    
    for x in dir(thing):
        if not x.startswith('_'):
            print(x)
            

Set your variables and authenticate

You know everything went OK if you see the message "Access granted" after running this.

In [123]:
import github3
            
# fill in your GitHub username and your preferred repository name:
my_username = ''
my_repo_name = ''

# generate a token for yourself in GitHub; paste it in below    
g = github3.login(token='')

if g:
    print('Access granted.')
    
Access granted.
In [124]:
# What have we just wrought?

############## return value ######################
# github3.login() returns: <GitHub at 0x104a2e910>
##################################################

# So that's a GitHub object.

if g:
    print('What can be done with a GitHub object:')
    print('=====================================')
    public(g)
    
What can be done with a GitHub object:
=====================================
authorization
authorize
check_authorization
create_gist
create_issue
create_key
create_repo
delete_key
emojis
etag
feeds
follow
from_json
gist
gitignore_template
gitignore_templates
is_following
is_starred
is_subscribed
issue
iter_all_repos
iter_all_users
iter_authorizations
iter_emails
iter_events
iter_followers
iter_following
iter_gists
iter_issues
iter_keys
iter_notifications
iter_org_issues
iter_orgs
iter_repo_issues
iter_repos
iter_starred
iter_subscriptions
iter_user_issues
iter_user_repos
iter_user_teams
key
last_modified
login
markdown
membership_in
meta
octocat
organization
organization_memberships
pubsubhubbub
pull_request
rate_limit
ratelimit_remaining
refresh
repository
revoke_authorization
revoke_authorizations
search_code
search_issues
search_repositories
search_users
set_client_id
set_user_agent
star
subscribe
to_json
unfollow
unstar
unsubscribe
update_user
user
zen

Now we're going to make a repository!

This will make an actual repository in your actual account on GitHub. So... You know, here's where it gets real.

In [125]:
# Create the repository
repo_dict = {'name': my_repo_name, 
        'description': 'I made this repo with an API!', 
        'homepage': '', 
        'private': False, 
        'has_issues': True,
        'has_wiki': True, 
        'has_downloads': False }

repo = None

# pop is really cool!
# it grabs the value matching the provided key and removes it from the dictionary
if repo_dict.get('name'):
    repo = g.create_repo(repo_dict.pop('name'), **repo_dict)

if repo:
    print('Repo created!')
    
Repo created!
In [126]:
# What did we make this time?

############## return value #####################
# GitHub.create_repo() returns an object of type: 
#       <class 'github3.repos.repo.Repository'>
# print(repo) displays: my_username/my_repo_name
#################################################

# So that's a Repository object.

if repo:
    print('What can be done with a Repository object:')
    print('=========================================')
    public(repo)
    
What can be done with a Repository object:
=========================================
add_collaborator
archive
archive_urlt
asset
assignees_urlt
blob
blobs_urlt
branch
branches_urlt
clone_url
comments_urlt
commit
commit_comment
commits_urlt
compare_commits
compare_urlt
contents
contents_urlt
contributors_url
create_blob
create_comment
create_commit
create_deployment
create_file
create_fork
create_hook
create_issue
create_key
create_label
create_milestone
create_pull
create_pull_from_issue
create_ref
create_release
create_status
create_tag
create_tree
created_at
default_branch
delete
delete_file
delete_key
delete_subscription
description
download_url
edit
etag
events_url
fork
fork_count
forks
forks_count
from_json
full_name
git_commit
git_commits_urlt
git_refs_urlt
git_tags_urlt
git_url
has_downloads
has_issues
has_wiki
homepage
hook
hooks_url
html_url
id
is_assignee
is_collaborator
issue
issue_comment_urlt
issue_events_urlt
issues_urlt
iter_assignees
iter_branches
iter_code_frequency
iter_collaborators
iter_comments
iter_comments_on_commit
iter_commit_activity
iter_commits
iter_contributor_statistics
iter_contributors
iter_deployments
iter_events
iter_forks
iter_hooks
iter_issue_events
iter_issues
iter_keys
iter_labels
iter_languages
iter_milestones
iter_network_events
iter_notifications
iter_pages_builds
iter_pulls
iter_refs
iter_releases
iter_stargazers
iter_statuses
iter_subscribers
iter_tags
iter_teams
key
label
labels_urlt
language
languages_url
last_modified
latest_pages_build
mark_notifications
master_branch
merge
merges_url
milestone
milestones_urlt
mirror_url
name
notifications_urlt
open_issues
open_issues_count
owner
pages
parent
private
pull_request
pulls_urlt
pushed_at
ratelimit_remaining
readme
ref
refresh
release
remove_collaborator
set_subscription
size
source
ssh_url
stargazers
stargazers_url
statuses_urlt
subscribers_url
subscription
subscription_url
svn_url
tag
tags_url
teams_url
to_json
tree
trees_urlt
update_file
update_label
updated_at
watchers
weekly_commit_count
In [127]:
# You can retrieve a repository if, for instance, you didn't just create one
r = g.repository(my_username, my_repo_name)

if r:
    print('Repository {0} is saved as "r".\n'.format(r.name))
    
if r == repo:
    print('This step was unnecessary; we already had our repository in a variable.')
    
Repository api-test is saved as "r".

This step was unnecessary; we already had our repository in a variable.

Add files to the repository

Two files - a README (because good manners) and something called 'file.md.'

In [128]:
# Now we're going to add two files, README.md and file.md

# Here's the content we want to put inside the files (replace, if you want!)
contents = 'API Test\n======\n\nThis is the README for api-test. It was added via api.'
contents2 = 'This is the text of a file. It was added via api.'

# This is the message that will show up in the commit 
commit_message = 'This was added via API, woo!'

create = r.create_file('README.md', commit_message, contents)
create2 = r.create_file('file.md', commit_message, contents2)

if create and create2:
    print('The files were both created!')
The files were both created!
In [129]:
# And now what have we done?

############## return value ###############################################
# Repository.create_file() returns:
# {
# u'content': <Content [README.md]>, 
# u'commit': <Commit [Your Name:00fca0f40d69166ab01bcd123a0569f463a4e335]>, 
# u'ETag': '"5b78bfcf6cf345d6c950d462630850c6"', 
# u'Last-Modified': u''
# }
###########################################################################

# So that's a dict object (yeah, plain old Python dicts, go figure)

# But notice it has a Content object in it and a Commit object in it

# We'll get to Content objects in a moment, but here's the skinny on a Commit object

if create:
    print('What can be done with a Commit object:')
    print('=====================================')    
    public(create['commit'])
What can be done with a Commit object:
=====================================
author
author_as_User
committer
committer_as_User
etag
from_json
html_url
last_modified
message
parents
ratelimit_remaining
refresh
sha
to_json
tree

Now update one of the files

We'll be updating file.md.

In [130]:
# This is going to look like a digression, but it's relevant

# Let's look at the contents of the repository
all_the_files = r.contents('/')

print('You have the following files in your repository: ')
print(all_the_files)

# Get just one of the files, because we're going to update it
file_to_update = r.contents('file.md')
You have the following files in your repository: 
{u'file.md': <Content [file.md]>, u'README.md': <Content [README.md]>}
In [131]:
# And now?

############## return value ####################
# Repository.contents('filename') returns:
#     <class 'github3.repos.contents.Contents'>
# Printing file_to_update displays:
#     <Content [file.md]>
################################################

# So, that's a Content object

if file_to_update:
    print('What can be done with a Content object:')
    print('======================================')
    public(file_to_update)
    
What can be done with a Content object:
======================================
content
decoded
delete
encoding
etag
from_json
git_url
html_url
last_modified
links
name
path
ratelimit_remaining
refresh
sha
size
submodule_git_url
target
to_json
type
update
In [132]:
# Here's the text of the update we want to make
update = 'This file was updated via the api.'

# Here's why we needed to grab that file; we needed its SHA
# A SHA is a checksum, and you could go read a lot about it...
# BUT if you don't care, it's basically what GitHub uses as the ID of a file
# or of a commit
# or of a tree
the_sha = file_to_update.sha

commit_message = 'Update made via API'

update_return = r.update_file('file.md', commit_message, update, the_sha)
In [133]:
# What have we got now?

############## return value ################################################
# Repository.update_file() returns:
# {
# u'content': <Content [file.md]>, 
# u'commit': <Commit [Your Name:4e18f28f46188a9dd3bb7603ce8339cf57e5d026]>, 
# u'ETag': '"dacf99cb0dde35b6210c1df729df2a4f"', 
# u'Last-Modified': u''
# }
###########################################################################

# Another dict! With another Content and Commit in it!
# (It makes sense. Updating is a lot like creating.)

Now delete a file

It isn't CRUD without the D. (Create Read Update and Delete)

In [134]:
r.create_file('deleteme.md', 'This file was added via api, but it is doomed.', contents)

delete_sha = r.contents('deleteme.md').sha

delete_return = r.delete_file('deleteme.md', 'this file was deleted via api', delete_sha)

if delete_return:
    print('File \'deleteme.md\' was created and then deleted.')
File 'deleteme.md' was created and then deleted.
In [135]:
# What has happened?

############## return value ####################################
# Repository.delete_file() returns:
# <Commit [Your Name:539ef374984b0ae4809662ae6a198f214135f123]>
################################################################

# A commit object!

And... trees...

In [136]:
# And get the tree, because, tree
# You can get a tree with a SHA or with a branch name
# We didn't make any branches, explicitly, so we're on branch 'master'
t = r.tree('master')
In [137]:
# Finally?

############## return value ####################################
# Repository.tree() returns:
# <Tree [224bc857d84e2ab6743ae7ebf1b85562343e2d39]>
################################################################

if t:
    print('What can be done with a Tree object:')
    print('===================================')
    public(t)
What can be done with a Tree object:
===================================
etag
from_json
last_modified
ratelimit_remaining
recurse
refresh
sha
to_json
tree
In [138]:
# Wait wait wait. Tree has a thing that you can do called tree?!

# Let's explore.

if t:
    print('\nHere is Tree.tree:')
    print('=================')
    print('It is type: {0}'.format(type(t.tree)))
    print('\nOK, a list. Cool. ... A list of what?')
    print('\nWhen you print the list, it looks like:')
    print(t.tree)
    print('\nHmm, the list contains objects of type Hash, neat.')
    print('\nAnd what does it look like if we print a Hash?')
    print(t.tree[0])
    print('\nOh. The same.')
    print('\n\nWhat can be done with a Hash object (one item in the Tree.tree list):')
    print('====================================================================')
    public(t.tree[0])
    
Here is Tree.tree:
=================
It is type: <type 'list'>

OK, a list. Cool. ... A list of what?

When you print the list, it looks like:
[<Hash [11e1377f4f31293d07a9a8dd3c7108c9e6f49cc1]>, <Hash [83bbf2c2a533d04d12fc25ff60d6d60fb604a182]>]

Hmm, the list contains objects of type Hash, neat.

And what does it look like if we print a Hash?
<Hash [11e1377f4f31293d07a9a8dd3c7108c9e6f49cc1]>

Oh. The same.


What can be done with a Hash object (one item in the Tree.tree list):
====================================================================
etag
from_json
last_modified
mode
path
sha
size
to_json
type
url