diff options
author | 2012-02-06 21:39:59 +0100 | |
---|---|---|
committer | 2012-02-07 19:22:59 +0100 | |
commit | 86453d2fb40e21280aeeee7e5ee11229bcb5eaac (patch) | |
tree | 2490c76fbbdef9bc14e5c1c2b83b632b1401eb2e /pygithub3 | |
parent | Something confusing about PUT request (diff) | |
download | python-github3-86453d2fb40e21280aeeee7e5ee11229bcb5eaac.tar.xz python-github3-86453d2fb40e21280aeeee7e5ee11229bcb5eaac.zip |
Clean requests interface
Refactorize on build request to support specific requests cleaner
*Also support to verbose on requests library
Diffstat (limited to 'pygithub3')
-rw-r--r-- | pygithub3/core/client.py | 8 | ||||
-rw-r--r-- | pygithub3/core/errors.py | 19 | ||||
-rw-r--r-- | pygithub3/core/result.py | 2 | ||||
-rw-r--r-- | pygithub3/exceptions.py | 36 | ||||
-rw-r--r-- | pygithub3/requests/__init__.py | 106 | ||||
-rw-r--r-- | pygithub3/requests/users/__init__.py | 2 | ||||
-rw-r--r-- | pygithub3/requests/users/emails.py | 36 | ||||
-rw-r--r-- | pygithub3/requests/users/followers.py | 56 | ||||
-rw-r--r-- | pygithub3/requests/users/keys.py | 59 | ||||
-rw-r--r-- | pygithub3/requests/users/user.py | 25 | ||||
-rw-r--r-- | pygithub3/resources/base.py | 3 | ||||
-rw-r--r-- | pygithub3/services/base.py | 84 | ||||
-rw-r--r-- | pygithub3/services/users.py | 72 |
13 files changed, 224 insertions, 284 deletions
diff --git a/pygithub3/core/client.py b/pygithub3/core/client.py index 866d40a..d9ecf81 100644 --- a/pygithub3/core/client.py +++ b/pygithub3/core/client.py @@ -18,7 +18,7 @@ class Client(object): """ It can be configurated - :login, :password, :user, :repo, :token, :per_page, :base_url + :login, :password, :user, :repo, :token, :per_page, :base_url, :verbose """ self.requester = requests.session() @@ -58,6 +58,8 @@ class Client(object): def __set_params(self, config): self.requester.params['per_page'] = config.get('per_page') + if config.get('verbose'): + self.requester.config = {'verbose': config['verbose']} def __parse_kwargs(func): """ Decorator to put extra args into requests.params """ @@ -106,4 +108,6 @@ class Client(object): return response def head(self, request, **kwargs): - return self.request('head', request, **kwargs) + response = self.request('head', request, **kwargs) + assert response.status_code != '200' + return response diff --git a/pygithub3/core/errors.py b/pygithub3/core/errors.py index 1a9ed0c..ae7bcc4 100644 --- a/pygithub3/core/errors.py +++ b/pygithub3/core/errors.py @@ -1,19 +1,12 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -import json +try: + import simplejson as json +except ImportError: + import json - -class BadRequest(Exception): - pass - - -class UnprocessableEntity(Exception): - pass - - -class NotFound(Exception): - pass +from pygithub3.exceptions import NotFound, BadRequest, UnprocessableEntity class GithubError(object): @@ -34,7 +27,7 @@ class GithubError(object): raise BadRequest("400 - %s" % self.debug.get('message')) def error_422(self): - errors = self.debug.get('errors') + errors = self.debug.get('errors', ()) errors = ['Resource: {resource}: {field} => {message} ({code})'.format( **error) for error in errors] diff --git a/pygithub3/core/result.py b/pygithub3/core/result.py index 5d8ea7a..226d6ab 100644 --- a/pygithub3/core/result.py +++ b/pygithub3/core/result.py @@ -48,7 +48,7 @@ class Method(object): all_args.update(page=page) response = self.method(self.request, **all_args) self.__set_last_page_from(response) - resource = self.request.get_resource() + resource = self.request.resource self.cache[str(page)] = resource.loads(response.content) return self.cache[str(page)] diff --git a/pygithub3/exceptions.py b/pygithub3/exceptions.py new file mode 100644 index 0000000..65881bb --- /dev/null +++ b/pygithub3/exceptions.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + + +class DoesNotExists(Exception): + """ Raised when `Request` factory can't find the subclass """ + pass + + +class UriInvalid(Exception): + """ Raised when `Request` factory's maker isn't in a valid form """ + pass + + +class ValidationError(Exception): + """ Raised when a `Request` doesn't have the necessary args to make a + valid URI """ + pass + + +class BadRequest(Exception): + """ Raised when server response is 400 """ + pass + + +class UnprocessableEntity(Exception): + """ Raised when server response is 400 """ + pass + + +class NotFound(Exception): + """ Raised when server response is 404 + + Catched with a pygithub3-exception to `services.base.Base._bool` method + """ + pass diff --git a/pygithub3/requests/__init__.py b/pygithub3/requests/__init__.py index 725ea12..9fdde27 100644 --- a/pygithub3/requests/__init__.py +++ b/pygithub3/requests/__init__.py @@ -8,60 +8,77 @@ try: except ImportError: import json -ABS_IMPORT_PREFIX = 'pygithub3.requests' +from pygithub3.exceptions import DoesNotExists, UriInvalid, ValidationError +from pygithub3.resources.base import Raw +ABS_IMPORT_PREFIX = 'pygithub3.requests' -class RequestNotFound(Exception): - pass +class Body(object): -class RequestUriInvalid(Exception): - pass + def __init__(self, content, schema): + self.content = content + self.schema = schema + def dumps(self): + if not self.content: + return None + return json.dumps(self.parse()) -class RequestValidationError(Exception): - pass + def parse(self): + if not self.schema: + return self.content + if not hasattr(self.content, 'items'): + raise ValidationError("'%s' needs a content dictionary" + % self.__class__.__name__) + return {key: self.content[key] for key in self.schema + if key in self.content} class Request(object): """ """ + uri = '' + resource = Raw + body_schema = () + def __init__(self, args): """ """ + self.body = args.pop('body', None) self.args = args - self.validate() - self.uri = self.set_uri() + self.clean() - def validate(self): - raise NotImplementedError + def clean(self): + self.uri = self.clean_uri() or self.uri + self.body = Body(self.clean_body(), self.body_schema) - def set_uri(self): - raise NotImplementedError + def clean_body(self): + return self.body - def get_data(self): - raise NotImplementedError + def clean_uri(self): + return None - def get_uri(self): - return str(self.uri).strip('/') - - def get_resource(self): - return getattr(self, 'resource', '') + @property + def resource(self): + return self.resource def __getattr__(self, name): return self.args.get(name) def __str__(self): - return self.get_uri() + return self.populate_uri() + + def populate_uri(self): + try: + populated_uri = self.uri.format(**self.args) + except KeyError: + raise ValidationError( + "'%s' request wasn't be able to populate the uri '%s' with " + "'%s' args" % (self.__class__.__name__, self.uri, self.args)) + return str(populated_uri).strip('/') - def _parse_simple_dict(self, to_parse): - if not hasattr(to_parse, 'items'): - raise RequestValidationError("'%s' needs a data dictionary" - % self.__class__.__name__) - update_params = { - valid_key: to_parse[valid_key] - for valid_key in self.valid - if valid_key in to_parse} - return update_params + def get_body(self): + return self.body.dumps() class Factory(object): @@ -69,29 +86,19 @@ class Factory(object): import_pattern = re.compile(r'^(\w+\.)+\w+$') - def __init__(self): - """ """ - self.args = {} - - def config_with(self, **kwargs): - self.args = kwargs - - def clear_config(self): - self.args = {} - def __validate(func): """ """ - def wrapper(self, request_uri): + def wrapper(self, request_uri, **kwargs): if not Factory.import_pattern.match(request_uri): - raise RequestUriInvalid("'%s' isn't valid form" % request_uri) - return func(self, request_uri.lower()) + raise UriInvalid("'%s' isn't valid form" % request_uri) + return func(self, request_uri.lower(), **kwargs) return wrapper def __dispatch(func): """ """ - def wrapper(self, request_uri): + def wrapper(self, request_uri, **kwargs): module_chunk, s, request_chunk = request_uri.rpartition('.') try: # TODO: CamelCase and under_score support, now only Class Name @@ -99,19 +106,18 @@ class Factory(object): % (ABS_IMPORT_PREFIX, module_chunk)) request = getattr(module, request_chunk.capitalize()) except ImportError: - raise RequestNotFound("'%s' module does not exists" + raise DoesNotExists("'%s' module does not exists" % module_chunk) except AttributeError: - raise RequestNotFound( + raise DoesNotExists( "'%s' request doesn't exists into '%s' module" % (request_chunk.capitalize(), module_chunk)) - return func(self, request) + return func(self, request, **kwargs) return wrapper @__validate @__dispatch - def __call__(self, request=''): - request = request(self.args) - self.clear_config() + def __call__(self, request='', **kwargs): + request = request(kwargs) assert isinstance(request, Request) return request diff --git a/pygithub3/requests/users/__init__.py b/pygithub3/requests/users/__init__.py index 93ab7b6..11e6a39 100644 --- a/pygithub3/requests/users/__init__.py +++ b/pygithub3/requests/users/__init__.py @@ -1,4 +1,4 @@ # -*- encoding: utf-8 -*- -from pygithub3.requests import Request, json, RequestValidationError +from pygithub3.requests import Request, ValidationError from user import * diff --git a/pygithub3/requests/users/emails.py b/pygithub3/requests/users/emails.py index f7fb284..7622fcc 100644 --- a/pygithub3/requests/users/emails.py +++ b/pygithub3/requests/users/emails.py @@ -1,44 +1,28 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -from . import Request, json -from pygithub3.resources.base import Raw +import re +from . import Request -class List(Request): - - resource = Raw - def validate(self): - pass +class List(Request): - def set_uri(self): - return 'user/emails' + uri = 'users/emails' class Add(Request): - resource = Raw + uri = 'user/emails' - def validate(self): - pass + def clean_body(self): - def get_data(self): - return json.dumps(self.emails) + def is_email(email): + return re.match(r'.*', email) # TODO: email regex ;) - def set_uri(self): - return 'user/emails' + return filter(is_email, self.body) class Delete(Request): - resource = Raw - - def validate(self): - pass - - def get_data(self): - return json.dumps(self.emails) - - def set_uri(self): - return 'user/emails' + uri = 'user/emails' diff --git a/pygithub3/requests/users/followers.py b/pygithub3/requests/users/followers.py index f2bfc1a..5f59032 100644 --- a/pygithub3/requests/users/followers.py +++ b/pygithub3/requests/users/followers.py @@ -1,76 +1,40 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -from . import Request, RequestValidationError +from . import Request from pygithub3.resources.users import User -from pygithub3.resources.base import Raw class List(Request): resource = User + uri = 'users/{user}/followers' - def validate(self): - pass - - def set_uri(self): - if self.user: - return 'users/%s/followers' % self.user - else: + def clean_uri(self): + if not self.user: return 'user/followers' class Listfollowing(Request): resource = User + uri = 'users/{user}/following' - def validate(self): - pass - - def set_uri(self): - if self.user: - return 'users/%s/following' % self.user - else: + def clean_uri(self): + if not self.user: return 'user/following' class Isfollowing(Request): - resource = Raw - - def validate(self): - if not self.user: - raise RequestValidationError( - "'%s' request needs a user" % self.__class__.__name__) - - def set_uri(self): - return 'user/following/%s' % self.user + uri = 'user/following/{user}' class Follow(Request): - resource = Raw - - def validate(self): - if not self.user: - raise RequestValidationError( - "'%s' request needs a user" % self.__class__.__name__) - - def set_uri(self): - return 'user/following/%s' % self.user + uri = 'user/following/{user}' class Unfollow(Request): - resource = User - - def validate(self): - if not self.user: - raise RequestValidationError( - "'%s' request needs a user" % self.__class__.__name__) - - def set_uri(self): - return 'user/following/%s' % self.user - - def get_data(self): - pass + uri = 'user/following/{user}' diff --git a/pygithub3/requests/users/keys.py b/pygithub3/requests/users/keys.py index f5bb386..9afa3cd 100644 --- a/pygithub3/requests/users/keys.py +++ b/pygithub3/requests/users/keys.py @@ -1,79 +1,36 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -from . import Request, json, RequestValidationError +from . import Request from pygithub3.resources.users import Key -from pygithub3.resources.base import Raw class List(Request): resource = Key - - def validate(self): - pass - - def set_uri(self): - return 'user/keys' + uri = 'user/key' class Get(Request): resource = Key - - def validate(self): - if not self.key_id: - raise RequestValidationError("'%s' needs key_id" - % self.__class__.__name__) - - def set_uri(self): - return 'user/keys/%s' % self.key_id + uri = 'user/keys/{key_id}' class Add(Request): resource = Key - valid = ('title', 'key') - - def validate(self): - self.add_data = self._parse_simple_dict(self.add_data) - - def set_uri(self): - return 'user/keys' - - def get_data(self): - return json.dumps(self.add_data) + uri = 'user/keys' + body_schema = ('title', 'key') class Update(Request): resource = Key - valid = ('title', 'key') - - def validate(self): - if not self.key_id: - raise RequestValidationError("'%s' needs key_id" - % self.__class__.__name__) - self.update_params = self._parse_simple_dict(self.update_with) - - def set_uri(self): - return 'user/keys/%s' % self.key_id - - def get_data(self): - return json.dumps(self.update_params) + body_schema = ('title', 'key') + uri = 'user/keys/{key_id}' class Delete(Request): - resource = Raw - - def validate(self): - if not self.key_id: - raise RequestValidationError("'%s' needs key_id" - % self.__class__.__name__) - - def set_uri(self): - return 'user/keys/%s' % self.key_id - - def get_data(self): - return None + uri = 'user/keys/{key_id}' diff --git a/pygithub3/requests/users/user.py b/pygithub3/requests/users/user.py index d4b3667..78ab6cd 100644 --- a/pygithub3/requests/users/user.py +++ b/pygithub3/requests/users/user.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -from . import Request, json +from . import Request, ValidationError from pygithub3.resources.users import User __all__ = ('Get', 'Update') @@ -10,27 +10,16 @@ __all__ = ('Get', 'Update') class Get(Request): resource = User + uri = 'users/{user}' - def validate(self): - pass - - def set_uri(self): - if self.user: - return 'users/%s' % self.user - else: + def clean_uri(self): + if not self.user: return 'user' class Update(Request): resource = User - valid = ('name', 'email', 'blog', 'company', 'location', 'hireable', 'bio') - - def validate(self): - self.update_params = self._parse_simple_dict(self.update_with) - - def get_data(self): - return json.dumps(self.update_params) - - def set_uri(self): - return 'user' + uri = 'user' + body_schema = ( + 'name', 'email', 'blog', 'company', 'location','hireable', 'bio') diff --git a/pygithub3/resources/base.py b/pygithub3/resources/base.py index 620df3e..ae038bd 100644 --- a/pygithub3/resources/base.py +++ b/pygithub3/resources/base.py @@ -22,6 +22,9 @@ class Resource(object): for attr in self._attrs: setattr(self, attr, self._attrs[attr]) + def __repr__(self): + return self.__str__() + @classmethod def loads(self, json_content): resource_chunk = json.loads(json_content) diff --git a/pygithub3/services/base.py b/pygithub3/services/base.py index 1ade763..c659470 100644 --- a/pygithub3/services/base.py +++ b/pygithub3/services/base.py @@ -10,42 +10,40 @@ from pygithub3.core.errors import NotFound class Base(object): def __init__(self, **config): - self.client = Client(**config) - self.get_request = Factory() + self._client = Client(**config) + self.make_request = Factory() def get_user(self): - return self.client.user + return self._client.user def set_user(self, user): - self.client.user = user + self._client.user = user def get_repo(self): - return self.client.repo + return self._client.repo def set_repo(self, repo): - self.client.repo = repo + self._client.repo = repo - def _config_request(self, **kwargs): - self.get_request.config_with(**kwargs) + def set_credentials(self, login, password): + self._client.set_credentials(login, password) - def _bool(self, request_uri, **kwargs): - request = self.get_request(request_uri) + def set_token(self, token): + self._client.set_token(token) + + def _bool(self, request, **kwargs): try: - self.client.head(request, **kwargs) + self._client.head(request, **kwargs) return True except NotFound: return False - def _patch(self, request_uri, **kwargs): - request = self.get_request(request_uri) - resource = request.get_resource() - input_data = request.get_data() - response = self.client.patch(request, data=input_data, **kwargs) - return resource.loads(response.content) + def _patch(self, request, **kwargs): + input_data = request.get_body() + response = self._client.patch(request, data=input_data, **kwargs) + return request.resource.loads(response.content) - def _put(self, request_uri, **kwargs): - request = self.get_request(request_uri) - resource = request.get_resource() + def _put(self, request, **kwargs): """ Bug in Github API? requests library? I must send data as empty string when the specifications' of some PUT @@ -60,31 +58,25 @@ class Base(object): For that reason I must do a conditional because I don't want to return an empty string on follow-user request because it could be confused - Related: ht + Related: https://github.com/github/developer.github.com/pull/52 """ - input_data = request.get_data() or '' - response = self.client.put(request, data=input_data, **kwargs) + input_data = request.get_body() or '' + response = self._client.put(request, data=input_data, **kwargs) if response.status_code != '204': # != NO_CONTENT - return resource.loads(response.content) - - def _delete(self, request_uri, **kwargs): - request = self.get_request(request_uri) - input_data = request.get_data() - self.client.delete(request, data=input_data, **kwargs) - - def _post(self, request_uri, **kwargs): - request = self.get_request(request_uri) - resource = request.get_resource() - input_data = request.get_data() - response = self.client.post(request, data=input_data, **kwargs) - return resource.loads(response.content) - - def _get(self, request_uri, **kwargs): - request = self.get_request(request_uri) - resource = request.get_resource() - response = self.client.get(request, **kwargs) - return resource.loads(response.content) - - def _get_result(self, request_uri, **kwargs): - request = self.get_request(request_uri) - return Result(self.client, request, **kwargs) + return request.resource.loads(response.content) + + def _delete(self, request, **kwargs): + input_data = request.get_body() + self._client.delete(request, data=input_data, **kwargs) + + def _post(self, request, **kwargs): + input_data = request.get_body() + response = self._client.post(request, data=input_data, **kwargs) + return request.resource.loads(response.content) + + def _get(self, request, **kwargs): + response = self._client.get(request, **kwargs) + return request.resource.loads(response.content) + + def _get_result(self, request, **kwargs): + return Result(self._client, request, **kwargs) diff --git a/pygithub3/services/users.py b/pygithub3/services/users.py index 9ec15e8..84002b2 100644 --- a/pygithub3/services/users.py +++ b/pygithub3/services/users.py @@ -7,60 +7,71 @@ from .base import Base class Keys(Base): def list(self): - return self._get_result('users.keys.list') + request = self.make_request('users.keys.list') + return self._get_result(request) def get(self, key_id): - self._config_request(key_id=key_id) - return self._get('users.keys.get') + request = self.make_request('users.keys.get', + key_id=key_id) + return self._get(request) def add(self, data): - self._config_request(add_data=data) - return self._post('users.keys.add') + request = self.make_request('users.keys.add', + body=data) + return self._post(request) def update(self, key_id, data): - self._config_request(key_id=key_id, update_with=data) - return self._patch('users.keys.update') + request = self.make_request('users.keys.update', + key_id=key_id, body=data) + return self._patch(request) def delete(self, key_id): - self._config_request(key_id=key_id) - self._delete('users.keys.delete') + request = self.make_request('users.keys.delete', + key_id=key_id) + self._delete(request) class Followers(Base): def list(self, user=None): - self._config_request(user=user or self.get_user()) - return self._get_result('users.followers.list') + request = self.make_request('users.followers.list', + user=user or self.get_user()) + return self._get_result(request) def list_following(self, user=None): - self._config_request(user=user or self.get_user()) - return self._get_result('users.followers.listfollowing') + request = self.make_request('users.followers.listfollowing', + user=user or self.get_user()) + return self._get_result(request) def is_following(self, user): - self._config_request(user=user) - return self._bool('users.followers.isfollowing') + request = self.make_request('users.followers.isfollowing', + user=user) + return self._bool(request) def follow(self, user): - self._config_request(user=user) - self._put('users.followers.follow') + request = self.make_request('users.followers.follow', + user=user) + self._put(request) def unfollow(self, user): - self._config_request(user=user) - self._delete('users.followers.unfollow') + request = self.make_request('users.followers.unfollow', + user=user) + self._delete(request) class Emails(Base): def list(self): - return self._get_result('users.emails.list') + request = self.make_request('users.emails.list') + return self._get_result(request) - def add(self, *args): - self._config_request(emails=args) - return self._post('users.emails.add') + def add(self, *emails): + request = self.make_request('users.emails.add', body=emails) + return self._post(request) - def delete(self, *args): - self._config_request(emails=args) - self._delete('users.emails.delete') + def delete(self, *emails): + request = self.make_request('users.emails.delete', body=emails) + self._delete(request) class User(Base): @@ -72,9 +83,10 @@ class User(Base): super(User, self).__init__(**kwargs) def get(self, user=None): - self._config_request(user=user or self.get_user()) - return self._get('users.get') + request = self.make_request('users.get', + user=user or self.get_user()) + return self._get(request) def update(self, data): - self._config_request(update_with=data) - return self._patch('users.update') + request = self.make_request('users.update', body=data) + return self._patch(request) |