Source code for virtual_finance_api.client

# -*- coding: utf-8 -*-

import requests
import logging

try:
    import rapidjson as json

except ImportError as err:
    import json

from .exceptions import VirtualFinanceAPIError, ConversionHookError
from .endpoints.apirequest import VirtualAPIRequest

logger = logging.getLogger(__name__)

DEFAULT_HEADERS = {"Accept-Encoding": "gzip, deflate"}


[docs]class Client:
[docs] def __init__(self, headers: dict = None, request_params: dict = None) -> None: """Instantiate a Client instance. Parameters ---------- headers : dict (optional) optional headers to set to requests request_params : dict (optional) optional parameters to set to requests for details pls. check requests.readthedocs.io Example ------- >>> import virtual_finance_api as fa >>> import virtual_finance_api.endpoints.yahoo as yh >>> import json >>> client = fa.Client() >>> r = yh.Profile('IBM') >>> try: ... rv = client.request(r) ... except VirtualFinanceAPIError as err: ... print(err) ... else: ... print(json.dumps(rv['pageViews'], indent=2)) { "shortTermTrend": "NEUTRAL", "midTermTrend": "UP", "longTermTrend": "UP", "maxAge": 1 } """ self._client = requests.Session() self._request_params = request_params if request_params else {} self._client.headers.update(DEFAULT_HEADERS) if headers: self._client.headers.update(headers) logger.info("applying headers %s", ",".join(headers.keys()))
@property def request_params(self) -> dict: """request_params property.""" return self._request_params
[docs] def request(self, endpoint: VirtualAPIRequest): """Perform a request for the APIRequest instance 'endpoint'. Parameters ---------- endpoint : APIRequest (required) The endpoint parameter contains an instance of an APIRequest containing the endpoint, method and optionally other parameters or body data. Raises ------ VirtualFinanceAPIError in case of HTTP response code >= 400 requests-lib exceptions Possible exceptions from the requests library, those are re-raised. """ method = endpoint.method.lower() params = None try: params = getattr(endpoint, "params") except AttributeError: # request does not have params params = {} headers = {} if hasattr(endpoint, "HEADERS"): headers = getattr(endpoint, "HEADERS") request_args = {} if method == "get": request_args["params"] = params elif hasattr(endpoint, "data") and endpoint.data: request_args["json"] = endpoint.data # if any parameter for request then merge them request_args.update(self._request_params) url = "{}/{}".format(endpoint.DOMAIN, endpoint) # perform the actual request func = getattr(self._client, method) headers = headers if headers else {} response = None content = None try: logger.info("performing request %s", url) response = func(url, headers=headers, **request_args) # in case of a virtual request: # process the primary response into the desired response # by calling the _conversion_hook if hasattr(endpoint, "_conversion_hook"): # only allow _conversion_hook for VirtualAPIRequest instances assert isinstance(endpoint, VirtualAPIRequest) try: content = endpoint._conversion_hook( response.content.decode("utf-8") ) except ConversionHookError as err: logger.error("%s: conversion error: %s", endpoint, err) endpoint.status_code = err.code # raise exception just as if the server returned a 4xx code raise VirtualFinanceAPIError(endpoint.status_code, err) # to prevent back and forth conversion to keep the flow correct # lets hack a bit and keep track of the already converted # content in the content variable # else: # # response content is read-only # setattr(response, '_content', # json.dumps(content).encode('utf-8')) except requests.RequestException as err: logger.error("request %s failed [%s]", url, err) raise err # Handle error responses if response.status_code >= 400: logger.error( "request %s failed [%d,%s]", url, response.status_code, response.content.decode("utf-8"), ) raise VirtualFinanceAPIError( response.status_code, response.content.decode("utf-8") ) if content is None: content = response.content.decode("utf-8") endpoint.status_code = response.status_code if endpoint.RESPONSE_TYPE == "json": try: if isinstance(content, str): content = json.loads(content) except Exception as err: logger.error("Error loading JSON response ... %s", err) raise ValueError( f"request: {endpoint}, " "response could not be loaded as JSON" ) # update endpoint endpoint.response = content return content