From efa58887aa170aefe844d1c4a3f6c9d8d030548d Mon Sep 17 00:00:00 2001 From: Vincent Jacques Date: Thu, 10 May 2012 19:16:36 +0200 Subject: [PATCH] Test Branch and Commit attributes --- .../description.000.human_readable.json | 32 ++++- .../description.001.normalized.json | 124 ++++++++++++++++-- doc/ReferenceOfClasses.md | 30 ++++- src/github/Commit.py | 18 ++- src/github/CommitFile.py | 84 ++++++++++++ src/github/CommitStats.py | 45 +++++++ test/Framework/__init__.py | 4 +- test/GitObjects.py | 28 ++++ test/ReplayData/Branch.setUp.txt | 15 +++ test/ReplayData/Branch.testAttributes.txt | 5 + 10 files changed, 366 insertions(+), 19 deletions(-) create mode 100644 src/github/CommitFile.py create mode 100644 src/github/CommitStats.py create mode 100644 test/ReplayData/Branch.setUp.txt create mode 100644 test/ReplayData/Branch.testAttributes.txt diff --git a/codegen/JsonDescriptionOfGithubApiV3/description.000.human_readable.json b/codegen/JsonDescriptionOfGithubApiV3/description.000.human_readable.json index 82c97373..b7e4df24 100644 --- a/codegen/JsonDescriptionOfGithubApiV3/description.000.human_readable.json +++ b/codegen/JsonDescriptionOfGithubApiV3/description.000.human_readable.json @@ -275,13 +275,39 @@ "collections": [ ] }, + { + "name": "CommitStats", + "attributes": [ + { "name": "deletions", "type": "integer" }, + { "name": "additions", "type": "integer" }, + { "name": "total", "type": "integer" } + ], + "collections": [ + ] + }, + { + "name": "CommitFile", + "attributes": [ + { "name": "additions", "type": "@todo" }, + { "name": "blob_url", "type": "@todo" }, + { "name": "changes", "type": "@todo" }, + { "name": "deletions", "type": "@todo" }, + { "name": "filename", "type": "@todo" }, + { "name": "patch", "type": "@todo" }, + { "name": "raw_url", "type": "@todo" }, + { "name": "sha", "type": "@todo" }, + { "name": "status", "type": "@todo" } + ], + "collections": [ + ] + }, { "name": "Commit", "attributes": [ - { "name": "files", "type": "@todo" }, - { "name": "parents", "type": "@todo" }, + { "name": "files", "type": "list:CommitFile" }, + { "name": "parents", "type": "list:Commit" }, { "name": "sha", "type": "string" }, - { "name": "stats", "type": "@todo" }, + { "name": "stats", "type": "CommitStats" }, { "name": "url", "type": "string" }, { "name": "author", "type": "NamedUser" }, { "name": "committer", "type": "NamedUser" }, diff --git a/codegen/JsonDescriptionOfGithubApiV3/description.001.normalized.json b/codegen/JsonDescriptionOfGithubApiV3/description.001.normalized.json index 74e73dcb..7d819f06 100644 --- a/codegen/JsonDescriptionOfGithubApiV3/description.001.normalized.json +++ b/codegen/JsonDescriptionOfGithubApiV3/description.001.normalized.json @@ -1761,17 +1761,17 @@ }, { "type": { - "simple": true, - "cardinality": "scalar", - "name": "@todo" + "simple": false, + "cardinality": "list", + "name": "CommitFile" }, "name": "files" }, { "type": { - "simple": true, - "cardinality": "scalar", - "name": "@todo" + "simple": false, + "cardinality": "list", + "name": "Commit" }, "name": "parents" }, @@ -1785,9 +1785,9 @@ }, { "type": { - "simple": true, + "simple": false, "cardinality": "scalar", - "name": "@todo" + "name": "CommitStats" }, "name": "stats" }, @@ -2066,6 +2066,114 @@ } ] }, + { + "attributes": [ + { + "type": { + "simple": true, + "cardinality": "scalar", + "name": "@todo" + }, + "name": "additions" + }, + { + "type": { + "simple": true, + "cardinality": "scalar", + "name": "@todo" + }, + "name": "blob_url" + }, + { + "type": { + "simple": true, + "cardinality": "scalar", + "name": "@todo" + }, + "name": "changes" + }, + { + "type": { + "simple": true, + "cardinality": "scalar", + "name": "@todo" + }, + "name": "deletions" + }, + { + "type": { + "simple": true, + "cardinality": "scalar", + "name": "@todo" + }, + "name": "filename" + }, + { + "type": { + "simple": true, + "cardinality": "scalar", + "name": "@todo" + }, + "name": "patch" + }, + { + "type": { + "simple": true, + "cardinality": "scalar", + "name": "@todo" + }, + "name": "raw_url" + }, + { + "type": { + "simple": true, + "cardinality": "scalar", + "name": "@todo" + }, + "name": "sha" + }, + { + "type": { + "simple": true, + "cardinality": "scalar", + "name": "@todo" + }, + "name": "status" + } + ], + "name": "CommitFile", + "methods": [] + }, + { + "attributes": [ + { + "type": { + "simple": true, + "cardinality": "scalar", + "name": "integer" + }, + "name": "additions" + }, + { + "type": { + "simple": true, + "cardinality": "scalar", + "name": "integer" + }, + "name": "deletions" + }, + { + "type": { + "simple": true, + "cardinality": "scalar", + "name": "integer" + }, + "name": "total" + } + ], + "name": "CommitStats", + "methods": [] + }, { "attributes": [ { diff --git a/doc/ReferenceOfClasses.md b/doc/ReferenceOfClasses.md index e46e42e0..89398ae4 100644 --- a/doc/ReferenceOfClasses.md +++ b/doc/ReferenceOfClasses.md @@ -190,10 +190,10 @@ Attributes * `author`: `NamedUser` * `commit`: `GitCommit` * `committer`: `NamedUser` -* `files` -* `parents` +* `files`: list of `CommitFile` +* `parents`: list of `Commit` * `sha`: string -* `stats` +* `stats`: `CommitStats` * `url`: string Comments @@ -232,6 +232,30 @@ Modification * `edit( body )` * `body` +Class `CommitFile` +================== + +Attributes +---------- +* `additions` +* `blob_url` +* `changes` +* `deletions` +* `filename` +* `patch` +* `raw_url` +* `sha` +* `status` + +Class `CommitStats` +=================== + +Attributes +---------- +* `additions`: integer +* `deletions`: integer +* `total`: integer + Class `Download` ================ diff --git a/src/github/Commit.py b/src/github/Commit.py index 1203464e..8ca86b15 100644 --- a/src/github/Commit.py +++ b/src/github/Commit.py @@ -2,8 +2,11 @@ # Do not modify it manually, your work would be lost. import PaginatedList +import CommitFile import NamedUser import GitCommit +import CommitStats +import Commit import CommitComment # This allows None as a valid value for an optional parameter @@ -130,14 +133,23 @@ class Commit( object ): assert isinstance( attributes[ "committer" ], dict ) self.__committer = NamedUser.NamedUser( self.__requester, attributes[ "committer" ], lazy = True ) if "files" in attributes and attributes[ "files" ] is not None: - self.__files = attributes[ "files" ] + assert isinstance( attributes[ "files" ], list ) and ( len( attributes[ "files" ] ) == 0 or isinstance( attributes[ "files" ][ 0 ], dict ) ) + self.__files = [ + CommitFile.CommitFile( self.__requester, element, lazy = True ) + for element in attributes[ "files" ] + ] if "parents" in attributes and attributes[ "parents" ] is not None: - self.__parents = attributes[ "parents" ] + assert isinstance( attributes[ "parents" ], list ) and ( len( attributes[ "parents" ] ) == 0 or isinstance( attributes[ "parents" ][ 0 ], dict ) ) + self.__parents = [ + Commit( self.__requester, element, lazy = True ) + for element in attributes[ "parents" ] + ] if "sha" in attributes and attributes[ "sha" ] is not None: assert isinstance( attributes[ "sha" ], ( str, unicode ) ) self.__sha = attributes[ "sha" ] if "stats" in attributes and attributes[ "stats" ] is not None: - self.__stats = attributes[ "stats" ] + assert isinstance( attributes[ "stats" ], dict ) + self.__stats = CommitStats.CommitStats( self.__requester, attributes[ "stats" ], lazy = True ) if "url" in attributes and attributes[ "url" ] is not None: assert isinstance( attributes[ "url" ], ( str, unicode ) ) self.__url = attributes[ "url" ] diff --git a/src/github/CommitFile.py b/src/github/CommitFile.py new file mode 100644 index 00000000..56a74354 --- /dev/null +++ b/src/github/CommitFile.py @@ -0,0 +1,84 @@ +# WARNING: this file is generated automaticaly. +# Do not modify it manually, your work would be lost. + +import PaginatedList +# This allows None as a valid value for an optional parameter + +class DefaultValueForOptionalParametersType: + pass +DefaultValueForOptionalParameters = DefaultValueForOptionalParametersType() + +class CommitFile( object ): + def __init__( self, requester, attributes, lazy ): + self.__requester = requester + self.__completed = False + self.__initAttributes() + self.__useAttributes( attributes ) + + @property + def additions( self ): + return self.__additions + + @property + def blob_url( self ): + return self.__blob_url + + @property + def changes( self ): + return self.__changes + + @property + def deletions( self ): + return self.__deletions + + @property + def filename( self ): + return self.__filename + + @property + def patch( self ): + return self.__patch + + @property + def raw_url( self ): + return self.__raw_url + + @property + def sha( self ): + return self.__sha + + @property + def status( self ): + return self.__status + + def __initAttributes( self ): + self.__additions = None + self.__blob_url = None + self.__changes = None + self.__deletions = None + self.__filename = None + self.__patch = None + self.__raw_url = None + self.__sha = None + self.__status = None + + def __useAttributes( self, attributes ): + #@todo No need to check if attribute is in attributes when attribute is mandatory + if "additions" in attributes and attributes[ "additions" ] is not None: + self.__additions = attributes[ "additions" ] + if "blob_url" in attributes and attributes[ "blob_url" ] is not None: + self.__blob_url = attributes[ "blob_url" ] + if "changes" in attributes and attributes[ "changes" ] is not None: + self.__changes = attributes[ "changes" ] + if "deletions" in attributes and attributes[ "deletions" ] is not None: + self.__deletions = attributes[ "deletions" ] + if "filename" in attributes and attributes[ "filename" ] is not None: + self.__filename = attributes[ "filename" ] + if "patch" in attributes and attributes[ "patch" ] is not None: + self.__patch = attributes[ "patch" ] + if "raw_url" in attributes and attributes[ "raw_url" ] is not None: + self.__raw_url = attributes[ "raw_url" ] + if "sha" in attributes and attributes[ "sha" ] is not None: + self.__sha = attributes[ "sha" ] + if "status" in attributes and attributes[ "status" ] is not None: + self.__status = attributes[ "status" ] diff --git a/src/github/CommitStats.py b/src/github/CommitStats.py new file mode 100644 index 00000000..8cda88c0 --- /dev/null +++ b/src/github/CommitStats.py @@ -0,0 +1,45 @@ +# WARNING: this file is generated automaticaly. +# Do not modify it manually, your work would be lost. + +import PaginatedList +# This allows None as a valid value for an optional parameter + +class DefaultValueForOptionalParametersType: + pass +DefaultValueForOptionalParameters = DefaultValueForOptionalParametersType() + +class CommitStats( object ): + def __init__( self, requester, attributes, lazy ): + self.__requester = requester + self.__completed = False + self.__initAttributes() + self.__useAttributes( attributes ) + + @property + def additions( self ): + return self.__additions + + @property + def deletions( self ): + return self.__deletions + + @property + def total( self ): + return self.__total + + def __initAttributes( self ): + self.__additions = None + self.__deletions = None + self.__total = None + + def __useAttributes( self, attributes ): + #@todo No need to check if attribute is in attributes when attribute is mandatory + if "additions" in attributes and attributes[ "additions" ] is not None: + assert isinstance( attributes[ "additions" ], int ) + self.__additions = attributes[ "additions" ] + if "deletions" in attributes and attributes[ "deletions" ] is not None: + assert isinstance( attributes[ "deletions" ], int ) + self.__deletions = attributes[ "deletions" ] + if "total" in attributes and attributes[ "total" ] is not None: + assert isinstance( attributes[ "total" ], int ) + self.__total = attributes[ "total" ] diff --git a/test/Framework/__init__.py b/test/Framework/__init__.py index ca289970..61f55266 100644 --- a/test/Framework/__init__.py +++ b/test/Framework/__init__.py @@ -4,7 +4,7 @@ import unittest import httplib import traceback -sys.path.append( os.path.join( "..", "src" ) ) +sys.path.append( os.path.join( os.path.dirname( __file__ ), "..", "..", "src" ) ) import github class FakeHttpResponse: @@ -91,7 +91,7 @@ class TestCase( unittest.TestCase ): def __openFile( self, mode ): for ( _, _, functionName, _ ) in traceback.extract_stack(): if functionName.startswith( "test" ) or functionName == "setUp" or functionName == "tearDown": - fileName = os.path.join( "ReplayData", self.__class__.__name__ + "." + functionName + ".txt" ) + fileName = os.path.join( os.path.dirname( __file__ ), "..", "ReplayData", self.__class__.__name__ + "." + functionName + ".txt" ) if fileName != self.__fileName: self.__closeReplayFileIfNeeded() self.__fileName = fileName diff --git a/test/GitObjects.py b/test/GitObjects.py index 8044afd8..e839e087 100644 --- a/test/GitObjects.py +++ b/test/GitObjects.py @@ -43,3 +43,31 @@ class GitTree( Framework.TestCase ): self.assertEqual( self.t.sha, "f492784d8ca837779650d1fb406a1a3587a764ad" ) self.assertEqual( len( self.t.tree ), 11 ) self.assertEqual( self.t.url, "https://api.github.com/repos/jacquev6/PyGithub/git/trees/f492784d8ca837779650d1fb406a1a3587a764ad" ) + +class Branch( Framework.TestCase ): + def setUp( self ): + Framework.TestCase.setUp( self ) + self.b = self.g.get_user().get_repo( "PyGithub" ).get_branches()[ 0 ] + + def testAttributes( self ): + self.assertEqual( self.b.name, "topic/RewriteWithGeneratedCode" ) + self.assertEqual( self.b.commit.author.login, "jacquev6" ) + self.assertEqual( self.b.commit.commit.url, "https://api.github.com/repos/jacquev6/PyGithub/git/commits/1292bf0e22c796e91cc3d6e24b544aece8c21f2a" ) + self.assertEqual( self.b.commit.committer.login, "jacquev6" ) + self.assertEqual( len( self.b.commit.files ), 1 ) + self.assertEqual( self.b.commit.files[ 0 ].additions, 0 ) + self.assertEqual( self.b.commit.files[ 0 ].blob_url, "https://github.com/jacquev6/PyGithub/blob/1292bf0e22c796e91cc3d6e24b544aece8c21f2a/github/GithubObjects/GitAuthor.py" ) + self.assertEqual( self.b.commit.files[ 0 ].changes, 20 ) + self.assertEqual( self.b.commit.files[ 0 ].deletions, 20 ) + self.assertEqual( self.b.commit.files[ 0 ].filename, "github/GithubObjects/GitAuthor.py" ) + self.assertTrue( isinstance( self.b.commit.files[ 0 ].patch, ( str, unicode ) ) ) + self.assertEqual( self.b.commit.files[ 0 ].raw_url, "https://github.com/jacquev6/PyGithub/raw/1292bf0e22c796e91cc3d6e24b544aece8c21f2a/github/GithubObjects/GitAuthor.py" ) + self.assertEqual( self.b.commit.files[ 0 ].sha, "1292bf0e22c796e91cc3d6e24b544aece8c21f2a" ) + self.assertEqual( self.b.commit.files[ 0 ].status, "modified" ) + self.assertEqual( len( self.b.commit.parents ), 1 ) + self.assertEqual( self.b.commit.parents[ 0 ].sha, "b46ed0dfde5ad02d3b91eb54a41c5ed960710eae" ) + self.assertEqual( self.b.commit.sha, "1292bf0e22c796e91cc3d6e24b544aece8c21f2a" ) + self.assertEqual( self.b.commit.stats.deletions, 20 ) + self.assertEqual( self.b.commit.stats.additions, 0 ) + self.assertEqual( self.b.commit.stats.total, 20 ) + self.assertEqual( self.b.commit.url, "https://api.github.com/repos/jacquev6/PyGithub/commits/1292bf0e22c796e91cc3d6e24b544aece8c21f2a" ) diff --git a/test/ReplayData/Branch.setUp.txt b/test/ReplayData/Branch.setUp.txt new file mode 100644 index 00000000..0a7ff8ec --- /dev/null +++ b/test/ReplayData/Branch.setUp.txt @@ -0,0 +1,15 @@ +GET /user {} null +200 +[('status', '200 OK'), ('x-ratelimit-remaining', '4996'), ('content-length', '801'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"a75680b0a273750e28712b17de498c31"'), ('date', 'Thu, 10 May 2012 13:56:54 GMT'), ('content-type', 'application/json; charset=utf-8')] +{"private_gists":5,"type":"User","url":"https://api.github.com/users/jacquev6","public_repos":10,"disk_usage":16776,"collaborators":0,"plan":{"private_repos":5,"collaborators":1,"space":614400,"name":"micro"},"blog":"http://vincent-jacques.net","hireable":false,"login":"jacquev6","email":"vincent@vincent-jacques.net","bio":"","html_url":"https://github.com/jacquev6","created_at":"2010-07-09T06:10:06Z","company":"Criteo","location":"Paris, France","total_private_repos":5,"followers":13,"following":24,"name":"Vincent Jacques","gravatar_id":"b68de5ae38616c296fa345d2b9df2225","public_gists":1,"id":327146,"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"} + +GET /repos/jacquev6/PyGithub {} null +200 +[('status', '200 OK'), ('x-ratelimit-remaining', '4995'), ('content-length', '1097'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"47c45737a82a30cca731e2768121d6ad"'), ('date', 'Thu, 10 May 2012 13:56:55 GMT'), ('content-type', 'application/json; charset=utf-8')] +{"mirror_url":null,"homepage":"http://vincent-jacques.net/PyGithub","clone_url":"https://github.com/jacquev6/PyGithub.git","url":"https://api.github.com/repos/jacquev6/PyGithub","has_downloads":true,"watchers":13,"permissions":{"admin":true,"pull":true,"push":true},"git_url":"git://github.com/jacquev6/PyGithub.git","has_wiki":false,"has_issues":true,"fork":false,"forks":2,"language":"Python","size":264,"description":"Python library implementing the full Github API v3","html_url":"https://github.com/jacquev6/PyGithub","private":false,"created_at":"2012-02-25T12:53:47Z","open_issues":15,"svn_url":"https://github.com/jacquev6/PyGithub","owner":{"url":"https://api.github.com/users/jacquev6","login":"jacquev6","gravatar_id":"b68de5ae38616c296fa345d2b9df2225","id":327146,"avatar_url":"https://secure.gravatar.com/avatar/b68de5ae38616c296fa345d2b9df2225?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png"},"name":"PyGithub","pushed_at":"2012-05-09T16:22:47Z","id":3544490,"ssh_url":"git@github.com:jacquev6/PyGithub.git","updated_at":"2012-05-09T16:22:48Z"} + +GET /repos/jacquev6/PyGithub/branches {} null +200 +[('status', '200 OK'), ('x-ratelimit-remaining', '4994'), ('content-length', '769'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"2dada6dafd332016bcdf06e42487e520"'), ('date', 'Thu, 10 May 2012 13:56:55 GMT'), ('content-type', 'application/json; charset=utf-8')] +[{"name":"topic/RewriteWithGeneratedCode","commit":{"sha":"1292bf0e22c796e91cc3d6e24b544aece8c21f2a","url":"https://api.github.com/repos/jacquev6/PyGithub/commits/1292bf0e22c796e91cc3d6e24b544aece8c21f2a"}},{"name":"master","commit":{"sha":"4303c5b90e2216d927155e9609436ccb8984c495","url":"https://api.github.com/repos/jacquev6/PyGithub/commits/4303c5b90e2216d927155e9609436ccb8984c495"}},{"name":"topic/DependencyGraph","commit":{"sha":"05157f11f29a3ac057e35d2487880c5d08bd69af","url":"https://api.github.com/repos/jacquev6/PyGithub/commits/05157f11f29a3ac057e35d2487880c5d08bd69af"}},{"name":"develop","commit":{"sha":"4303c5b90e2216d927155e9609436ccb8984c495","url":"https://api.github.com/repos/jacquev6/PyGithub/commits/4303c5b90e2216d927155e9609436ccb8984c495"}}] + diff --git a/test/ReplayData/Branch.testAttributes.txt b/test/ReplayData/Branch.testAttributes.txt new file mode 100644 index 00000000..eecee1bc --- /dev/null +++ b/test/ReplayData/Branch.testAttributes.txt @@ -0,0 +1,5 @@ +GET /repos/jacquev6/PyGithub/commits/1292bf0e22c796e91cc3d6e24b544aece8c21f2a {} null +200 +[('status', '200 OK'), ('x-ratelimit-remaining', '4993'), ('content-length', '3445'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"8fe19b9f161802e61f531cec78c69463"'), ('date', 'Thu, 10 May 2012 13:56:56 GMT'), ('content-type', 'application/json; charset=utf-8')] +{"url":"https://api.github.com/repos/jacquev6/PyGithub/commits/1292bf0e22c796e91cc3d6e24b544aece8c21f2a","stats":{"deletions":20,"additions":0,"total":20},"committer":{"url":"https://api.github.com/users/jacquev6","avatar_url":"https://secure.gravatar.com/avatar/b68de5ae38616c296fa345d2b9df2225?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","login":"jacquev6","gravatar_id":"b68de5ae38616c296fa345d2b9df2225","id":327146},"files":[{"status":"modified","changes":20,"blob_url":"https://github.com/jacquev6/PyGithub/blob/1292bf0e22c796e91cc3d6e24b544aece8c21f2a/github/GithubObjects/GitAuthor.py","deletions":20,"raw_url":"https://github.com/jacquev6/PyGithub/raw/1292bf0e22c796e91cc3d6e24b544aece8c21f2a/github/GithubObjects/GitAuthor.py","patch":"@@ -14,22 +14,17 @@ def __init__( self, requester, attributes, lazy ):\n self.__completed = False\n self.__initAttributes()\n self.__useAttributes( attributes )\n- if not lazy:\n- self.__complete()\n \n @property\n def date( self ):\n- self.__completeIfNeeded( self.__date )\n return self.__date\n \n @property\n def email( self ):\n- self.__completeIfNeeded( self.__email )\n return self.__email\n \n @property\n def name( self ):\n- self.__completeIfNeeded( self.__name )\n return self.__name\n \n def __initAttributes( self ):\n@@ -37,21 +32,6 @@ def __initAttributes( self ):\n self.__email = None\n self.__name = None\n \n- def __completeIfNeeded( self, testedAttribute ):\n- if not self.__completed and testedAttribute is None:\n- self.__complete()\n-\n- # @todo Do not generate __complete if type has no url attribute\n- def __complete( self ):\n- status, headers, data = self.__requester.request(\n- \"GET\",\n- self.__url,\n- None,\n- None\n- )\n- self.__useAttributes( data )\n- self.__completed = True\n-\n def __useAttributes( self, attributes ):\n #@todo No need to check if attribute is in attributes when attribute is mandatory\n if \"date\" in attributes and attributes[ \"date\" ] is not None:","additions":0,"filename":"github/GithubObjects/GitAuthor.py","sha":"1292bf0e22c796e91cc3d6e24b544aece8c21f2a"}],"sha":"1292bf0e22c796e91cc3d6e24b544aece8c21f2a","parents":[{"url":"https://api.github.com/repos/jacquev6/PyGithub/commits/b46ed0dfde5ad02d3b91eb54a41c5ed960710eae","sha":"b46ed0dfde5ad02d3b91eb54a41c5ed960710eae"}],"commit":{"url":"https://api.github.com/repos/jacquev6/PyGithub/git/commits/1292bf0e22c796e91cc3d6e24b544aece8c21f2a","message":"Remove completion functions from GitAuthor","committer":{"email":"vincent@vincent-jacques.net","date":"2012-05-09T09:22:33-07:00","name":"Vincent Jacques"},"tree":{"url":"https://api.github.com/repos/jacquev6/PyGithub/git/trees/4c6bd50994f0f9823f898b1c6c964ad7d4fa11ab","sha":"4c6bd50994f0f9823f898b1c6c964ad7d4fa11ab"},"author":{"email":"vincent@vincent-jacques.net","date":"2012-05-09T09:22:33-07:00","name":"Vincent Jacques"}},"author":{"url":"https://api.github.com/users/jacquev6","avatar_url":"https://secure.gravatar.com/avatar/b68de5ae38616c296fa345d2b9df2225?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","login":"jacquev6","gravatar_id":"b68de5ae38616c296fa345d2b9df2225","id":327146}} +