diff --git a/github/Github.py b/github/Github.py index 12eacfd9..f6d4acf4 100644 --- a/github/Github.py +++ b/github/Github.py @@ -23,6 +23,7 @@ class Github( object ): None, None ) + NamedUser.NamedUser._checkStatus( status, data ) return NamedUser.NamedUser( self.__requester, data, completed = True ) def get_organization( self, login ): @@ -32,6 +33,7 @@ class Github( object ): None, None ) + Organization.Organization._checkStatus( status, data ) return Organization.Organization( self.__requester, data, completed = True ) def get_gist( self, id ): @@ -41,10 +43,12 @@ class Github( object ): None, None ) + Gist.Gist._checkStatus( status, data ) return Gist.Gist( self.__requester, data, completed = True ) def get_gists( self ): status, headers, data = self.__requester.request( "GET", "https://api.github.com/gists/public", None, None ) + Gist.Gist._checkStatus( status, data ) return PaginatedList.PaginatedList( Gist.Gist, self.__requester, diff --git a/github/GithubObject.py b/github/GithubObject.py index 485d345a..fe772774 100644 --- a/github/GithubObject.py +++ b/github/GithubObject.py @@ -13,14 +13,17 @@ class BasicGithubObject( object ): def _request( self, verb, url, parameters, input ): return self._requester.request( verb, url, parameters, input ) - def _parentUrl( self, url ): + @staticmethod + def _parentUrl( url ): return "/".join( url.split( "/" )[ : -1 ] ) - def _checkStatus( self, status, data ): + @staticmethod + def _checkStatus( status, data ): if status >= 400: raise GithubException.GithubException( status, data ) - def _NoneIfNotSet( self, value ): + @staticmethod + def _NoneIfNotSet( value ): if value is NotSet: return None else: @@ -42,5 +45,6 @@ class GithubObject( BasicGithubObject ): None, None ) + self._checkStatus( status, data ) self._useAttributes( data ) self._completed = True diff --git a/github/PaginatedList.py b/github/PaginatedList.py index d2af8b7c..8ca566f7 100644 --- a/github/PaginatedList.py +++ b/github/PaginatedList.py @@ -1,3 +1,5 @@ +import GithubObject + class PaginatedList: def __init__( self, contentClass, requester, headers, data ): self.__requester = requester @@ -49,6 +51,7 @@ class PaginatedList: def __fetchNextPage( self ): status, headers, data = self.__requester.request( "GET", self.__nextUrl, None, None ) + GithubObject.GithubObject._checkStatus( status, data ) return self.__appendData( headers, data ) def __appendData( self, headers, data ): diff --git a/github/Requester.py b/github/Requester.py index ef14244d..d62b5c19 100644 --- a/github/Requester.py +++ b/github/Requester.py @@ -39,7 +39,8 @@ class Requester: cnx.close() - self.rate_limiting = ( int( headers[ "x-ratelimit-remaining" ] ), int( headers[ "x-ratelimit-limit" ] ) ) + if "x-ratelimit-remaining" in headers and "x-ratelimit-limit" in headers: + self.rate_limiting = ( int( headers[ "x-ratelimit-remaining" ] ), int( headers[ "x-ratelimit-limit" ] ) ) # print verb, url, parameters, input, "==>", status, str( headers )[ :30 ], str( output )[ :30 ] return status, headers, output diff --git a/test/Exceptions.py b/test/Exceptions.py index 677145e1..da920ec2 100644 --- a/test/Exceptions.py +++ b/test/Exceptions.py @@ -2,9 +2,10 @@ import github import Framework +# To stay compatible with Python 2.6, we do not use self.assertRaises with only one argument class Exceptions( Framework.TestCase ): def testInvalidInput( self ): - try: # Stay compatible with Python 2.6: do not use self.assertRaises with only one argument + try: self.g.get_user().create_key( "Bad key", "xxx" ) self.fail( "Should have raised" ) except github.GithubException, exception: @@ -24,3 +25,30 @@ class Exceptions( Framework.TestCase ): } ) self.assertEqual( str( exception ), "422 {u\'message\': u\'Validation Failed\', u\'errors\': [{u\'field\': u\'key\', u\'message\': u\"key is invalid. It must begin with \'ssh-rsa\' or \'ssh-dss\'. Check that you\'re copying the public half of the key\", u\'code\': u\'custom\', u\'resource\': u\'PublicKey\'}]}" ) + + def testUnknownObject( self ): + try: + self.g.get_user().get_repo( "Xxx" ) + self.fail( "Should have raised" ) + except github.GithubException, exception: + self.assertEqual( exception.status, 404 ) + self.assertEqual( exception.data, { "message": "Not Found" } ) + self.assertEqual( str( exception ), "404 {u'message': u'Not Found'}" ) + + def testUnknownUser( self ): + try: + self.g.get_user( "ThisUserShouldReallyNotExist" ) + self.fail( "Should have raised" ) + except github.GithubException, exception: + self.assertEqual( exception.status, 404 ) + self.assertEqual( exception.data, { "message": "Not Found" } ) + self.assertEqual( str( exception ), "404 {u'message': u'Not Found'}" ) + + def testBadAuthentication( self ): + try: + github.Github( "BadUser", "BadPassword" ).get_user().login + self.fail( "Should have raised" ) + except github.GithubException, exception: + self.assertEqual( exception.status, 401 ) + self.assertEqual( exception.data, { "message": "Bad credentials" } ) + self.assertEqual( str( exception ), "401 {u'message': u'Bad credentials'}" ) diff --git a/test/ReplayData/Exceptions.testBadAuthentication.txt b/test/ReplayData/Exceptions.testBadAuthentication.txt new file mode 100644 index 00000000..084559c4 --- /dev/null +++ b/test/ReplayData/Exceptions.testBadAuthentication.txt @@ -0,0 +1,5 @@ +GET /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"} + diff --git a/test/ReplayData/Exceptions.testUnknownObject.txt b/test/ReplayData/Exceptions.testUnknownObject.txt new file mode 100644 index 00000000..9d3f7c1d --- /dev/null +++ b/test/ReplayData/Exceptions.testUnknownObject.txt @@ -0,0 +1,10 @@ +GET /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":""} + +GET /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"} + diff --git a/test/ReplayData/Exceptions.testUnknownUser.txt b/test/ReplayData/Exceptions.testUnknownUser.txt new file mode 100644 index 00000000..c41ea245 --- /dev/null +++ b/test/ReplayData/Exceptions.testUnknownUser.txt @@ -0,0 +1,5 @@ +GET /users/ThisUserShouldReallyNotExist {'Authorization': 'Basic login_and_password_removed'} null +404 +[('status', '404 Not Found'), ('x-ratelimit-remaining', '4968'), ('content-length', '23'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"e66a7a6c91e2c26803f3f49feb7a883f"'), ('date', 'Sat, 02 Jun 2012 12:24:43 GMT'), ('content-type', 'application/json; charset=utf-8')] +{"message":"Not Found"} +