Skip to content

Commit 5244964

Browse files
THEHighlandernhomar
authored andcommitted
Add ability to access github Release Asset API. (#525)
* Add ability to access github Release Asset API. See: https://developer.github.com/v3/repos/releases
1 parent ebe7277 commit 5244964

14 files changed

+733
-8
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@
3535
/developer.github.com/
3636
/gh-pages/
3737
/doc/doctrees/
38+
.vscode*

github/GitRelease.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
# #
2323
# ##############################################################################
2424

25+
from os.path import basename
2526
import github.GithubObject
2627
import github.GitAuthor
28+
import github.GitReleaseAsset
2729

2830

2931
class GitRelease(github.GithubObject.CompletableGithubObject):
@@ -34,6 +36,14 @@ class GitRelease(github.GithubObject.CompletableGithubObject):
3436
def __repr__(self):
3537
return self.get__repr__({"title": self._title.value})
3638

39+
@property
40+
def id(self):
41+
"""
42+
:type: integer
43+
"""
44+
self._completeIfNotSet(self._id)
45+
return self._id.value
46+
3747
@property
3848
def body(self):
3949
"""
@@ -116,7 +126,36 @@ def update_release(self, name, message, draft=False, prerelease=False):
116126
)
117127
return github.GitRelease.GitRelease(self._requester, headers, data, completed=True)
118128

129+
def upload_asset(self, path, label="", content_type=""):
130+
assert isinstance(path, (str, unicode)), path
131+
assert isinstance(label, (str, unicode)), label
132+
133+
post_parameters = {
134+
"name": basename(path),
135+
"label": label
136+
}
137+
headers = {}
138+
if len(content_type) > 0:
139+
headers["Content-Type"] = content_type
140+
resp_headers, data = self._requester.requestBlobAndCheck(
141+
"POST",
142+
self.upload_url.split("{?")[0],
143+
parameters=post_parameters,
144+
headers=headers,
145+
input=path
146+
)
147+
return github.GitReleaseAsset.GitReleaseAsset(self._requester, resp_headers, data, completed=True)
148+
149+
def get_assets(self):
150+
return github.PaginatedList.PaginatedList(
151+
github.GitReleaseAsset.GitReleaseAsset,
152+
self._requester,
153+
self.url + "/assets",
154+
None
155+
)
156+
119157
def _initAttributes(self):
158+
self._id = github.GithubObject.NotSet
120159
self._body = github.GithubObject.NotSet
121160
self._title = github.GithubObject.NotSet
122161
self._tag_name = github.GithubObject.NotSet
@@ -126,6 +165,8 @@ def _initAttributes(self):
126165
self._html_url = github.GithubObject.NotSet
127166

128167
def _useAttributes(self, attributes):
168+
if "id" in attributes:
169+
self._id = self._makeIntAttribute(attributes["id"])
129170
if "body" in attributes:
130171
self._body = self._makeStringAttribute(attributes["body"])
131172
if "name" in attributes:

github/GitReleaseAsset.py

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# ########################## Copyrights and license ############################
4+
# #
5+
# Copyright 2012 Vincent Jacques <[email protected]> #
6+
# Copyright 2012 Zearin <[email protected]> #
7+
# Copyright 2013 AKFish <[email protected]> #
8+
# Copyright 2013 Vincent Jacques <[email protected]> # #
9+
# Copyright 2017 Chris McBride <[email protected]> #
10+
# #
11+
# This file is part of PyGithub. #
12+
# http://pygithub.github.io/PyGithub/v1/index.html #
13+
# #
14+
# PyGithub is free software: you can redistribute it and/or modify it under #
15+
# the terms of the GNU Lesser General Public License as published by the Free #
16+
# Software Foundation, either version 3 of the License, or (at your option) #
17+
# any later version. #
18+
# #
19+
# PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY #
20+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
21+
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more #
22+
# details. #
23+
# #
24+
# You should have received a copy of the GNU Lesser General Public License #
25+
# along with PyGithub. If not, see <http://www.gnu.org/licenses/>. #
26+
# #
27+
# ##############################################################################
28+
29+
import github.GithubObject
30+
31+
32+
class GitReleaseAsset(github.GithubObject.CompletableGithubObject):
33+
"""
34+
This class represents GitReleaseAssets as returned by
35+
GET /repos/:owner/:repo/releases/assets/:id
36+
See:
37+
https://developer.github.com/v3/repos/releases/#get-a-single-release-asset
38+
"""
39+
40+
def __repr__(self):
41+
return self.get__repr__({"url": self.url})
42+
43+
@property
44+
def url(self):
45+
"""
46+
:type: string
47+
"""
48+
self._completeIfNotSet(self._url)
49+
return self._url.value
50+
51+
@property
52+
def id(self):
53+
"""
54+
:type: integer
55+
"""
56+
self._completeIfNotSet(self._id)
57+
return self._id.value
58+
59+
@property
60+
def name(self):
61+
"""
62+
:type: string
63+
"""
64+
self._completeIfNotSet(self._name)
65+
return self._name.value
66+
67+
@name.setter
68+
def name(self, value):
69+
"""
70+
:type: string
71+
"""
72+
self._completeIfNotSet(self._name)
73+
self._name.value = value
74+
75+
@property
76+
def label(self):
77+
"""
78+
:type: string
79+
"""
80+
self._completeIfNotSet(self._label)
81+
return self._label.value
82+
83+
@label.setter
84+
def label(self, value):
85+
"""
86+
:type: string
87+
"""
88+
self._completeIfNotSet(self._label)
89+
self._label.value = value
90+
91+
@property
92+
def content_type(self):
93+
"""
94+
:type: string
95+
"""
96+
self._completeIfNotSet(self._content_type)
97+
return self._content_type.value
98+
99+
@property
100+
def state(self):
101+
"""
102+
:type: string
103+
"""
104+
self._completeIfNotSet(self._state)
105+
return self._state.value
106+
107+
@property
108+
def size(self):
109+
"""
110+
:type: integer
111+
"""
112+
self._completeIfNotSet(self._size)
113+
return self._size.value
114+
115+
@property
116+
def download_count(self):
117+
"""
118+
:type: integer
119+
"""
120+
self._completeIfNotSet(self._download_count)
121+
return self._download_count.value
122+
123+
@property
124+
def created_at(self):
125+
"""
126+
:type: datetime
127+
"""
128+
self._completeIfNotSet(self._created_at)
129+
return self._created_at.value
130+
131+
@property
132+
def updated_at(self):
133+
"""
134+
:type: datetime
135+
"""
136+
self._completeIfNotSet(self._updated_at)
137+
return self._updated_at.value
138+
139+
@property
140+
def browser_download_url(self):
141+
"""
142+
:type: string
143+
"""
144+
self._completeIfNotSet(self._browser_download_url)
145+
return self._browser_download_url.value
146+
147+
@property
148+
def uploader(self):
149+
"""
150+
:type: github.NamedUser.NamedUser
151+
"""
152+
self._completeIfNotSet(self._uploader)
153+
return self._uploader.value
154+
155+
def delete_asset(self):
156+
"""
157+
Delete asset from the release.
158+
:rtype: bool
159+
"""
160+
headers, data = self._requester.requestJsonAndCheck(
161+
"DELETE",
162+
self.url
163+
)
164+
return True
165+
166+
def update_asset(self, name, label=""):
167+
"""
168+
Update asset metadata.
169+
:rtype: github.GitReleaseAsset.GitReleaseAsset
170+
"""
171+
assert isinstance(name, (str, unicode)), name
172+
assert isinstance(label, (str, unicode)), label
173+
post_parameters = {
174+
"name": name,
175+
"label": label
176+
}
177+
headers, data = self._requester.requestJsonAndCheck(
178+
"PATCH",
179+
self.url,
180+
input=post_parameters
181+
)
182+
return GitReleaseAsset(self._requester, headers, data, completed=True)
183+
184+
def _initAttributes(self):
185+
self._url = github.GithubObject.NotSet
186+
self._id = github.GithubObject.NotSet
187+
self._name = github.GithubObject.NotSet
188+
self._label = github.GithubObject.NotSet
189+
self._uploader = github.GithubObject.NotSet
190+
self._content_type = github.GithubObject.NotSet
191+
self._state = github.GithubObject.NotSet
192+
self._size = github.GithubObject.NotSet
193+
self._download_count = github.GithubObject.NotSet
194+
self._created_at = github.GithubObject.NotSet
195+
self._updated_at = github.GithubObject.NotSet
196+
self._browser_download_url = github.GithubObject.NotSet
197+
198+
def _useAttributes(self, attributes):
199+
if "url" in attributes: # pragma no branch
200+
self._url = self._makeStringAttribute(attributes["url"])
201+
if "id" in attributes: # pragma no branch
202+
self._id = self._makeIntAttribute(attributes["id"])
203+
if "name" in attributes: # pragma no branch
204+
self._name = self._makeStringAttribute(attributes["name"])
205+
if "label" in attributes: # pragma no branch
206+
self._label = self._makeStringAttribute(attributes["label"])
207+
if "uploader" in attributes: # pragma no branch
208+
self._uploader = github.NamedUser.NamedUser(self._requester, {}, \
209+
attributes["uploader"], completed=True)
210+
if "content_type" in attributes: # pragma no branch
211+
self._content_type = self._makeStringAttribute(attributes["content_type"])
212+
if "state" in attributes: # pragma no branch
213+
self._state = self._makeStringAttribute(attributes["state"])
214+
if "size" in attributes: # pragma no branch
215+
self._size = self._makeIntAttribute(attributes["size"])
216+
if "download_count" in attributes: # pragma no branch
217+
self._download_count = self._makeIntAttribute(attributes["download_count"])
218+
if "created_at" in attributes: # pragma no branch
219+
self._created_at = self._makeDatetimeAttribute(attributes["created_at"])
220+
if "updated_at" in attributes: # pragma no branch
221+
self._updated_at = self._makeDatetimeAttribute(attributes["updated_at"])
222+
if "browser_download_url" in attributes: # pragma no branch
223+
self._browser_download_url = self._makeStringAttribute(attributes["browser_download_url"])

github/Repository.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import github.Organization
4747
import github.GitRef
4848
import github.GitRelease
49+
import github.GitReleaseAsset
4950
import github.Issue
5051
import github.Repository
5152
import github.PullRequest
@@ -2299,6 +2300,15 @@ def _hub(self, mode, event, callback, secret):
22992300
def _identity(self):
23002301
return self.owner.login + "/" + self.name
23012302

2303+
def get_release_asset(self, id):
2304+
assert isinstance(id, (int)), id
2305+
2306+
resp_headers, data = self._requester.requestJsonAndCheck(
2307+
"GET",
2308+
self.url + "/releases/assets/" + str(id)
2309+
)
2310+
return github.GitReleaseAsset.GitReleaseAsset(self._requester, resp_headers, data, completed=True)
2311+
23022312
def _initAttributes(self):
23032313
self._archive_url = github.GithubObject.NotSet
23042314
self._assignees_url = github.GithubObject.NotSet

github/Requester.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
import Consts
4343
import re
4444
import os
45+
import mimetypes
46+
from io import IOBase
4547

4648
atLeastPython26 = sys.hexversion >= 0x02060000
4749
atLeastPython3 = sys.hexversion >= 0x03000000
@@ -174,6 +176,11 @@ def requestJsonAndCheck(self, verb, url, parameters=None, headers=None, input=No
174176
def requestMultipartAndCheck(self, verb, url, parameters=None, headers=None, input=None):
175177
return self.__check(*self.requestMultipart(verb, url, parameters, headers, input))
176178

179+
def requestBlobAndCheck(self, verb, url, parameters=None, headers=None, input=None):
180+
o = urlparse.urlparse(url)
181+
self.__hostname = o.hostname
182+
return self.__check(*self.requestBlob(verb, url, parameters, headers, input))
183+
177184
def __check(self, status, responseHeaders, output):
178185
output = self.__structuredFromJson(output)
179186
if status >= 400:
@@ -228,6 +235,20 @@ def encode(input):
228235

229236
return self.__requestEncode(None, verb, url, parameters, headers, input, encode)
230237

238+
def requestBlob(self, verb, url, parameters={}, headers={}, input=None):
239+
def encode(local_path):
240+
if "Content-Type" in headers:
241+
mime_type = headers["Content-Type"]
242+
else:
243+
guessed_type = mimetypes.guess_type(input)
244+
mime_type = guessed_type[0] if guessed_type[0] is not None else "application/octet-stream"
245+
f = open(local_path, 'rb')
246+
return mime_type, f
247+
248+
if input:
249+
headers["Content-Length"] = os.path.getsize(input)
250+
return self.__requestEncode(None, verb, url, parameters, headers, input, encode)
251+
231252
def __requestEncode(self, cnx, verb, url, parameters, requestHeaders, input, encode):
232253
assert verb in ["HEAD", "GET", "POST", "PATCH", "PUT", "DELETE"]
233254
if parameters is None:
@@ -283,6 +304,9 @@ def __requestRaw(self, cnx, verb, url, requestHeaders, input):
283304
output = response.read()
284305

285306
cnx.close()
307+
if input:
308+
if isinstance(input, IOBase):
309+
input.close()
286310

287311
self.__log(verb, url, requestHeaders, input, status, responseHeaders, output)
288312

@@ -305,8 +329,8 @@ def __makeAbsoluteUrl(self, url):
305329
url = self.__prefix + url
306330
else:
307331
o = urlparse.urlparse(url)
308-
assert o.hostname == self.__hostname
309-
assert o.path.startswith(self.__prefix)
332+
assert o.hostname in [self.__hostname, "uploads.github.com"], o.hostname
333+
assert o.path.startswith((self.__prefix, "/api/uploads"))
310334
assert o.port == self.__port
311335
url = o.path
312336
if o.query != "":

0 commit comments

Comments
 (0)