Skip to content

Commit 31faf02

Browse files
committed
Raise exceptions for unexpected status codes
Also raise exceptions on any 400 or greater status code as discussed in sigmavirus24gh-670 As it stands today, it's hard for people to determine whether None is returned because that's the API's response or because None is what we return in the event of a 404 or some other "error"-like status code. This change undoes that ambiguity and allows people to handle missing or permission-related problems using exceptions.
1 parent 508efde commit 31faf02

8 files changed

+40
-24
lines changed

github3/exceptions.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,12 @@ class ConnectionError(TransportError):
134134
msg_format = 'A connection-level exception occurred: {0}'
135135

136136

137+
class UnexpectedResponse(ResponseError):
138+
"""Exception class for responses that were unexpected."""
139+
140+
pass
141+
142+
137143
class UnprocessableResponseBody(ResponseError):
138144
"""Exception class for response objects that cannot be handled."""
139145

github3/models.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -144,21 +144,31 @@ def _instance_or_null(self, instance_class, json):
144144

145145
return instance_class(json, self)
146146

147-
def _json(self, response, status_code, include_cache_info=True):
147+
def _json(self, response, expected_status_code, include_cache_info=True):
148148
ret = None
149-
if self._boolean(response, status_code, 404) and response.content:
150-
LOG.info('Attempting to get JSON information from a Response '
151-
'with status code %d expecting %d',
152-
response.status_code, status_code)
149+
if response is None:
150+
return None
151+
152+
actual_status_code = response.status_code
153+
if actual_status_code != expected_status_code:
154+
if actual_status_code >= 400:
155+
raise exceptions.error_for(response)
156+
LOG.warning('Expected status_code %d but got %d',
157+
expected_status_code,
158+
actual_status_code)
159+
try:
153160
ret = response.json()
154-
headers = response.headers
155-
if (include_cache_info and
156-
(headers.get('Last-Modified') or headers.get('ETag')) and
157-
isinstance(ret, dict)):
158-
ret['Last-Modified'] = response.headers.get(
159-
'Last-Modified', ''
160-
)
161-
ret['ETag'] = response.headers.get('ETag', '')
161+
except ValueError:
162+
raise exceptions.UnexpectedResponse(response)
163+
164+
headers = response.headers
165+
if (include_cache_info and
166+
(headers.get('Last-Modified') or headers.get('ETag')) and
167+
isinstance(ret, dict)):
168+
ret['Last-Modified'] = response.headers.get(
169+
'Last-Modified', ''
170+
)
171+
ret['ETag'] = response.headers.get('ETag', '')
162172
LOG.info('JSON was %sreturned', 'not ' if ret is None else '')
163173
return ret
164174

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"http_interactions": [{"request": {"body": {"encoding": "utf-8", "string": ""}, "headers": {"User-Agent": "github3.py/1.0.0a4", "Accept-Encoding": "gzip, deflate", "Accept": "application/vnd.github.v3.full+json", "Connection": "keep-alive", "Accept-Charset": "utf-8", "Content-Type": "application/json", "Authorization": "token <AUTH_TOKEN>"}, "method": "GET", "uri": "https://api.github.com/orgs/praw-dev"}, "response": {"body": {"encoding": "utf-8", "base64_string": "H4sIAAAAAAAAA52Sy2rDMBBFfyVo7cSPPJoISim0i64aSqDQjVFsNVYrS0IaObgh/97xg+Kkm7rghSzNuRrNvSci9UEoQomx7DjNeUUCInJC49VmtV6vA+KtxNMCwDgahsyI2UFA4fezTJehtgcXDkjLjXbp35GwBfBKXnEFo8iOQLTQ+nMU2QLNM53zfBTZEYiWvNxzO4rtkVPYLc6oYvxeiiz9h9glOdRkFQNmry1oN928t847bjOtAEfeuujD3u276naBfeXcZVYYELoJBm4oVnJcbWsotJq88DwXMLnfPk1eLTOG28kDuiG1KVERy1HTMFUTqryUAdljxDodqTPWqz4yC0VjfMlEE7DGSOZSDBRT4qutSo3VHzwDRyhYz7uCNjACtK1/HfdD6SJFlz/zPQjXaEQBeddS6iMaN/gTCpvDswJKeTW2QdAHGc8sZ8DzlAG2nURxMo2W0yjeJQmNEzpfvuFTvMkvam6mWJZsdtGcLvBbNzVQm2aoz4MXk/M3ZwQa6ZADAAA=", "string": ""}, "headers": {"Server": "GitHub.com", "Date": "Sat, 30 Dec 2017 01:58:10 GMT", "Content-Type": "application/json; charset=utf-8", "Transfer-Encoding": "chunked", "Status": "200 OK", "X-RateLimit-Limit": "5000", "X-RateLimit-Remaining": "4987", "X-RateLimit-Reset": "1514600339", "Cache-Control": "private, max-age=60, s-maxage=60", "Vary": "Accept, Authorization, Cookie, X-GitHub-OTP", "ETag": "W/\"b1b88913bbbff5f15259690f7a9c8fd2\"", "Last-Modified": "Fri, 29 Dec 2017 03:43:48 GMT", "X-OAuth-Scopes": "admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user", "X-Accepted-OAuth-Scopes": "admin:org, read:org, repo, user, write:org", "X-GitHub-Media-Type": "github.v3; param=full; format=json", "Access-Control-Expose-Headers": "ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval", "Access-Control-Allow-Origin": "*", "Content-Security-Policy": "default-src 'none'", "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "deny", "X-XSS-Protection": "1; mode=block", "X-Runtime-rack": "0.031086", "Content-Encoding": "gzip", "X-GitHub-Request-Id": "E1A2:FD07:6C03970:BE8C6D2:5A46F2B2"}, "status": {"code": 200, "message": "OK"}, "url": "https://api.github.com/orgs/praw-dev"}, "recorded_at": "2017-12-30T01:58:10"}, {"request": {"body": {"encoding": "utf-8", "string": ""}, "headers": {"User-Agent": "github3.py/1.0.0a4", "Accept-Encoding": "gzip, deflate", "Accept": "application/vnd.github.v3.full+json", "Connection": "keep-alive", "Accept-Charset": "utf-8", "Content-Type": "application/json", "Authorization": "token <AUTH_TOKEN>"}, "method": "GET", "uri": "https://api.github.com/users/bboe/events/orgs/praw-dev?per_page=100"}, "response": {"body": {"encoding": "utf-8", "base64_string": "H4sIAAAAAAAAAyXMMQ7CMAxA0asgs5J66NYDMHIFFBKTWkrsKnEiAeLurWD8etL/QKHWfCJY4KZ2umqXCBeIGnohMW+scu81H76abW1BjDQo60Z1Smxrf0xBC44ZfTAebC88XKzhOXMz9w/31Oq8OK3JC79/W/juVxAMdn4AAAA=", "string": ""}, "headers": {"Server": "GitHub.com", "Date": "Sat, 30 Dec 2017 01:58:11 GMT", "Content-Type": "application/json; charset=utf-8", "Transfer-Encoding": "chunked", "Status": "404 Not Found", "X-RateLimit-Limit": "5000", "X-RateLimit-Remaining": "4986", "X-RateLimit-Reset": "1514600339", "X-OAuth-Scopes": "admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user", "X-Accepted-OAuth-Scopes": "", "X-GitHub-Media-Type": "github.v3; param=full; format=json", "Access-Control-Expose-Headers": "ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval", "Access-Control-Allow-Origin": "*", "Content-Security-Policy": "default-src 'none'", "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "deny", "X-XSS-Protection": "1; mode=block", "X-Runtime-rack": "0.020654", "Content-Encoding": "gzip", "X-GitHub-Request-Id": "E1A2:FD07:6C03977:BE8C6DE:5A46F2B2"}, "status": {"code": 404, "message": "Not Found"}, "url": "https://api.github.com/users/bboe/events/orgs/praw-dev?per_page=100"}, "recorded_at": "2017-12-30T01:58:11"}], "recorded_with": "betamax/0.8.0"}
1+
{"http_interactions": [{"request": {"body": {"encoding": "utf-8", "string": ""}, "headers": {"User-Agent": "github3.py/1.0.0a4", "Accept-Encoding": "gzip, deflate", "Accept": "application/vnd.github.v3.full+json", "Connection": "keep-alive", "Accept-Charset": "utf-8", "Content-Type": "application/json", "Authorization": "Basic <BASIC_AUTH>"}, "method": "GET", "uri": "https://api.github.com/orgs/testgh3py"}, "response": {"body": {"encoding": "utf-8", "base64_string": "H4sIAAAAAAAAA51Ty27cMAz8F52T2N5XsgaK5g9y6akXg7ZpW41sGRK1wXax/97xI1g3eyjcm0RxSA5ndFHG1rpTqRL2Ujfb/qwelC5Vut3H25fnJH5QwRm8NyK9T6OIev1Ua2lC/lTYNrKu9tES67i3PluBiUYEuvKJO1kHnSDANta+r4OOiIGr94HXQScIsC23Obt14BlziabDFWX6kBtdZP9T7W/osiidSMh9FWIM+mRWMHh2he0Eex/FDNGn6t9P33aYrGRfON2LtrBIF4zBpslnEJ06/ZuGeNY7+4sL8SoVF3hKGCXVYt357nmeeBI9hb3mQK39UAOByhpjP7DXxU139XhrpDVfOC3MuPRh4ZiEy4wE5t3EyctjnDzGmx/JId0naRL/BL3Ql//MkXPPqPC2oAykWCEDbhoL5exGxn506Hkf/4zcWJbav2fBU43qIFaANOXWEZY2Ec+1MaCdcUt6+IC1o6oiQWKLsD291sPDoNtgIUNQ6KI6aodpK8eMqO+pwPX4fNgfNrvjEXl3E1dog5E9toW+O4CmU3wd5K8oGJn4zXKya+H/0RAKKy4X36CgLpvWfkNono1x/QNbN+QzawQAAA==", "string": ""}, "headers": {"Server": "GitHub.com", "Date": "Tue, 13 Mar 2018 01:17:23 GMT", "Content-Type": "application/json; charset=utf-8", "Transfer-Encoding": "chunked", "Status": "200 OK", "X-RateLimit-Limit": "5000", "X-RateLimit-Remaining": "4999", "X-RateLimit-Reset": "1520907443", "Cache-Control": "private, max-age=60, s-maxage=60", "Vary": "Accept, Authorization, Cookie, X-GitHub-OTP", "ETag": "W/\"aeb872f9a370af8ed7b35785339e771f\"", "Last-Modified": "Tue, 02 Jan 2018 16:51:10 GMT", "X-GitHub-Media-Type": "github.v3; param=full; format=json", "Access-Control-Expose-Headers": "ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval", "Access-Control-Allow-Origin": "*", "Content-Security-Policy": "default-src 'none'", "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "deny", "X-XSS-Protection": "1; mode=block", "X-Runtime-rack": "0.065453", "Content-Encoding": "gzip", "X-GitHub-Request-Id": "C3E0:4347:278796A:58BF508:5AA726A3"}, "status": {"code": 200, "message": "OK"}, "url": "https://api.github.com/orgs/testgh3py"}, "recorded_at": "2018-03-13T01:17:23"}, {"request": {"body": {"encoding": "utf-8", "string": ""}, "headers": {"User-Agent": "github3.py/1.0.0a4", "Accept-Encoding": "gzip, deflate", "Accept": "application/vnd.github.v3.full+json", "Connection": "keep-alive", "Accept-Charset": "utf-8", "Content-Type": "application/json", "Authorization": "Basic <BASIC_AUTH>"}, "method": "GET", "uri": "https://api.github.com/users/gh3test/events/orgs/testgh3py?per_page=100"}, "response": {"body": {"encoding": "utf-8", "base64_string": "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", "string": ""}, "headers": {"Server": "GitHub.com", "Date": "Tue, 13 Mar 2018 01:17:23 GMT", "Content-Type": "application/json; charset=utf-8", "Transfer-Encoding": "chunked", "Status": "200 OK", "X-RateLimit-Limit": "5000", "X-RateLimit-Remaining": "4998", "X-RateLimit-Reset": "1520907443", "Cache-Control": "private, max-age=60, s-maxage=60", "Vary": "Accept, Authorization, Cookie, X-GitHub-OTP", "ETag": "W/\"4c7cef6d2494717538c0f069aba765c7\"", "Last-Modified": "Sun, 07 Jan 2018 12:31:46 GMT", "X-Poll-Interval": "60", "X-GitHub-Media-Type": "github.v3; param=full; format=json", "Access-Control-Expose-Headers": "ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval", "Access-Control-Allow-Origin": "*", "Content-Security-Policy": "default-src 'none'", "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "deny", "X-XSS-Protection": "1; mode=block", "X-Runtime-rack": "0.059044", "Content-Encoding": "gzip", "X-GitHub-Request-Id": "C3E0:4347:2787975:58BF51A:5AA726A3"}, "status": {"code": 200, "message": "OK"}, "url": "https://api.github.com/users/gh3test/events/orgs/testgh3py?per_page=100"}, "recorded_at": "2018-03-13T01:17:23"}], "recorded_with": "betamax/0.8.0"}

0 commit comments

Comments
 (0)