Source code for eater.api.http

# -*- coding: utf-8 -*-
"""
    eater.api
    ~~~~~~~~~

    Eater API classes and utilities.
"""

from abc import abstractmethod

import requests
from schematics import Model

from eater.api.base import BaseEater
from eater.errors import EaterTimeoutError, EaterConnectError, EaterUnexpectedError


[docs]class HTTPEater(BaseEater): """ Eat JSON HTTP APIs for breakfast. """ #: An instance of requests Session session = None #: The HTTP method to use to make the API call. method = 'get' def __init__(self, *, session: requests.Session=None, auth: tuple=None, headers: requests.structures.CaseInsensitiveDict=None): if session is None: self.create_session(auth, headers) def __call__(self, *args, **kwargs): return self.request(*args, **kwargs) @property @abstractmethod def url(self) -> str: """ Returns the URL to the endpoint. """
[docs] def request(self, *, request_model: Model=None, **kwargs) -> Model: """ Make a HTTP request of of type method. """ request_model = self.create_request_model(request_model=request_model, **kwargs) kwargs = self.get_request_kwargs(request_model=request_model) try: response = getattr(self.session, self.method)(self.url, **kwargs) return self.create_response_model(response, request_model) except requests.Timeout: raise EaterTimeoutError("%s.%s for URL '%s' timed out." % ( type(self).__name__, self.method, self.url )) except requests.RequestException as exc_info: raise EaterConnectError("Exception raised for URL '%s'." % url) from exc_info
[docs] def create_response_model(self, response: requests.Response, request_model: Model) -> Model: """ Given a requests Response object, return the response model. :param response: A requests.Response object representing the response from the API. :param request_model: The model used to generate the request. """ if response.status_code >= 400: raise EaterUnexpectedError("Received unexpected HTTP response '%s %s' for URL '%s'." % ( response.status_code, response.reason, response.url, )) if response.headers['content-type'] == 'application/json': raw_data = response.json() return self.response_cls(raw_data=raw_data) raise NotImplementedError( "Content type '%s' is not implemented. Class %s should implement a handle_response method." % ( response.headers['content-type'], type(self), ) )
[docs] def create_request_model(self, request_model: Model=None, **kwargs) -> Model: """ Create the request model either from kwargs or request_model. """ if request_model is None: request_model = self.request_cls(raw_data=kwargs) return request_model
[docs] def get_request_kwargs(self, request_model: Model, **kwargs) -> dict: """ Retrieve a dict of kwargs to supply to requests. """ kwargs['json'] = request_model.to_primitive() return kwargs
[docs] def create_session(self, auth: tuple=None, headers: requests.structures.CaseInsensitiveDict=None) -> requests.Session: """ Create an instance of a requests Session. """ if self.session is None: self.session = requests.Session() if auth: self.session.auth = auth if headers: self.session.headers.update(headers) return self.session