Skip to content

Commit

Permalink
Merge branch 'topic/SpecificExceptions' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
jacquev6 committed Apr 22, 2013
2 parents 50bf07c + dff0946 commit 73f9ae8
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 5 deletions.
5 changes: 5 additions & 0 deletions doc/utilities.rst
Expand Up @@ -6,6 +6,11 @@ Logging

.. autofunction:: github.enable_console_debug_logging

Error Handling
--------------

.. autoclass:: github.GithubException.GithubException()

Default argument
----------------

Expand Down
34 changes: 31 additions & 3 deletions github/GithubException.py
Expand Up @@ -17,13 +17,41 @@

class GithubException(Exception):
"""
This class represents GithubExceptions as returned for example by http://developer.github.com/v3/todo
Error handling in PyGithub is done with exceptions. This class is the base of all exceptions raised by PyGithub.
Some other types of exceptions might be raised by underlying libraries, for example for network-related issues.
"""

def __init__(self, status, data):
Exception.__init__(self)
self.status = status
self.data = data
self.__status = status
self.__data = data

@property
def status(self):
"""
The status returned by the Github API
"""
return self.__status

@property
def data(self):
"""
The (decoded) data returned by the Github API
"""
return self.__data

def __str__(self):
return str(self.status) + " " + str(self.data)


class BadCredentialsException(GithubException):
"""
Exception raised in case of bad credentials (when Github API replies with a 401 or 403 HTML status)
"""


class UnknownObjectException(GithubException):
"""
Exception raised a non-existing object is requested (when Github API replies with a 404 HTML status)
"""
9 changes: 8 additions & 1 deletion github/Requester.py
Expand Up @@ -96,9 +96,16 @@ def requestMultipartAndCheck(self, verb, url, parameters, input):
def __check(self, status, responseHeaders, output):
output = self.__structuredFromJson(output)
if status >= 400:
raise GithubException.GithubException(status, output)
raise self.__createException(status, output)
return responseHeaders, output

def __createException(self, status, output):
if status == 401 and output["message"] == "Bad credentials":
return GithubException.BadCredentialsException(status, output)
if status == 404 and output["message"] == "Not Found":
return GithubException.UnknownObjectException(status, output)
return GithubException.GithubException(status, output)

def __structuredFromJson(self, data):
if len(data) == 0:
return None
Expand Down
2 changes: 1 addition & 1 deletion github/__init__.py
Expand Up @@ -25,7 +25,7 @@
import logging

from MainClass import Github
from GithubException import GithubException
from GithubException import GithubException, BadCredentialsException, UnknownObjectException
from InputFileContent import InputFileContent
from InputGitAuthor import InputGitAuthor
from InputGitTreeElement import InputGitTreeElement
Expand Down
8 changes: 8 additions & 0 deletions github/tests/Exceptions.py
Expand Up @@ -88,3 +88,11 @@ def testBadAuthentication(self):
else:
self.assertEqual(str(exception), "401 {'message': 'Bad credentials'}") # pragma no cover (Covered with Python 3)
self.assertTrue(raised)


class SpecificExceptions(Framework.TestCase):
def testBadCredentials(self):
self.assertRaises(github.BadCredentialsException, lambda: github.Github("BadUser", "BadPassword").get_user().login)

def testUnknownObject(self):
self.assertRaises(github.UnknownObjectException, lambda: self.g.get_user().get_repo("Xxx"))
11 changes: 11 additions & 0 deletions github/tests/ReplayData/SpecificExceptions.testBadCredentials.txt
@@ -0,0 +1,11 @@
https
GET
api.github.com
None
/user
{'Authorization': 'Basic login_and_password_removed'}
null
401
[('status', '401 Unauthorized'), ('content-length', '29'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('etag', '"ca6a3702f840b6bff0bb1bca6be0337c"'), ('date', 'Sat, 02 Jun 2012 12:12:32 GMT'), ('content-type', 'application/json; charset=utf-8')]
{"message":"Bad credentials"}

22 changes: 22 additions & 0 deletions github/tests/ReplayData/SpecificExceptions.testUnknownObject.txt
@@ -0,0 +1,22 @@
https
GET
api.github.com
None
/user
{'Authorization': 'Basic login_and_password_removed'}
null
200
[('status', '200 OK'), ('x-ratelimit-remaining', '4971'), ('content-length', '801'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"0e6c8f1cbb0c4f0eae96d8a76de9a43f"'), ('date', 'Sat, 02 Jun 2012 12:11:46 GMT'), ('content-type', 'application/json; charset=utf-8')]
{"type":"User","total_private_repos":5,"company":"Criteo","gravatar_id":"b68de5ae38616c296fa345d2b9df2225","login":"jacquev6","public_gists":3,"email":"vincent@vincent-jacques.net","owned_private_repos":5,"avatar_url":"https://secure.gravatar.com/avatar/b68de5ae38616c296fa345d2b9df2225?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","private_gists":5,"collaborators":0,"created_at":"2010-07-09T06:10:06Z","blog":"http://vincent-jacques.net","location":"Paris, France","url":"https://api.github.com/users/jacquev6","following":24,"disk_usage":16988,"public_repos":10,"name":"Vincent Jacques","hireable":false,"followers":13,"html_url":"https://github.com/jacquev6","id":327146,"plan":{"private_repos":5,"collaborators":1,"space":614400,"name":"micro"},"bio":""}

https
GET
api.github.com
None
/repos/jacquev6/Xxx
{'Authorization': 'Basic login_and_password_removed'}
null
404
[('status', '404 Not Found'), ('x-ratelimit-remaining', '4970'), ('content-length', '23'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"e66a7a6c91e2c26803f3f49feb7a883f"'), ('date', 'Sat, 02 Jun 2012 12:11:47 GMT'), ('content-type', 'application/json; charset=utf-8')]
{"message":"Not Found"}

0 comments on commit 73f9ae8

Please sign in to comment.