diff options
-rw-r--r-- | github3/api.py | 45 | ||||
-rw-r--r-- | github3/exceptions.py | 8 | ||||
-rw-r--r-- | github3/handlers/__init__.py | 1 | ||||
-rw-r--r-- | github3/handlers/base.py | 25 | ||||
-rw-r--r-- | github3/handlers/user.py | 54 | ||||
-rw-r--r-- | github3/helpers.py | 21 | ||||
-rw-r--r-- | github3/models.py | 246 | ||||
-rw-r--r-- | github3/models/__init__.py | 4 | ||||
-rw-r--r-- | github3/models/base.py | 149 | ||||
-rw-r--r-- | github3/models/gists.py | 49 | ||||
-rw-r--r-- | github3/models/orgs.py | 28 | ||||
-rw-r--r-- | github3/models/repos.py | 30 | ||||
-rw-r--r-- | github3/models/user.py | 54 |
13 files changed, 433 insertions, 281 deletions
diff --git a/github3/api.py b/github3/api.py index 4cb0199..d99682a 100644 --- a/github3/api.py +++ b/github3/api.py @@ -15,11 +15,9 @@ 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 PAGING_SIZE = 100 @@ -171,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.""" @@ -182,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""" @@ -199,27 +190,13 @@ class Github(GithubCore): super(Github, self).__init__() self.is_authenticated = False - - def get_user(self, username): - """Get a single user.""" - return self._get_resource(('users', username), User) - - - def get_me(self): - """Get the authenticated user.""" - return self._get_resource(('user'), CurrentUser) - - def get_repo(self, username, reponame): - """Get the given repo.""" - return self._get_resource(('repos', username, reponame), Repo) - - def get_org(self, login): - """Get organization.""" - return self._get_resource(('orgs', login), Org) - - def post_gist(self, json_data): - self.session.post(settings.base_url+'gist', json_data) - + 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: + self._user_handler = handlers.User(self, username) + return self._user_handler class ResponseError(Exception): """The API Response was unexpected.""" diff --git a/github3/exceptions.py b/github3/exceptions.py new file mode 100644 index 0000000..72da776 --- /dev/null +++ b/github3/exceptions.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# +# author: David Medina + +class AnomUser(Exception): + """ Exception for AnomUser handler """ + pass diff --git a/github3/handlers/__init__.py b/github3/handlers/__init__.py new file mode 100644 index 0000000..126efcb --- /dev/null +++ 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 new file mode 100644 index 0000000..93a4680 --- /dev/null +++ b/github3/handlers/base.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# +# author: David Medina + +class Handler(object): + """ Abstract handler, that inject github.api """ + + def __init__(self, gh): + self._gh = gh + super(Handler, self).__init__() + + def _extend_url(self, *args): + return self._url + args + + def _get_resource(self, *args, **kwargs): + url = self._extend_url(*args) + map_model = kwargs.get('model', self._model) + return self._gh._get_resource(url, map_model, **kwargs) + + def _get_resources(self, *args, **kwargs): + 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 new file mode 100644 index 0000000..c59607d --- /dev/null +++ b/github3/handlers/user.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# +# author: David Medina + +from .base import Handler +import github3.models as models + +class User(Handler): + """ Handler to query public user api """ + + def __init__(self, gh, username): + if not username: + raise exceptions.AnomUser("%s need a username" % self.__class__) + + self._url = ('users', username) + self._model = models.User + self.username = username + super(User, self).__init__(gh) + + def __repr__(self): + return '<Handler.AnomUser> %s' % self.username + + def get(self): + return self._get_resource() + + def get_followers(self, limit=None): + return self._get_resources('followers') + + def get_following(self, limit=None): + return self._get_resources('following') + + 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.AuthUser + super(AnomUser, self).__init__(gh) + + def __repr__(self): + return '<Handler.User>' diff --git a/github3/helpers.py b/github3/helpers.py index abdeb4a..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 @@ -166,4 +185,4 @@ def get_scope(f, args=None): # scrub readability.models namespace scope = scope.replace('readability.api.', '') - return scope
\ No newline at end of file + return scope diff --git a/github3/models.py b/github3/models.py deleted file mode 100644 index 3c86687..0000000 --- a/github3/models.py +++ /dev/null @@ -1,246 +0,0 @@ -""" -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 '<plan {0}>'.format(str(self.name)) - - - -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 '<user {0}>'.format(self.login) - - 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 CurrentUser(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 '<current-user {0}>'.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) - - - -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 '<org {0}>'.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 '<gist %s/%s>' % (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 '<repo {0}/{1}>'.format(self.owner.login, self.name) - # owner diff --git a/github3/models/__init__.py b/github3/models/__init__.py new file mode 100644 index 0000000..6e4db1b --- /dev/null +++ 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 new file mode 100644 index 0000000..1b1ce89 --- /dev/null +++ b/github3/models/base.py @@ -0,0 +1,149 @@ +""" +github3.models +~~~~~~~~~~~~~~ + +This module 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), + 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() + + 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 '<org {0}>'.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 '<File gist> %s' % self.filename + +class GistFork(BaseResource): + _strs = ['url'] + _dates = ['created_at'] + _map = {'user': User} + + def __repr__(self): + return '<Gist fork> %s>' % self.user.login + +class ChangeStatus(BaseResource): + _ints = ['deletions', 'additions', 'total'] + + def __repr__(self): + return '<Gist history> 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 '<gist %s/%s>' % (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 '<org {0}>'.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 '<Repo {0}/{1}>'.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..d58d9b2 --- /dev/null +++ b/github3/models/user.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# +# author: David Medina + +from .base import BaseResource + +class Plan(BaseResource): + """Github Plan object model.""" + + _strs = ['name'] + _ints = ['space', 'collaborators', 'private_repos'] + + def __repr__(self): + return '<Plan {0}>'.format(str(self.name)) + +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) + + def __repr__(self): + return '<User {0}>'.format(self.login) + + 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'] + + @property + def ri(self): + return ('user',) + + def __repr__(self): + return '<AuthUser {0}>'.format(self.login) + |