# coding=utf-8 import hashlib import inspect import time from collections import OrderedDict from urlparse import urljoin import typing import requests import logging import simplejson as json from simplejson import JSONDecodeError from library.jdopen.client.api.base import BaseJdOpenAPI from library.jdopen.constants import JdOpenResultCode from library.jdbase.exceptions import JDException, JDNetworkException if typing.TYPE_CHECKING: from requests import Response logger = logging.getLogger(__name__) class JdOpenBaseClient(object): API_BASE_URL = "" def __new__(cls, *args, **kwargs): self = super(JdOpenBaseClient, cls).__new__(cls) apis = inspect.getmembers(self, lambda x: inspect.isclass(x) and issubclass(x, BaseJdOpenAPI)) for apiName, apiClient in apis: setattr(self, apiName, apiClient(self)) return self def __init__(self, agentNum, accessKey, secretKey, timeout=None, auto_retry=True): self._agentNum = agentNum self._accessKey = accessKey self._secretKey = secretKey self._timeout = timeout self._auto_retry = auto_retry @property def agentNum(self): return self._agentNum @staticmethod def _decode_result(res): # type:(Response) -> typing.Optional[dict, str] """ 解析request返回的结果 """ try: result = json.loads(res.content.decode('utf-8', 'ignore'), strict=False) except (TypeError, ValueError, JSONDecodeError): logger.debug(u'错误的解析结构', exc_info=True) return res return result def calc_token(self, timestamp, path=None, body=None): data = OrderedDict() data["secretKey"] = self._secretKey data["timestamp"] = timestamp if path: data["path"] = path if body is not None: if isinstance(body, dict): data["body"] = json.dumps(body, separators=(",", ":")) else: data["body"] = body s = '&'.join([u'{}={}'.format(k, v) for k, v in data.items()]) s = s.encode('utf-8') token = hashlib.sha1(s).hexdigest().upper() return token def _request(self, method, path, **kwargs): # 修补url logger.info("[{} send request], method = {}, path = {}, data = {}".format(self.__class__.__name__, method, path, kwargs)) urlBase = kwargs.pop("urlBase", self.API_BASE_URL) url = urljoin(urlBase, path) # 修补data数据 if isinstance(kwargs.get("data", ""), dict): data = kwargs.pop("data") kwargs['data'] = json.dumps(data, separators=(',', ':')) # 修补请求头 timestamp = kwargs.pop("timestamp", str(int(time.time()))) headers = { "Content-Type": "application/json", "accessKey": self._accessKey, "timestamp": timestamp, "token": self.calc_token(timestamp, path, body=kwargs.get("data", None)) } kwargs.setdefault("params", {}) kwargs.setdefault("timeout", self._timeout) processor = kwargs.pop("processor", None) with requests.sessions.Session() as _session: res = _session.request( method=method, url=url, headers=headers, **kwargs ) try: res.raise_for_status() except requests.RequestException as rre: logger.info("[{} send request] error! status code = {}, error = {}".format(self.__class__.__name__, res.status_code, rre)) raise JDNetworkException( errCode='HTTP{}'.format(res.status_code), errMsg=rre.message, client=self, request=rre.request, response=rre.response ) return self._handle_result( res, method, url, processor, **kwargs ) def _handle_result(self, res, method, url, processor, **kwargs): result = self._decode_result(res) logger.info("[{} handle_result] method = {}; url = {}; result = {}".format( self.__class__.__name__, method, url, result)) if processor: return processor(self, result) else: if "result" not in result: return result if result["result"] == JdOpenResultCode.SUCCESS: return processor(result) if processor else result error = result["error"] raise JDException( errCode = error.get("errorCode", ''), errMsg = error.get("errorMsg", ''), client = self, request = res.request, response = res) def get(self, url, **kwargs): return self._request("get", url, **kwargs) def post(self, url, **kwargs): return self._request("post", url, **kwargs)