10000 Flesh out API for pull file download · charbu01/github3.py@5695b1f · GitHub
[go: up one dir, main page]

Skip to content

Commit 5695b1f

Browse files
committed
Flesh out API for pull file download
1 parent 6d36b2c commit 5695b1f

File tree

4 files changed

+70
-22
lines changed

4 files changed

+70
-22
lines changed

LATEST_VERSION_NOTES.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
.. vim: set tw=100
22
3+
1.0.0a2: 2015-02-01
4+
~~~~~~~~~~~~~~~~~~~
5+
6+
Breaking Changes (since 1.0.0a1)
7+
````````````````````````````````
8+
9+
- When you download a release asset, instead of returning ``True`` or
10+
``False``, it will return the name of the file in which it saved the asset.
11+
12+
Features Added (since 1.0.0a1)
13+
``````````````````````````````
14+
15+
- You can now download a file in a pull request to a file on disk.
16+
17+
- You can retrieve the contents of the file in a pull request as bytes.
18+
19+
320
1.0.0a1: 2014-12-07
421
~~~~~~~~~~~~~~~~~~~
522

github3/pulls.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@
1010

1111
from re import match
1212
from json import dumps
13+
14+
from . import models
15+
from . import utils
1316
from .repos.commit import RepoCommit
14-
from .models import GitHubObject, GitHubCore, BaseComment
1517
from .users import User
1618
from .decorators import requires_auth
1719
from .issues.comment import IssueComment
1820
from uritemplate import URITemplate
1921

2022

21-
class PullDestination(GitHubCore):
23+
class PullDestination(models.GitHubCore):
2224

2325
"""The :class:`PullDestination <PullDestination>` object.
2426
@@ -50,7 +52,7 @@ def _repr(self):
5052
return '<{0} [{1}]>'.format(self.direction, self.label)
5153

5254

53-
class PullFile(GitHubCore):
55+
class PullFile(models.GitHubCore):
5456

5557
"""The :class:`PullFile <PullFile>` object.
5658
@@ -80,16 +82,36 @@ def _update_attributes(self, pfile):
8082
def _repr(self):
8183
return '<Pull Request File [{0}]>'.format(self.filename)
8284

83-
def contents(self, stream=False):
84-
"""Return the contents of the raw file.
85+
def download(self, path=None):
86+
"""Download the contents for this file to disk.
87+
88+
:param path: (optional), path where the file should be saved
89+
to, default is the filename provided in the headers and will be
90+
written in the current directory.
91+
it can take a file-like object as well
92+
:type path: str, file-like object
93+
:returns: bool -- True if successful, False otherwise
94+
"""
95+
headers = {'Accept': 'application/octet-stream'}
96+
resp = self._get(self.raw_url, stream=True, headers=headers)
97+
if self._boolean(resp, 200, 404):
98+
return utils.stream_response_to_file(resp, path)
99+
return None
100+
101+
def contents(self):
102+
"""Return the contents of the file as bytes.
85103
86104
:param stream: When true, the resulting object can be iterated over via
87105
``iter_contents``.
88106
"""
89-
return self.session.get(self.raw_url, stream=stream)
107+
headers = {'Accept': 'application/octect-stream'}
108+
resp = self._get(self.raw_url, headers=headers)
109+
if self._boolean(resp, 200, 404):
110+
return resp.content
111+
return b''
90112

91113

92-
class PullRequest(GitHubCore):
114+
class PullRequest(models.GitHubCore):
93115

94116
"""The :class:`PullRequest <PullRequest>` object.
95117
@@ -349,7 +371,7 @@ def update(self, title=None, body=None, state=None):
349371
return False
350372

351373

352-
class ReviewComment(BaseComment):
374+
class ReviewComment(models.BaseComment):
353< 341A /code>375

354376
"""The :class:`ReviewComment <ReviewComment>` object.
355377

github3/repos/release.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from ..decorators import requires_auth
66
from ..exceptions import error_for
77
from ..models import GitHubCore
8-
from ..utils import stream_response_to_file
8+
from .. import utils
99
from uritemplate import URITemplate
1010

1111

@@ -188,7 +188,8 @@ def download(self, path=''):
188188
written in the current directory.
189189
it can take a file-like object as well
190190
:type path: str, file
191-
:returns: bool -- True if successful, False otherwise
191+
:returns: name of the file, if successful otherwise ``None``
192+
:rtype: str
192193
"""
193194
headers = {
194195
'Accept': 'application/octet-stream'
@@ -207,9 +208,8 @@ def download(self, path=''):
207208
headers=headers)
208209

209210
if self._boolean(resp, 200, 404):
210-
stream_response_to_file(resp, path)
211-
return True
212-
return False
211+
return utils.stream_response_to_file(resp, path)
212+
return None
213213

214214
def edit(self, name, label=None):
215215
"""Edit this asset.

github3/utils.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# -*- coding: utf-8 -*-
22
"""A collection of useful utilities."""
3-
from collections import Callable
4-
from datetime import datetime, timedelta, tzinfo
5-
from requests.compat import basestring
3+
import collections
4+
import datetime
65
import re
76

7+
from requests import compat
8+
89
# with thanks to https://code.google.com/p/jquery-localtime/issues/detail?id=4
910
ISO_8601 = re.compile("^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[0-1]|0"
1011
"[1-9]|[1-2][0-9])(T(2[0-3]|[0-1][0-9]):([0-5][0-9]):([0"
@@ -32,10 +33,10 @@ def timestamp_parameter(timestamp, allow_none=True):
3233
return None
3334
raise ValueError("Timestamp value cannot be None")
3435

35-
if isinstance(timestamp, datetime):
36+
if isinstance(timestamp, datetime.datetime):
3637
return timestamp.isoformat() + 'Z'
3738

38-
if isinstance(timestamp, basestring):
39+
if isinstance(timestamp, compat.basestring):
3940
if not ISO_8601.match(timestamp):
4041
raise ValueError(("Invalid timestamp: %s is not a valid ISO-8601"
4142
" formatted date") % timestamp)
@@ -44,12 +45,12 @@ def timestamp_parameter(timestamp, allow_none=True):
4445
raise ValueError("Cannot accept type %s for timestamp" % type(timestamp))
4546

4647

47-
class UTC(tzinfo):
48+
class UTC(datetime.tzinfo):
4849

4950
"""Yet another UTC reimplementation, to avoid a dependency on pytz or
5051
dateutil."""
5152

52-
ZERO = timedelta(0)
53+
ZERO = datetime.timedelta(0)
5354

5455
def __repr__(self):
5556
return 'UTC()'
@@ -73,22 +74,30 @@ def stream_response_to_file(response, path=None):
7374
:param response: A Response object from requests
7475
:type response: requests.models.Response
7576
:param str path: The full path and file name used to save the response
77+
:return: path to the file
78+
:rtype: str
7679
"""
7780
pre_opened = False
7881
fd = None
82+
filename = None
7983
if path:
80-
if isinstance(getattr(path, 'write', None), Callable):
84+
if isinstance(getattr(path, 'write', None), collections.Callable):
8185
pre_opened = True
8286
fd = path
87+
filename = getattr(fd, 'name', None)
8388
else:
8489
fd = open(path, 'wb')
90+
filename = path
8591
else:
8692
header = response.headers['content-disposition']
8793
i = header.find('filename=') + len('filename=')
88-
fd = open(header[i:], 'wb')
94+
filename = header[i:]
95+
fd = open(filename, 'wb')
8996

9097
for chunk in response.iter_content(chunk_size=512):
9198
fd.write(chunk)
9299

93100
if not pre_opened:
94101
fd.close()
102+
103+
return filename

0 commit comments

Comments
 (0)
0