Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitfbc140b

Browse files
committed
Merge branch 'pr/213' into develop
2 parentsaa0b457 +e0ca437 commitfbc140b

File tree

8 files changed

+260
-33
lines changed

8 files changed

+260
-33
lines changed

‎github3/decorators.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def auth_wrapper(self, *args, **kwargs):
4040
fromgithub3.modelsimportGitHubError
4141
# Mock a 401 response
4242
r=generate_fake_error_response(
43-
'{"message": "Requires authentication"}'.encode()
43+
'{"message": "Requires authentication"}'
4444
)
4545
raiseGitHubError(r)
4646
returnauth_wrapper
@@ -61,18 +61,40 @@ def auth_wrapper(self, *args, **kwargs):
6161
fromgithub3.modelsimportGitHubError
6262
# Mock a 401 response
6363
r=generate_fake_error_response(
64-
('{"message": "Requires username/password authentication"}'
65-
).encode()
64+
'{"message": "Requires username/password authentication"}'
6665
)
6766
raiseGitHubError(r)
6867
returnauth_wrapper
6968

7069

70+
defrequires_app_credentials(func):
71+
"""Require client_id and client_secret to be associated.
72+
73+
This is used to note and enforce which methods require a client_id and
74+
client_secret to be used.
75+
76+
"""
77+
@wraps(func)
78+
defauth_wrapper(self,*args,**kwargs):
79+
client_id,client_secret=self._session.retrieve_client_credentials()
80+
ifclient_idandclient_secret:
81+
returnfunc(self,*args,**kwargs)
82+
else:
83+
fromgithub3.modelsimportGitHubError
84+
# Mock a 401 response
85+
r=generate_fake_error_response(
86+
'{"message": "Requires username/password authentication"}'
87+
)
88+
raiseGitHubError(r)
89+
90+
returnauth_wrapper
91+
92+
7193
defgenerate_fake_error_response(msg,status_code=401,encoding='utf-8'):
7294
r=Response()
7395
r.status_code=status_code
7496
r.encoding=encoding
75-
r.raw=RequestsStringIO(msg)
97+
r.raw=RequestsStringIO(msg.encode())
7698
r._content_consumed=True
7799
r._content=r.raw.read()
78100
returnr

‎github3/github.py

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
88
"""
99

10-
fromjsonimportdumps
11-
fromrequestsimportsession
1210
fromgithub3.authsimportAuthorization
13-
fromgithub3.decoratorsimportrequires_auth,requires_basic_auth
11+
fromgithub3.decoratorsimport (requires_auth,requires_basic_auth,
12+
requires_app_credentials)
1413
fromgithub3.eventsimportEvent
1514
fromgithub3.gistsimportGist
1615
fromgithub3.issuesimportIssue,issue_params
@@ -103,19 +102,23 @@ def authorize(self, login, password, scopes=None, note='', note_url='',
103102
:returns: :class:`Authorization <Authorization>`
104103
"""
105104
json=None
106-
auth=self._session.author (loginandpassword)
105+
# TODO: Break this behaviour in 1.0 (Don't rely on self._session.auth)
106+
auth=None
107+
ifself._session.auth:
108+
auth=self._session.auth
109+
elifloginandpassword:
110+
auth= (login,password)
111+
107112
ifauth:
108113
url=self._build_url('authorizations')
109114
data= {'note':note,'note_url':note_url,
110115
'client_id':client_id,'client_secret':client_secret}
111116
ifscopes:
112117
data['scopes']=scopes
113-
ifself._session.auth:
118+
119+
withself._session.temporary_basic_auth(*auth):
114120
json=self._json(self._post(url,data=data),201)
115-
else:
116-
ses=session()
117-
ses.auth= (login,password)
118-
json=self._json(ses.post(url,data=dumps(data)),201)
121+
119122
returnAuthorization(json,self)ifjsonelseNone
120123

121124
defcheck_authorization(self,access_token):
@@ -1017,6 +1020,43 @@ def repository(self, owner, repository):
10171020
json=self._json(self._get(url),200)
10181021
returnRepository(json,self)ifjsonelseNone
10191022

1023+
@requires_app_credentials
1024+
defrevoke_authorization(self,access_token):
1025+
"""Revoke specified authorization for an OAuth application.
1026+
1027+
Revoke all authorization tokens created by your application. This will
1028+
only work if you have already called ``set_client_id``.
1029+
1030+
:param str access_token: (required), the access_token to revoke
1031+
:returns: bool -- True if successful, False otherwise
1032+
"""
1033+
client_id,client_secret=self._session.retrieve_client_credentials()
1034+
url=self._build_url('applications',str(client_id),'tokens',
1035+
access_token)
1036+
withself._session.temporary_basic_auth(client_id,client_secret):
1037+
response=self._delete(url,params={'client_id':None,
1038+
'client_secret':None})
1039+
1040+
returnself._boolean(response,204,404)
1041+
1042+
@requires_app_credentials
1043+
defrevoke_authorizations(self):
1044+
"""Revoke all authorizations for an OAuth application.
1045+
1046+
Revoke all authorization tokens created by your application. This will
1047+
only work if you have already called ``set_client_id``.
1048+
1049+
:param str client_id: (required), the client_id of your application
1050+
:returns: bool -- True if successful, False otherwise
1051+
"""
1052+
client_id,client_secret=self._session.retrieve_client_credentials()
1053+
url=self._build_url('applications',str(client_id),'tokens')
1054+
withself._session.temporary_basic_auth(client_id,client_secret):
1055+
response=self._delete(url,params={'client_id':None,
1056+
'client_secret':None})
1057+
1058+
returnself._boolean(response,204,404)
1059+
10201060
defsearch_code(self,query,sort=None,order=None,per_page=None,
10211061
text_match=False,number=-1,etag=None):
10221062
"""Find code via the code search API.

‎github3/repos/hook.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,6 @@ def delete(self):
6262
"""
6363
returnself._boolean(self._delete(self._api),204,404)
6464

65-
@requires_auth
66-
defdelete_subscription(self):
67-
"""Delete the user's subscription to this repository.
68-
69-
:returns: bool
70-
"""
71-
url=self._build_url('subscription',base_url=self._api)
72-
returnself._boolean(self._delete(url),204,404)
73-
7465
@requires_auth
7566
defedit(self,config={},events=[],add_events=[],rm_events=[],
7667
active=True):
@@ -105,6 +96,15 @@ def edit(self, config={}, events=[], add_events=[], rm_events=[],
10596

10697
returnFalse
10798

99+
@requires_auth
100+
defping(self):
101+
"""Ping this hook.
102+
103+
:returns: bool
104+
"""
105+
url=self._build_url('pings',base_url=self._api)
106+
returnself._boolean(self._post(url),204,404)
107+
108108
@requires_auth
109109
deftest(self):
110110
"""Test this hook

‎github3/repos/repo.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,15 @@ def delete_key(self, key_id):
885885
url=self._build_url('keys',str(key_id),base_url=self._api)
886886
returnself._boolean(self._delete(url),204,404)
887887

888+
@requires_auth
889+
defdelete_subscription(self):
890+
"""Delete the user's subscription to this repository.
891+
892+
:returns: bool
893+
"""
894+
url=self._build_url('subscription',base_url=self._api)
895+
returnself._boolean(self._delete(url),204,404)
896+
888897
@requires_auth
889898
defedit(self,
890899
name,
@@ -1068,6 +1077,18 @@ def iter_code_frequency(self, number=-1, etag=None):
10681077
url=self._build_url('stats','code_frequency',base_url=self._api)
10691078
returnself._iter(int(number),url,list,etag=etag)
10701079

1080+
defiter_collaborators(self,number=-1,etag=None):
1081+
"""Iterate over the collaborators of this repository.
1082+
1083+
:param int number: (optional), number of collaborators to return.
1084+
Default: -1 returns all comments
1085+
:param str etag: (optional), ETag from a previous request to the same
1086+
endpoint
1087+
:returns: generator of :class:`User <github3.users.User>`\ s
1088+
"""
1089+
url=self._build_url('collaborators',base_url=self._api)
1090+
returnself._iter(int(number),url,User,etag=etag)
1091+
10711092
defiter_comments(self,number=-1,etag=None):
10721093
"""Iterate over comments on all commits in the repository.
10731094

‎github3/session.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
fromcollectionsimportCallable
55
fromgithub3import__version__
66
fromloggingimportgetLogger
7+
fromcontextlibimportcontextmanager
78

89
__url_cache__= {}
910
__logs__=getLogger(__package__)
@@ -43,6 +44,9 @@ def basic_auth(self, username, password):
4344

4445
self.auth= (username,password)
4546

47+
# Disable token authentication
48+
self.headers.pop('Authorization',None)
49+
4650
defbuild_url(self,*args,**kwargs):
4751
"""Builds a new API url from scratch."""
4852
parts= [kwargs.get('base_url')orself.base_url]
@@ -83,6 +87,15 @@ def request(self, *args, **kwargs):
8387
response=new_response
8488
returnresponse
8589

90+
defretrieve_client_credentials(self):
91+
"""Return the client credentials.
92+
93+
:returns: tuple(client_id, client_secret)
94+
"""
95+
client_id=self.params.get('client_id')
96+
client_secret=self.params.get('client_secret')
97+
return (client_id,client_secret)
98+
8699
deftwo_factor_auth_callback(self,callback):
87100
ifnotcallback:
88101
return
@@ -104,3 +117,17 @@ def token_auth(self, token):
104117
self.headers.update({
105118
'Authorization':'token {0}'.format(token)
106119
})
120+
# Unset username/password so we stop sending them
121+
self.auth=None
122+
123+
@contextmanager
124+
deftemporary_basic_auth(self,*auth):
125+
old_basic_auth=self.auth
126+
old_token_auth=self.headers.get('Authorization')
127+
128+
self.basic_auth(*auth)
129+
yield
130+
131+
self.auth=old_basic_auth
132+
ifold_token_auth:
133+
self.headers['Authorization']=old_token_auth

‎tests/test_repos.py

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,17 @@ def test_delete_key(self):
453453
assertself.repo.delete_key(2)
454454
self.mock_assertions()
455455

456+
deftest_delete_subscription(self):
457+
self.response('',204)
458+
self.delete(self.api+'subscription')
459+
460+
self.assertRaises(github3.GitHubError,self.repo.delete_subscription)
461+
self.not_called()
462+
463+
self.login()
464+
assertself.repo.delete_subscription()
465+
self.mock_assertions()
466+
456467
deftest_edit(self):
457468
self.response('repo')
458469
self.patch(self.api[:-1])
@@ -557,6 +568,15 @@ def test_iter_branches(self):
557568
assertisinstance(b,repos.branch.Branch)
558569
self.mock_assertions()
559570

571+
deftest_iter_collaborators(self):
572+
self.response('user',_iter=True)
573+
self.get(self.api+'collaborators')
574+
self.conf= {'params': {'per_page':100}}
575+
576+
u=next(self.repo.iter_collaborators())
577+
assertisinstance(u,github3.users.User)
578+
self.mock_assertions()
579+
560580
deftest_iter_comments(self):
561581
self.response('repo_comment',_iter=True)
562582
self.get(self.api+'comments')
@@ -1208,17 +1228,6 @@ def test_delete(self):
12081228
assertself.hook.delete()
12091229
self.mock_assertions()
12101230

1211-
deftest_delete_subscription(self):
1212-
self.response('',204)
1213-
self.delete(self.api+'/subscription')
1214-
1215-
self.assertRaises(github3.GitHubError,self.hook.delete_subscription)
1216-
self.not_called()
1217-
1218-
self.login()
1219-
assertself.hook.delete_subscription()
1220-
self.mock_assertions()
1221-
12221231
deftest_edit(self):
12231232
self.response('hook',200)
12241233
self.patch(self.api)
@@ -1263,6 +1272,19 @@ def test_test(self):
12631272
assertself.hook.test()
12641273
self.mock_assertions()
12651274

1275+
deftest_ping(self):
1276+
# Funny name, no?
1277+
self.response('',204)
1278+
self.post(self.api+'/pings')
1279+
self.conf= {}
1280+
1281+
self.assertRaises(github3.GitHubError,self.hook.ping)
1282+
self.not_called()
1283+
1284+
self.login()
1285+
assertself.hook.ping()
1286+
self.mock_assertions()
1287+
12661288

12671289
classTestRepoComment(BaseCase):
12681290
def__init__(self,methodName='runTest'):

‎tests/unit/test_github.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,42 @@ def test_two_factor_login(self):
1414
deftest_can_login_without_two_factor_callback(self):
1515
self.instance.login('username','password')
1616
self.instance.login(token='token')
17+
18+
19+
classTestGitHubAuthorizations(UnitHelper):
20+
described_class=GitHub
21+
example_data=None
22+
23+
defcreate_session_mock(self,*args):
24+
session=super(TestGitHubAuthorizations,
25+
self).create_session_mock(*args)
26+
session.retrieve_client_credentials.return_value= ('id','secret')
27+
returnsession
28+
29+
deftest_revoke_authorization(self):
30+
"""Test that GitHub#revoke_authorization calls the expected methods.
31+
32+
It should use the session's delete and temporary_basic_auth methods.
33+
"""
34+
self.instance.revoke_authorization('access_token')
35+
self.session.delete.assert_called_once_with(
36+
'https://api.github.com/applications/id/tokens/access_token',
37+
params={'client_id':None,'client_secret':None}
38+
)
39+
self.session.temporary_basic_auth.assert_called_once_with(
40+
'id','secret'
41+
)
42+
43+
deftest_revoke_authorizations(self):
44+
"""Test that GitHub#revoke_authorizations calls the expected methods.
45+
46+
It should use the session's delete and temporary_basic_auth methods.
47+
"""
48+
self.instance.revoke_authorizations()
49+
self.session.delete.assert_called_once_with(
50+
'https://api.github.com/applications/id/tokens',
51+
params={'client_id':None,'client_secret':None}
52+
)
53+
self.session.temporary_basic_auth.assert_called_once_with(
54+
'id','secret'
55+
)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp