Goals

hello world of github3


In [1]:
from github3 import (login, GitHub)
from github_settings import (username, password, token)
from itertools import islice

#gh = login(username, password=password)
gh = login(token=token)

# set up an anonymous user to see what can be done without authn
anon = GitHub()

#user = gh.user('rdhyee-GITenberg')
user = gh.user()
# <User [sigmavirus24:Ian Cordasco]>

print(user.name)
# Ian Cordasco
print(user.login)
# sigmavirus24
print(user.followers)
# 4

for f in gh.iter_followers():
    print(str(f))

kennethreitz = gh.user('kennethreitz')
# <User [kennethreitz:Kenneth Reitz]>

print(kennethreitz.name)
print(kennethreitz.login)
print(kennethreitz.followers)

# let's grab just a subset of the followers
followers = [str(f) for f in islice(gh.iter_followers('kennethreitz'), 100)]


Raymond Yee (GITenberg)
rdhyee-GITenberg
8
deejf
ghardin
brodieaustin
julescarbon
whitten
novotelituk
davidjbrossard
marcellmars
Kenneth Reitz
kennethreitz
8824

creating and deleting a gist


In [2]:
# let's use a token instead
# create a gist https://github3py.readthedocs.org/en/master/examples/gist.html#creating-a-gist-after-authenticating

files = {
    'spam.txt' : {
        'content': 'What... is the air-speed velocity of an unladen swallow?'
        }
    }
gist = gh.create_gist('Answer this to cross the bridge', files, public=False)
# gist == <Gist [gist-id]>
print(gist.html_url)


https://gist.github.com/539f0c0af3740b209053

In [3]:
# delete gist?

gist.delete()


Out[3]:
True

authorizations


In [ ]:
# need to login with username, password

In [44]:
gh = login(username, password)

def authorization_description_already_exists(e):
    """
    Given an exception e when trying to create a token, is the exception the result of a duplicate description
    """
    if (e.code == 422 and 
        e.message == u'Validation Failed' and 
        (u'already_exists', u'description') in [(error['code'], error['field']) for error in e.errors]):
        return True
    else:
        return False
    
try:
    token = gh.authorize(username, password, scopes=('public_repo'), note='test token 2016.03.17')
except Exception as e:
    if authorization_description_already_exists(e):
        print ("duplicate key description")
    else:
        raise e


duplicate key description

In [22]:
gh.authorization?

In [23]:
for auth in gh.iter_authorizations():
    print (auth.id, auth.name)


(19521699, u'for Project GITenberg')
(20426594, u'automatic releases for GITenberg/Adventures-of-Huckleberry-Finn_76')
(21004405, u'automatic releases for GITenberg/Don-Quixote_996')
(21006951, u'automatic releases for GITenberg/Dubliners_2814')
(21008241, u'automatic releases for GITenberg/Moby-Dick--Or-The-Whale_2701')
(21008348, u'automatic releases for GITenberg/Pride-and-Prejudice_1342')
(21035224, u'automatic releases for GITenberg/The-Adventures-of-Sherlock-Holmes_1661')
(21035544, u'automatic releases for GITenberg/The-Brothers-Karamazov_28054')
(21035701, u'automatic releases for GITenberg/The-Time-Machine_35')
(21035773, u'automatic releases for GITenberg/Frankenstein_84')
(21035786, u'automatic releases for GITenberg/Middlemarch_145')
(21035868, u'automatic releases for GITenberg/The-Call-of-the-Wild_215')
(21035904, u'automatic releases for GITenberg/The-Strange-Case-of-Dr.-Jekyll-and-Mr.-Hyde_42')
(21036020, u'automatic releases for GITenberg/A-Tale-of-Two-Cities_98')
(21036211, u'automatic releases for GITenberg/Crime-and-Punishment_2554')
(21036323, u'automatic releases for GITenberg/Dracula_345')
(21036340, u'automatic releases for GITenberg/Flatland--A-Romance-of-Many-Dimensions--Illustrated-_201')
(21036378, u'automatic releases for GITenberg/Household-Stories-by-the-Brothers-Grimm_19068')
(21036391, u'automatic releases for GITenberg/Heart-of-Darkness_219')
(21036410, u'automatic releases for GITenberg/A-Journey-into-the-Interior-of-the-Earth_3748')
(21036428, u'automatic releases for GITenberg/Jude-the-Obscure_153')
(21037542, u'automatic releases for GITenberg/King-Solomon-s-Mines_2166')
(21037558, u'automatic releases for GITenberg/Little-Women_514')
(21037574, u'automatic releases for GITenberg/Madame-Bovary_2413')
(21037592, u'automatic releases for GITenberg/The-Life-and-Adventures-of-Robinson-Crusoe_521')
(21037664, u'automatic releases for GITenberg/The-Jungle-Book_236')
(21037692, u'automatic releases for GITenberg/The-Red-Badge-of-Courage_73')
(21037703, u'automatic releases for GITenberg/The-Scarlet-Letter_33')
(21037830, u'automatic releases for GITenberg/The-War-of-the-Worlds_36')
(21037854, u'automatic releases for GITenberg/The-Wonderful-Wizard-of-Oz_55')
(21037867, u'automatic releases for GITenberg/This-Side-of-Paradise_805')
(21037940, u'automatic releases for GITenberg/Metamorphosis_5200')
(21038253, u'automatic releases for GITenberg/Anna-Karenina_1399')
(21038278, u'automatic releases for GITenberg/Gulliver-s-Travels_829')
(21038298, u'automatic releases for GITenberg/Les-Mis-rables_135')
(21038315, u'automatic releases for GITenberg/Swann-s-Way_7178')
(21038441, u'automatic releases for GITenberg/The-Count-of-Monte-Cristo_1184')
(21038459, u'automatic releases for GITenberg/The-Three-Musketeers_1257')
(21038482, u'automatic releases for GITenberg/Through-the-Looking-Glass_12')
(21038495, u'automatic releases for GITenberg/Twenty-Thousand-Leagues-under-the-Sea_164')
(21038528, u'automatic releases for GITenberg/War-and-Peace_2600')
(21038541, u'automatic releases for GITenberg/Winesburg-Ohio--A-Group-of-Tales-of-Ohio-Small-Town-Life_416')
(21038566, u'automatic releases for GITenberg/My-Antonia_242')
(21048772, u'automatic releases for GITenberg/Jane-Eyre_1260')
(21049887, u'automatic releases for GITenberg/Narrative-of-the-Life-of-Frederick-Douglass-an-American-Slave_23')
(21050097, u'automatic releases for GITenberg/The-Jungle_140')
(22164821, u'automatic releases for GITenberg/The-Awakening-and-Selected-Short-Stories_160')
(26999698, u'automatic releases for GITenberg/Divine-Comedy-Longfellow-s-Translation-Hell_1001')
(27837347, u'github3.py hello world')
(29095503, u'automatic releases for GITenberg/Notre-Dame-De-Paris_2610')
(29158296, u'automatic releases for GITenberg/Alice-s-Adventures-in-Wonderland-HTML-Edition_928')
(29251921, u'automatic releases for GITenberg/Bleak-House_1023')
(29501323, u'test token 2016.03.17')
(20409611, u'Travis CI')
(27837641, u'Gist')

releases

Goal: can we read off list of files from a given release

Example: https://github.com/GITenberg/Adventures-of-Huckleberry-Finn_76/releases/tag/0.0.50

Since we're currently using github3.py/repo.py at 0.9.3, which doesn't have Repository.release_from_tag , we borrow Repository.release_from_tag in v 1.0.0a4


In [ ]:
# adapted from 
# https://github.com/sigmavirus24/github3.py/blob/38de787e465bffc63da73d23dc51f50d86dc903d/github3/repos/repo.py#L1781-L1793

from github3.repos.release import Release

def release_from_tag(repo, tag_name):
    """Get a release by tag name.
    release_from_tag() returns a release with specified tag
    while release() returns a release with specified release id
    :param str tag_name: (required) name of tag
    :returns: :class:`Release <github3.repos.release.Release>`
    """
    url = repo._build_url('releases', 'tags', tag_name,
                          base_url=repo._api)
    json = repo._json(repo._get(url), 200)
    return Release(json, repo) if json else None

In [ ]:
from itertools import islice

# instantiate repo

(repo_owner, repo_name) = ('GITenberg', 'Adventures-of-Huckleberry-Finn_76')

repo = gh.repository(repo_owner, repo_name)
repo_anon = anon.repository(repo_owner, repo_name)

# can use either authenticated repo, or anonymous access repo_anon
# loop through releases

for (i, release) in enumerate(islice(repo_anon.iter_releases(),3)):
    print (i, release.id, release.tag_name)
    
print "\n"

for (i, release) in enumerate(islice(repo.iter_releases(),3)):
    print (i, release.id, release.tag_name)

In [ ]:
release = release_from_tag(repo, '0.0.50')
(release.id, release.tag_name)

for asset in release.iter_assets():
    # pick out some of the attributes of Asset
    # https://github.com/sigmavirus24/github3.py/blob/0.9.3/github3/repos/release.py#L145-L164
    
    print (asset.id, asset.name, asset.content_type, asset.download_url, asset.download_count)

consolidating code for releases


In [ ]:
# a function which given a repo and tag, returns which of epub, pdf, mobi are available

from github3 import (login, GitHub)
from github3.repos.release import Release

# a token can generated at https://github.com/settings/tokens -- no private access needed
from github_settings import GITHUB_PUBLIC_TOKEN

# release_from_tag adapted from 
# https://github.com/sigmavirus24/github3.py/blob/38de787e465bffc63da73d23dc51f50d86dc903d/github3/repos/repo.py#L1781-L1793

def release_from_tag(repo, tag_name):
    """Get a release by tag name.
    release_from_tag() returns a release with specified tag
    while release() returns a release with specified release id
    :param str tag_name: (required) name of tag
    :returns: :class:`Release <github3.repos.release.Release>`
    """
    url = repo._build_url('releases', 'tags', tag_name,
                          base_url=repo._api)
    json = repo._json(repo._get(url), 200)
    return Release(json, repo) if json else None


def ebooks_in_github_release(repo_owner, repo_name, tag, token=None):
    """
    returns a list of (book_type, book_name) for a given GitHub release (specified by 
    owner, name, tag).  token is a GitHub authorization token -- useful for accessing
    higher rate limit in the GitHub API
    """
    
    # epub, mobi, pdf, html, text
    # map mimetype to file extension
    EBOOK_FORMATS = {'application/epub+zip':'epub',
                 'application/x-mobipocket-ebook': 'mobi',
                 'application/pdf': 'pdf',
                 'text/plain': 'text',
                 'text/html':'html'}
    
    if token is not None:
        gh = login(token=token)
    else:
        # anonymous access
        gh = GitHub()
        
    repo = gh.repository(repo_owner, repo_name)
    release = release_from_tag(repo, tag)
    
    return [(EBOOK_FORMATS.get(asset.content_type), asset.name) 
            for asset in release.iter_assets() 
            if EBOOK_FORMATS.get(asset.content_type) is not None]
 

# test out ebooks_in_github_release
(repo_owner, repo_name) = ('GITenberg', 'Adventures-of-Huckleberry-Finn_76')
ebooks_in_github_release(repo_owner, repo_name, '0.0.50', token=GITHUB_PUBLIC_TOKEN)

In [ ]:
# https://github.com/GITenberg/Adventures-of-Huckleberry-Finn_76/raw/master/metadata.yaml

from urlparse import urlparse

url = "https://github.com/GITenberg/Adventures-of-Huckleberry-Finn_76/raw/master/metadata.yaml"
url_path = urlparse(url).path.split("/")
(repo_owner, repo_name) = (url_path[1], url_path[2])
(repo_owner, repo_name)

looking for .asciidoc files in a given repo


In [ ]:
master_branch = repo.branch('master')
master_branch.commit.sha

master_tree = repo.tree(master_branch.commit.sha)
for hash_ in master_tree.tree:
    print (hash_.path, hash_.type)

In [ ]:
def asciidoc_in_repo_root(repo, branch ='master'):
    """return list of asciidocs in the root of repo"""
    repo_branch = repo.branch(branch)
    tree = repo.tree(repo_branch.commit.sha)
    return [hash_.path 
            for hash_ in tree.tree
            if hash_.path.endswith('.asciidoc')]


asciidoc_in_repo_root(repo)

In [ ]:
try:
    repo_data = {
    'name': 'TEST REPO',
    'description': 'can I create a repo using github3.py?',
    'homepage': '',
    'private': False,
    'has_issues': True,
    'has_wiki': True
    }

    if repo_data.get('name'):
        r = gh.create_repo(repo_data.pop('name'), **repo_data)

    if r:
        print("Created {0} successfully.".format(r.name))
        
except Exception as e:
    
    print (e)

Now that I have an empty repo, how to do the equivalent of the following?

echo "# TEST-REPO" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin https://github.com/rdhyee-GITenberg/TEST-REPO.git
git push -u origin master

In [ ]:
# once we have a repo, we can instantiate it
repo = gh.repository('rdhyee-GITenberg', 'TEST-REPO')
repo

challenges of using the GitHub API to do many things / simplification

python - How to create a commit and push into repo with GitHub API v3? - Stack Overflow

But there is a File CRUD API: repo.create_file() in github3.py/repo.py at 0.9.3


In [ ]:
# now have r -- a repo

# let's read off key parameters
# latest commit

# borrow code from https://github.com/sigmavirus24/github3.py/blob/d3552f77778c5f570cdd7efa5c80c0b88b8d9ad7/tests/integration/test_repos_repo.py#L239

data = {
    'path': 'README.md',
    'message': 'first pass',
    'content': b'Hello world',
    'branch': 'master'
}

created_file = repo.create_file(**data)
created_file

In [ ]:
# create a tag?

import arrow

commit = created_file['commit']
commit.sha

user = gh.user()

tag_data =  {
    'tag': '0.0.1',
    'message': 'tag 0.0.1',
    'sha': commit.sha,
    'obj_type': 'commit',
    'tagger': {
        'name': user.name,
        'email': user.email,
        'date': arrow.utcnow().isoformat()
    },
    'lightweight': False
}

tag = repo.create_tag(**tag_data)
tag

In [ ]:
# get list of current tags

list(repo.iter_tags())

In [ ]:
# directory_contents (in v 1+)
# [github3.py/test_repos_repo.py at d3552f77778c5f570cdd7efa5c80c0b88b8d9ad7 · sigmavirus24/github3.py](https://github.com/sigmavirus24/github3.py/blob/d3552f77778c5f570cdd7efa5c80c0b88b8d9ad7/tests/integration/test_repos_repo.py#L475-L486)

# 
repo.contents("")

In [ ]:
# grab content
content = repo.contents("README.md", ref='master')

In [ ]:
new_content = content.decoded.decode('utf-8') + u"\n" + u"line 2"

data = {
    'message': 'second pass',
    'content': new_content.encode('utf-8'),
}

commit = content.update(**data)
commit

In [ ]:
# tag again

tag_data =  {
    'tag': '0.0.2',
    'message': 'tag 0.0.2',
    'sha': commit.sha,
    'obj_type': 'commit',
    'tagger': {
        'name': user.name,
        'email': user.email,
        'date': arrow.utcnow().isoformat()
    },
    'lightweight': False
}

tag = repo.create_tag(**tag_data)
tag

In [ ]: