8000 Future proofing against requests · alexcouper/github3.py@088c484 · GitHub 8000
[go: up one dir, main page]

Skip to content

Commit 088c484

Browse files
committed
Future proofing against requests
Oh yeah, the GitHub object can now be used as a context manager! Woohoo! Stole the inspiration from requests.Session.
1 parent f1bbcd4 commit 088c484

File tree

6 files changed

+51
-20
lines changed

6 files changed

+51
-20
lines changed

HISTORY.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ History/Changelog
1111
as well. See:
1212
github/developer.github.com@b95f291a47954154a6a8cd7c2296cdda9b610164
1313

14+
- ``github3.GitHub`` can now be used as a context manager, e.g.,
15+
::
16+
17+
with github.GitHub() as gh:
18+
u = gh.user('sigmavirus24')
19+
1420
- Switch from expecter to unittest2 (python 2.6 only) and standard unittest
1521

1622
0.6.1: 2013-04-06

github3/decorators.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,9 @@ def auth_wrapper(self, *args, **kwargs):
3232
else:
3333
from github3.models import GitHubError
3434
# Mock a 401 response
35-
r = Response()
36-
r.status_code = 401
37-
r.encoding = 'utf-8'
38-
r.raw = StringIO('{"message": "Requires authentication"}'.encode())
35+
r = generate_fake_error_response(
36+
'{"message": "Requires authentication"}'.encode()
37+
)
3938
raise GitHubError(r)
4039
return auth_wrapper
4140

@@ -50,15 +49,23 @@ def auth_wrapper(self, *args, **kwargs):
5049
else:
5150
from github3.models import GitHubError
5251
# Mock a 401 response
53-
r = Response()
54-
r.status_code = 401
55-
r.encoding = 'utf-8'
56-
msg = ('{"message": "Requires username/password '
57-
'authentication"}').encode()
58-
r.raw = StringIO(msg)
52+
r = generate_fake_error_response(
53+
('{"message": "Requires username/password authentication"}'
54+
).encode()
55+
)
5956
raise GitHubError(r)
6057
return auth_wrapper
6158

59+
60+
def generate_fake_error_response(msg, status_code=401, encoding='utf-8'):
61+
r = Response()
62+
r.status_code = status_code
63+
r.encoding = encoding
64+
r.raw = StringIO(msg)
65+
r._content_consumed = True
66+
r._content = r.raw.read()
67+
return r
68+
6269
# Use mock decorators when generating documentation, so all functino signatures
6370
# are displayed correctly
6471
if os.getenv('GENERATING_DOCUMENTATION', None) == 'github3':

github3/github.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ def __repr__(self):
5757
return '<GitHub [{0[0]}]>'.format(self._session.auth)
5858
return '<GitHub at 0x{0:x}>'.format(id(self))
5959

60+
def __enter__(self):
61+
return self
62+
63+
def __exit__(self, *args):
64+
pass
65+
6066
@requires_auth
6167
def _iter_follow(self, which, number, etag):
6268
url = self._build_url('user', which)

tests/test_github.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import github3
2-
from mock import patch
2+
from mock import patch, Mock
33
from tests.utils import (expect, BaseCase, load)
44

55

@@ -11,6 +11,13 @@ def test_init(self):
1111
g = github3.GitHub(token='foo')
1212
expect(repr(g).endswith('{0:x}>'.format(id(g))))
1313

14+
def test_context_manager(self):
15+
with github3.GitHub() as gh:
16+
gh.__exit__ = Mock()
17+
expect(gh).isinstance(github3.GitHub)
18+
19+
gh.__exit__.assert_called()
20+
1421
def test_authorization(self):
1522
self.response('authorization')
1623
self.get('https://api.github.com/authorizations/10')

tests/test_models.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import github3
22
import requests
3-
from tests.utils import BaseCase, TestCase, expect, BytesIO, is_py3
3+
from tests.utils import BaseCase, TestCase, expect, RequestsBytesIO, is_py3
44

55

66
class TestGitHubObject(TestCase):
@@ -22,7 +22,7 @@ def test_json(self):
2222
r = requests.Response()
2323
r.headers['Last-Modified'] = 'foo'
2424
r.headers['ETag'] = 'bar'
25-
r.raw = BytesIO('{}'.encode() if is_py3 else '{}')
25+
r.raw = RequestsBytesIO('{}'.encode() if is_py3 else '{}')
2626
r.status_code = 200
2727

2828
json = self.g._json(r, 200)
@@ -32,7 +32,7 @@ def test_json(self):
3232
def test_boolean(self):
3333
r = requests.Response()
3434
r.status_code = 512
35-
r.raw = BytesIO('{}'.encode() if is_py3 else '{}')
35+
r.raw = RequestsBytesIO('{}'.encode() if is_py3 else '{}')
3636

3737
with expect.githuberror():
3838
self.g._boolean(r, 200, 404)
@@ -51,7 +51,7 @@ def __init__(self, methodName='runTest'):
5151
self.r = requests.Response()
5252
self.r.status_code = 400
5353
message = '{"message": "m", "errors": ["e"]}'
54-
self.r.raw = BytesIO(message.encode() if is_py3 else message)
54+
self.r.raw = RequestsBytesIO(message.encode() if is_py3 else message)
5555
self.error = github3.models.GitHubError(self.r)
5656

5757
def test_repr(self):
@@ -66,6 +66,6 @@ def test_message(self):
6666
def test_amazon(self):
6767
r = requests.Response()
6868
r.status_code = 400
69-
r.raw = BytesIO()
69+
r.raw = RequestsBytesIO()
7070
e = github3.models.GitHubError(r)
7171
expect(e.message) == '[No message]'

tests/utils.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,13 @@ def response(self, path_name, status_code=200, enc='utf-8',
108108

109109
if _iter:
110110
content = '[{0}]'.format(content)
111-
r.raw = BytesIO(content.encode())
111+
r.raw = RequestsBytesIO(content.encode())
112112
elif is_py3:
113-
r.raw = BytesIO(content.encode())
113+
r.raw = RequestsBytesIO(content.encode())
114114
else:
115-
r.raw = BytesIO(content)
115+
r.raw = RequestsBytesIO(content)
116116
else:
117-
r.raw = BytesIO()
117+
r.raw = RequestsBytesIO()
118118

119119
if headers:
120120
r.headers = CaseInsensitiveDict(headers)
@@ -139,3 +139,8 @@ def put(self, url):
139139

140140
def not_called(self):
141141
expect(self.request.called).is_False()
142+
143+
144+
class RequestsBytesIO(BytesIO):
145+
def read(self, chunk_size, *args, **kwargs):
146+
return super(RequestsBytesIO, self).read(chunk_size)

0 commit comments

Comments
 (0)
0