gh_api.py
141 lines
| 4.1 KiB
| text/x-python
|
PythonLexer
/ tools / gh_api.py
MinRK
|
r11584 | """Functions for Github API requests.""" | ||
Thomas Kluyver
|
r6694 | |||
try: | ||||
input = raw_input | ||||
except NameError: | ||||
pass | ||||
MinRK
|
r11583 | import re | ||
import sys | ||||
MinRK
|
r7761 | |||
Thomas Kluyver
|
r6694 | import requests | ||
import getpass | ||||
import json | ||||
MinRK
|
r11584 | try: | ||
import requests_cache | ||||
except ImportError: | ||||
Matthias Bussonnier
|
r22016 | print("cache not available, install `requests_cache` for caching.", file=sys.stderr) | ||
MinRK
|
r11584 | else: | ||
MinRK
|
r16674 | requests_cache.install_cache("gh_api", expire_after=3600) | ||
MinRK
|
r11584 | |||
Thomas Kluyver
|
r6694 | # Keyring stores passwords by a 'username', but we're not storing a username and | ||
# password | ||||
Matthias Bussonnier
|
r24938 | import socket | ||
fake_username = 'ipython_tools_%s' % socket.gethostname().replace('.','_').replace('-','_') | ||||
Thomas Kluyver
|
r6694 | |||
Thomas Kluyver
|
r7863 | class Obj(dict): | ||
"""Dictionary with attribute access to names.""" | ||||
def __getattr__(self, name): | ||||
try: | ||||
return self[name] | ||||
Ram Rachum
|
r25833 | except KeyError as e: | ||
raise AttributeError(name) from e | ||||
Skipper Seabold
|
r13169 | |||
Thomas Kluyver
|
r7863 | def __setattr__(self, name, val): | ||
self[name] = val | ||||
Thomas Kluyver
|
r6711 | token = None | ||
Thomas Kluyver
|
r6694 | def get_auth_token(): | ||
Thomas Kluyver
|
r6711 | global token | ||
Skipper Seabold
|
r13169 | |||
Thomas Kluyver
|
r6711 | if token is not None: | ||
return token | ||||
Skipper Seabold
|
r13169 | |||
Thomas Kluyver
|
r6711 | import keyring | ||
Thomas Kluyver
|
r6694 | token = keyring.get_password('github', fake_username) | ||
if token is not None: | ||||
return token | ||||
Skipper Seabold
|
r13169 | |||
Matthias Bussonnier
|
r27410 | print( | ||
Andrew Kreimer
|
r28881 | "Get a token from https://github.com/settings/tokens with public repo and gist." | ||
Matthias Bussonnier
|
r27410 | ) | ||
token = getpass.getpass("Token: ", stream=sys.stderr) | ||||
Thomas Kluyver
|
r6694 | keyring.set_password('github', fake_username, token) | ||
return token | ||||
Thomas Kluyver
|
r6711 | def make_auth_header(): | ||
return {'Authorization': 'token ' + get_auth_token()} | ||||
Skipper Seabold
|
r13169 | |||
MinRK
|
r11583 | def get_pull_request(project, num, auth=False): | ||
Matthias BUSSONNIER
|
r7448 | """get pull request info by number | ||
""" | ||||
Thomas Kluyver
|
r7866 | url = "https://api.github.com/repos/{project}/pulls/{num}".format(project=project, num=num) | ||
MinRK
|
r11583 | if auth: | ||
header = make_auth_header() | ||||
else: | ||||
header = None | ||||
Min RK
|
r20266 | print("fetching %s" % url, file=sys.stderr) | ||
MinRK
|
r11583 | response = requests.get(url, headers=header) | ||
Thomas Kluyver
|
r6711 | response.raise_for_status() | ||
Thomas Kluyver
|
r7863 | return json.loads(response.text, object_hook=Obj) | ||
Matthias BUSSONNIER
|
r7254 | |||
MinRK
|
r11583 | element_pat = re.compile(r'<(.+?)>') | ||
rel_pat = re.compile(r'rel=[\'"](\w+)[\'"]') | ||||
MinRK
|
r12423 | def get_paged_request(url, headers=None, **params): | ||
MinRK
|
r11583 | """get a full list, handling APIv3's paging""" | ||
results = [] | ||||
MinRK
|
r12423 | params.setdefault("per_page", 100) | ||
MinRK
|
r11583 | while True: | ||
MinRK
|
r16674 | if '?' in url: | ||
params = None | ||||
print("fetching %s" % url, file=sys.stderr) | ||||
else: | ||||
print("fetching %s with %s" % (url, params), file=sys.stderr) | ||||
MinRK
|
r12423 | response = requests.get(url, headers=headers, params=params) | ||
MinRK
|
r11583 | response.raise_for_status() | ||
results.extend(response.json()) | ||||
if 'next' in response.links: | ||||
url = response.links['next']['url'] | ||||
else: | ||||
break | ||||
return results | ||||
MinRK
|
r12423 | def get_issues_list(project, auth=False, **params): | ||
"""get issues list""" | ||||
params.setdefault("state", "closed") | ||||
url = "https://api.github.com/repos/{project}/issues".format(project=project) | ||||
MinRK
|
r11583 | if auth: | ||
headers = make_auth_header() | ||||
else: | ||||
headers = None | ||||
MinRK
|
r12423 | pages = get_paged_request(url, headers=headers, **params) | ||
MinRK
|
r11583 | return pages | ||
MinRK
|
r7761 | |||
Skipper Seabold
|
r13169 | def get_milestones(project, auth=False, **params): | ||
Min RK
|
r20436 | params.setdefault('state', 'all') | ||
Skipper Seabold
|
r13169 | url = "https://api.github.com/repos/{project}/milestones".format(project=project) | ||
if auth: | ||||
headers = make_auth_header() | ||||
else: | ||||
headers = None | ||||
Skipper Seabold
|
r13247 | milestones = get_paged_request(url, headers=headers, **params) | ||
return milestones | ||||
Skipper Seabold
|
r13169 | |||
def get_milestone_id(project, milestone, auth=False, **params): | ||||
Skipper Seabold
|
r13247 | milestones = get_milestones(project, auth=auth, **params) | ||
for mstone in milestones: | ||||
if mstone['title'] == milestone: | ||||
return mstone['number'] | ||||
Skipper Seabold
|
r13169 | else: | ||
raise ValueError("milestone %s not found" % milestone) | ||||
MinRK
|
r12423 | def is_pull_request(issue): | ||
"""Return True if the given issue is a pull request.""" | ||||
return bool(issue.get('pull_request', {}).get('html_url', None)) | ||||
MinRK
|
r16790 | def get_authors(pr): | ||
print("getting authors for #%i" % pr['number'], file=sys.stderr) | ||||
h = make_auth_header() | ||||
r = requests.get(pr['commits_url'], headers=h) | ||||
r.raise_for_status() | ||||
commits = r.json() | ||||
authors = [] | ||||
for commit in commits: | ||||
author = commit['commit']['author'] | ||||
authors.append("%s <%s>" % (author['name'], author['email'])) | ||||
return authors | ||||