From 64cead6d48f8c6a65ca89f90abc2fa010a36adaf Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Sat, 14 Sep 2013 08:47:18 +0200 Subject: [PATCH 1/8] Fix strings encoding (Closes #6) --- ChangeLog | 4 ++++ gitlab.py | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index d65ba934f..a4cb9954c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Version 0.4 + + * Fix strings encoding (Closes #6) + Version 0.3 * Use PRIVATE-TOKEN header for passing the auth token diff --git a/gitlab.py b/gitlab.py index 22e5a3b26..fc340867f 100644 --- a/gitlab.py +++ b/gitlab.py @@ -18,6 +18,7 @@ import json import requests +import sys __title__ = 'python-gitlab' __version__ = '0.3' @@ -298,8 +299,10 @@ def update(self, obj): # build a dict of data that can really be sent to server d = {} for k, v in obj.__dict__.items(): - if type(v) in (int, str, unicode, bool): + if type(v) in (int, str, bool): d[k] = str(v) + elif type(v) == unicode: + d[k] = str(v.encode(sys.stdout.encoding, "replace")) try: r = requests.put(url, d, headers=self.headers, verify=self.ssl_verify) @@ -520,6 +523,8 @@ def _obj_to_str(obj): elif isinstance(obj, list): s = ", ".join([GitlabObject._obj_to_str(x) for x in obj]) return "[ %s ]" %s + elif isinstance(obj, unicode): + return obj.encode(sys.stdout.encoding, "replace") else: return str(obj) @@ -530,7 +535,7 @@ def pretty_print(self, depth=0): if k == self.idAttr: continue v = self.__dict__[k] - pretty_k = k.replace('_', '-') + pretty_k = k.replace('_', '-').encode(sys.stdout.encoding, "replace") if isinstance(v, GitlabObject): if depth == 0: print("%s:" % pretty_k) From 6b0c678aa8a3081d17fc2852d64828f04f49b91b Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Sat, 14 Sep 2013 08:55:53 +0200 Subject: [PATCH 2/8] Allow to get a project commit (GitLab 6.1) --- ChangeLog | 1 + gitlab.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index a4cb9954c..7c25108cc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ Version 0.4 * Fix strings encoding (Closes #6) + * Allow to get a project commit (GitLab 6.1) Version 0.3 diff --git a/gitlab.py b/gitlab.py index fc340867f..897a178d8 100644 --- a/gitlab.py +++ b/gitlab.py @@ -645,7 +645,6 @@ def unprotect(self): class ProjectCommit(GitlabObject): _url = '/projects/%(project_id)s/repository/commits' - canGet = False canDelete = False canUpdate = False canCreate = False From 4006ab26ce73d03a8d74cfd978d93117ce8d68d6 Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Sat, 14 Sep 2013 09:05:09 +0200 Subject: [PATCH 3/8] ProjectMergeRequest: fix Note() method --- ChangeLog | 1 + gitlab.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 7c25108cc..c43b2ff73 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ Version 0.4 * Fix strings encoding (Closes #6) * Allow to get a project commit (GitLab 6.1) + * ProjectMergeRequest: fix Note() method Version 0.3 diff --git a/gitlab.py b/gitlab.py index 897a178d8..78af89d28 100644 --- a/gitlab.py +++ b/gitlab.py @@ -748,7 +748,7 @@ class ProjectMergeRequest(GitlabObject): def Note(self, id=None, **kwargs): return self._getListOrObject(ProjectMergeRequestNote, id, - project_id=self.id, + project_id=self.project_id, merge_request_id=self.id, **kwargs) From c9aedf21464b4c219aac4f46b53d591dc4f1ef16 Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Sat, 14 Sep 2013 11:55:10 +0200 Subject: [PATCH 4/8] Implement Gitlab 6.1 new methods - Project: tree, blob - ProjectCommit: diff, blob --- ChangeLog | 1 + gitlab | 2 +- gitlab.py | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index c43b2ff73..d8586ca14 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,7 @@ Version 0.4 * Fix strings encoding (Closes #6) * Allow to get a project commit (GitLab 6.1) * ProjectMergeRequest: fix Note() method + * Gitlab 6.1 methods: diff, blob (commit), tree, blob (project) Version 0.3 diff --git a/gitlab b/gitlab index 370cef170..44e0217a0 100755 --- a/gitlab +++ b/gitlab @@ -169,7 +169,7 @@ def do_get(cls, d): die("%s objects can't be retrieved" % what) id = None - if cls not in [gitlab.CurrentUser]: + if cls not in [gitlab.CurrentUser, gitlab.ProjectCommitDiff]: id = get_id() try: diff --git a/gitlab.py b/gitlab.py index 78af89d28..9414fc2a0 100644 --- a/gitlab.py +++ b/gitlab.py @@ -651,6 +651,25 @@ class ProjectCommit(GitlabObject): requiredListAttrs = ['project_id'] shortPrintAttr = 'title' + def diff(self): + url = '/projects/%(project_id)s/repository/commits/%(commit_id)s/diff' % \ + {'project_id': self.project_id, 'commit_id': self.id} + r = self.gitlab.rawGet(url) + if r.status_code == 200: + return r.json() + + raise GitlabGetError() + + def blob(self, filepath): + url = '/projects/%(project_id)s/repository/blobs/%(commit_id)s' % \ + {'project_id': self.project_id, 'commit_id': self.id} + url += '?filepath=%s' % filepath + r = self.gitlab.rawGet(url) + if r.status_code == 200: + return r.content + + raise GitlabGetError() + class ProjectKey(GitlabObject): _url = '/projects/%(project_id)s/keys' @@ -865,6 +884,24 @@ def Tag(self, id=None, **kwargs): project_id=self.id, **kwargs) + def tree(self, path='', ref_name=''): + url = "%s/%s/repository/tree" % (self._url, self.id) + url += '?path=%s&ref_name=%s' % (path, ref_name) + r = self.gitlab.rawGet(url) + if r.status_code == 200: + return r.json() + + raise GitlabGetError() + + def blob(self, sha, filepath): + url = "%s/%s/repository/blobs/%s" % (self._url, self.id, sha) + url += '?filepath=%s' % (filepath) + r = self.gitlab.rawGet(url) + if r.status_code == 200: + return r.content + + raise GitlabGetError() + class TeamMember(GitlabObject): _url = '/user_teams/%(team_id)s/members' From 4f001b4fe53661c5069ce6c689363bae4b8f7b51 Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Sun, 15 Sep 2013 14:53:18 +0200 Subject: [PATCH 5/8] drop leftovers from local tests --- gitlab | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitlab b/gitlab index 44e0217a0..370cef170 100755 --- a/gitlab +++ b/gitlab @@ -169,7 +169,7 @@ def do_get(cls, d): die("%s objects can't be retrieved" % what) id = None - if cls not in [gitlab.CurrentUser, gitlab.ProjectCommitDiff]: + if cls not in [gitlab.CurrentUser]: id = get_id() try: From 09ef68f3743bb32add0da7d5cd562dac5df00c26 Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Sun, 15 Sep 2013 15:22:05 +0200 Subject: [PATCH 6/8] minor syntax/pep8 updates --- gitlab.py | 133 ++++++++++++++++++++++++------------------------------ 1 file changed, 60 insertions(+), 73 deletions(-) diff --git a/gitlab.py b/gitlab.py index 9414fc2a0..b8042948b 100644 --- a/gitlab.py +++ b/gitlab.py @@ -75,7 +75,8 @@ class GitlabAuthenticationError(Exception): class Gitlab(object): """Represents a GitLab server connection""" - def __init__(self, url, private_token=None, email=None, password=None, ssl_verify=True): + def __init__(self, url, private_token=None, + email=None, password=None, ssl_verify=True): """Stores informations about the server url: the URL of the Gitlab server @@ -122,12 +123,8 @@ def setUrl(self, url): def setToken(self, token): """Sets the private token for authentication""" - if token: - self.private_token = token - self.headers = {"PRIVATE-TOKEN": token} - else: - self.private_token = None - self.headers = {} + self.private_token = token if token else None + self.headers = {"PRIVATE-TOKEN": token} if token else None def setCredentials(self, email, password): """Sets the email/login and password for authentication""" @@ -137,52 +134,48 @@ def setCredentials(self, email, password): def rawGet(self, path): url = '%s%s' % (self._url, path) try: - r = requests.get(url, headers=self.headers, verify=self.ssl_verify) + return requests.get(url, + headers=self.headers, + verify=self.ssl_verify) except: raise GitlabConnectionError( "Can't connect to GitLab server (%s)" % self._url) - return r - def rawPost(self, path, data): url = '%s%s' % (self._url, path) try: - r = requests.post(url, data, - headers=self.headers, - verify=self.ssl_verify) + return requests.post(url, data, + headers=self.headers, + verify=self.ssl_verify) except: raise GitlabConnectionError( "Can't connect to GitLab server (%s)" % self._url) - return r - def rawPut(self, path): url = '%s%s' % (self._url, path) try: - r = requests.put(url, headers=self.headers, verify=self.ssl_verify) + return requests.put(url, + headers=self.headers, + verify=self.ssl_verify) except: raise GitlabConnectionError( "Can't connect to GitLab server (%s)" % self._url) - return r - def list(self, obj_class, **kwargs): missing = [] for k in obj_class.requiredListAttrs: if k not in kwargs: - missing.append (k) + missing.append(k) if missing: - raise GitlabListError('Missing attribute(s): %s' % \ - ", ".join(missing)) + raise GitlabListError('Missing attribute(s): %s' % + ", ".join(missing)) - url = obj_class._url - if kwargs: - url = obj_class._url % kwargs + url = obj_class._url % kwargs url = '%s%s' % (self._url, url) if kwargs: url += "?%s" % ("&".join( - ["%s=%s" % (k, v) for k, v in kwargs.items()])) + ["%s=%s" % (k, v) for k, v in kwargs.items()])) try: r = requests.get(url, headers=self.headers, verify=self.ssl_verify) @@ -211,24 +204,16 @@ def get(self, obj_class, id=None, **kwargs): missing = [] for k in obj_class.requiredGetAttrs: if k not in kwargs: - missing.append (k) + missing.append(k) if missing: - raise GitlabListError('Missing attribute(s): %s' % \ - ", ".join(missing)) + raise GitlabListError('Missing attribute(s): %s' % + ", ".join(missing)) - url = obj_class._url - if kwargs: - url = obj_class._url % kwargs + url = obj_class._url % kwargs if id is not None: - try: - url = '%s%s/%d' % \ - (self._url, url, id) - except TypeError: # id might be a str (ProjectBranch) - url = '%s%s/%s' % \ - (self._url, url, id) + url = '%s%s/%s' % (self._url, url, str(id)) else: - url = '%s%s' % \ - (self._url, url) + url = '%s%s' % (self._url, url) try: r = requests.get(url, headers=self.headers, verify=self.ssl_verify) @@ -247,11 +232,12 @@ def get(self, obj_class, id=None, **kwargs): def delete(self, obj): url = obj._url % obj.__dict__ - url = '%s%s/%d' % \ - (self._url, url, obj.id) + url = '%s%s/%s' % (self._url, url, str(obj.id)) try: - r = requests.delete(url, headers=self.headers, verify=self.ssl_verify) + r = requests.delete(url, + headers=self.headers, + verify=self.ssl_verify) except: raise GitlabConnectionError( "Can't connect to GitLab server (%s)" % self._url) @@ -268,18 +254,18 @@ def create(self, obj): missing = [] for k in obj.requiredCreateAttrs: if k not in obj.__dict__: - missing.append (k) + missing.append(k) if missing: - raise GitlabCreateError('Missing attribute(s): %s' % \ - ", ".join(missing)) + raise GitlabCreateError('Missing attribute(s): %s' % + ", ".join(missing)) url = obj._url % obj.__dict__ url = '%s%s' % (self._url, url) try: - # TODO: avoid too much work on the server side by filtering the - # __dict__ keys - r = requests.post(url, obj.__dict__, headers=self.headers, verify=self.ssl_verify) + r = requests.post(url, obj.__dict__, + headers=self.headers, + verify=self.ssl_verify) except: raise GitlabConnectionError( "Can't connect to GitLab server (%s)" % self._url) @@ -293,8 +279,7 @@ def create(self, obj): def update(self, obj): url = obj._url % obj.__dict__ - url = '%s%s/%d' % \ - (self._url, url, obj.id) + url = '%s%s/%s' % (self._url, url, str(obj.id)) # build a dict of data that can really be sent to server d = {} @@ -305,7 +290,9 @@ def update(self, obj): d[k] = str(v.encode(sys.stdout.encoding, "replace")) try: - r = requests.put(url, d, headers=self.headers, verify=self.ssl_verify) + r = requests.put(url, d, + headers=self.headers, + verify=self.ssl_verify) except: raise GitlabConnectionError( "Can't connect to GitLab server (%s)" % self._url) @@ -427,17 +414,16 @@ def _getListOrObject(self, cls, id, **kwargs): if id is None: if not cls.canList: raise GitlabGetError - return cls.list(self.gitlab, **kwargs) + elif isinstance(id, dict): if not cls.canCreate: raise GitlabCreateError - return cls(self.gitlab, id, **kwargs) + else: if not cls.canGet: raise GitlabGetError - return cls(self.gitlab, id, **kwargs) def _getObject(self, k, v): @@ -511,18 +497,20 @@ def short_print(self, depth=0): id = self.__dict__[self.idAttr] print("%s%s: %s" % (" " * depth * 2, self.idAttr, id)) if self.shortPrintAttr: - print ("%s%s: %s" % (" " * depth * 2, - self.shortPrintAttr.replace('_', '-'), - self.__dict__[self.shortPrintAttr])) + print("%s%s: %s" % (" " * depth * 2, + self.shortPrintAttr.replace('_', '-'), + self.__dict__[self.shortPrintAttr])) @staticmethod def _obj_to_str(obj): if isinstance(obj, dict): - s = ", ".join(["%s: %s" % (x, GitlabObject._obj_to_str(y)) for (x, y) in obj.items()]) + s = ", ".join(["%s: %s" % + (x, GitlabObject._obj_to_str(y)) + for (x, y) in obj.items()]) return "{ %s }" % s elif isinstance(obj, list): s = ", ".join([GitlabObject._obj_to_str(x) for x in obj]) - return "[ %s ]" %s + return "[ %s ]" % s elif isinstance(obj, unicode): return obj.encode(sys.stdout.encoding, "replace") else: @@ -535,7 +523,8 @@ def pretty_print(self, depth=0): if k == self.idAttr: continue v = self.__dict__[k] - pretty_k = k.replace('_', '-').encode(sys.stdout.encoding, "replace") + pretty_k = k.replace('_', '-').encode(sys.stdout.encoding, + "replace") if isinstance(v, GitlabObject): if depth == 0: print("%s:" % pretty_k) @@ -589,8 +578,7 @@ class Group(GitlabObject): shortPrintAttr = 'name' def transfer_project(self, id): - url = '/groups/%d/projects/%d' % \ - (self.id, id) + url = '/groups/%d/projects/%d' % (self.id, id) r = self.gitlab.rawPost(url, None) if r.status_code != 201: raise GitlabTransferProjectError() @@ -625,10 +613,8 @@ class ProjectBranch(GitlabObject): def protect(self, protect=True): url = self._url % {'project_id': self.project_id} - if protect: - url = "%s/%s/protect" % (url, self.name) - else: - url = "%s/%s/unprotect" % (url, self.name) + action = 'protect' if protect else 'unprotect' + url = "%s/%s/%s" % (url, self.name, action) r = self.gitlab.rawPut(url) if r.status_code == 200: @@ -653,22 +639,22 @@ class ProjectCommit(GitlabObject): def diff(self): url = '/projects/%(project_id)s/repository/commits/%(commit_id)s/diff' % \ - {'project_id': self.project_id, 'commit_id': self.id} + {'project_id': self.project_id, 'commit_id': self.id} r = self.gitlab.rawGet(url) if r.status_code == 200: return r.json() - raise GitlabGetError() + raise GitlabGetError def blob(self, filepath): url = '/projects/%(project_id)s/repository/blobs/%(commit_id)s' % \ - {'project_id': self.project_id, 'commit_id': self.id} + {'project_id': self.project_id, 'commit_id': self.id} url += '?filepath=%s' % filepath r = self.gitlab.rawGet(url) if r.status_code == 200: return r.content - raise GitlabGetError() + raise GitlabGetError class ProjectKey(GitlabObject): @@ -762,7 +748,8 @@ class ProjectMergeRequest(GitlabObject): canDelete = False requiredListAttrs = ['project_id'] requiredGetAttrs = ['project_id'] - requiredCreateAttrs = ['project_id', 'source_branch', 'target_branch', 'title'] + requiredCreateAttrs = ['project_id', 'source_branch', + 'target_branch', 'title'] optionalCreateAttrs = ['assignee_id'] def Note(self, id=None, **kwargs): @@ -891,7 +878,7 @@ def tree(self, path='', ref_name=''): if r.status_code == 200: return r.json() - raise GitlabGetError() + raise GitlabGetError def blob(self, sha, filepath): url = "%s/%s/repository/blobs/%s" % (self._url, self.id, sha) @@ -900,7 +887,7 @@ def blob(self, sha, filepath): if r.status_code == 200: return r.content - raise GitlabGetError() + raise GitlabGetError class TeamMember(GitlabObject): From 71c87508a268fafbcb0043617ec3aa7ed0e733fd Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Thu, 26 Sep 2013 20:38:58 +0200 Subject: [PATCH 7/8] Add support for Gitlab 6.1 group members --- ChangeLog | 1 + gitlab.py | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index d8586ca14..9da1715be 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,7 @@ Version 0.4 * Allow to get a project commit (GitLab 6.1) * ProjectMergeRequest: fix Note() method * Gitlab 6.1 methods: diff, blob (commit), tree, blob (project) + * Add support for Gitlab 6.1 group members Version 0.3 diff --git a/gitlab.py b/gitlab.py index b8042948b..628bf1853 100644 --- a/gitlab.py +++ b/gitlab.py @@ -187,7 +187,7 @@ def list(self, obj_class, **kwargs): cls = obj_class if obj_class._returnClass: cls = obj_class._returnClass - l = [cls(self, item) for item in r.json()] + l = [cls(self, item) for item in r.json() if item is not None] if kwargs: for k, v in kwargs.items(): if k in ('page', 'per_page'): @@ -571,12 +571,26 @@ def Key(self, id=None, **kwargs): return CurrentUserKey(self.gitlab, id) +class GroupMember(GitlabObject): + _url = '/groups/%(group_id)s/members' + canGet = False + canUpdate = False + requiredCreateAttrs = ['group_id', 'user_id', 'access_level'] + requiredDeleteAttrs = ['group_id', 'user_id'] + shortPrintAttr = 'username' + + class Group(GitlabObject): _url = '/groups' _constructorTypes = {'projects': 'Project'} requiredCreateAttrs = ['name', 'path'] shortPrintAttr = 'name' + def Member(self, id=None, **kwargs): + return self._getListOrObject(GroupMember, id, + group_id=self.id, + **kwargs) + def transfer_project(self, id): url = '/groups/%d/projects/%d' % (self.id, id) r = self.gitlab.rawPost(url, None) From 5f0136c7f84c7c6235d360aee6104232639a1d63 Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Thu, 26 Sep 2013 20:43:05 +0200 Subject: [PATCH 8/8] version bump --- gitlab.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitlab.py b/gitlab.py index 628bf1853..c6dfed2a4 100644 --- a/gitlab.py +++ b/gitlab.py @@ -21,7 +21,7 @@ import sys __title__ = 'python-gitlab' -__version__ = '0.3' +__version__ = '0.4' __author__ = 'Gauvain Pocentek' __email__ = 'gauvain@pocentek.net' __license__ = 'LGPL3'