aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--github3/__init__.py2
-rw-r--r--github3/core.py124
-rw-r--r--github3/handlers/base.py62
-rw-r--r--github3/models/base.py145
-rw-r--r--github3/models/gists.py68
-rw-r--r--github3/models/orgs.py27
-rw-r--r--github3/models/repos.py35
-rw-r--r--github3/models/user.py69
8 files changed, 236 insertions, 296 deletions
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 '<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)
-#
-#
+ 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 '<File gist> %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 '<Gist fork> %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 '<Gist history> 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 '<GistHistory %s/%s>' % (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 '<gist %s/%s>' % (self.user.login, self.description)
-
+ return '<Gist %s/%s>' % (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 '<org {0}>'.format(self.login)
+ return '<Org %s>' % 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 '<Repo {0}/{1}>'.format(self.owner.login, self.name)
- # owner
+ return '<Repo %s/%s>' % (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 '<Plan {0}>'.format(str(self.name))
+ return '<Plan %s>' % 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 '<Key {0}>'.format(str(self.title))
+ return '<Key %s>' % 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 '<model.User {0}>'.format(self.login)
+ return '<User %s>' % 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 '<model.AuthUser {0}>'.format(self.login)
+ return '<AuthUser %s>' % self.login