base.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. # coding=utf-8
  2. import hashlib
  3. import inspect
  4. import time
  5. from collections import OrderedDict
  6. from urlparse import urljoin
  7. import typing
  8. import requests
  9. import logging
  10. import simplejson as json
  11. from simplejson import JSONDecodeError
  12. from library.jdopen.client.api.base import BaseJdOpenAPI
  13. from library.jdopen.constants import JdOpenResultCode
  14. from library.jdopen.exceptions import JdOpenException
  15. if typing.TYPE_CHECKING:
  16. from requests import Response
  17. logger = logging.getLogger(__name__)
  18. class JdOpenBaseClient(object):
  19. API_BASE_URL = ""
  20. def __new__(cls, *args, **kwargs):
  21. self = super(JdOpenBaseClient, cls).__new__(cls)
  22. apis = inspect.getmembers(self, lambda x: inspect.isclass(x) and issubclass(x, BaseJdOpenAPI))
  23. for apiName, apiClient in apis:
  24. setattr(self, apiName, apiClient(self))
  25. return self
  26. def __init__(self, accessKey, secretKey, timeout=None, auto_retry=True):
  27. self._accessKey = accessKey
  28. self._secretKey = secretKey
  29. self._timeout = timeout
  30. self._auto_retry = auto_retry
  31. @staticmethod
  32. def _decode_result(res): # type:(Response) -> typing.Optional[dict, str]
  33. """
  34. 解析request返回的结果
  35. """
  36. try:
  37. result = json.loads(res.content.decode('utf-8', 'ignore'), strict=False)
  38. except (TypeError, ValueError, JSONDecodeError):
  39. logger.debug(u'错误的解析结构', exc_info=True)
  40. return res
  41. return result
  42. def calc_token(self, timestamp, path=None, body=None):
  43. data = OrderedDict()
  44. data["secretKey"] = self._secretKey
  45. data["timestamp"] = timestamp
  46. if path:
  47. data["path"] = path
  48. if body is not None:
  49. if isinstance(body, dict):
  50. data["body"] = json.dumps(body, separators=(",", ":"))
  51. else:
  52. data["body"] = body
  53. s = '&'.join([u'{}={}'.format(k, v) for k, v in data.items()])
  54. s = s.encode('utf-8')
  55. token = hashlib.sha1(s).hexdigest().upper()
  56. return token
  57. def _request(self, method, path, **kwargs):
  58. # 修补url
  59. logger.info("[{} send request], method = {}, path = {}, data = {}".format(self.__class__.__name__, method, path, kwargs))
  60. urlBase = kwargs.pop("urlBase", self.API_BASE_URL)
  61. url = urljoin(urlBase, path)
  62. # 修补data数据
  63. if isinstance(kwargs.get("data", ""), dict):
  64. data = kwargs.pop("data")
  65. kwargs['data'] = json.dumps(data, separators=(',', ':'))
  66. # 修补请求头
  67. timestamp = kwargs.pop("timestamp", str(int(time.time())))
  68. headers = {
  69. "Content-Type": "application/json",
  70. "accessKey": self._accessKey,
  71. "timestamp": timestamp,
  72. "token": self.calc_token(timestamp, path, body=kwargs.get("data", None))
  73. }
  74. kwargs.setdefault("params", {})
  75. kwargs.setdefault("timeout", self._timeout)
  76. callback = kwargs.pop("callback", None)
  77. with requests.sessions.Session() as _session:
  78. res = _session.request(
  79. method=method,
  80. url=url,
  81. headers=headers,
  82. **kwargs
  83. )
  84. try:
  85. res.raise_for_status()
  86. except requests.RequestException as rre:
  87. logger.info("[{} send request] error! status code = {}, error = {}".format(self.__class__.__name__, res.status_code, rre))
  88. raise JdOpenException(
  89. errCode='HTTP{}'.format(res.status_code),
  90. errMsg=rre.message,
  91. client=self,
  92. request=rre.request,
  93. response=rre.response
  94. )
  95. return self._handle_result(
  96. res, method, url, callback, **kwargs
  97. )
  98. def _handle_result(self, res, method, url, callback, **kwargs):
  99. """
  100. 主要用户重试
  101. """
  102. result = self._decode_result(res)
  103. logger.info("[{} handle_result] result = {}".format(self.__class__.__name__, result))
  104. if "result" not in result:
  105. return result
  106. # 正常请求
  107. if result["result"] == JdOpenResultCode.SUCCESS:
  108. return callback(result) if callback else result
  109. # 异常请求
  110. error = result["error"]
  111. raise JdOpenException(
  112. errCode=error.get("errorCode", ''),
  113. errMsg=error.get("errorMsg", ''),
  114. client=self,
  115. request=res.request,
  116. response=res
  117. )
  118. def get(self, url, **kwargs):
  119. return self._request("get", url, **kwargs)
  120. def post(self, url, **kwargs):
  121. return self._request("post", url, **kwargs)