##// END OF EJS Templates
Drop support for deceased Github API v2
Thomas Kluyver -
Show More
@@ -1,215 +1,201
1 1 """Functions for Github authorisation."""
2 2 from __future__ import print_function
3 3
4 4 try:
5 5 input = raw_input
6 6 except NameError:
7 7 pass
8 8
9 9 import os
10 10
11 11 import requests
12 12 import getpass
13 13 import json
14 14
15 15 # Keyring stores passwords by a 'username', but we're not storing a username and
16 16 # password
17 17 fake_username = 'ipython_tools'
18 18
19 19 class Obj(dict):
20 20 """Dictionary with attribute access to names."""
21 21 def __getattr__(self, name):
22 22 try:
23 23 return self[name]
24 24 except KeyError:
25 25 raise AttributeError(name)
26 26
27 27 def __setattr__(self, name, val):
28 28 self[name] = val
29 29
30 30 token = None
31 31 def get_auth_token():
32 32 global token
33 33
34 34 if token is not None:
35 35 return token
36 36
37 37 import keyring
38 38 token = keyring.get_password('github', fake_username)
39 39 if token is not None:
40 40 return token
41 41
42 42 print("Please enter your github username and password. These are not "
43 43 "stored, only used to get an oAuth token. You can revoke this at "
44 44 "any time on Github.")
45 45 user = input("Username: ")
46 46 pw = getpass.getpass("Password: ")
47 47
48 48 auth_request = {
49 49 "scopes": [
50 50 "public_repo",
51 51 "gist"
52 52 ],
53 53 "note": "IPython tools",
54 54 "note_url": "https://github.com/ipython/ipython/tree/master/tools",
55 55 }
56 56 response = requests.post('https://api.github.com/authorizations',
57 57 auth=(user, pw), data=json.dumps(auth_request))
58 58 response.raise_for_status()
59 59 token = json.loads(response.text)['token']
60 60 keyring.set_password('github', fake_username, token)
61 61 return token
62 62
63 63 def make_auth_header():
64 64 return {'Authorization': 'token ' + get_auth_token()}
65 65
66 66 def post_issue_comment(project, num, body):
67 67 url = 'https://api.github.com/repos/{project}/issues/{num}/comments'.format(project=project, num=num)
68 68 payload = json.dumps({'body': body})
69 69 r = requests.post(url, data=payload, headers=make_auth_header())
70 70
71 71 def post_gist(content, description='', filename='file', auth=False):
72 72 """Post some text to a Gist, and return the URL."""
73 73 post_data = json.dumps({
74 74 "description": description,
75 75 "public": True,
76 76 "files": {
77 77 filename: {
78 78 "content": content
79 79 }
80 80 }
81 81 }).encode('utf-8')
82 82
83 83 headers = make_auth_header() if auth else {}
84 84 response = requests.post("https://api.github.com/gists", data=post_data, headers=headers)
85 85 response.raise_for_status()
86 86 response_data = json.loads(response.text)
87 87 return response_data['html_url']
88 88
89 def get_pull_request(project, num, github_api=3):
89 def get_pull_request(project, num):
90 90 """get pull request info by number
91
92 github_api : version of github api to use
93 91 """
94 if github_api==2 :
95 url = "http://github.com/api/v2/json/pulls/{project}/{num}".format(project=project, num=num)
96 elif github_api == 3:
97 url = "https://api.github.com/repos/{project}/pulls/{num}".format(project=project, num=num)
92 url = "https://api.github.com/repos/{project}/pulls/{num}".format(project=project, num=num)
98 93 response = requests.get(url)
99 94 response.raise_for_status()
100 if github_api == 2 :
101 return json.loads(response.text)['pull']
102 95 return json.loads(response.text, object_hook=Obj)
103 96
104 def get_pulls_list(project, github_api=3):
97 def get_pulls_list(project):
105 98 """get pull request list
106
107 github_api : version of github api to use
108 99 """
109 if github_api == 3 :
110 url = "https://api.github.com/repos/{project}/pulls".format(project=project)
111 else :
112 url = "http://github.com/api/v2/json/pulls/{project}".format(project=project)
100 url = "https://api.github.com/repos/{project}/pulls".format(project=project)
113 101 response = requests.get(url)
114 102 response.raise_for_status()
115 if github_api == 2 :
116 return json.loads(response.text)['pulls']
117 103 return json.loads(response.text)
118 104
119 105 # encode_multipart_formdata is from urllib3.filepost
120 106 # The only change is to iter_fields, to enforce S3's required key ordering
121 107
122 108 def iter_fields(fields):
123 109 fields = fields.copy()
124 110 for key in ('key', 'acl', 'Filename', 'success_action_status', 'AWSAccessKeyId',
125 111 'Policy', 'Signature', 'Content-Type', 'file'):
126 112 yield (key, fields.pop(key))
127 113 for (k,v) in fields.items():
128 114 yield k,v
129 115
130 116 def encode_multipart_formdata(fields, boundary=None):
131 117 """
132 118 Encode a dictionary of ``fields`` using the multipart/form-data mime format.
133 119
134 120 :param fields:
135 121 Dictionary of fields or list of (key, value) field tuples. The key is
136 122 treated as the field name, and the value as the body of the form-data
137 123 bytes. If the value is a tuple of two elements, then the first element
138 124 is treated as the filename of the form-data section.
139 125
140 126 Field names and filenames must be unicode.
141 127
142 128 :param boundary:
143 129 If not specified, then a random boundary will be generated using
144 130 :func:`mimetools.choose_boundary`.
145 131 """
146 132 # copy requests imports in here:
147 133 from io import BytesIO
148 134 from requests.packages.urllib3.filepost import (
149 135 choose_boundary, six, writer, b, get_content_type
150 136 )
151 137 body = BytesIO()
152 138 if boundary is None:
153 139 boundary = choose_boundary()
154 140
155 141 for fieldname, value in iter_fields(fields):
156 142 body.write(b('--%s\r\n' % (boundary)))
157 143
158 144 if isinstance(value, tuple):
159 145 filename, data = value
160 146 writer(body).write('Content-Disposition: form-data; name="%s"; '
161 147 'filename="%s"\r\n' % (fieldname, filename))
162 148 body.write(b('Content-Type: %s\r\n\r\n' %
163 149 (get_content_type(filename))))
164 150 else:
165 151 data = value
166 152 writer(body).write('Content-Disposition: form-data; name="%s"\r\n'
167 153 % (fieldname))
168 154 body.write(b'Content-Type: text/plain\r\n\r\n')
169 155
170 156 if isinstance(data, int):
171 157 data = str(data) # Backwards compatibility
172 158 if isinstance(data, six.text_type):
173 159 writer(body).write(data)
174 160 else:
175 161 body.write(data)
176 162
177 163 body.write(b'\r\n')
178 164
179 165 body.write(b('--%s--\r\n' % (boundary)))
180 166
181 167 content_type = b('multipart/form-data; boundary=%s' % boundary)
182 168
183 169 return body.getvalue(), content_type
184 170
185 171
186 172 def post_download(project, filename, name=None, description=""):
187 173 """Upload a file to the GitHub downloads area"""
188 174 if name is None:
189 175 name = os.path.basename(filename)
190 176 with open(filename, 'rb') as f:
191 177 filedata = f.read()
192 178
193 179 url = "https://api.github.com/repos/{project}/downloads".format(project=project)
194 180
195 181 payload = json.dumps(dict(name=name, size=len(filedata),
196 182 description=description))
197 183 response = requests.post(url, data=payload, headers=make_auth_header())
198 184 response.raise_for_status()
199 185 reply = json.loads(response.content)
200 186 s3_url = reply['s3_url']
201 187
202 188 fields = dict(
203 189 key=reply['path'],
204 190 acl=reply['acl'],
205 191 success_action_status=201,
206 192 Filename=reply['name'],
207 193 AWSAccessKeyId=reply['accesskeyid'],
208 194 Policy=reply['policy'],
209 195 Signature=reply['signature'],
210 196 file=(reply['name'], filedata),
211 197 )
212 198 fields['Content-Type'] = reply['mime_type']
213 199 data, content_type = encode_multipart_formdata(fields)
214 200 s3r = requests.post(s3_url, data=data, headers={'Content-Type': content_type})
215 201 return s3r
General Comments 0
You need to be logged in to leave comments. Login now