diff options
author | 2011-11-09 00:35:13 +0100 | |
---|---|---|
committer | 2011-11-09 00:35:13 +0100 | |
commit | ed34830ab64837d16b2943887045a44f0760ee8e (patch) | |
tree | f91c1bebb8b11cccc5bab07dfb54ea5031923888 /github3/api.py | |
parent | Fix/update typo. avatar_url in User (diff) | |
download | python-github3-ed34830ab64837d16b2943887045a44f0760ee8e.tar.xz python-github3-ed34830ab64837d16b2943887045a44f0760ee8e.zip |
New design. Merge develop branch
Diffstat (limited to 'github3/api.py')
-rw-r--r-- | github3/api.py | 312 |
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 |