From 5f74f480a677b2bc609e5ad39e33f7e67cdda971 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Thu, 16 Dec 2010 22:59:12 +0100 Subject: [PATCH 1/5] Add teams and organizations --- github2/client.py | 4 ++++ github2/organizations.py | 52 ++++++++++++++++++++++++++++++++++++++++ github2/teams.py | 27 +++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 github2/organizations.py create mode 100644 github2/teams.py diff --git a/github2/client.py b/github2/client.py index f294b45..893457f 100644 --- a/github2/client.py +++ b/github2/client.py @@ -3,6 +3,8 @@ from github2.repositories import Repositories from github2.users import Users from github2.commits import Commits +from github2.organizations import Organizations +from github2.teams import Teams class Github(object): @@ -38,6 +40,8 @@ def __init__(self, username=None, api_token=None, debug=False, self.users = Users(self.request) self.repos = Repositories(self.request) self.commits = Commits(self.request) + self.organizations = Organizations(self.request) + self.teams = Teams(self.request) def project_for_user_repo(self, user, repo): return "/".join([user, repo]) diff --git a/github2/organizations.py b/github2/organizations.py new file mode 100644 index 0000000..28369ac --- /dev/null +++ b/github2/organizations.py @@ -0,0 +1,52 @@ +from github2.core import BaseData, GithubCommand, Attribute, DateAttribute +from github2.users import User +from github2.teams import Team +from github2.repositories import Repository +import urllib + + +class Organization(BaseData): + """ An organization """ + id = Attribute("The organization id") + name = Attribute("The name for the organization.") + disk_usage = Attribute("Currently used disk space") + billing_email = Attribute("The billing email for the organization.") + gravatar_id = Attribute("The id of the organization's gravatar image.") + location = Attribute("Where this organization is located.") + followers_count = Attribute("Number of users following this organization.") + following_count = Attribute("Number of users this organization is following.") + public_gist_count = Attribute("Number of active public gists owned by the organization.") + public_repo_count = Attribute("Number of active public repos owned by the organization.") + owned_private_repo_count = Attribute("Number of active private repos owned by the organization") + total_private_repo_count = Attribute("Number of active private repos connected to the organization.") + private_gist_count = Attribute("Number of active private gists owned by the organization.") + plan = Attribute("The current active github plan for the organization.") + collaborators = Attribute("Number of collaborators of the organization.") + login = Attribute("The username for the organization.") + + def __repr__(self): + return "" % self.login + + +class Organizations(GithubCommand): + """ GithubCommand for getting an Organization """ + domain = "organizations" + + def show(self, org_name): + """ returns an organization """ + return self.get_value(org_name, filter="organization", + datatype=Organization) + + def teams(self, org_name): + """ Returns the teams owned by an organization """ + return self.get_values(org_name, "teams", filter='teams', datatype=Team) + + def repositories(self, org_name): + """ Returns the list of repos owned by an org """ + return self.get_values(org_name, "repositories", filter="repositories", + datatype=Repository) + + def public_members(self, org_name): + """ returns the list of public members """ + return self.get_values(org_name, "public_members", filter="users", + datatype=User) diff --git a/github2/teams.py b/github2/teams.py new file mode 100644 index 0000000..72054f1 --- /dev/null +++ b/github2/teams.py @@ -0,0 +1,27 @@ +from github2.core import BaseData, GithubCommand, Attribute, DateAttribute +from github2.users import User +from github2.repositories import Repository +import urllib + +class Team(BaseData): + """ A team """ + name = Attribute("The team name") + id = Attribute("The team id") + permission = Attribute("The permissions the team has") + + def __repr__(self): + return "" % self.name + + +class Teams(GithubCommand): + """ Github command for operating on teams """ + domain = "teams" + + def members(self, team_id): + """ Returns the list of members of a team """ + return self.get_values(str(team_id), "members", filter="users", + datatype=User) + + def repositories(self, team_id): + return self.get_values(str(team_id), "repositories", filter='repositories', + datatype=Repository) From 8c6fea5307534ff0a3d339d1bdfb10bb4f0257e6 Mon Sep 17 00:00:00 2001 From: Paul Querna Date: Wed, 19 Jan 2011 16:20:21 -0800 Subject: [PATCH 2/5] Add support for DELETE methods on requests --- github2/core.py | 5 +++-- github2/request.py | 9 +++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/github2/core.py b/github2/core.py index b7c0a1e..1b5afbc 100644 --- a/github2/core.py +++ b/github2/core.py @@ -35,8 +35,9 @@ def make_request(self, command, *args, **kwargs): filter = kwargs.get("filter") post_data = kwargs.get("post_data") or {} method = kwargs.get("method", "GET") - if post_data or method.upper() == "POST": - response = self.request.post(self.domain, command, *args, + if post_data or method != "GET": + func = getattr(self.request, method.lower()) + response = func(self.domain, command, *args, **post_data) else: response = self.request.get(self.domain, command, *args) diff --git a/github2/request.py b/github2/request.py index e8c2188..fdcb088 100644 --- a/github2/request.py +++ b/github2/request.py @@ -71,6 +71,11 @@ def get(self, *path_components): path_components = filter(None, path_components) return self.make_request("/".join(path_components)) + def delete(self, *path_components, **extra_post_data): + path_components = filter(None, path_components) + return self.make_request("/".join(path_components), extra_post_data, + method="DELETE") + def post(self, *path_components, **extra_post_data): path_components = filter(None, path_components) return self.make_request("/".join(path_components), extra_post_data, @@ -114,8 +119,8 @@ def raw_request(self, url, extra_post_data, method="GET"): response = connection.getresponse() response_text = response.read() if self.debug: - sys.stderr.write("URL:[%s] POST_DATA:%s RESPONSE_TEXT: [%s]\n" % ( - path, post_data, response_text)) + sys.stderr.write("URL:[%s] METHOD:%s BODY:%s RESPONSE_TEXT: [%s]\n" % ( + path, method, post_data, response_text)) if response.status >= 400: raise RuntimeError("unexpected response from github.com %d: %r" % ( response.status, response_text)) From 72243813683d2a49e8baf8d93d36d1825b564441 Mon Sep 17 00:00:00 2001 From: Paul Querna Date: Wed, 19 Jan 2011 16:27:46 -0800 Subject: [PATCH 3/5] Add {add,remove} member to the Team API --- github2/teams.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/github2/teams.py b/github2/teams.py index 72054f1..4eb4731 100644 --- a/github2/teams.py +++ b/github2/teams.py @@ -17,6 +17,16 @@ class Teams(GithubCommand): """ Github command for operating on teams """ domain = "teams" + def add_member(self, team_id, username): + """Adds a member to a team.""" + return self.make_request(str(team_id), "members", method="POST", + post_data={'name': username}) + + def remove_member(self, team_id, username): + """Removes a member from a team.""" + return self.make_request(str(team_id), "members", method="DELETE", + post_data={'name': username}) + def members(self, team_id): """ Returns the list of members of a team """ return self.get_values(str(team_id), "members", filter="users", From f123d09f454253bd89e35628314193f89ef3b5b3 Mon Sep 17 00:00:00 2001 From: Paul Querna Date: Fri, 21 Jan 2011 13:55:17 -0800 Subject: [PATCH 4/5] Add better support for passing a dict of query args around so that DELETE operations that don't have a body can work correctly --- github2/core.py | 9 +++++---- github2/request.py | 22 ++++++++++++---------- github2/teams.py | 2 +- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/github2/core.py b/github2/core.py index 1b5afbc..103bef3 100644 --- a/github2/core.py +++ b/github2/core.py @@ -35,12 +35,13 @@ def make_request(self, command, *args, **kwargs): filter = kwargs.get("filter") post_data = kwargs.get("post_data") or {} method = kwargs.get("method", "GET") - if post_data or method != "GET": - func = getattr(self.request, method.lower()) - response = func(self.domain, command, *args, + query = kwargs.get("query", None) + func = getattr(self.request, method.lower()) + if post_data: + response = func(self.domain, command, *args, query=query, **post_data) else: - response = self.request.get(self.domain, command, *args) + response = func(self.domain, command, *args, query=query) if filter: return response[filter] return response diff --git a/github2/request.py b/github2/request.py index fdcb088..7230805 100644 --- a/github2/request.py +++ b/github2/request.py @@ -67,21 +67,21 @@ def encode_authentication_data(self, extra_post_data): post_data.update(extra_post_data) return urlencode(post_data) - def get(self, *path_components): + def get(self, *path_components, **kwargs): path_components = filter(None, path_components) - return self.make_request("/".join(path_components)) + return self.make_request("/".join(path_components), query=kwargs.get('query')) - def delete(self, *path_components, **extra_post_data): + def delete(self, *path_components, **kwargs): path_components = filter(None, path_components) - return self.make_request("/".join(path_components), extra_post_data, - method="DELETE") + return self.make_request("/".join(path_components), + method="DELETE", query=kwargs.get('query')) def post(self, *path_components, **extra_post_data): path_components = filter(None, path_components) return self.make_request("/".join(path_components), extra_post_data, method="POST") - def make_request(self, path, extra_post_data=None, method="GET"): + def make_request(self, path, query=None, extra_post_data=None, method="GET"): if self.delay: since_last = (datetime.datetime.now() - self.last_request) since_last_in_seconds = (since_last.days * 24 * 60 * 60) + since_last.seconds + (since_last.microseconds/1000000.0) @@ -92,15 +92,17 @@ def make_request(self, path, extra_post_data=None, method="GET"): time.sleep(duration) extra_post_data = extra_post_data or {} + query = query or {} url = "/".join([self.url_prefix, path]) - result = self.raw_request(url, extra_post_data, method=method) + + result = self.raw_request(url, extra_post_data, query, method=method) if self.delay: self.last_request = datetime.datetime.now() return result - def raw_request(self, url, extra_post_data, method="GET"): - scheme, netloc, path, params, query, fragment = urlparse(url) + def raw_request(self, url, extra_post_data, query, method="GET"): + scheme, netloc, path, params, uquery, fragment = urlparse(url) hostname = netloc.split(':')[0] post_data = None headers = self.http_headers @@ -111,7 +113,7 @@ def raw_request(self, url, extra_post_data, method="GET"): headers["Content-Length"] = str(len(post_data)) else: path = urlunparse((scheme, netloc, path, params, - self.encode_authentication_data(parse_qs(query)), + self.encode_authentication_data(query), fragment)) connector = self.connector_for_scheme[scheme] connection = connector(hostname) diff --git a/github2/teams.py b/github2/teams.py index 4eb4731..d71a9fe 100644 --- a/github2/teams.py +++ b/github2/teams.py @@ -25,7 +25,7 @@ def add_member(self, team_id, username): def remove_member(self, team_id, username): """Removes a member from a team.""" return self.make_request(str(team_id), "members", method="DELETE", - post_data={'name': username}) + query={'name': username}) def members(self, team_id): """ Returns the list of members of a team """ From e6b6c0bf8d6b9e45ac352d74ee3f2c7167dd6250 Mon Sep 17 00:00:00 2001 From: Paul Querna Date: Sat, 22 Jan 2011 23:23:06 -0800 Subject: [PATCH 5/5] Fix handling of post data since adding query support --- github2/request.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/github2/request.py b/github2/request.py index 7230805..3953f83 100644 --- a/github2/request.py +++ b/github2/request.py @@ -76,10 +76,10 @@ def delete(self, *path_components, **kwargs): return self.make_request("/".join(path_components), method="DELETE", query=kwargs.get('query')) - def post(self, *path_components, **extra_post_data): + def post(self, *path_components, **kwargs): path_components = filter(None, path_components) - return self.make_request("/".join(path_components), extra_post_data, - method="POST") + return self.make_request("/".join(path_components), extra_post_data=kwargs, + method="POST", query=kwargs.pop('query', None)) def make_request(self, path, query=None, extra_post_data=None, method="GET"): if self.delay: