##// END OF EJS Templates
fix typo in gh_api.get_pulls_list
MinRK -
Show More
@@ -1,276 +1,276 b''
1 1 """Functions for Github API requests."""
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 import re
11 11 import sys
12 12
13 13 import requests
14 14 import getpass
15 15 import json
16 16
17 17 try:
18 18 import requests_cache
19 19 except ImportError:
20 20 print("no cache")
21 21 else:
22 22 requests_cache.install_cache("gh_api")
23 23
24 24 # Keyring stores passwords by a 'username', but we're not storing a username and
25 25 # password
26 26 fake_username = 'ipython_tools'
27 27
28 28 class Obj(dict):
29 29 """Dictionary with attribute access to names."""
30 30 def __getattr__(self, name):
31 31 try:
32 32 return self[name]
33 33 except KeyError:
34 34 raise AttributeError(name)
35 35
36 36 def __setattr__(self, name, val):
37 37 self[name] = val
38 38
39 39 token = None
40 40 def get_auth_token():
41 41 global token
42 42
43 43 if token is not None:
44 44 return token
45 45
46 46 import keyring
47 47 token = keyring.get_password('github', fake_username)
48 48 if token is not None:
49 49 return token
50 50
51 51 print("Please enter your github username and password. These are not "
52 52 "stored, only used to get an oAuth token. You can revoke this at "
53 53 "any time on Github.")
54 54 user = input("Username: ")
55 55 pw = getpass.getpass("Password: ")
56 56
57 57 auth_request = {
58 58 "scopes": [
59 59 "public_repo",
60 60 "gist"
61 61 ],
62 62 "note": "IPython tools",
63 63 "note_url": "https://github.com/ipython/ipython/tree/master/tools",
64 64 }
65 65 response = requests.post('https://api.github.com/authorizations',
66 66 auth=(user, pw), data=json.dumps(auth_request))
67 67 response.raise_for_status()
68 68 token = json.loads(response.text)['token']
69 69 keyring.set_password('github', fake_username, token)
70 70 return token
71 71
72 72 def make_auth_header():
73 73 return {'Authorization': 'token ' + get_auth_token()}
74 74
75 75 def post_issue_comment(project, num, body):
76 76 url = 'https://api.github.com/repos/{project}/issues/{num}/comments'.format(project=project, num=num)
77 77 payload = json.dumps({'body': body})
78 78 requests.post(url, data=payload, headers=make_auth_header())
79 79
80 80 def post_gist(content, description='', filename='file', auth=False):
81 81 """Post some text to a Gist, and return the URL."""
82 82 post_data = json.dumps({
83 83 "description": description,
84 84 "public": True,
85 85 "files": {
86 86 filename: {
87 87 "content": content
88 88 }
89 89 }
90 90 }).encode('utf-8')
91 91
92 92 headers = make_auth_header() if auth else {}
93 93 response = requests.post("https://api.github.com/gists", data=post_data, headers=headers)
94 94 response.raise_for_status()
95 95 response_data = json.loads(response.text)
96 96 return response_data['html_url']
97 97
98 98 def get_pull_request(project, num, auth=False):
99 99 """get pull request info by number
100 100 """
101 101 url = "https://api.github.com/repos/{project}/pulls/{num}".format(project=project, num=num)
102 102 if auth:
103 103 header = make_auth_header()
104 104 else:
105 105 header = None
106 106 response = requests.get(url, headers=header)
107 107 response.raise_for_status()
108 108 return json.loads(response.text, object_hook=Obj)
109 109
110 110 def get_pull_request_files(project, num, auth=False):
111 111 """get list of files in a pull request"""
112 112 url = "https://api.github.com/repos/{project}/pulls/{num}/files".format(project=project, num=num)
113 113 if auth:
114 114 header = make_auth_header()
115 115 else:
116 116 header = None
117 117 return get_paged_request(url, headers=header)
118 118
119 119 element_pat = re.compile(r'<(.+?)>')
120 120 rel_pat = re.compile(r'rel=[\'"](\w+)[\'"]')
121 121
122 122 def get_paged_request(url, headers=None, **params):
123 123 """get a full list, handling APIv3's paging"""
124 124 results = []
125 125 params.setdefault("per_page", 100)
126 126 while True:
127 127 print("fetching %s with %s" % (url, params), file=sys.stderr)
128 128 response = requests.get(url, headers=headers, params=params)
129 129 response.raise_for_status()
130 130 results.extend(response.json())
131 131 if 'next' in response.links:
132 132 url = response.links['next']['url']
133 133 else:
134 134 break
135 135 return results
136 136
137 137 def get_pulls_list(project, auth=False, **params):
138 138 """get pull request list"""
139 139 params.setdefault("state", "closed")
140 140 url = "https://api.github.com/repos/{project}/pulls".format(project=project)
141 141 if auth:
142 142 headers = make_auth_header()
143 143 else:
144 144 headers = None
145 pages = get_paged_request(url, headers=headers, params=params)
145 pages = get_paged_request(url, headers=headers, **params)
146 146 return pages
147 147
148 148 def get_issues_list(project, auth=False, **params):
149 149 """get issues list"""
150 150 params.setdefault("state", "closed")
151 151 url = "https://api.github.com/repos/{project}/issues".format(project=project)
152 152 if auth:
153 153 headers = make_auth_header()
154 154 else:
155 155 headers = None
156 156 pages = get_paged_request(url, headers=headers, **params)
157 157 return pages
158 158
159 159 def get_milestones(project, auth=False, **params):
160 160 url = "https://api.github.com/repos/{project}/milestones".format(project=project)
161 161 if auth:
162 162 headers = make_auth_header()
163 163 else:
164 164 headers = None
165 165 milestones = get_paged_request(url, headers=headers, **params)
166 166 return milestones
167 167
168 168 def get_milestone_id(project, milestone, auth=False, **params):
169 169 milestones = get_milestones(project, auth=auth, **params)
170 170 for mstone in milestones:
171 171 if mstone['title'] == milestone:
172 172 return mstone['number']
173 173 else:
174 174 raise ValueError("milestone %s not found" % milestone)
175 175
176 176 def is_pull_request(issue):
177 177 """Return True if the given issue is a pull request."""
178 178 return bool(issue.get('pull_request', {}).get('html_url', None))
179 179
180 180 # encode_multipart_formdata is from urllib3.filepost
181 181 # The only change is to iter_fields, to enforce S3's required key ordering
182 182
183 183 def iter_fields(fields):
184 184 fields = fields.copy()
185 185 for key in ('key', 'acl', 'Filename', 'success_action_status', 'AWSAccessKeyId',
186 186 'Policy', 'Signature', 'Content-Type', 'file'):
187 187 yield (key, fields.pop(key))
188 188 for (k,v) in fields.items():
189 189 yield k,v
190 190
191 191 def encode_multipart_formdata(fields, boundary=None):
192 192 """
193 193 Encode a dictionary of ``fields`` using the multipart/form-data mime format.
194 194
195 195 :param fields:
196 196 Dictionary of fields or list of (key, value) field tuples. The key is
197 197 treated as the field name, and the value as the body of the form-data
198 198 bytes. If the value is a tuple of two elements, then the first element
199 199 is treated as the filename of the form-data section.
200 200
201 201 Field names and filenames must be unicode.
202 202
203 203 :param boundary:
204 204 If not specified, then a random boundary will be generated using
205 205 :func:`mimetools.choose_boundary`.
206 206 """
207 207 # copy requests imports in here:
208 208 from io import BytesIO
209 209 from requests.packages.urllib3.filepost import (
210 210 choose_boundary, six, writer, b, get_content_type
211 211 )
212 212 body = BytesIO()
213 213 if boundary is None:
214 214 boundary = choose_boundary()
215 215
216 216 for fieldname, value in iter_fields(fields):
217 217 body.write(b('--%s\r\n' % (boundary)))
218 218
219 219 if isinstance(value, tuple):
220 220 filename, data = value
221 221 writer(body).write('Content-Disposition: form-data; name="%s"; '
222 222 'filename="%s"\r\n' % (fieldname, filename))
223 223 body.write(b('Content-Type: %s\r\n\r\n' %
224 224 (get_content_type(filename))))
225 225 else:
226 226 data = value
227 227 writer(body).write('Content-Disposition: form-data; name="%s"\r\n'
228 228 % (fieldname))
229 229 body.write(b'Content-Type: text/plain\r\n\r\n')
230 230
231 231 if isinstance(data, int):
232 232 data = str(data) # Backwards compatibility
233 233 if isinstance(data, six.text_type):
234 234 writer(body).write(data)
235 235 else:
236 236 body.write(data)
237 237
238 238 body.write(b'\r\n')
239 239
240 240 body.write(b('--%s--\r\n' % (boundary)))
241 241
242 242 content_type = b('multipart/form-data; boundary=%s' % boundary)
243 243
244 244 return body.getvalue(), content_type
245 245
246 246
247 247 def post_download(project, filename, name=None, description=""):
248 248 """Upload a file to the GitHub downloads area"""
249 249 if name is None:
250 250 name = os.path.basename(filename)
251 251 with open(filename, 'rb') as f:
252 252 filedata = f.read()
253 253
254 254 url = "https://api.github.com/repos/{project}/downloads".format(project=project)
255 255
256 256 payload = json.dumps(dict(name=name, size=len(filedata),
257 257 description=description))
258 258 response = requests.post(url, data=payload, headers=make_auth_header())
259 259 response.raise_for_status()
260 260 reply = json.loads(response.content)
261 261 s3_url = reply['s3_url']
262 262
263 263 fields = dict(
264 264 key=reply['path'],
265 265 acl=reply['acl'],
266 266 success_action_status=201,
267 267 Filename=reply['name'],
268 268 AWSAccessKeyId=reply['accesskeyid'],
269 269 Policy=reply['policy'],
270 270 Signature=reply['signature'],
271 271 file=(reply['name'], filedata),
272 272 )
273 273 fields['Content-Type'] = reply['mime_type']
274 274 data, content_type = encode_multipart_formdata(fields)
275 275 s3r = requests.post(s3_url, data=data, headers={'Content-Type': content_type})
276 276 return s3r
General Comments 0
You need to be logged in to leave comments. Login now