diff --git a/github2/client.py b/github2/client.py index 79bd446..e9de604 100644 --- a/github2/client.py +++ b/github2/client.py @@ -1,7 +1,7 @@ from github2.request import GithubRequest from github2.issues import Issues from github2.repositories import Repositories -from github2.users import Users +from github2.users import Users, LegacySearch from github2.commits import Commits class Github(object): @@ -33,6 +33,7 @@ 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.search = LegacySearch(self.request) def project_for_user_repo(self, user, repo): return "/".join([user, repo]) diff --git a/github2/commits.py b/github2/commits.py index 3d4a700..dbe31e8 100644 --- a/github2/commits.py +++ b/github2/commits.py @@ -26,9 +26,9 @@ def __repr__(self): class Commits(GithubCommand): domain = "commits" - def list(self, project, branch="master", file=None): + def list(self, project, branch="master", file=None,page=1): return self.get_values("list", project, branch, file, - filter="commits", datatype=Commit) + filter="commits", datatype=Commit, get_data = {'page':page}) def show(self, project, sha): return self.get_value("show", project, sha, diff --git a/github2/core.py b/github2/core.py index a97d0e4..7250b78 100644 --- a/github2/core.py +++ b/github2/core.py @@ -35,12 +35,13 @@ def __init__(self, request): def make_request(self, command, *args, **kwargs): filter = kwargs.get("filter") post_data = kwargs.get("post_data") or {} + get_data = kwargs.get("get_data") or {} method = kwargs.get("method", "GET") if post_data or method.upper() == "POST": response = self.request.post(self.domain, command, *args, **post_data) else: - response = self.request.get(self.domain, command, *args) + response = self.request.get(self.domain, command, *args,**get_data) if filter: return response[filter] return response @@ -148,14 +149,16 @@ def constructor(self, **kwargs): _contribute_method("__init__", constructor) def to_dict(self): - _meta = self.meta + _meta = self._meta dict_ = vars(self) return dict([(attr_name, _meta[attr_name].from_python(attr_value)) for attr_name, attr_value in dict_.items()]) - _contribute_method("__dict__", to_dict) + # I don't understand what this is trying to do. + # whatever it was meant to do is broken and is breaking the ability to call "vars" on instantiations, which is breaking all kindsa shit. -AS + #_contribute_method("__dict__", to_dict) def iterate(self): - not_empty = lambda e: e is not None + not_empty = lambda e: e[1] is not None #AS I *think* this is what was intended. return iter(filter(not_empty, vars(self).items())) _contribute_method("__iter__", iterate) diff --git a/github2/repositories.py b/github2/repositories.py index f13ca5c..8090802 100644 --- a/github2/repositories.py +++ b/github2/repositories.py @@ -1,5 +1,5 @@ from github2.core import BaseData, GithubCommand, Attribute - +import urllib class Repository(BaseData): name = Attribute("Name of repository.") @@ -20,12 +20,11 @@ def __repr__(self): class Repositories(GithubCommand): domain = "repos" - def search(self, query): - return self.make_request("search", query, filter="repositories") + def search(self, query,page=1): + return self.make_request("search",urllib.quote_plus(query), filter="repositories",get_data = {'start_page':page}) def show(self, project): - return self.get_value("show", project, filter="repository", - datatype=Repository) + return self.get_value("", project, datatype=Repository) def list(self, for_user=None): """Return a list of all repositories for a user. @@ -64,8 +63,7 @@ def set_public(self, repo_name): def list_collaborators(self, project): """Lists all the collaborators in a project (user/repro).""" - return self.make_request("show", project, "collaborators", - filter="collaborators") + return self.make_request("", project, "collaborators") def add_collaborator(self, repo_name, username): """Adds an add_collaborator to a repro. @@ -85,6 +83,9 @@ def remove_collaborator(self, repo_name, username): def network(self, project): return self.make_request("show", project, "network", filter="network") + def languages(self, project): + return self.make_request("", project, "languages") + def tags(self, project): return self.make_request("show", project, "tags", filter="tags") @@ -104,5 +105,4 @@ def watching(self, for_user=None): def list_contributors(self, project): """Lists all the contributors in a project (user/repo).""" - return self.make_request("show", project, "contributors", - filter="contributors") + return self.make_request("", project, "contributors") diff --git a/github2/request.py b/github2/request.py old mode 100644 new mode 100755 index cd8fa08..8e25d53 --- a/github2/request.py +++ b/github2/request.py @@ -11,16 +11,17 @@ from cgi import parse_qs from urllib import urlencode -GITHUB_URL = "https://github.com" - -URL_PREFIX = "https://github.com/api/v2/json" +GITHUB_URL = "https://api.github.com" +#URL_PREFIX = "https://github.com/api/v2/json" +URL_PREFIX = "api.github.com/" class GithubError(Exception): """An error occured when making a request to the Github API.""" class GithubRequest(object): github_url = GITHUB_URL - url_format = "%(github_url)s/api/%(api_version)s/%(api_format)s" + #url_format = "%(github_url)s/api/%(api_version)s/%(api_format)s" + url_format = "%(github_url)s" api_version = "v2" api_format = "json" GithubError = GithubError @@ -60,9 +61,9 @@ 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,**extra_get_data): path_components = filter(None, path_components) - return self.make_request("/".join(path_components)) + return self.make_request("/".join(path_components),extra_post_data=extra_get_data) def post(self, *path_components, **extra_post_data): path_components = filter(None, path_components) @@ -71,10 +72,10 @@ def post(self, *path_components, **extra_post_data): def make_request(self, path, extra_post_data=None, method="GET"): if self.delay: - since_last = (datetime.datetime.now() - self.last_request - ).total_seconds() - if since_last < self.delay: - duration = self.delay - since_last + since_last = (datetime.datetime.now() - self.last_request) + since_last_in_seconds = since_last.days * 24 * 60 * 60 + since_last.seconds + if since_last_in_seconds < self.delay: + duration = self.delay - since_last_in_seconds if self.debug: sys.stderr.write("delaying API call %s\n" % duration) time.sleep(duration) @@ -93,14 +94,16 @@ def raw_request(self, url, extra_post_data, method="GET"): hostname = netloc.split(':')[0] post_data = None headers = self.http_headers - headers["Accept"] = "text/html" + headers["Accept"] = "application/json" method = method.upper() - if extra_post_data or method == "POST": + if method == "POST": post_data = self.encode_authentication_data(extra_post_data) headers["Content-Length"] = str(len(post_data)) else: + query_dict= parse_qs(query) + query_dict.update(extra_post_data) path = urlunparse((scheme, netloc, path, params, - self.encode_authentication_data(parse_qs(query)), + self.encode_authentication_data(query_dict), fragment)) connector = self.connector_for_scheme[scheme] connection = connector(hostname) @@ -114,6 +117,10 @@ def raw_request(self, url, extra_post_data, method="GET"): raise RuntimeError("unexpected response from github.com %d: %r" % ( response.status, response_text)) json = simplejson.loads(response_text) + + if type(json) == list: + return json + if json.get("error"): raise self.GithubError(json["error"][0]["error"]) diff --git a/github2/users.py b/github2/users.py old mode 100644 new mode 100755 index 863dc72..90cd562 --- a/github2/users.py +++ b/github2/users.py @@ -1,5 +1,6 @@ from github2.core import BaseData, GithubCommand, Attribute, DateAttribute - +import urllib +from repositories import Repository class User(BaseData): id = Attribute("The user id") @@ -31,22 +32,33 @@ def __repr__(self): class Users(GithubCommand): - domain = "user" - - def search(self, query): - return self.get_values("search", query, filter="users", datatype=User) + domain = "users" def show(self, username): - return self.get_value("show", username, filter="user", datatype=User) + return self.get_value("", username, datatype=User) def followers(self, username): - return self.make_request("show", username, "followers", filter="users") + return self.make_request(username, "followers") def following(self, username): - return self.make_request("show", username, "following", filter="users") + return self.make_request(username, "following") def follow(self, other_user): return self.make_request("follow", other_user) def unfollow(self, other_user): return self.make_request("unfollow", other_user) + def repos(self, username): + return self.make_request(username, "repos") + def watching(self,username): + return self.make_request(username, "watched") + +class LegacySearch(GithubCommand): + domain = 'legacy' + def user(self, query,page=1): + return self.get_values("user/search", urllib.quote_plus(query), filter="users", datatype=User) + + def user_by_email(self, query,page=1): + return self.get_value("user/email", query, filter="user", datatype=User) + def repo(self, query,page=1): + return self.get_values("repos/search", urllib.quote_plus(query), filter="repositories", get_data = {'start_page':page}) diff --git a/tests/unit.py b/tests/unit.py index eff3d8b..73ec12f 100644 --- a/tests/unit.py +++ b/tests/unit.py @@ -14,21 +14,69 @@ def test_issue(self): self.assertEqual(str, type(repr(i))) -class RateLimits(unittest.TestCase): + +class UserAPI(unittest.TestCase): """ - How should we handle actual API calls such that tests can run? - Perhaps the library should support a ~/.python_github2.conf from which to get the auth? + test out the apis for users """ - def test_delays(self): - import datetime, time - USERNAME='' - API_KEY='' - client = Github(username=USERNAME, api_token=API_KEY, - requests_per_second=.5) - client.users.show('defunkt') - start = datetime.datetime.now() - client.users.show('mojombo') - end = datetime.datetime.now() - self.assertGreaterEqual((end-start).total_seconds(), 2.0, - "Expected .5 reqs per second to require a 2 second delay between calls.") - \ No newline at end of file + def test_get(self): + client = Github() + res = client.users.show('defunkt') + self.assertNotEqual(res, None) + + def test_serialize(self): + client = Github() + res = dict(client.users.show('defunkt')) + self.assertNotEqual(res, None) + + def test_followers(self): + client = Github() + res = client.users.followers('defunkt') + self.assertNotEqual(res, None) + self.assertNotEqual(len(res), 0) + def test_following(self): + client = Github() + res = client.users.following('defunkt') + self.assertNotEqual(res, None) + self.assertNotEqual(len(res), 0) + def test_search_by_email(self): + client = Github() + res = client.search.user_by_email('chris@github.com') + print res + self.assertNotEqual(res, None) + def test_search_by_name(self): + client = Github() + res = client.search.user('Wanstrath') + print res + self.assertNotEqual(res, None) + + +class RepoAPI(unittest.TestCase): + """ + test apis for repos + """ + def test_search(self): + client = Github() + res = client.search.repo('rails') + self.assertNotEqual(res, None) + + def test_get(self): + client = Github() + res = client.repos.show('rails/rails') + self.assertNotEqual(res, None) + def test_languages(self): + client = Github() + res = client.repos.languages('rails/rails') + self.assertNotEqual(res, None) + def test_contributors(self): + client = Github() + res = client.repos.list_contributors('rails/rails') + self.assertNotEqual(res, None) + def test_user_repos(self): + client = Github() + res = client.users.repos('rails') + self.assertNotEqual(res, None) + def test_watching(self): + client = Github() + res = client.users.watching('defunkt') + self.assertNotEqual(res, None)