From c23698e64f3244636290ffa4065f3b70ba5151e9 Mon Sep 17 00:00:00 2001 From: David Medina Date: Sat, 29 Oct 2011 16:11:27 +0200 Subject: Wip on handlers --- github3/models/__init__.py | 0 github3/models/base.py | 178 +++++++++++++++++++++++++++++++++++++++++++++ github3/models/user.py | 79 ++++++++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 github3/models/__init__.py create mode 100644 github3/models/base.py create mode 100644 github3/models/user.py (limited to 'github3/models') diff --git a/github3/models/__init__.py b/github3/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/github3/models/base.py b/github3/models/base.py new file mode 100644 index 0000000..618e57f --- /dev/null +++ b/github3/models/base.py @@ -0,0 +1,178 @@ +""" +github3.models +~~~~~~~~~~~~~~ + +This module provides the Github3 object model. +""" + +import json + +from .helpers import to_python, to_api, key_diff + + +class BaseResource(object): + """A BaseResource object.""" + + _strs = [] + _ints = [] + _dates = [] + _bools = [] + _map = {} + _writeable = [] + _cache = {} + + + def __init__(self): + self._bootstrap() + super(BaseResource, self).__init__() + + + def __dir__(self): + return self.keys() + + def _bootstrap(self): + """Bootstraps the model object based on configured values.""" + + for attr in self.keys(): + setattr(self, attr, None) + + def keys(self): + return self._strs + self._ints + self._dates + self._bools + self._map.keys() + + def dict(self): + d = dict() + for k in self.keys(): + d[k] = self.__dict__.get(k) + + return d + + @classmethod + def new_from_dict(cls, d, gh=None): + + return to_python( + obj=cls(), in_dict=d, + str_keys = cls._strs, + int_keys = cls._ints, + date_keys = cls._dates, + bool_keys = cls._bools, + object_map = cls._map, + _gh = gh + ) + + + def update(self): + deploy = key_diff(self._cache, self.dict(), pack=True) + + deploy = to_api(deploy, int_keys=self._ints, date_keys=self._dates, bool_keys=self._bools) + deploy = json.dumps(deploy) + + r = self._gh._patch_resource(self.ri, deploy) + return r + + +class Plan(BaseResource): + """Github Plan object model.""" + + _strs = ['name'] + _ints = ['space', 'collaborators', 'private_repos'] + + def __repr__(self): + return ''.format(str(self.name)) + +class Org(BaseResource): + """Github Organization object model.""" + + _strs = [ + 'login', 'url', 'avatar_url', 'name', 'company', 'blog', 'location', 'email' + 'html_url', 'type', 'billing_email'] + _ints = [ + 'id', 'public_repos', 'public_gists', 'followers', 'following', + 'total_private_repos', 'owned_private_repos', 'private_gists', 'disk_usage', + 'collaborators'] + _dates = ['created_at'] + _map = {'plan': Plan} + _writable = ['billing_email', 'blog', 'company', 'email', 'location', 'name'] + + @property + def ri(self): + return ('orgs', self.login) + + def __repr__(self): + return ''.format(self.login) + + def repos(self, limit=None): + return self._gh._get_resources(('orgs', self.login, 'repos'), Repo, limit=limit) + + def members(self, limit=None): + return self._gh._get_resources(('orgs', self.login, 'members'), User, limit=limit) + + def is_member(self, username): + if isinstance(username, User): + username = username.login + + r = self._gh._http_resource('GET', ('orgs', self.login, 'members', username), check_status=False) + return (r.status_code == 204) + + def publicize_member(self, username): + if isinstance(username, User): + username = username.login + + r = self._gh._http_resource('PUT', ('orgs', self.login, 'public_members', username), check_status=False, data='') + return (r.status_code == 204) + + def conceal_member(self, username): + if isinstance(username, User): + username = username.login + + r = self._gh._http_resource('DELETE', ('orgs', self.login, 'public_members', username), check_status=False) + return (r.status_code == 204) + + def remove_member(self, username): + if isinstance(username, User): + username = username.login + + r = self._gh._http_resource('DELETE', ('orgs', self.login, 'members', username), check_status=False) + return (r.status_code == 204) + + def public_members(self, limit=None): + return self._gh._get_resources(('orgs', self.login, 'public_members'), User, limit=limit) + + def is_public_member(self, username): + if isinstance(username, User): + username = username.login + + r = self._gh._http_resource('GET', ('orgs', self.login, 'public_members', username), check_status=False) + return (r.status_code == 204) + + +class Gist(BaseResource): + _strs = ['url', 'description', 'html_url', 'git_pull_url', 'git_push_url'] + _ints = ['id', 'comments'] + _bools = ['public'] + _dates = ['created_at'] + _map = {'user': User} #TODO: file + + @property + def ri(self): + return ('users', self.user.login, self.id) + + def __repr__(self): + return '' % (self.user.login, self.description) + +class Repo(BaseResource): + _strs = [ + 'url', 'html_url', 'clone_url', 'git_url', 'ssh_url', 'svn_url', + 'name', 'description', 'homepage', 'language', 'master_branch'] + _bools = ['private', 'fork'] + _ints = ['forks', 'watchers', 'size',] + _dates = ['pushed_at', 'created_at'] + _map = {'owner': User} + + + @property + def ri(self): + return ('repos', self.owner.login, self.name) + + def __repr__(self): + return ''.format(self.owner.login, self.name) + # owner diff --git a/github3/models/user.py b/github3/models/user.py new file mode 100644 index 0000000..cdbd04f --- /dev/null +++ b/github3/models/user.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# +# author: David Medina + +from .base import BaseResource + +class User(BaseResource): + """Github User object model.""" + + _strs = [ + 'login','avatar_url', 'url', 'name', 'company', 'blog', 'location', + 'email', 'bio', 'html_url'] + + _ints = ['id', 'public_repos', 'public_gists', 'followers', 'following'] + _dates = ['created_at',] + _bools = ['hireable', ] + # _map = {} + # _writeable = [] + + @property + def ri(self): + return ('users', self.login) + + def __repr__(self): + return ''.format(self.login) + + @property + def handler(self): + return self._gh.user_handler(self.login) + + def get_followers(self, limit=None): + return self.handler.followers(limit) + + def get_following(self, limit=None): + return self.handler.following(limit) + + def repos(self, limit=None): + return self._gh._get_resources(('users', self.login, 'repos'), Repo, limit=limit) + def repo(self, reponame): + return self._gh._get_resource(('repos', self.login, reponame), Repo) + + def orgs(self): + return self._gh._get_resources(('users', self.login, 'orgs'), Org) + + def gists(self): + return self._gh._get_resources(('users', self.login, 'gists'), Gist) + +class AuthUser(User): + """Github Current User object model.""" + + _ints = [ + 'id', 'public_repos', 'public_gists', 'followers', 'following', + 'total_private_repos', 'owned_private_repos', 'private_gists', + 'disk_usage', 'collaborators'] + _map = {'plan': Plan} + _writeable = ['name', 'email', 'blog', 'company', 'location', 'hireable', 'bio'] + + @property + def ri(self): + return ('user',) + + def __repr__(self): + return ''.format(self.login) + + def repos(self, limit=None): + return self._gh._get_resources(('user', 'repos'), Repo, limit=limit) + + def repo(self, reponame): + return self._gh._get_resource(('repos', self.login, reponame), Repo) + + def orgs(self, limit=None): + return self._gh._get_resources(('user', 'orgs'), Org, limit=limit) + + def org(self, orgname): + return self._gh._get_resource(('orgs', orgname), Org) + + def gists(self, limit=None): + return self._gh._get_resources('gists', Gist, limit=limit) -- cgit v1.2.3-59-g8ed1b From b17dbea56f59aa5403e5722e12878c7183742551 Mon Sep 17 00:00:00 2001 From: David Medina Date: Tue, 1 Nov 2011 12:30:25 +0100 Subject: Decouple Handlers and Models Handler User complete --- github3/api.py | 18 ++--- github3/handlers/__init__.py | 1 + github3/handlers/base.py | 3 +- github3/handlers/user.py | 19 ++++- github3/helpers.py | 19 +++++ github3/models/__init__.py | 4 + github3/models/base.py | 187 ++++++++++++++++++------------------------- github3/models/gists.py | 49 ++++++++++++ github3/models/orgs.py | 28 +++++++ github3/models/repos.py | 30 +++++++ github3/models/user.py | 49 +++--------- 11 files changed, 244 insertions(+), 163 deletions(-) create mode 100644 github3/models/gists.py create mode 100644 github3/models/orgs.py create mode 100644 github3/models/repos.py (limited to 'github3/models') diff --git a/github3/api.py b/github3/api.py index 7b63b2d..d99682a 100644 --- a/github3/api.py +++ b/github3/api.py @@ -15,7 +15,6 @@ from decorator import decorator from .packages import omnijson as json from .packages.link_header import parse_link_value -from .models import * from .helpers import is_collection, to_python, to_api, get_scope from .config import settings import handlers @@ -170,6 +169,9 @@ class GithubCore(object): page += 1 + def _get_bool(self, resource): + resp = self._http_resource('GET', resource, check_status=False) + return True if resp.status_code == 204 else False def _to_map(self, obj, iterable): """Maps given dict iterable to a given Resource object.""" @@ -181,16 +183,6 @@ class GithubCore(object): return a - def _get_url(self, resource): - - if is_collection(resource): - resource = map(str, resource) - resource = '/'.join(resource) - - return resource - - - class Github(GithubCore): """docstring for Github""" @@ -198,8 +190,8 @@ class Github(GithubCore): super(Github, self).__init__() self.is_authenticated = False - def user_handler(self, username=None): - if not getattr(self, '_user_handler'): + def user_handler(self, username=None, force=False): + if force or not getattr(self, '_user_handler', False): if self.is_authenticated: self._user_handler = handlers.AuthUser(self) else: diff --git a/github3/handlers/__init__.py b/github3/handlers/__init__.py index e69de29..126efcb 100644 --- a/github3/handlers/__init__.py +++ b/github3/handlers/__init__.py @@ -0,0 +1 @@ +from user import AuthUser, User diff --git a/github3/handlers/base.py b/github3/handlers/base.py index 6a5ac65..93a4680 100644 --- a/github3/handlers/base.py +++ b/github3/handlers/base.py @@ -3,8 +3,6 @@ # # author: David Medina -import models - class Handler(object): """ Abstract handler, that inject github.api """ @@ -24,3 +22,4 @@ class Handler(object): url = self._extend_url(*args) map_model = kwargs.get('model', self._model) return self._gh._get_resources(url, map_model, **kwargs) + diff --git a/github3/handlers/user.py b/github3/handlers/user.py index 16f8363..c59607d 100644 --- a/github3/handlers/user.py +++ b/github3/handlers/user.py @@ -4,6 +4,7 @@ # author: David Medina from .base import Handler +import github3.models as models class User(Handler): """ Handler to query public user api """ @@ -13,7 +14,7 @@ class User(Handler): raise exceptions.AnomUser("%s need a username" % self.__class__) self._url = ('users', username) - self._model = models.AnomUser + self._model = models.User self.username = username super(User, self).__init__(gh) @@ -29,12 +30,24 @@ class User(Handler): def get_following(self, limit=None): return self._get_resources('following') -class AuthUser(AnomUser): + def get_repos(self, limit=None): + return self._get_resources('repos', model=models.Repo) + + def get_watched(self, limit=None): + return self._get_resources('watched', model=models.Repo) + + def get_orgs(self, limit=None): + return self._get_resources('orgs', model=models.Org) + + def get_gists(self, limit=None): + return self._get_resources('gists', model=models.Gist) + +class AuthUser(User): """ Handler to query public/private api for authenticated user """ def __init__(self, gh): self._url = ('user',) - self._model = models.User + self._model = models.AuthUser super(AnomUser, self).__init__(gh) def __repr__(self): diff --git a/github3/helpers.py b/github3/helpers.py index 498a005..205e097 100644 --- a/github3/helpers.py +++ b/github3/helpers.py @@ -57,6 +57,7 @@ def to_python(obj, date_keys=None, int_keys=None, object_map=None, + list_map=None, bool_keys=None, **kwargs): """Extends a given object for API Consumption. @@ -96,14 +97,32 @@ def to_python(obj, if object_map: for (k, v) in object_map.items(): if in_dict.get(k): + if v == 'self': + v = obj.__class__ d[k] = v.new_from_dict(in_dict.get(k)) + if list_map: + for k, model in list_map.items(): + nested_map = in_dict.get(k) + if nested_map: + if getattr(nested_map, 'items', False): + map_dict = {} + for nested_item, nested_dict in nested_map.items(): + map_dict[nested_item] = model.new_from_dict(nested_dict) + d[k] = map_dict + else: + map_list = [] + for item_map in nested_map: + map_list.append(model.new_from_dict(item_map)) + d[k] = map_list + obj.__dict__.update(d) obj.__dict__.update(kwargs) # Save the dictionary, for write comparisons. obj._cache = d obj.__cache = in_dict + obj.post_map() return obj diff --git a/github3/models/__init__.py b/github3/models/__init__.py index e69de29..6e4db1b 100644 --- a/github3/models/__init__.py +++ b/github3/models/__init__.py @@ -0,0 +1,4 @@ +from .user import AuthUser, User +from .repos import Repo +from .orgs import Org +from .gists import Gist diff --git a/github3/models/base.py b/github3/models/base.py index 618e57f..1b1ce89 100644 --- a/github3/models/base.py +++ b/github3/models/base.py @@ -6,9 +6,9 @@ This module provides the Github3 object model. """ import json +import inspect -from .helpers import to_python, to_api, key_diff - +from github3.helpers import to_python, to_api, key_diff class BaseResource(object): """A BaseResource object.""" @@ -18,15 +18,24 @@ class BaseResource(object): _dates = [] _bools = [] _map = {} + _list_map = {} _writeable = [] _cache = {} + def post_map(self): + try: + handler = self.handler() + methods = filter(lambda x: x[0].startswith('get') and callable(x), + inspect.getmembers(handler, inspect.ismethod)) + for name, callback in methods: + setattr(self, method, callback) + except: + pass def __init__(self): self._bootstrap() super(BaseResource, self).__init__() - def __dir__(self): return self.keys() @@ -56,6 +65,7 @@ class BaseResource(object): date_keys = cls._dates, bool_keys = cls._bools, object_map = cls._map, + list_map = cls._list_map, _gh = gh ) @@ -70,109 +80,70 @@ class BaseResource(object): return r -class Plan(BaseResource): - """Github Plan object model.""" - - _strs = ['name'] - _ints = ['space', 'collaborators', 'private_repos'] - - def __repr__(self): - return ''.format(str(self.name)) - -class Org(BaseResource): - """Github Organization object model.""" - - _strs = [ - 'login', 'url', 'avatar_url', 'name', 'company', 'blog', 'location', 'email' - 'html_url', 'type', 'billing_email'] - _ints = [ - 'id', 'public_repos', 'public_gists', 'followers', 'following', - 'total_private_repos', 'owned_private_repos', 'private_gists', 'disk_usage', - 'collaborators'] - _dates = ['created_at'] - _map = {'plan': Plan} - _writable = ['billing_email', 'blog', 'company', 'email', 'location', 'name'] - - @property - def ri(self): - return ('orgs', self.login) - - def __repr__(self): - return ''.format(self.login) - - def repos(self, limit=None): - return self._gh._get_resources(('orgs', self.login, 'repos'), Repo, limit=limit) - - def members(self, limit=None): - return self._gh._get_resources(('orgs', self.login, 'members'), User, limit=limit) - - def is_member(self, username): - if isinstance(username, User): - username = username.login - - r = self._gh._http_resource('GET', ('orgs', self.login, 'members', username), check_status=False) - return (r.status_code == 204) - - def publicize_member(self, username): - if isinstance(username, User): - username = username.login - - r = self._gh._http_resource('PUT', ('orgs', self.login, 'public_members', username), check_status=False, data='') - return (r.status_code == 204) - - def conceal_member(self, username): - if isinstance(username, User): - username = username.login - - r = self._gh._http_resource('DELETE', ('orgs', self.login, 'public_members', username), check_status=False) - return (r.status_code == 204) - - def remove_member(self, username): - if isinstance(username, User): - username = username.login - - r = self._gh._http_resource('DELETE', ('orgs', self.login, 'members', username), check_status=False) - return (r.status_code == 204) - - def public_members(self, limit=None): - return self._gh._get_resources(('orgs', self.login, 'public_members'), User, limit=limit) - - def is_public_member(self, username): - if isinstance(username, User): - username = username.login - - r = self._gh._http_resource('GET', ('orgs', self.login, 'public_members', username), check_status=False) - return (r.status_code == 204) - - -class Gist(BaseResource): - _strs = ['url', 'description', 'html_url', 'git_pull_url', 'git_push_url'] - _ints = ['id', 'comments'] - _bools = ['public'] - _dates = ['created_at'] - _map = {'user': User} #TODO: file - - @property - def ri(self): - return ('users', self.user.login, self.id) - - def __repr__(self): - return '' % (self.user.login, self.description) - -class Repo(BaseResource): - _strs = [ - 'url', 'html_url', 'clone_url', 'git_url', 'ssh_url', 'svn_url', - 'name', 'description', 'homepage', 'language', 'master_branch'] - _bools = ['private', 'fork'] - _ints = ['forks', 'watchers', 'size',] - _dates = ['pushed_at', 'created_at'] - _map = {'owner': User} - - - @property - def ri(self): - return ('repos', self.owner.login, self.name) - def __repr__(self): - return ''.format(self.owner.login, self.name) - # owner +#class Org(BaseResource): +# """Github Organization object model.""" +# +# _strs = [ +# 'login', 'url', 'avatar_url', 'name', 'company', 'blog', 'location', 'email' +# 'html_url', 'type', 'billing_email'] +# _ints = [ +# 'id', 'public_repos', 'public_gists', 'followers', 'following', +# 'total_private_repos', 'owned_private_repos', 'private_gists', 'disk_usage', +# 'collaborators'] +# _dates = ['created_at'] +# _map = {'plan': Plan} +# _writable = ['billing_email', 'blog', 'company', 'email', 'location', 'name'] +# +# @property +# def ri(self): +# return ('orgs', self.login) +# +# def __repr__(self): +# return ''.format(self.login) +# +# def repos(self, limit=None): +# return self._gh._get_resources(('orgs', self.login, 'repos'), Repo, limit=limit) +# +# def members(self, limit=None): +# return self._gh._get_resources(('orgs', self.login, 'members'), User, limit=limit) +# +# def is_member(self, username): +# if isinstance(username, User): +# username = username.login +# +# r = self._gh._http_resource('GET', ('orgs', self.login, 'members', username), check_status=False) +# return (r.status_code == 204) +# +# def publicize_member(self, username): +# if isinstance(username, User): +# username = username.login +# +# r = self._gh._http_resource('PUT', ('orgs', self.login, 'public_members', username), check_status=False, data='') +# return (r.status_code == 204) +# +# def conceal_member(self, username): +# if isinstance(username, User): +# username = username.login +# +# r = self._gh._http_resource('DELETE', ('orgs', self.login, 'public_members', username), check_status=False) +# return (r.status_code == 204) +# +# def remove_member(self, username): +# if isinstance(username, User): +# username = username.login +# +# r = self._gh._http_resource('DELETE', ('orgs', self.login, 'members', username), check_status=False) +# return (r.status_code == 204) +# +# def public_members(self, limit=None): +# return self._gh._get_resources(('orgs', self.login, 'public_members'), User, limit=limit) +# +# def is_public_member(self, username): +# if isinstance(username, User): +# username = username.login +# +# r = self._gh._http_resource('GET', ('orgs', self.login, 'public_members', username), check_status=False) +# return (r.status_code == 204) +# +# diff --git a/github3/models/gists.py b/github3/models/gists.py new file mode 100644 index 0000000..5ad61c3 --- /dev/null +++ b/github3/models/gists.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# +# author: David Medina + +from .base import BaseResource +from .user import User + +class File(BaseResource): + _strs = ['filename', 'raw_url', 'content', 'language', 'type'] + _ints = ['size'] + + def __repr__(self): + return ' %s' % self.filename + +class GistFork(BaseResource): + _strs = ['url'] + _dates = ['created_at'] + _map = {'user': User} + + def __repr__(self): + return ' %s>' % self.user.login + +class ChangeStatus(BaseResource): + _ints = ['deletions', 'additions', 'total'] + + def __repr__(self): + return ' change_status>' + +class GistHistory(BaseResource): + _strs = ['url', 'version'] + _map = {'user': User, 'change_status': ChangeStatus} + _dates = ['committed_at'] + +class Gist(BaseResource): + _strs = ['url', 'description', 'html_url', 'git_pull_url', 'git_push_url'] + _ints = ['id', 'comments'] + _bools = ['public'] + _dates = ['created_at'] + _map = {'user': User} + _list_map = {'files': File, 'forks': GistFork, 'history': GistHistory} + + @property + def ri(self): + return ('users', self.user.login, self.id) + + def __repr__(self): + return '' % (self.user.login, self.description) + diff --git a/github3/models/orgs.py b/github3/models/orgs.py new file mode 100644 index 0000000..1ce638e --- /dev/null +++ b/github3/models/orgs.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# +# author: David Medina + +from .base import BaseResource +from .user import Plan + +class Org(BaseResource): + """Github Organization object model.""" + + _strs = [ + 'login', 'url', 'avatar_url', 'name', 'company', 'blog', 'location', 'email' + 'html_url', 'type', 'billing_email'] + _ints = [ + 'id', 'public_repos', 'public_gists', 'followers', 'following', + 'total_private_repos', 'owned_private_repos', 'private_gists', 'disk_usage', + 'collaborators'] + _dates = ['created_at'] + _map = {'plan': Plan} + _writable = ['billing_email', 'blog', 'company', 'email', 'location', 'name'] + + @property + def ri(self): + return ('orgs', self.login) + + def __repr__(self): + return ''.format(self.login) diff --git a/github3/models/repos.py b/github3/models/repos.py new file mode 100644 index 0000000..8dbe970 --- /dev/null +++ b/github3/models/repos.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# +# author: David Medina + +from .base import BaseResource +from .user import User +from .orgs import Org + +class Repo(BaseResource): + _strs = [ + 'url', 'html_url', 'clone_url', 'git_url', 'ssh_url', 'svn_url', + 'name', 'description', 'homepage', 'language', 'master_branch'] + _bools = ['private', 'fork', 'has_issues', 'has_wiki', 'has_downloads'] + _ints = ['forks', 'watchers', 'size', 'open_issues'] + _dates = ['pushed_at', 'created_at'] + _map = { + 'owner': User, + 'organization': Org, + 'parent': 'self', + 'source': 'self', + } + + @property + def ri(self): + return ('repos', self.owner.login, self.name) + + def __repr__(self): + return ''.format(self.owner.login, self.name) + # owner diff --git a/github3/models/user.py b/github3/models/user.py index cdbd04f..d58d9b2 100644 --- a/github3/models/user.py +++ b/github3/models/user.py @@ -5,18 +5,25 @@ from .base import BaseResource +class Plan(BaseResource): + """Github Plan object model.""" + + _strs = ['name'] + _ints = ['space', 'collaborators', 'private_repos'] + + def __repr__(self): + return ''.format(str(self.name)) + class User(BaseResource): """Github User object model.""" _strs = [ 'login','avatar_url', 'url', 'name', 'company', 'blog', 'location', - 'email', 'bio', 'html_url'] + 'email', 'bio', 'html_url', 'type'] _ints = ['id', 'public_repos', 'public_gists', 'followers', 'following'] _dates = ['created_at',] _bools = ['hireable', ] - # _map = {} - # _writeable = [] @property def ri(self): @@ -25,26 +32,8 @@ class User(BaseResource): def __repr__(self): return ''.format(self.login) - @property def handler(self): - return self._gh.user_handler(self.login) - - def get_followers(self, limit=None): - return self.handler.followers(limit) - - def get_following(self, limit=None): - return self.handler.following(limit) - - def repos(self, limit=None): - return self._gh._get_resources(('users', self.login, 'repos'), Repo, limit=limit) - def repo(self, reponame): - return self._gh._get_resource(('repos', self.login, reponame), Repo) - - def orgs(self): - return self._gh._get_resources(('users', self.login, 'orgs'), Org) - - def gists(self): - return self._gh._get_resources(('users', self.login, 'gists'), Gist) + return self._gh.user_handler(self.login, force=True) class AuthUser(User): """Github Current User object model.""" @@ -61,19 +50,5 @@ class AuthUser(User): return ('user',) def __repr__(self): - return ''.format(self.login) - - def repos(self, limit=None): - return self._gh._get_resources(('user', 'repos'), Repo, limit=limit) - - def repo(self, reponame): - return self._gh._get_resource(('repos', self.login, reponame), Repo) - - def orgs(self, limit=None): - return self._gh._get_resources(('user', 'orgs'), Org, limit=limit) - - def org(self, orgname): - return self._gh._get_resource(('orgs', orgname), Org) + return ''.format(self.login) - def gists(self, limit=None): - return self._gh._get_resources('gists', Gist, limit=limit) -- cgit v1.2.3-59-g8ed1b From c0a5c2c68764112afcbf9795c7194a8eafc8352c Mon Sep 17 00:00:00 2001 From: David Medina Date: Tue, 1 Nov 2011 13:14:29 +0100 Subject: Fix bug. PEP8 --- github3/models/base.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'github3/models') diff --git a/github3/models/base.py b/github3/models/base.py index 1b1ce89..f98af2d 100644 --- a/github3/models/base.py +++ b/github3/models/base.py @@ -25,10 +25,11 @@ class BaseResource(object): def post_map(self): try: handler = self.handler() - methods = filter(lambda x: x[0].startswith('get') and callable(x), - inspect.getmembers(handler, inspect.ismethod)) + methods = filter( + lambda x: x[0].startswith('get') and callable(x[1]), + inspect.getmembers(handler, inspect.ismethod)) for name, callback in methods: - setattr(self, method, callback) + setattr(self, name, callback) except: pass -- cgit v1.2.3-59-g8ed1b From 15b7cc0fe44d02a9f2271041cd69140d63279ff1 Mon Sep 17 00:00:00 2001 From: David Medina Date: Tue, 1 Nov 2011 19:24:15 +0100 Subject: Fix names. Added raw request Think about return user handler more elegant --- github3/api.py | 10 +++++++--- github3/handlers/base.py | 4 ++++ github3/handlers/user.py | 15 +++++++++++---- github3/models/user.py | 7 +++++-- 4 files changed, 27 insertions(+), 9 deletions(-) (limited to 'github3/models') diff --git a/github3/api.py b/github3/api.py index d99682a..b0a2c96 100644 --- a/github3/api.py +++ b/github3/api.py @@ -173,6 +173,10 @@ class GithubCore(object): resp = self._http_resource('GET', resource, check_status=False) return True if resp.status_code == 204 else False + def _get_raw(self, resource): + resp = self._http_resource('GET', resource) + return self._resource_deserialize(resp.content) + def _to_map(self, obj, iterable): """Maps given dict iterable to a given Resource object.""" @@ -190,9 +194,9 @@ class Github(GithubCore): super(Github, self).__init__() self.is_authenticated = False - def user_handler(self, username=None, force=False): - if force or not getattr(self, '_user_handler', False): - if self.is_authenticated: + def user_handler(self, username=None, **kwargs): + if kwargs.get('force') or not getattr(self, '_user_handler', False): + if kwargs.get('private'): self._user_handler = handlers.AuthUser(self) else: self._user_handler = handlers.User(self, username) diff --git a/github3/handlers/base.py b/github3/handlers/base.py index 93a4680..3bd4bd1 100644 --- a/github3/handlers/base.py +++ b/github3/handlers/base.py @@ -13,6 +13,10 @@ class Handler(object): def _extend_url(self, *args): return self._url + args + def _get_raw(self, *args, **kwargs): + url = self._extend_url(*args) + return self._gh._get_raw(url, **kwargs) + def _get_resource(self, *args, **kwargs): url = self._extend_url(*args) map_model = kwargs.get('model', self._model) diff --git a/github3/handlers/user.py b/github3/handlers/user.py index 09ea031..47f1a92 100644 --- a/github3/handlers/user.py +++ b/github3/handlers/user.py @@ -20,7 +20,7 @@ class User(Handler): super(User, self).__init__(gh) def __repr__(self): - return ' %s' % self.username + return ' %s' % self.username def get(self): return self._get_resource() @@ -48,8 +48,15 @@ class AuthUser(User): def __init__(self, gh): self._url = ('user',) - self._model = models.AuthUser - super(AnomUser, self).__init__(gh) + self._model = models.User + super(User, self).__init__(gh) def __repr__(self): - return '' + return '' + + def get(self): + return self._get_resource(model=models.AuthUser) + + def get_emails(self): + return self._get_raw('emails') + diff --git a/github3/models/user.py b/github3/models/user.py index d58d9b2..7c730f6 100644 --- a/github3/models/user.py +++ b/github3/models/user.py @@ -30,7 +30,7 @@ class User(BaseResource): return ('users', self.login) def __repr__(self): - return ''.format(self.login) + return ''.format(self.login) def handler(self): return self._gh.user_handler(self.login, force=True) @@ -45,10 +45,13 @@ class AuthUser(User): _map = {'plan': Plan} _writeable = ['name', 'email', 'blog', 'company', 'location', 'hireable', 'bio'] + def handler(self): + return self._gh.user_handler(self.login, force=True, private=True) + @property def ri(self): return ('user',) def __repr__(self): - return ''.format(self.login) + return ''.format(self.login) -- cgit v1.2.3-59-g8ed1b From 81f483aabf6d40f7d3379fb2a85ecb6a5fd218fb Mon Sep 17 00:00:00 2001 From: David Medina Date: Tue, 1 Nov 2011 22:23:41 +0100 Subject: Complete AuthUser handler --- github3/handlers/user.py | 12 ++++++++++++ github3/models/__init__.py | 2 +- github3/models/user.py | 9 +++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) (limited to 'github3/models') diff --git a/github3/handlers/user.py b/github3/handlers/user.py index 47f1a92..5f5d130 100644 --- a/github3/handlers/user.py +++ b/github3/handlers/user.py @@ -60,3 +60,15 @@ class AuthUser(User): def get_emails(self): return self._get_raw('emails') + def get_is_following(self, user): + username = getattr(user, 'login', False) + if not username: + username = user + return self._get_bool('following', username) + + def get_keys(self): + return self._get_resources('keys', model=models.Key) + + def get_key(self, key_id): + return self._get_resource('keys', key_id, model=models.Key) + diff --git a/github3/models/__init__.py b/github3/models/__init__.py index 6e4db1b..ff0c28a 100644 --- a/github3/models/__init__.py +++ b/github3/models/__init__.py @@ -1,4 +1,4 @@ -from .user import AuthUser, User +from .user import AuthUser, User, Key from .repos import Repo from .orgs import Org from .gists import Gist diff --git a/github3/models/user.py b/github3/models/user.py index 7c730f6..3f0efc4 100644 --- a/github3/models/user.py +++ b/github3/models/user.py @@ -14,6 +14,15 @@ class Plan(BaseResource): def __repr__(self): return ''.format(str(self.name)) +class Key(BaseResource): + """Github Key object model.""" + + _strs = ['url', 'title', 'key'] + _ints = ['id'] + + def __repr__(self): + return ''.format(str(self.title)) + class User(BaseResource): """Github User object model.""" -- cgit v1.2.3-59-g8ed1b From 6f2d2115fa578e2d5aa611fe03474341d73f73cc Mon Sep 17 00:00:00 2001 From: David Medina Date: Sun, 6 Nov 2011 18:14:51 +0100 Subject: Modelizer class. json<->model parser Also reorganize code --- github3/__init__.py | 2 - github3/core.py | 124 +++++++++++++++++++++++++++++++++------- github3/handlers/base.py | 62 +++++--------------- github3/models/base.py | 145 +++-------------------------------------------- github3/models/gists.py | 68 +++++++++++++++------- github3/models/orgs.py | 27 ++++----- github3/models/repos.py | 35 ++++++------ github3/models/user.py | 69 +++++++++++----------- 8 files changed, 236 insertions(+), 296 deletions(-) (limited to 'github3/models') diff --git a/github3/__init__.py b/github3/__init__.py index fcb882b..40a96af 100644 --- a/github3/__init__.py +++ b/github3/__init__.py @@ -1,3 +1 @@ # -*- coding: utf-8 -*- - -from core import * diff --git a/github3/core.py b/github3/core.py index d6eed22..0aa5949 100644 --- a/github3/core.py +++ b/github3/core.py @@ -1,30 +1,114 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# +# author: David Medina -""" -github3.core -~~~~~~~~~~~~ +class Paginate: + """ Paginate resource iterator -This module provides the entrance point for the GitHub3 module. -""" + :param resource: URL resource + :param requester: Bound method to request. See `GithubCore.get` + """ -__version__ = '0.0.0' -__license__ = 'MIT' -__author__ = 'Kenneth Reitz' + def __init__(self, resource, requester): + self.resource = resource + self.requester = requester + self.page = 1 -from .api import Github + def _last_page(self, link): + """ Get and cached last page from link header """ + if not getattr(self, 'last', False): + from github3.packages.link_header import parse_link_value + from urlparse import urlparse, parse_qs + for link, rels in parse_link_value(link).items(): + if rels.get('rel') == 'last': + query = urlparse(link).query + self.last = int(parse_qs(query).get('page').pop()) -def no_auth(): - """Returns an un-authenticated Github object.""" + return self.last - gh = Github() + def __iter__(self): + return self - return gh + def initial(self): + """ First request. Force requester to paginate returning link header """ + link, content = self.requester(self.resource, paginate=True, page=1) + self.last = self._last_page(link) if link else 1 + return content -def basic_auth(username, password): - """Returns an authenticated Github object, via HTTP Basic.""" + def next(self): + if self.page == 1: + content = self.initial() + self.page += 1 + return content + else: + if self.page > self.last: + raise StopIteration + else: + content = self.requester(self.resource, page=self.page) + self.page += 1 + return content - gh = Github() - gh.is_authenticated = True - gh.session.auth = (username, password) +class Modelizer(object): + """ Converter json into model and vice versa """ - return gh + def __init__(self, model): + self.model = model + self.attrs = {} + + def _parse_date(self, string_date): + from datetime import datetime + try: + date = datetime.strptime(string_date, '%Y-%m-%dT%H:%M:%SZ') + except TypeError: + date = None + + return date + + def _parse_map(self, model, raw_resource): + if model == 'self': + model = self.model + + return Modelizer(model).loads(raw_resource) + + def _parse_collection_map(self, model, raw_resources): + # Dict of resources (Ex: Gist file) + if getattr(raw_resources, 'items', False): + dict_map = {} + for key, raw_resource in raw_resources.items(): + dict_map[key] = Modelizer(model).loads(raw_resource) + return dict_map + # list of resources + else: + return [Modelizer(model).loads(raw_resource) + for raw_resource in raw_resources] + + def loads(self, raw_resource): + attrs = {} + idl = self.model.idl() + attrs.update( + {attr: raw_resource[attr] for attr in idl.get('strs',()) + if raw_resource.get(attr)}) + attrs.update( + {attr: raw_resource[attr] for attr in idl.get('ints',()) + if raw_resource.get(attr)}) + attrs.update( + {attr: self._parse_date(raw_resource[attr]) + for attr in idl.get('dates',()) if raw_resource.get(attr)}) + attrs.update( + {attr: raw_resource[attr] for attr in idl.get('bools',()) + if raw_resource.get(attr)}) + attrs.update( + {attr: self._parse_map(model, raw_resource[attr]) + for attr, model in idl.get('maps',{}).items() + if raw_resource.get(attr)}) + attrs.update( + {attr: self._parse_collection_map(model, raw_resource[attr]) + for attr, model in idl.get('collection_maps',{}).items() + if raw_resource.get(attr)}) + + return self.model(attrs) + + def dumps(self): + # return JSON + pass diff --git a/github3/handlers/base.py b/github3/handlers/base.py index 3f82817..0d8be0e 100644 --- a/github3/handlers/base.py +++ b/github3/handlers/base.py @@ -3,53 +3,7 @@ # # author: David Medina -import github3.exceptions as ghexceptions - -class Paginate: - """ Paginate resource iterator - - :param resource: URL resource - :param requester: Bound method to request. See `GithubCore.get` - """ - - def __init__(self, resource, requester): - self.resource = resource - self.requester = requester - self.page = 1 - - def _last_page(self, link): - """ Get and cached last page from link header """ - if not getattr(self, 'last', False): - from github3.packages.link_header import parse_link_value - from urlparse import urlparse, parse_qs - for link, rels in parse_link_value(link).items(): - if rels.get('rel') == 'last': - query = urlparse(link).query - self.last = int(parse_qs(query).get('page').pop()) - - return self.last - - def __iter__(self): - return self - - def initial(self): - """ First request. Force requester to paginate returning link header """ - link, content = self.requester(self.resource, paginate=True, page=1) - self.last = self._last_page(link) if link else 1 - return content - - def next(self): - if self.page == 1: - content = self.initial() - self.page += 1 - return content - else: - if self.page > self.last: - raise StopIteration - else: - content = self.requester(self.resource, page=self.page) - self.page += 1 - return content +from github3.core import Paginate, Modelizer class Handler(object): """ Handler base. Requests to API and modelize responses """ @@ -60,9 +14,11 @@ class Handler(object): def _bool(self, resource, **kwargs): """ Handler request to boolean response """ + + from github3.exceptions import NotFound try: response = self._gh.head(resource, **kwargs) - except ghexceptions.NotFound: + except NotFound: return False assert response.status_code == 204 return True @@ -70,13 +26,21 @@ class Handler(object): #TODO: if limit is multiple of per_page... it do another request for nothing def _get_resources(self, resource, model=None, limit=None): """ Hander request to multiple resources """ + page_resources = Paginate(resource, self._gh.get) counter = 1 for page in page_resources: for raw_resource in page: if limit and counter > limit: break counter += 1 - yield raw_resource + yield Modelizer(model or self.model).loads(raw_resource) + #yield raw_resource else: continue break + + def _get_resource(self, resource, model=None): + """ Handler request to single request """ + + raw_resource = self._gh.get(resource) + return Modelizer(model or self.model).loads(raw_resource) diff --git a/github3/models/base.py b/github3/models/base.py index f98af2d..df0c82b 100644 --- a/github3/models/base.py +++ b/github3/models/base.py @@ -2,149 +2,18 @@ github3.models ~~~~~~~~~~~~~~ -This module provides the Github3 object model. +This package provides the Github3 object model. """ -import json -import inspect - -from github3.helpers import to_python, to_api, key_diff - class BaseResource(object): """A BaseResource object.""" - _strs = [] - _ints = [] - _dates = [] - _bools = [] - _map = {} - _list_map = {} - _writeable = [] - _cache = {} - - def post_map(self): - try: - handler = self.handler() - methods = filter( - lambda x: x[0].startswith('get') and callable(x[1]), - inspect.getmembers(handler, inspect.ismethod)) - for name, callback in methods: - setattr(self, name, callback) - except: - pass - - def __init__(self): - self._bootstrap() + def __init__(self, attrs=None): + if attrs: + for attr, value in attrs.items(): + setattr(self, attr, value) super(BaseResource, self).__init__() - def __dir__(self): - return self.keys() - - def _bootstrap(self): - """Bootstraps the model object based on configured values.""" - - for attr in self.keys(): - setattr(self, attr, None) - - def keys(self): - return self._strs + self._ints + self._dates + self._bools + self._map.keys() - - def dict(self): - d = dict() - for k in self.keys(): - d[k] = self.__dict__.get(k) - - return d - @classmethod - def new_from_dict(cls, d, gh=None): - - return to_python( - obj=cls(), in_dict=d, - str_keys = cls._strs, - int_keys = cls._ints, - date_keys = cls._dates, - bool_keys = cls._bools, - object_map = cls._map, - list_map = cls._list_map, - _gh = gh - ) - - - def update(self): - deploy = key_diff(self._cache, self.dict(), pack=True) - - deploy = to_api(deploy, int_keys=self._ints, date_keys=self._dates, bool_keys=self._bools) - deploy = json.dumps(deploy) - - r = self._gh._patch_resource(self.ri, deploy) - return r - - - -#class Org(BaseResource): -# """Github Organization object model.""" -# -# _strs = [ -# 'login', 'url', 'avatar_url', 'name', 'company', 'blog', 'location', 'email' -# 'html_url', 'type', 'billing_email'] -# _ints = [ -# 'id', 'public_repos', 'public_gists', 'followers', 'following', -# 'total_private_repos', 'owned_private_repos', 'private_gists', 'disk_usage', -# 'collaborators'] -# _dates = ['created_at'] -# _map = {'plan': Plan} -# _writable = ['billing_email', 'blog', 'company', 'email', 'location', 'name'] -# -# @property -# def ri(self): -# return ('orgs', self.login) -# -# def __repr__(self): -# return ''.format(self.login) -# -# def repos(self, limit=None): -# return self._gh._get_resources(('orgs', self.login, 'repos'), Repo, limit=limit) -# -# def members(self, limit=None): -# return self._gh._get_resources(('orgs', self.login, 'members'), User, limit=limit) -# -# def is_member(self, username): -# if isinstance(username, User): -# username = username.login -# -# r = self._gh._http_resource('GET', ('orgs', self.login, 'members', username), check_status=False) -# return (r.status_code == 204) -# -# def publicize_member(self, username): -# if isinstance(username, User): -# username = username.login -# -# r = self._gh._http_resource('PUT', ('orgs', self.login, 'public_members', username), check_status=False, data='') -# return (r.status_code == 204) -# -# def conceal_member(self, username): -# if isinstance(username, User): -# username = username.login -# -# r = self._gh._http_resource('DELETE', ('orgs', self.login, 'public_members', username), check_status=False) -# return (r.status_code == 204) -# -# def remove_member(self, username): -# if isinstance(username, User): -# username = username.login -# -# r = self._gh._http_resource('DELETE', ('orgs', self.login, 'members', username), check_status=False) -# return (r.status_code == 204) -# -# def public_members(self, limit=None): -# return self._gh._get_resources(('orgs', self.login, 'public_members'), User, limit=limit) -# -# def is_public_member(self, username): -# if isinstance(username, User): -# username = username.login -# -# r = self._gh._http_resource('GET', ('orgs', self.login, 'public_members', username), check_status=False) -# return (r.status_code == 204) -# -# + def idl(self): + raise NotImplementedError('Each model need subcass that method') diff --git a/github3/models/gists.py b/github3/models/gists.py index 5ad61c3..d1b416d 100644 --- a/github3/models/gists.py +++ b/github3/models/gists.py @@ -7,43 +7,71 @@ from .base import BaseResource from .user import User class File(BaseResource): - _strs = ['filename', 'raw_url', 'content', 'language', 'type'] - _ints = ['size'] + """ File model """ + + @classmethod + def idl(self): + return { + 'strs': ['filename', 'raw_url', 'content', 'language', 'type'], + 'ints': ['size'], + } def __repr__(self): return ' %s' % self.filename class GistFork(BaseResource): - _strs = ['url'] - _dates = ['created_at'] - _map = {'user': User} + """ GistFork model """ + + @classmethod + def idl(self): + return { + 'strs': ['url'], + 'dates': ['created_at'], + 'maps': {'user': User} + } def __repr__(self): return ' %s>' % self.user.login class ChangeStatus(BaseResource): - _ints = ['deletions', 'additions', 'total'] + """ ChangeStatus model """ + + @classmethod + def idl(self): + return { + 'ints': ['deletions', 'additions', 'total'], + } def __repr__(self): return ' change_status>' class GistHistory(BaseResource): - _strs = ['url', 'version'] - _map = {'user': User, 'change_status': ChangeStatus} - _dates = ['committed_at'] + """ """ + + @classmethod + def idl(self): + return { + 'strs': ['url', 'version'], + 'maps': {'user': User, 'change_status': ChangeStatus}, + 'dates': ['committed_at'], + } + + def __repr__(self): + return '' % (self.user, self.committed_at) class Gist(BaseResource): - _strs = ['url', 'description', 'html_url', 'git_pull_url', 'git_push_url'] - _ints = ['id', 'comments'] - _bools = ['public'] - _dates = ['created_at'] - _map = {'user': User} - _list_map = {'files': File, 'forks': GistFork, 'history': GistHistory} + """ """ - @property - def ri(self): - return ('users', self.user.login, self.id) + @classmethod + def idl(self): + return { + 'strs': ['url', 'description', 'html_url', 'git_pull_url', 'git_push_url'], + 'ints': ['id', 'comments'], + 'bools': ['public'], + 'dates': ['created_at'], + 'maps': {'user': User}, + 'collection_maps': {'files': File, 'forks': GistFork, 'history': GistHistory}, + } def __repr__(self): - return '' % (self.user.login, self.description) - + return '' % (self.user, self.description) diff --git a/github3/models/orgs.py b/github3/models/orgs.py index 1ce638e..840b51a 100644 --- a/github3/models/orgs.py +++ b/github3/models/orgs.py @@ -9,20 +9,17 @@ from .user import Plan class Org(BaseResource): """Github Organization object model.""" - _strs = [ - 'login', 'url', 'avatar_url', 'name', 'company', 'blog', 'location', 'email' - 'html_url', 'type', 'billing_email'] - _ints = [ - 'id', 'public_repos', 'public_gists', 'followers', 'following', - 'total_private_repos', 'owned_private_repos', 'private_gists', 'disk_usage', - 'collaborators'] - _dates = ['created_at'] - _map = {'plan': Plan} - _writable = ['billing_email', 'blog', 'company', 'email', 'location', 'name'] - - @property - def ri(self): - return ('orgs', self.login) + @classmethod + def idl(self): + return { + 'strs': ['login', 'url', 'avatar_url', 'name', 'company', 'blog', + 'location', 'email', 'html_url', 'type', 'billing_email'], + 'ints': ['id', 'public_repos', 'public_gists', 'followers', + 'following', 'total_private_repos', 'owned_private_repos', + 'private_gists', 'disk_usage', 'collaborators'], + 'dates': ['created_at'], + 'maps': {'plan': plan} + } def __repr__(self): - return ''.format(self.login) + return '' % self.login diff --git a/github3/models/repos.py b/github3/models/repos.py index 8dbe970..ba6ac33 100644 --- a/github3/models/repos.py +++ b/github3/models/repos.py @@ -8,23 +8,24 @@ from .user import User from .orgs import Org class Repo(BaseResource): - _strs = [ - 'url', 'html_url', 'clone_url', 'git_url', 'ssh_url', 'svn_url', - 'name', 'description', 'homepage', 'language', 'master_branch'] - _bools = ['private', 'fork', 'has_issues', 'has_wiki', 'has_downloads'] - _ints = ['forks', 'watchers', 'size', 'open_issues'] - _dates = ['pushed_at', 'created_at'] - _map = { - 'owner': User, - 'organization': Org, - 'parent': 'self', - 'source': 'self', - } + """ Repo model """ - @property - def ri(self): - return ('repos', self.owner.login, self.name) + @classmethod + def idl(self): + return { + 'strs': [ + 'url', 'html_url', 'clone_url', 'git_url', 'ssh_url', 'svn_url', + 'name', 'description', 'homepage', 'language', 'master_branch'], + 'ints': ['forks', 'watchers', 'size', 'open_issues'], + 'dates': ['created_at', 'pushed_at'], + 'bools': ['private', 'fork', 'has_issues', 'has_wiki', 'has_downloads'], + 'maps': { + 'owner': User, + 'organization': Org, + 'parent': 'self', + 'source': 'self', + } + } def __repr__(self): - return ''.format(self.owner.login, self.name) - # owner + return '' % (self.owner.login, self.name) diff --git a/github3/models/user.py b/github3/models/user.py index 3f0efc4..7ec7999 100644 --- a/github3/models/user.py +++ b/github3/models/user.py @@ -8,59 +8,58 @@ from .base import BaseResource class Plan(BaseResource): """Github Plan object model.""" - _strs = ['name'] - _ints = ['space', 'collaborators', 'private_repos'] + @classmethod + def idl(self): + return { + 'strs': ['name'], + 'ints': ['space', 'collaborators', 'private_repos'], + } def __repr__(self): - return ''.format(str(self.name)) + return '' % self.name class Key(BaseResource): """Github Key object model.""" - _strs = ['url', 'title', 'key'] - _ints = ['id'] + @classmethod + def idl(self): + return { + 'strs': ['url', 'title', 'key'], + 'ints': ['id'], + } def __repr__(self): - return ''.format(str(self.title)) + return '' % self.title class User(BaseResource): """Github User object model.""" - _strs = [ - 'login','avatar_url', 'url', 'name', 'company', 'blog', 'location', - 'email', 'bio', 'html_url', 'type'] - - _ints = ['id', 'public_repos', 'public_gists', 'followers', 'following'] - _dates = ['created_at',] - _bools = ['hireable', ] - - @property - def ri(self): - return ('users', self.login) + @classmethod + def idl(self): + return { + 'strs': ['login','avatar_url', 'url', 'name', 'company', 'blog', + 'location', 'email', 'bio', 'html_url', 'type'], + 'ints': [ + 'id', 'public_repos', 'public_gists', 'followers', 'following', + 'total_private_repos', 'owned_private_repos', 'private_gists', + 'disk_usage', 'collaborators'], + 'maps': {'plan': Plan}, + 'dates': ['created_at',], + 'bools': ['hireable', ], + } def __repr__(self): - return ''.format(self.login) + return '' % self.login - def handler(self): - return self._gh.user_handler(self.login, force=True) + #def handler(self): + # return self._gh.user_handler(self.login, force=True) class AuthUser(User): - """Github Current User object model.""" - - _ints = [ - 'id', 'public_repos', 'public_gists', 'followers', 'following', - 'total_private_repos', 'owned_private_repos', 'private_gists', - 'disk_usage', 'collaborators'] - _map = {'plan': Plan} - _writeable = ['name', 'email', 'blog', 'company', 'location', 'hireable', 'bio'] - - def handler(self): - return self._gh.user_handler(self.login, force=True, private=True) + """Github Authenticated User object model.""" - @property - def ri(self): - return ('user',) + #def handler(self): + # return self._gh.user_handler(self.login, force=True, private=True) def __repr__(self): - return ''.format(self.login) + return '' % self.login -- cgit v1.2.3-59-g8ed1b From 40cbe596f0d726461d65d6473c2d197f63232641 Mon Sep 17 00:00:00 2001 From: David Medina Date: Wed, 9 Nov 2011 02:10:29 +0100 Subject: Fixing bugs. Crazy night :S --- github3/api.py | 8 +++++++- github3/errors.py | 6 +++--- github3/handlers/base.py | 16 +++++++--------- github3/handlers/user.py | 26 +++++++++++++------------- github3/models/orgs.py | 2 +- github3/models/repos.py | 4 ++-- 6 files changed, 33 insertions(+), 29 deletions(-) (limited to 'github3/models') diff --git a/github3/api.py b/github3/api.py index 2066e94..b7435ff 100644 --- a/github3/api.py +++ b/github3/api.py @@ -43,7 +43,7 @@ class GithubCore(object): def head(self, request, **kwargs): """ HEAD request """ - return self._request('HEAD', request, **kwargs).headers + return self._request('HEAD', request, **kwargs) def post(self, request, data=None, **kwargs): """ @@ -71,11 +71,17 @@ class GithubCore(object): """ PUT request """ response = self._request('PUT', request, **kwargs) assert response.status_code == 204 + return response def delete(self, request, **kwargs): """ DELETE request """ + + data = kwargs.get('data') + if data: + kwargs['data'] = self._parser.dumps(data) response = self._request('DELETE', request, **kwargs) assert response.status_code == 204 + return response def _parse_args(self, request_args): """ diff --git a/github3/errors.py b/github3/errors.py index 96693be..09e616b 100644 --- a/github3/errors.py +++ b/github3/errors.py @@ -12,10 +12,10 @@ class GithubError(object): def __init__(self, response): self._parser = json self.status_code = response.status_code - if response.content: + try: self.debug = self._parser.loads(response.content) - else: - self.debug = {} + except ValueError: + self.debug = {'message': response.content} def error_400(self): return exceptions.BadRequest("400 - %s" % self.debug.get('message')) diff --git a/github3/handlers/base.py b/github3/handlers/base.py index 6bf13bf..50e2df8 100644 --- a/github3/handlers/base.py +++ b/github3/handlers/base.py @@ -15,9 +15,9 @@ class Handler(object): def _prefix_resource(self, resource): prefix = getattr(self, 'prefix', '') - return '/'.join((prefix, resource)) + return '/'.join((prefix, resource)).rstrip('/') - def _get_converter(self, kwargs): + def _get_converter(self, **kwargs): converter = kwargs.get( 'converter', # 1. in kwargs getattr(self, 'converter', # 2. in handler @@ -28,13 +28,11 @@ class Handler(object): def _put(self, resource, **kwargs): """ Put proxy request""" - resource = self._prefix_resource(resource) return self._bool(resource, method='put', **kwargs) def _delete(self, resource, **kwargs): """ Delete proxy request""" - resource = self._prefix_resource(resource) return self._bool(resource, method='delete', **kwargs) def _bool(self, resource, **kwargs): @@ -43,7 +41,7 @@ class Handler(object): from github3.exceptions import NotFound resource = self._prefix_resource(resource) try: - callback = getattr(self._gh, kwargs.get(method,''), self._gh.head) + callback = getattr(self._gh, kwargs.get('method',''), self._gh.head) response = callback(resource, **kwargs) except NotFound: return False @@ -55,13 +53,13 @@ class Handler(object): """ Hander request to multiple resources """ resource = self._prefix_resource(resource) - page_resources = Paginate(resource, self._gh.get, kwargs) + page_resources = Paginate(resource, self._gh.get, **kwargs) counter = 1 for page in page_resources: for raw_resource in page: if limit and counter > limit: break counter += 1 - converter = self._get_converter(kwargs) + converter = self._get_converter(**kwargs) converter.inject(model) yield converter.loads(raw_resource) else: @@ -73,7 +71,7 @@ class Handler(object): resource = self._prefix_resource(resource) raw_resource = self._gh.get(resource) - converter = self._get_converter(kwargs) + converter = self._get_converter(**kwargs) converter.inject(model) return converter.loads(raw_resource) @@ -82,6 +80,6 @@ class Handler(object): resource = self._prefix_resource(resource) raw_resource = self._gh.post(resource, data=data) - converter = self._get_converter(kwargs) + converter = self._get_converter(**kwargs) converter.inject(model) return converter.loads(raw_resource) diff --git a/github3/handlers/user.py b/github3/handlers/user.py index 75a0d3f..fb893b4 100644 --- a/github3/handlers/user.py +++ b/github3/handlers/user.py @@ -29,37 +29,37 @@ class User(Handler): def get(self): """ Return user """ - self._get_resource('', model=models.User) + return self._get_resource('', model=models.User) def get_followers(self): """ Return user's followers """ - self._get_resources('followers', model=models.User) + return self._get_resources('followers', model=models.User) def get_following(self): """ Return users that follow """ - self._get_resources('following', model=models.User) + return self._get_resources('following', model=models.User) def get_repos(self): """ Return user's public repositories """ - self._get_resources('repos', model=models.Repo) + return self._get_resources('repos', model=models.Repo) def get_watched(self): """ Return repositories that user whatch """ - self._get_resources('watched', model=models.Repo) + return self._get_resources('watched', model=models.Repo) def get_orgs(self): """ Return user's public organizations """ - self._get_resources('orgs', model=models.Org) + return self._get_resources('orgs', model=models.Org) def get_gists(self): """ Return user's gists """ - self._get_resources('gists', model=models.Gist) + return self._get_resources('gists', model=models.Gist) class AuthUser(User): """ User handler with public and private access """ @@ -70,7 +70,7 @@ class AuthUser(User): return ' %s>' % self._gh.session.auth[0] def get(self): - self._get_resource('', model=models.AuthUser) + return self._get_resource('', model=models.AuthUser) def get_emails(self): """ Return list of emails """ @@ -119,9 +119,9 @@ class AuthUser(User): """ parse_user = str(getattr(user, 'login', user)) - return self._put('following/%s', % parse_user) + return self._put('following/%s' % parse_user) - def unfollow(self, user) + def unfollow(self, user): """ Unfollow user @@ -129,14 +129,14 @@ class AuthUser(User): """ parse_user = str(getattr(user, 'login', user)) - return self._delete('following/%s', % parse_user) + return self._delete('following/%s' % parse_user) def get_keys(self): """ Get public keys """ return self._get_resources('keys', model=models.Key) - def get_key(self, key_id) + def get_key(self, key_id): """ Get public key by id """ return self._get_resource('keys/%s' % key_id, model=models.Key) @@ -151,7 +151,7 @@ class AuthUser(User): #TODO: render key.pub file key = { - 'title': kwargs.get('title','') + 'title': kwargs.get('title',''), 'key': kwargs.get('key','') } return self._post_resource('keys', data=key, model=models.Key) diff --git a/github3/models/orgs.py b/github3/models/orgs.py index 840b51a..5e66c35 100644 --- a/github3/models/orgs.py +++ b/github3/models/orgs.py @@ -18,7 +18,7 @@ class Org(BaseResource): 'following', 'total_private_repos', 'owned_private_repos', 'private_gists', 'disk_usage', 'collaborators'], 'dates': ['created_at'], - 'maps': {'plan': plan} + 'maps': {'plan': Plan} } def __repr__(self): diff --git a/github3/models/repos.py b/github3/models/repos.py index ba6ac33..d1b7b75 100644 --- a/github3/models/repos.py +++ b/github3/models/repos.py @@ -22,8 +22,8 @@ class Repo(BaseResource): 'maps': { 'owner': User, 'organization': Org, - 'parent': 'self', - 'source': 'self', + 'parent': self.__class__, + 'source': self.__class__, } } -- cgit v1.2.3-59-g8ed1b From 9e7d4e655d966b325cc9db190273f99220b6041b Mon Sep 17 00:00:00 2001 From: David Medina Date: Tue, 15 Nov 2011 23:26:31 +0100 Subject: API change: Added gravatar_id in User model Also fix some bugs, update test and PEP8 --- github3/converters.py | 12 ++++++------ github3/models/base.py | 3 +++ github3/models/user.py | 13 +++++++++---- github3/tests/converters_test.py | 7 +++++-- github3/tests/fixtures.py | 9 +++++++++ 5 files changed, 32 insertions(+), 12 deletions(-) (limited to 'github3/models') diff --git a/github3/converters.py b/github3/converters.py index 05e3435..58eb6b5 100644 --- a/github3/converters.py +++ b/github3/converters.py @@ -75,24 +75,24 @@ class Modelizer(Converter): idl = self.model.idl() attrs.update( {attr: raw_resource[attr] for attr in idl.get('strs',()) - if raw_resource.get(attr)}) + if attr in raw_resource}) attrs.update( {attr: raw_resource[attr] for attr in idl.get('ints',()) - if raw_resource.get(attr)}) + if attr in raw_resource}) attrs.update( {attr: self._parse_date(raw_resource[attr]) - for attr in idl.get('dates',()) if raw_resource.get(attr)}) + for attr in idl.get('dates',()) if attr in raw_resource}) attrs.update( {attr: raw_resource[attr] for attr in idl.get('bools',()) - if raw_resource.get(attr)}) + if attr in raw_resource}) attrs.update( {attr: self._parse_map(model, raw_resource[attr]) for attr, model in idl.get('maps',{}).items() - if raw_resource.get(attr)}) + if attr in raw_resource}) attrs.update( {attr: self._parse_collection_map(model, raw_resource[attr]) for attr, model in idl.get('collection_maps',{}).items() - if raw_resource.get(attr)}) + if attr in raw_resource}) return self.model(attrs) diff --git a/github3/models/base.py b/github3/models/base.py index df0c82b..bd07650 100644 --- a/github3/models/base.py +++ b/github3/models/base.py @@ -14,6 +14,9 @@ class BaseResource(object): setattr(self, attr, value) super(BaseResource, self).__init__() + def __len__(self): + return len(self.__dict__) + @classmethod def idl(self): raise NotImplementedError('Each model need subcass that method') diff --git a/github3/models/user.py b/github3/models/user.py index 7ec7999..e2d82b6 100644 --- a/github3/models/user.py +++ b/github3/models/user.py @@ -5,6 +5,7 @@ from .base import BaseResource + class Plan(BaseResource): """Github Plan object model.""" @@ -18,6 +19,7 @@ class Plan(BaseResource): def __repr__(self): return '' % self.name + class Key(BaseResource): """Github Key object model.""" @@ -31,20 +33,23 @@ class Key(BaseResource): def __repr__(self): return '' % self.title + class User(BaseResource): """Github User object model.""" @classmethod def idl(self): return { - 'strs': ['login','avatar_url', 'url', 'name', 'company', 'blog', - 'location', 'email', 'bio', 'html_url', 'type'], + 'strs': [ + 'login', 'avatar_url', 'gravatar_id', 'url', 'name', + 'company', 'blog', 'location', 'email', 'bio', 'html_url', + 'type'], 'ints': [ 'id', 'public_repos', 'public_gists', 'followers', 'following', 'total_private_repos', 'owned_private_repos', 'private_gists', 'disk_usage', 'collaborators'], 'maps': {'plan': Plan}, - 'dates': ['created_at',], + 'dates': ['created_at', ], 'bools': ['hireable', ], } @@ -54,6 +59,7 @@ class User(BaseResource): #def handler(self): # return self._gh.user_handler(self.login, force=True) + class AuthUser(User): """Github Authenticated User object model.""" @@ -62,4 +68,3 @@ class AuthUser(User): def __repr__(self): return '' % self.login - diff --git a/github3/tests/converters_test.py b/github3/tests/converters_test.py index 315477c..66eedc3 100644 --- a/github3/tests/converters_test.py +++ b/github3/tests/converters_test.py @@ -7,8 +7,10 @@ from unittest import TestCase from datetime import datetime API_STUB = { - 'test_str': 'string', 'test_int': 1, - 'test_date': '2008-01-14T04:33:35Z', 'test_bool': True, + 'test_str': 'string', + 'test_int': 1, + 'test_date': '2008-01-14T04:33:35Z', + 'test_bool': True, 'map': {'test_str': 'string'}, 'dict_map': { 'map1': { @@ -55,6 +57,7 @@ class TestModelizer(TestCase): def test_loads(self): parsed_model = self.modelizer.loads(API_STUB) + self.assertEquals(len(parsed_model), len(API_STUB)) self.assertEquals(parsed_model.test_str, 'string') self.assertEquals(parsed_model.test_int, 1) self.assertEquals( diff --git a/github3/tests/fixtures.py b/github3/tests/fixtures.py index 469b00a..02086f8 100644 --- a/github3/tests/fixtures.py +++ b/github3/tests/fixtures.py @@ -5,6 +5,7 @@ GET_USER = { "login": "octocat", "id": 1, "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "somehexcode", "url": "https://api.github.com/users/octocat", "name": "monalisa octocat", "company": "GitHub", @@ -24,24 +25,29 @@ GET_USER = { GET_LINK = '; rel="next", \ ; rel="last"' + GET_RESOURCES = [ {'login': 'octocat'}, {'login': 'octocat'} ] + GET_SHORT_USERS = [ { "login": "octocat", "id": 1, "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "somehexcode", "url": "https://api.github.com/users/octocat" }, { "login": "octocat", "id": 1, "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "somehexcode", "url": "https://api.github.com/users/octocat" }, ] + GET_SHORT_ORGS = [ { "login": "github", @@ -63,6 +69,7 @@ GET_SHORT_REPOS = [ "login": "octocat", "id": 1, "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "somehexcode", "url": "https://api.github.com/users/octocat" }, "name": "Hello-World", @@ -80,6 +87,7 @@ GET_SHORT_REPOS = [ "created_at": "2011-01-26T19:01:12Z" } ] + GET_SHORT_GISTS = [ { "url": "https://api.github.com/gists/1", @@ -90,6 +98,7 @@ GET_SHORT_GISTS = [ "login": "octocat", "id": 1, "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "somehexcode", "url": "https://api.github.com/users/octocat" }, "files": { -- cgit v1.2.3-59-g8ed1b From b04156007fa3411c2ae8228e7dbc01da64193eef Mon Sep 17 00:00:00 2001 From: David Medina Date: Thu, 17 Nov 2011 00:16:23 +0100 Subject: Del authors on code. Delegate it to AUTHORS ;) --- github3/api.py | 2 - github3/core.py | 2 - github3/errors.py | 2 - github3/exceptions.py | 2 - github3/handlers/base.py | 2 - github3/handlers/gists.py | 2 - github3/handlers/users.py | 2 - github3/helpers.py | 188 ---------------------------------------- github3/models/base.py | 8 +- github3/models/gists.py | 2 - github3/models/orgs.py | 2 - github3/models/repos.py | 2 - github3/models/user.py | 2 - github3/packages/link_header.py | 3 +- 14 files changed, 4 insertions(+), 217 deletions(-) delete mode 100644 github3/helpers.py (limited to 'github3/models') diff --git a/github3/api.py b/github3/api.py index a4ee4a9..d8d2ffb 100644 --- a/github3/api.py +++ b/github3/api.py @@ -1,7 +1,5 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -# -# author: David Medina import requests import json diff --git a/github3/core.py b/github3/core.py index b838bf1..ab71943 100644 --- a/github3/core.py +++ b/github3/core.py @@ -1,7 +1,5 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -# -# author: David Medina class Paginate: diff --git a/github3/errors.py b/github3/errors.py index eed1e6c..e96e2da 100644 --- a/github3/errors.py +++ b/github3/errors.py @@ -1,7 +1,5 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -# -# author: David Medina import json import github3.exceptions as exceptions diff --git a/github3/exceptions.py b/github3/exceptions.py index dbc4d2c..b9070d7 100644 --- a/github3/exceptions.py +++ b/github3/exceptions.py @@ -1,7 +1,5 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -# -# author: David Medina class BadRequest(Exception): pass diff --git a/github3/handlers/base.py b/github3/handlers/base.py index 0b3fa8a..4e9198f 100644 --- a/github3/handlers/base.py +++ b/github3/handlers/base.py @@ -1,7 +1,5 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -# -# author: David Medina from github3.core import Paginate from github3.converters import Modelizer diff --git a/github3/handlers/gists.py b/github3/handlers/gists.py index fa961b2..38418ca 100644 --- a/github3/handlers/gists.py +++ b/github3/handlers/gists.py @@ -1,7 +1,5 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -# -# author: Antti Kaihola from .base import Handler from .. import models diff --git a/github3/handlers/users.py b/github3/handlers/users.py index 84180b5..34ed013 100644 --- a/github3/handlers/users.py +++ b/github3/handlers/users.py @@ -1,7 +1,5 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -# -# author: David Medina from .base import Handler import github3.models as models diff --git a/github3/helpers.py b/github3/helpers.py deleted file mode 100644 index 205e097..0000000 --- a/github3/helpers.py +++ /dev/null @@ -1,188 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -github3.helpers -~~~~~~~~~~~~~~~~ - -This module provides various helper functions to the rest of the package. -""" - -import inspect -from datetime import datetime - -from dateutil.parser import parse as parse_datetime - - -def is_collection(obj): - """Tests if an object is a collection.""" - - col = getattr(obj, '__getitem__', False) - val = False if (not col) else True - - if isinstance(obj, basestring): - val = False - - return val - - -def key_diff(source, update, pack=False): - """Given two dictionaries, returns a list of the changed keys.""" - - source = dict(source) - update = dict(update) - - changed = [] - - for (k, v) in source.items(): - u_v = update.get(k) - - if (v != u_v) and (u_v is not None): - changed.append(k) - - if pack is False: - return changed - - d = dict() - - for k in changed: - d[k] = update[k] - - return d - - -# from arc90/python-readability-api -def to_python(obj, - in_dict, - str_keys=None, - date_keys=None, - int_keys=None, - object_map=None, - list_map=None, - bool_keys=None, **kwargs): - """Extends a given object for API Consumption. - - :param obj: Object to extend. - :param in_dict: Dict to extract data from. - :param string_keys: List of in_dict keys that will be extracted as strings. - :param date_keys: List of in_dict keys that will be extrad as datetimes. - :param object_map: Dict of {key, obj} map, for nested object results. - """ - - d = dict() - - if str_keys: - for in_key in str_keys: - d[in_key] = in_dict.get(in_key) - - if date_keys: - for in_key in date_keys: - in_date = in_dict.get(in_key) - try: - out_date = datetime.strptime(in_date, '%Y-%m-%dT%H:%M:%SZ') - except TypeError: - out_date = None - - d[in_key] = out_date - - if int_keys: - for in_key in int_keys: - if (in_dict is not None) and (in_dict.get(in_key) is not None): - d[in_key] = int(in_dict.get(in_key)) - - if bool_keys: - for in_key in bool_keys: - if in_dict.get(in_key) is not None: - d[in_key] = bool(in_dict.get(in_key)) - - if object_map: - for (k, v) in object_map.items(): - if in_dict.get(k): - if v == 'self': - v = obj.__class__ - d[k] = v.new_from_dict(in_dict.get(k)) - - if list_map: - for k, model in list_map.items(): - nested_map = in_dict.get(k) - if nested_map: - if getattr(nested_map, 'items', False): - map_dict = {} - for nested_item, nested_dict in nested_map.items(): - map_dict[nested_item] = model.new_from_dict(nested_dict) - d[k] = map_dict - else: - map_list = [] - for item_map in nested_map: - map_list.append(model.new_from_dict(item_map)) - d[k] = map_list - - obj.__dict__.update(d) - obj.__dict__.update(kwargs) - - # Save the dictionary, for write comparisons. - obj._cache = d - obj.__cache = in_dict - obj.post_map() - - return obj - - -# from arc90/python-readability-api -def to_api(in_dict, int_keys=None, date_keys=None, bool_keys=None): - """Extends a given object for API Production.""" - - # Cast all int_keys to int() - if int_keys: - for in_key in int_keys: - if (in_key in in_dict) and (in_dict.get(in_key, None) is not None): - in_dict[in_key] = int(in_dict[in_key]) - - # Cast all date_keys to datetime.isoformat - if date_keys: - for in_key in date_keys: - if (in_key in in_dict) and (in_dict.get(in_key, None) is not None): - - _from = in_dict[in_key] - - if isinstance(_from, basestring): - dtime = parse_datetime(_from) - - elif isinstance(_from, datetime): - dtime = _from - - in_dict[in_key] = dtime.isoformat() - - elif (in_key in in_dict) and in_dict.get(in_key, None) is None: - del in_dict[in_key] - - # Remove all Nones - for k, v in in_dict.items(): - if v is None: - del in_dict[k] - - return in_dict - - - -# from kennethreitz/showme -def get_scope(f, args=None): - """Get scope of given function for Exception scopes.""" - - if args is None: - args=list() - - scope = inspect.getmodule(f).__name__ - # guess that function is a method of it's class - try: - if f.func_name in dir(args[0].__class__): - scope += '.' + args[0].__class__.__name__ - scope += '.' + f.__name__ - else: - scope += '.' + f.__name__ - except IndexError: - scope += '.' + f.__name__ - - # scrub readability.models namespace - scope = scope.replace('readability.api.', '') - - return scope diff --git a/github3/models/base.py b/github3/models/base.py index bd07650..5295d07 100644 --- a/github3/models/base.py +++ b/github3/models/base.py @@ -1,9 +1,5 @@ -""" -github3.models -~~~~~~~~~~~~~~ - -This package provides the Github3 object model. -""" +#!/usr/bin/env python +# -*- encoding: utf-8 -*- class BaseResource(object): """A BaseResource object.""" diff --git a/github3/models/gists.py b/github3/models/gists.py index d1b416d..b317f57 100644 --- a/github3/models/gists.py +++ b/github3/models/gists.py @@ -1,7 +1,5 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -# -# author: David Medina from .base import BaseResource from .user import User diff --git a/github3/models/orgs.py b/github3/models/orgs.py index 5e66c35..b2dacbd 100644 --- a/github3/models/orgs.py +++ b/github3/models/orgs.py @@ -1,7 +1,5 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -# -# author: David Medina from .base import BaseResource from .user import Plan diff --git a/github3/models/repos.py b/github3/models/repos.py index d1b7b75..882fb37 100644 --- a/github3/models/repos.py +++ b/github3/models/repos.py @@ -1,7 +1,5 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -# -# author: David Medina from .base import BaseResource from .user import User diff --git a/github3/models/user.py b/github3/models/user.py index e2d82b6..93201bd 100644 --- a/github3/models/user.py +++ b/github3/models/user.py @@ -1,7 +1,5 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -# -# author: David Medina from .base import BaseResource diff --git a/github3/packages/link_header.py b/github3/packages/link_header.py index 3959604..5ad20f1 100644 --- a/github3/packages/link_header.py +++ b/github3/packages/link_header.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -*- encoding: utf-8 -*- """ HTTP Link Header Parsing @@ -86,4 +87,4 @@ def parse_link_value(instr): if __name__ == '__main__': import sys if len(sys.argv) > 1: - print parse_link_value(sys.argv[1]) \ No newline at end of file + print parse_link_value(sys.argv[1]) -- cgit v1.2.3-59-g8ed1b From 357985782664eba287e73e5bbbb28c1884adccea Mon Sep 17 00:00:00 2001 From: David Medina Date: Thu, 17 Nov 2011 00:17:48 +0100 Subject: Complete AuthUser handler test Update code putting tests in green --- github3/handlers/users.py | 46 +++++++++----- github3/models/user.py | 11 ++-- github3/tests/fixtures.py | 13 +++- github3/tests/user_handler_test.py | 121 ++++++++++++++++++++++++++++++++++++- 4 files changed, 167 insertions(+), 24 deletions(-) (limited to 'github3/models') diff --git a/github3/handlers/users.py b/github3/handlers/users.py index 34ed013..08c6171 100644 --- a/github3/handlers/users.py +++ b/github3/handlers/users.py @@ -155,7 +155,7 @@ class AuthUser(User): :param `user`: User model or username string """ - parse_user = str(getattr(user, 'login', user)) + parse_user = getattr(user, 'login', user) return self._bool('following/%s' % parse_user) def follow(self, user): @@ -179,23 +179,28 @@ class AuthUser(User): parse_user = getattr(user, 'login', user) return self._delete('following/%s' % parse_user) - def get_keys(self): + def get_keys(self, limit=None): """ Get public keys """ return self._get_resources('keys', model=models.Key, limit=limit) - def get_key(self, key_id): - """ Get public key by id """ + def get_key(self, key): + """ Get public key + + :param `key`: Key model or key id + + """ - return self._get_resource('keys/%s' % key_id, model=models.Key) + parse_key_id = getattr(key, 'id', key) + return self._get_resource('keys/%s' % parse_key_id, model=models.Key) def create_key(self, **kwargs): """ Create public key :param title - :param key: Key string + :param key: Key string (It must starts with 'ssh-rsa') """ #TODO: render key.pub file @@ -205,12 +210,17 @@ class AuthUser(User): } return self._post_resource('keys', data=key, model=models.Key) - def delete_key(self, key_id): - """ Delete public key """ + def delete_key(self, key): + """ Delete public key - return self._delete('keys/%s' % key_id) + :param `key`: Key model or key id - def get_repos(self, filter='all'): + """ + + parse_key_id = getattr(key, 'id', key) + return self._delete('keys/%s' % parse_key_id) + + def get_repos(self, filter='all', limit=None): """ Return user's public repositories @@ -224,8 +234,8 @@ class AuthUser(User): """ Return true if you are watching the user repository - :param owner: username - :param repo: repository name + :param owner: Model user or username string + :param repo: Model repo or repo name string is_watching_repo('copitux', 'python-github3') """ @@ -237,18 +247,22 @@ class AuthUser(User): """ Watch the repository - :param owner: username - :param repo: repository name + :param owner: Model user or username string + :param repo: Model repo or repo name string """ + owner = getattr(owner, 'login', owner) + repo = getattr(repo, 'name', repo) return self._put('watched/%s/%s' % (owner, repo)) def unwatch_repo(self, owner, repo): """ Unwatch the repository - :param owner: username - :param repo: repository name + :param owner: Model user or username string + :param repo: Model repo or repo name string """ + owner = getattr(owner, 'login', owner) + repo = getattr(repo, 'name', repo) return self._delete('watched/%s/%s' % (owner, repo)) diff --git a/github3/models/user.py b/github3/models/user.py index 93201bd..aed6f09 100644 --- a/github3/models/user.py +++ b/github3/models/user.py @@ -52,17 +52,18 @@ class User(BaseResource): } def __repr__(self): - return '' % self.login + return '' % getattr(self, 'login', 'without user') #def handler(self): - # return self._gh.user_handler(self.login, force=True) + # return self._gh.users class AuthUser(User): """Github Authenticated User object model.""" - #def handler(self): - # return self._gh.user_handler(self.login, force=True, private=True) - def __repr__(self): return '' % self.login + + #def handler(self): + # return self._gh.users + diff --git a/github3/tests/fixtures.py b/github3/tests/fixtures.py index 5f56753..ff5179c 100644 --- a/github3/tests/fixtures.py +++ b/github3/tests/fixtures.py @@ -23,8 +23,8 @@ GET_USER = { "type": "User" } -GET_LINK = '; rel="next", \ -; rel="last"' +GET_LINK = '; rel="next", \ +; rel="last"' GET_RESOURCES = [ {'login': 'octocat'}, @@ -153,3 +153,12 @@ GET_USER_EMAILS = [ "octocat@github.com", "support@github.com" ] + +GET_USER_KEYS = [ + { + "url": "https://api.github.com/user/keys/1", + "id": 1, + "title": "octocat@octomac", + "key": "ssh-rsa AAA..." + } +] diff --git a/github3/tests/user_handler_test.py b/github3/tests/user_handler_test.py index b1fd0d0..28fda3b 100644 --- a/github3/tests/user_handler_test.py +++ b/github3/tests/user_handler_test.py @@ -5,7 +5,7 @@ from unittest import TestCase from mock import Mock, patch from github3 import api from fixtures import * -from github3.models import User, AuthUser, Repo, Gist, Org +from github3.models import User, AuthUser, Repo, Gist, Org, Key from github3.exceptions import * @@ -15,6 +15,8 @@ class TestAuthUserHandler(TestCase): def setUp(self): self.gh = api.Github('test', 'pass') self.handler = self.gh.users + self.user_mock = Mock() + self.user_mock.login = 'user_model' @patch.object(api.Github, 'get') def test_get(self, get): @@ -48,6 +50,123 @@ class TestAuthUserHandler(TestCase): method='delete') self.assertTrue(emails) + @patch.object(api.Github, 'head') + def test_is_following(self, head): + response = head.return_value + response.status_code = 204 + self.assertTrue(self.handler.is_following('test')) + head.assert_called_with('user/following/test') + self.handler.is_following(self.user_mock) + head.assert_called_with('user/following/user_model') + + @patch.object(api.Github, 'put') + def test_follow(self, put): + response = put.return_value + response.status_code = 204 + self.assertTrue(self.handler.follow('test')) + put.assert_called_with('user/following/test', method='put') + + @patch.object(api.Github, 'delete') + def test_unfollow(self, delete): + response = delete.return_value + response.status_code = 204 + self.assertTrue(self.handler.unfollow('test')) + delete.assert_called_with('user/following/test', method='delete') + + @patch.object(api.Github, '_request') + def test_get_keys(self, request): + response = request.return_value + response.status_code = 200 + response.content = self.gh._parser.dumps(GET_USER_KEYS) + response.headers = {'link': GET_LINK} # 1 per page + keys = list(self.handler.get_keys()) + self.assertEquals(len(keys), 5) + self.assertIsInstance(keys[0], Key) + request.assert_called_with('GET', 'user/keys', page=5) + keys = list(self.handler.get_keys(limit=2)) + self.assertEquals(len(keys), 2) + + @patch.object(api.Github, 'get') + def test_get_key(self, get): + get.return_value = GET_USER_KEYS[0] + key = self.handler.get_key(1) + self.assertIsInstance(key, Key) + get.assert_called_with('user/keys/1') + model_key = Mock() + model_key.id = 1 + key = self.handler.get_key(model_key) + get.assert_called_with('user/keys/1') + + @patch.object(api.Github, 'post') + def test_create_key(self, post): + post.return_value = GET_USER_KEYS[0] + key_data = {'title': 'some', 'key': 'ssh-rsa AAA'} + created_key = self.handler.create_key(**key_data) + self.assertIsInstance(created_key, Key) + post.assert_called_with('user/keys', data=key_data) + + @patch.object(api.Github, 'delete') + def test_delete_key(self, delete): + response = delete.return_value + response.status_code = 204 + self.assertTrue(self.handler.delete_key(1)) + delete.assert_called_with('user/keys/1', method='delete') + model_key = Mock() + model_key.id = 1 + key = self.handler.delete_key(model_key) + delete.assert_called_with('user/keys/1', method='delete') + + @patch.object(api.Github, '_request') + def test_get_repos(self, request): + response = request.return_value + response.status_code = 200 + response.content = self.gh._parser.dumps(GET_SHORT_REPOS) + response.headers = {'link': GET_LINK} # 1 per page + repos = list(self.handler.get_repos(filter='public')) + self.assertEquals(len(repos), 5) + self.assertIsInstance(repos[0], Repo) + request.assert_called_with('GET', 'user/repos', + page=5, type='public') + repos = list(self.handler.get_repos(limit=2)) + self.assertEquals(len(repos), 2) + + @patch.object(api.Github, 'head') + def test_is_watching_repo(self, head): + response = head.return_value + response.status_code = 204 + self.assertTrue(self.handler.is_watching_repo('user', 'repo')) + head.assert_called_with('user/watched/user/repo') + model_user, model_repo = Mock(), Mock() + model_user.login = 'user' + model_repo.name = 'repo' + self.assertTrue(self.handler.is_watching_repo('user', 'repo')) + head.assert_called_with('user/watched/user/repo') + + @patch.object(api.Github, 'put') + def test_watch_repo(self, put): + response = put.return_value + response.status_code = 204 + self.assertTrue(self.handler.watch_repo('user', 'repo')) + put.assert_called_with('user/watched/user/repo', method='put') + model_user, model_repo = Mock(), Mock() + model_user.login = 'user' + model_repo.name = 'repo' + self.assertTrue(self.handler.watch_repo('user', 'repo')) + put.assert_called_with('user/watched/user/repo', method='put') + + @patch.object(api.Github, 'delete') + def test_unwatch_repo(self, delete): + response = delete.return_value + response.status_code = 204 + self.assertTrue(self.handler.unwatch_repo('user', 'repo')) + delete.assert_called_with('user/watched/user/repo', method='delete') + model_user, model_repo = Mock(), Mock() + model_user.login = 'user' + model_repo.name = 'repo' + self.assertTrue(self.handler.unwatch_repo('user', 'repo')) + delete.assert_called_with('user/watched/user/repo', method='delete') + + class TestUserHandler(TestCase): """ Test public api about users """ -- cgit v1.2.3-59-g8ed1b From 88e54d4dc2e9c49dadf1a9b8e14fc107270c9415 Mon Sep 17 00:00:00 2001 From: David Medina Date: Tue, 29 Nov 2011 22:19:26 +0100 Subject: pep8 --- github3/handlers/base.py | 12 +++++++----- github3/models/gists.py | 10 ++++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'github3/models') diff --git a/github3/handlers/base.py b/github3/handlers/base.py index 7bc55d2..b20bb7d 100644 --- a/github3/handlers/base.py +++ b/github3/handlers/base.py @@ -55,9 +55,9 @@ class Handler(object): def _get_converter(self, **kwargs): converter = kwargs.get( - 'converter', # 1. in kwargs - getattr(self, 'converter', # 2. in handler - Modelizer)) # 3. Default + 'converter', # 1. in kwargs + getattr(self, 'converter', # 2. in handler + Modelizer)) # 3. Default return converter() @@ -77,7 +77,8 @@ class Handler(object): from github3.exceptions import NotFound resource = self._prefix_resource(resource) try: - callback = getattr(self._gh, kwargs.get('method',''), self._gh.head) + callback = getattr(self._gh, kwargs.get('method', ''), + self._gh.head) response = callback(resource, **kwargs) except NotFound: return False @@ -97,7 +98,8 @@ class Handler(object): converter = self._get_converter(**kwargs) converter.inject(model) yield converter.loads(raw_resource) - if limit and counter > limit: break + if limit and counter > limit: + break else: continue break diff --git a/github3/models/gists.py b/github3/models/gists.py index b317f57..13f3a62 100644 --- a/github3/models/gists.py +++ b/github3/models/gists.py @@ -17,6 +17,7 @@ class File(BaseResource): def __repr__(self): return ' %s' % self.filename + class GistFork(BaseResource): """ GistFork model """ @@ -31,6 +32,7 @@ class GistFork(BaseResource): def __repr__(self): return ' %s>' % self.user.login + class ChangeStatus(BaseResource): """ ChangeStatus model """ @@ -43,6 +45,7 @@ class ChangeStatus(BaseResource): def __repr__(self): return ' change_status>' + class GistHistory(BaseResource): """ """ @@ -57,18 +60,21 @@ class GistHistory(BaseResource): def __repr__(self): return '' % (self.user, self.committed_at) + class Gist(BaseResource): """ """ @classmethod def idl(self): return { - 'strs': ['url', 'description', 'html_url', 'git_pull_url', 'git_push_url'], + 'strs': ['url', 'description', 'html_url', 'git_pull_url', + 'git_push_url'], 'ints': ['id', 'comments'], 'bools': ['public'], 'dates': ['created_at'], 'maps': {'user': User}, - 'collection_maps': {'files': File, 'forks': GistFork, 'history': GistHistory}, + 'collection_maps': {'files': File, 'forks': GistFork, + 'history': GistHistory}, } def __repr__(self): -- cgit v1.2.3-59-g8ed1b From 1b310d37945c6f846f2145817353d9d03916a2ae Mon Sep 17 00:00:00 2001 From: David Medina Date: Tue, 29 Nov 2011 23:24:15 +0100 Subject: GistComment model --- github3/models/__init__.py | 2 +- github3/models/gists.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'github3/models') diff --git a/github3/models/__init__.py b/github3/models/__init__.py index ff0c28a..0471393 100644 --- a/github3/models/__init__.py +++ b/github3/models/__init__.py @@ -1,4 +1,4 @@ from .user import AuthUser, User, Key from .repos import Repo from .orgs import Org -from .gists import Gist +from .gists import Gist, GistComment diff --git a/github3/models/gists.py b/github3/models/gists.py index 13f3a62..8979dbb 100644 --- a/github3/models/gists.py +++ b/github3/models/gists.py @@ -4,6 +4,23 @@ from .base import BaseResource from .user import User + +class GistComment(BaseResource): + """ Gist comment """ + + @classmethod + def idl(self): + return { + 'strs': ['url', 'body', 'body_text', 'body_html'], + 'ints': ['id'], + 'maps': {'user': User}, + 'dates': ['created_at'], + } + + def __repr__(self): + return '' % self.user.login + + class File(BaseResource): """ File model """ -- cgit v1.2.3-59-g8ed1b