Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to access github Release Asset API. #525

Merged
merged 10 commits into from
Nov 27, 2017
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@
/developer.github.com/
/gh-pages/
/doc/doctrees/
.vscode*
41 changes: 41 additions & 0 deletions github/GitRelease.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
# #
# ##############################################################################

from os.path import basename
import github.GithubObject
import github.GitAuthor
import github.GitReleaseAsset


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

@property
def id(self):
"""
:type: integer
"""
self._completeIfNotSet(self._id)
return self._id.value

@property
def body(self):
"""
Expand Down Expand Up @@ -116,7 +126,36 @@ def update_release(self, name, message, draft=False, prerelease=False):
)
return github.GitRelease.GitRelease(self._requester, headers, data, completed=True)

def upload_asset(self, path, label="", content_type=""):
assert isinstance(path, (str, unicode)), path
assert isinstance(label, (str, unicode)), label

post_parameters = {
"name": basename(path),
"label": label
}
headers = {}
if len(content_type) > 0:
headers["Content-Type"] = content_type
resp_headers, data = self._requester.requestBlobAndCheck(
"POST",
self.upload_url.split("{?")[0],
parameters=post_parameters,
headers=headers,
input=path
)
return github.GitReleaseAsset.GitReleaseAsset(self._requester, resp_headers, data, completed=True)

def get_assets(self):
return github.PaginatedList.PaginatedList(
github.GitReleaseAsset.GitReleaseAsset,
self._requester,
self.url + "/assets",
None
)

def _initAttributes(self):
self._id = github.GithubObject.NotSet
self._body = github.GithubObject.NotSet
self._title = github.GithubObject.NotSet
self._tag_name = github.GithubObject.NotSet
Expand All @@ -126,6 +165,8 @@ def _initAttributes(self):
self._html_url = github.GithubObject.NotSet

def _useAttributes(self, attributes):
if "id" in attributes:
self._id = self._makeIntAttribute(attributes["id"])
if "body" in attributes:
self._body = self._makeStringAttribute(attributes["body"])
if "name" in attributes:
Expand Down
223 changes: 223 additions & 0 deletions github/GitReleaseAsset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
# -*- coding: utf-8 -*-

# ########################## Copyrights and license ############################
# #
# Copyright 2012 Vincent Jacques <[email protected]> #
# Copyright 2012 Zearin <[email protected]> #
# Copyright 2013 AKFish <[email protected]> #
# Copyright 2013 Vincent Jacques <[email protected]> # #
# Copyright 2017 Chris McBride <[email protected]> #
# #
# This file is part of PyGithub. #
# http://pygithub.github.io/PyGithub/v1/index.html #
# #
# PyGithub is free software: you can redistribute it and/or modify it under #
# the terms of the GNU Lesser General Public License as published by the Free #
# Software Foundation, either version 3 of the License, or (at your option) #
# any later version. #
# #
# PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY #
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more #
# details. #
# #
# You should have received a copy of the GNU Lesser General Public License #
# along with PyGithub. If not, see <http://www.gnu.org/licenses/>. #
# #
# ##############################################################################

import github.GithubObject


class GitReleaseAsset(github.GithubObject.CompletableGithubObject):
"""
This class represents GitReleaseAssets as returned by
GET /repos/:owner/:repo/releases/assets/:id
See:
https://developer.github.com/v3/repos/releases/#get-a-single-release-asset
"""

def __repr__(self):
return self.get__repr__({"url": self.url})

@property
def url(self):
"""
:type: string
"""
self._completeIfNotSet(self._url)
return self._url.value

@property
def id(self):
"""
:type: integer
"""
self._completeIfNotSet(self._id)
return self._id.value

@property
def name(self):
"""
:type: string
"""
self._completeIfNotSet(self._name)
return self._name.value

@name.setter
def name(self, value):
"""
:type: string
"""
self._completeIfNotSet(self._name)
self._name.value = value

@property
def label(self):
"""
:type: string
"""
self._completeIfNotSet(self._label)
return self._label.value

@label.setter
def label(self, value):
"""
:type: string
"""
self._completeIfNotSet(self._label)
self._label.value = value

@property
def content_type(self):
"""
:type: string
"""
self._completeIfNotSet(self._content_type)
return self._content_type.value

@property
def state(self):
"""
:type: string
"""
self._completeIfNotSet(self._state)
return self._state.value

@property
def size(self):
"""
:type: integer
"""
self._completeIfNotSet(self._size)
return self._size.value

@property
def download_count(self):
"""
:type: integer
"""
self._completeIfNotSet(self._download_count)
return self._download_count.value

@property
def created_at(self):
"""
:type: datetime
"""
self._completeIfNotSet(self._created_at)
return self._created_at.value

@property
def updated_at(self):
"""
:type: datetime
"""
self._completeIfNotSet(self._updated_at)
return self._updated_at.value

@property
def browser_download_url(self):
"""
:type: string
"""
self._completeIfNotSet(self._browser_download_url)
return self._browser_download_url.value

@property
def uploader(self):
"""
:type: github.NamedUser.NamedUser
"""
self._completeIfNotSet(self._uploader)
return self._uploader.value

def delete_asset(self):
"""
Delete asset from the release.
:rtype: bool
"""
headers, data = self._requester.requestJsonAndCheck(
"DELETE",
self.url
)
return True

def update_asset(self, name, label=""):
"""
Update asset metadata.
:rtype: github.GitReleaseAsset.GitReleaseAsset
"""
assert isinstance(name, (str, unicode)), name
assert isinstance(label, (str, unicode)), label
post_parameters = {
"name": name,
"label": label
}
headers, data = self._requester.requestJsonAndCheck(
"PATCH",
self.url,
input=post_parameters
)
return GitReleaseAsset(self._requester, headers, data, completed=True)

def _initAttributes(self):
self._url = github.GithubObject.NotSet
self._id = github.GithubObject.NotSet
self._name = github.GithubObject.NotSet
self._label = github.GithubObject.NotSet
self._uploader = github.GithubObject.NotSet
self._content_type = github.GithubObject.NotSet
self._state = github.GithubObject.NotSet
self._size = github.GithubObject.NotSet
self._download_count = github.GithubObject.NotSet
self._created_at = github.GithubObject.NotSet
self._updated_at = github.GithubObject.NotSet
self._browser_download_url = github.GithubObject.NotSet

def _useAttributes(self, attributes):
if "url" in attributes: # pragma no branch
self._url = self._makeStringAttribute(attributes["url"])
if "id" in attributes: # pragma no branch
self._id = self._makeIntAttribute(attributes["id"])
if "name" in attributes: # pragma no branch
self._name = self._makeStringAttribute(attributes["name"])
if "label" in attributes: # pragma no branch
self._label = self._makeStringAttribute(attributes["label"])
if "uploader" in attributes: # pragma no branch
self._uploader = github.NamedUser.NamedUser(self._requester, {}, \
attributes["uploader"], completed=True)
if "content_type" in attributes: # pragma no branch
self._content_type = self._makeStringAttribute(attributes["content_type"])
if "state" in attributes: # pragma no branch
self._state = self._makeStringAttribute(attributes["state"])
if "size" in attributes: # pragma no branch
self._size = self._makeIntAttribute(attributes["size"])
if "download_count" in attributes: # pragma no branch
self._download_count = self._makeIntAttribute(attributes["download_count"])
if "created_at" in attributes: # pragma no branch
self._created_at = self._makeDatetimeAttribute(attributes["created_at"])
if "updated_at" in attributes: # pragma no branch
self._updated_at = self._makeDatetimeAttribute(attributes["updated_at"])
if "browser_download_url" in attributes: # pragma no branch
self._browser_download_url = self._makeStringAttribute(attributes["browser_download_url"])
10 changes: 10 additions & 0 deletions github/Repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import github.Organization
import github.GitRef
import github.GitRelease
import github.GitReleaseAsset
import github.Issue
import github.Repository
import github.PullRequest
Expand Down Expand Up @@ -2294,6 +2295,15 @@ def _hub(self, mode, event, callback, secret):
def _identity(self):
return self.owner.login + "/" + self.name

def get_release_asset(self, id):
assert isinstance(id, (int)), id

resp_headers, data = self._requester.requestJsonAndCheck(
"GET",
self.url + "/releases/assets/" + str(id)
)
return github.GitReleaseAsset.GitReleaseAsset(self._requester, resp_headers, data, completed=True)

def _initAttributes(self):
self._archive_url = github.GithubObject.NotSet
self._assignees_url = github.GithubObject.NotSet
Expand Down
28 changes: 26 additions & 2 deletions github/Requester.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
import Consts
import re
import os
import mimetypes
from io import IOBase

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

def requestBlobAndCheck(self, verb, url, parameters=None, headers=None, input=None):
o = urlparse.urlparse(url)
self.__hostname = o.hostname
return self.__check(*self.requestBlob(verb, url, parameters, headers, input))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldnt we change back the __hostname here? My subsequent api calls failed due to the host name assertion fails in requester..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See fix in #771


def __check(self, status, responseHeaders, output):
output = self.__structuredFromJson(output)
if status >= 400:
Expand Down Expand Up @@ -228,6 +235,20 @@ def encode(input):

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

def requestBlob(self, verb, url, parameters={}, headers={}, input=None):
def encode(local_path):
if "Content-Type" in headers:
mime_type = headers["Content-Type"]
else:
guessed_type = mimetypes.guess_type(input)
mime_type = guessed_type[0] if guessed_type[0] is not None else "application/octet-stream"
f = open(local_path, 'rb')
return mime_type, f

if input:
headers["Content-Length"] = os.path.getsize(input)
return self.__requestEncode(None, verb, url, parameters, headers, input, encode)

def __requestEncode(self, cnx, verb, url, parameters, requestHeaders, input, encode):
assert verb in ["HEAD", "GET", "POST", "PATCH", "PUT", "DELETE"]
if parameters is None:
Expand Down Expand Up @@ -283,6 +304,9 @@ def __requestRaw(self, cnx, verb, url, requestHeaders, input):
output = response.read()

cnx.close()
if input:
if isinstance(input, IOBase):
input.close()

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

Expand All @@ -305,8 +329,8 @@ def __makeAbsoluteUrl(self, url):
url = self.__prefix + url
else:
o = urlparse.urlparse(url)
assert o.hostname == self.__hostname
assert o.path.startswith(self.__prefix)
assert o.hostname in [self.__hostname, "uploads.github.com"], o.hostname
assert o.path.startswith((self.__prefix, "/api/uploads"))
assert o.port == self.__port
url = o.path
if o.query != "":
Expand Down
Loading