aboutsummaryrefslogtreecommitdiffstats
path: root/github3/api.py
diff options
context:
space:
mode:
Diffstat (limited to 'github3/api.py')
-rw-r--r--github3/api.py312
1 files changed, 106 insertions, 206 deletions
diff --git a/github3/api.py b/github3/api.py
index d11aef6..2066e94 100644
--- a/github3/api.py
+++ b/github3/api.py
@@ -1,223 +1,123 @@
-# -*- coding: utf-8 -*-
-
-"""
-github3.api
-~~~~~~~~~~~
-
-This module provies the core GitHub3 API interface.
-"""
-
-from urlparse import urlparse, parse_qs
+#!/usr/bin/env python
+# -*- encoding: utf-8 -*-
+#
+# author: David Medina
import requests
-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 json
+from errors import GithubError
-
-
-
-PAGING_SIZE = 100
+RESOURCES_PER_PAGE = 100
class GithubCore(object):
+ """
+ Wrapper to github api requests
+
+ Methods: get, head, post, patch, put, delete
+ """
- _rate_limit = None
- _rate_limit_remaining = None
+ requests_remaining = None
+ base_url = 'https://api.github.com/'
def __init__(self):
+ """
+ Init `requests.session`
+ Init JSON parser
+ """
self.session = requests.session()
- self.session.params = {'per_page': PAGING_SIZE}
-
-
- @staticmethod
- def _resource_serialize(o):
- """Returns JSON serialization of given object."""
- return json.dumps(o)
-
-
- @staticmethod
- def _resource_deserialize(s):
- """Returns dict deserialization of a given JSON string."""
-
- try:
- return json.loads(s)
- except ValueError:
- raise ResponseError('The API Response was not valid.')
-
-
- @staticmethod
- def _generate_url(endpoint):
- """Generates proper endpoint URL."""
-
- if is_collection(endpoint):
- resource = map(str, endpoint)
- resource = '/'.join(endpoint)
+ self.session.params = {'per_page': RESOURCES_PER_PAGE}
+ self._parser = json
+
+ def get(self, request, paginate=False, **kwargs):
+ """
+ GET request
+
+ :param paginate: Boolean to return link header to paginate
+ """
+ response = self._request('GET', request, **kwargs)
+ content = self._parser.loads(response.content)
+ if paginate:
+ return response.headers.get('link'), content
else:
- resource = endpoint
-
- return (settings.base_url + resource)
-
-
- def _requests_post_hook(self, r):
- """Post-processing for HTTP response objects."""
-
- self._ratelimit = int(r.headers.get('x-ratelimit-limit', -1))
- self._ratelimit_remaining = int(r.headers.get('x-ratelimit-remaining', -1))
-
- return r
-
-
- def _http_resource(self, verb, endpoint, params=None, check_status=True, **etc):
-
- url = self._generate_url(endpoint)
- args = (verb, url)
-
- if params:
- kwargs = {'params': params}
- kwargs.update(etc)
+ return content
+
+ def head(self, request, **kwargs):
+ """ HEAD request """
+ return self._request('HEAD', request, **kwargs).headers
+
+ def post(self, request, data=None, **kwargs):
+ """
+ POST request
+
+ :param data: raw python object to send
+ """
+ kwargs['data'] = self._parser.dumps(data)
+ response = self._request('POST', request, **kwargs)
+ assert response.status_code == 201
+ return self._parser.loads(response.content)
+
+ def patch(self, request, data=None, **kwargs):
+ """
+ PATCH request
+
+ :param data: raw python object to send
+ """
+ kwargs['data'] = self._parser.dumps(data)
+ response = self._request('PATCH', request, **kwargs)
+ assert response.status_code == 200
+ return self._parser.loads(response.content)
+
+ def put(self, request, **kwargs):
+ """ PUT request """
+ response = self._request('PUT', request, **kwargs)
+ assert response.status_code == 204
+
+ def delete(self, request, **kwargs):
+ """ DELETE request """
+ response = self._request('DELETE', request, **kwargs)
+ assert response.status_code == 204
+
+ def _parse_args(self, request_args):
+ """
+ Arg's parser to `_request` method
+
+ It check keyword args to parse extra request args to params
+ Sample:
+ _parse_args(arg1=1, arg2=2) => params = {'arg1': 1, 'arg2': 2}
+ """
+ request_core = (
+ 'params','data','headers','cookies','files','auth','tiemout',
+ 'allow_redirects','proxies','return_response','config')
+ request_params = request_args.get('params')
+ extra_params = {}
+ for k, v in request_args.items():
+ if k in request_core: continue
+ extra_params.update({k: v})
+ del request_args[k]
+ if request_params:
+ request_args['params'].update(extra_params)
else:
- kwargs = etc
-
- r = self.session.request(*args, **kwargs)
- r = self._requests_post_hook(r)
-
- if check_status:
- r.raise_for_status()
-
- return r
-
-
- def _get_resource(self, resource, obj, **kwargs):
-
- r = self._http_resource('GET', resource, params=kwargs)
- item = self._resource_deserialize(r.content)
-
- return obj.new_from_dict(item, gh=self)
-
- def _patch_resource(self, resource, data, **kwargs):
- r = self._http_resource('PATCH', resource, data=data, params=kwargs)
- msg = self._resource_deserialize(r.content)
-
- return msg
-
+ request_args['params'] = extra_params
- @staticmethod
- def _total_pages_from_header(link_header):
+ return request_args
- if link_header is None:
- return link_header
-
- page_info = {}
-
- for link in link_header.split(','):
-
- uri, meta = map(str.strip, link.split(';'))
-
- # Strip <>'s
- uri = uri[1:-1]
-
- # Get query params from header.
- q = parse_qs(urlparse(uri).query)
- meta = meta[5:-1]
-
- page_info[meta] = q
-
- try:
- return int(page_info['last']['page'].pop())
- except KeyError:
- return True
-
- def _get_resources(self, resource, obj, limit=None, **kwargs):
-
- if limit is not None:
- assert limit > 0
-
- moar = True
- is_truncated = (limit > PAGING_SIZE) or (limit is None)
- r_count = 0
- page = 1
-
- while moar:
-
- if not is_truncated:
- kwargs['per_page'] = limit
- moar = False
- else:
- kwargs['page'] = page
- if limit:
- if (limit - r_count) < PAGING_SIZE:
- kwargs['per_page'] = (limit - r_count)
- moar = False
-
- r = self._http_resource('GET', resource, params=kwargs)
- max_page = self._total_pages_from_header(r.headers['link'])
-
- if (max_page is True) or (max_page is None):
- moar = False
-
- d_items = self._resource_deserialize(r.content)
-
- for item in d_items:
- if (r_count < limit) or (limit is None):
- r_count += 1
- yield obj.new_from_dict(item, gh=self)
- else:
- moar = False
-
- page += 1
-
-
- def _to_map(self, obj, iterable):
- """Maps given dict iterable to a given Resource object."""
-
- a = list()
-
- for it in iterable:
- a.append(obj.new_from_dict(it, rdd=self))
-
- return a
-
- def _get_url(self, resource):
-
- if is_collection(resource):
- resource = map(str, resource)
- resource = '/'.join(resource)
-
- return resource
+ def _request(self, verb, request, **kwargs):
+ """
+ Http request wrapper
+ :param verb: Http method
+ :param request : Url query request
+ :param kwargs: Keyword args to request
+ """
+ request = self.base_url + request
+ parsed_args = self._parse_args(kwargs)
+ response = self.session.request(verb, request, **parsed_args)
+ self.requests_remaining = response.headers.get(
+ 'x-ratelimit-remaining',-1)
+ error = GithubError(response)
+ error.process()
+ return response
class Github(GithubCore):
- """docstring for Github"""
-
- def __init__(self):
- 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)
-
-
-class ResponseError(Exception):
- """The API Response was unexpected."""
-
+ pass