core.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. import asyncio
  2. import aiohttp
  3. import logging
  4. import io
  5. import os
  6. import ssl
  7. import time
  8. import re
  9. import certifi
  10. import json
  11. from requests import status_codes, adapters, PreparedRequest
  12. from typing import Any, Dict, Optional, Union
  13. from enum import Enum
  14. from urllib.parse import urlencode, urlparse
  15. from requests import status_codes, adapters, PreparedRequest, Session
  16. from darabonba.exceptions import RequiredArgumentException, RetryError
  17. from darabonba.model import DaraModel
  18. from darabonba.request import DaraRequest
  19. from darabonba.response import DaraResponse
  20. from darabonba.utils.stream import BaseStream, SSEResponseWrapper, SyncSSEResponseWrapper
  21. from darabonba.policy.retry import RetryOptions, RetryPolicyContext
  22. DEFAULT_CONNECT_TIMEOUT = 5000
  23. DEFAULT_READ_TIMEOUT = 10000
  24. DEFAULT_POOL_SIZE = 10
  25. DEFAULT_POOL_MAXSIZE = DEFAULT_POOL_SIZE * 4
  26. MAX_DELAY_TIME = 120 * 1000
  27. MIN_DELAY_TIME = 100
  28. logger = logging.getLogger('darabonba-core')
  29. logger.setLevel(logging.DEBUG)
  30. ch = logging.StreamHandler()
  31. logger.addHandler(ch)
  32. class _ModelEncoder(json.JSONEncoder):
  33. def default(self, o: Any) -> Any:
  34. if isinstance(o, DaraModel):
  35. return o.to_map()
  36. elif isinstance(o, bytes):
  37. return o.decode('utf-8')
  38. super().default(o)
  39. class TLSVersion(Enum):
  40. TLSv1 = 'TLSv1'
  41. TLSv1_1 = 'TLSv1.1'
  42. TLSv1_2 = 'TLSv1.2'
  43. TLSv1_3 = 'TLSv1.3'
  44. class _TLSAdapter(adapters.HTTPAdapter):
  45. """A HTTPAdapter that uses an arbitrary TLS version."""
  46. def __init__(self, ssl_context=None, **kwargs):
  47. self.ssl_context = ssl_context
  48. super().__init__(**kwargs)
  49. def init_poolmanager(self, *args, **kwargs):
  50. """Override the init_poolmanager method to set the SSL."""
  51. kwargs['ssl_context'] = self.ssl_context
  52. super().init_poolmanager(*args, **kwargs)
  53. class DaraCore:
  54. _sessions = {}
  55. http_adapter = adapters.HTTPAdapter(pool_connections=DEFAULT_POOL_SIZE, pool_maxsize=DEFAULT_POOL_MAXSIZE)
  56. https_adapter = adapters.HTTPAdapter(pool_connections=DEFAULT_POOL_SIZE, pool_maxsize=DEFAULT_POOL_MAXSIZE)
  57. @staticmethod
  58. def to_json_string(
  59. val: Any,
  60. ) -> str:
  61. """
  62. Stringify a value by JSON format
  63. @return: the JSON format string
  64. """
  65. if isinstance(val, str):
  66. return str(val)
  67. return json.dumps(
  68. val, cls=_ModelEncoder, ensure_ascii=False, separators=(",", ":")
  69. )
  70. @staticmethod
  71. def _resolve_pool_maxsize(runtime_option=None) -> int:
  72. """
  73. Resolve urllib3 / aiohttp pool size from runtime maxIdleConns.
  74. Aligns with Go tea: MaxIdleConns / MaxIdleConnsPerHost.
  75. """
  76. runtime_option = runtime_option or {}
  77. max_idle = runtime_option.get('maxIdleConns')
  78. if max_idle is None:
  79. return DEFAULT_POOL_MAXSIZE
  80. try:
  81. max_idle = int(max_idle)
  82. except (TypeError, ValueError):
  83. return DEFAULT_POOL_MAXSIZE
  84. if max_idle > 0:
  85. return max_idle
  86. return DEFAULT_POOL_MAXSIZE
  87. @staticmethod
  88. def _set_tls_minimum_version(sls_context, tls_min_version):
  89. context = sls_context
  90. if tls_min_version is not None:
  91. if tls_min_version == 'TLSv1':
  92. context.minimum_version = ssl.TLSVersion.TLSv1
  93. elif tls_min_version == 'TLSv1.1':
  94. context.minimum_version = ssl.TLSVersion.TLSv1_1
  95. elif tls_min_version == 'TLSv1.2':
  96. context.minimum_version = ssl.TLSVersion.TLSv1_2
  97. elif tls_min_version == 'TLSv1.3':
  98. context.minimum_version = ssl.TLSVersion.TLSv1_3
  99. return context
  100. @staticmethod
  101. def get_adapter(prefix, tls_min_version: str = None, pool_size: int = None):
  102. pool_maxsize = pool_size if pool_size is not None and pool_size > 0 else DEFAULT_POOL_MAXSIZE
  103. ca_cert = certifi.where()
  104. context = ssl.create_default_context()
  105. if ca_cert and prefix.upper() == 'HTTPS':
  106. context = DaraCore._set_tls_minimum_version(context, tls_min_version)
  107. context.load_verify_locations(ca_cert)
  108. adapter = _TLSAdapter(
  109. ssl_context=context,
  110. pool_connections=DEFAULT_POOL_SIZE,
  111. pool_maxsize=pool_maxsize,
  112. )
  113. return adapter
  114. @staticmethod
  115. def _prepare_http_debug(request, symbol):
  116. base = ''
  117. for key, value in request.headers.items():
  118. base += f'\n{symbol} {key} : {value}'
  119. return base
  120. @staticmethod
  121. def _do_http_debug(request, response):
  122. # logger the request
  123. url = urlparse(request.url)
  124. request_base = f'\n> {request.method.upper()} {url.path + url.query} HTTP/1.1'
  125. logger.debug(request_base + DaraCore._prepare_http_debug(request, '>'))
  126. # logger the response
  127. response_base = f'\n< HTTP/1.1 {response.status_code}' \
  128. f' {status_codes._codes.get(response.status_code)[0].upper()}'
  129. logger.debug(response_base + DaraCore._prepare_http_debug(response, '<'))
  130. @staticmethod
  131. def compose_url(request):
  132. host = request.headers.get('host')
  133. if not host:
  134. raise RequiredArgumentException('endpoint')
  135. else:
  136. host = host.rstrip('/')
  137. protocol = f'{request.protocol.lower()}://'
  138. pathname = request.pathname
  139. if host.startswith(('http://', 'https://')):
  140. protocol = ''
  141. if request.port == 80:
  142. port = ''
  143. else:
  144. port = f':{request.port}'
  145. url = protocol + host + port + pathname
  146. if request.query:
  147. if "?" in url:
  148. if not url.endswith("&"):
  149. url += "&"
  150. else:
  151. url += "?"
  152. encode_query = {}
  153. for key in request.query:
  154. value = request.query[key]
  155. if value is not None:
  156. encode_query[key] = str(value)
  157. url += urlencode(encode_query)
  158. return url.rstrip("?&")
  159. @staticmethod
  160. async def async_do_action(
  161. request: DaraRequest,
  162. runtime_option=None
  163. ) -> DaraResponse:
  164. runtime_option = runtime_option or {}
  165. url = DaraCore.compose_url(request)
  166. ignore_ssl = runtime_option.get('ignoreSSL', False)
  167. verify: Union[bool, str] = not ignore_ssl
  168. tls_min_version = runtime_option.get('tlsMinVersion')
  169. if isinstance(tls_min_version, Enum):
  170. tls_min_version = tls_min_version.value
  171. if verify:
  172. ca = runtime_option.get('ca')
  173. if ca is not None:
  174. verify = ca
  175. cert = runtime_option.get('cert', None)
  176. timeout = runtime_option.get('timeout')
  177. connect_timeout = runtime_option.get('connectTimeout') or timeout or DEFAULT_CONNECT_TIMEOUT
  178. read_timeout = runtime_option.get('readTimeout') or timeout or DEFAULT_READ_TIMEOUT
  179. connect_timeout, read_timeout = (int(connect_timeout) / 1000, int(read_timeout) / 1000)
  180. proxy = None
  181. if request.protocol.upper() == 'HTTP':
  182. proxy = runtime_option.get('httpProxy')
  183. if not proxy:
  184. proxy = os.environ.get('HTTP_PROXY') or os.environ.get('http_proxy')
  185. elif request.protocol.upper() == 'HTTPS':
  186. proxy = runtime_option.get('httpsProxy')
  187. if not proxy:
  188. proxy = os.environ.get('HTTPS_PROXY') or os.environ.get('https_proxy')
  189. pool_maxsize = DaraCore._resolve_pool_maxsize(runtime_option)
  190. connector = None
  191. ca_cert = certifi.where()
  192. ssl_context = None
  193. if isinstance(verify, str) and request.protocol.upper() == 'HTTPS':
  194. ssl_context = ssl.create_default_context()
  195. ssl_context = DaraCore._set_tls_minimum_version(ssl_context, tls_min_version)
  196. ssl_context.load_verify_locations(verify)
  197. # Handle cert if provided
  198. if cert is not None:
  199. if isinstance(cert, (list, tuple)):
  200. ssl_context.load_cert_chain(certfile=cert[0], keyfile=cert[1] if len(cert) > 1 else None)
  201. else:
  202. ssl_context.load_cert_chain(certfile=cert, keyfile=None)
  203. connector = aiohttp.TCPConnector(
  204. ssl=ssl_context, limit=pool_maxsize, limit_per_host=pool_maxsize
  205. )
  206. elif ca_cert and request.protocol.upper() == 'HTTPS' and verify:
  207. ssl_context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
  208. ssl_context = DaraCore._set_tls_minimum_version(ssl_context, tls_min_version)
  209. ssl_context.load_verify_locations(ca_cert)
  210. # Handle cert if provided
  211. if cert is not None:
  212. if isinstance(cert, (list, tuple)):
  213. ssl_context.load_cert_chain(certfile=cert[0], keyfile=cert[1] if len(cert) > 1 else None)
  214. else:
  215. ssl_context.load_cert_chain(certfile=cert, keyfile=None)
  216. connector = aiohttp.TCPConnector(
  217. ssl=ssl_context, limit=pool_maxsize, limit_per_host=pool_maxsize
  218. )
  219. else:
  220. verify = False
  221. connector = aiohttp.TCPConnector(limit=pool_maxsize, limit_per_host=pool_maxsize)
  222. timeout = aiohttp.ClientTimeout(
  223. sock_read=read_timeout,
  224. sock_connect=connect_timeout
  225. )
  226. async with aiohttp.ClientSession(
  227. connector=connector
  228. ) as s:
  229. body = b''
  230. if isinstance(request.body, BaseStream):
  231. for content in request.body:
  232. body += content
  233. elif isinstance(request.body, str):
  234. body = request.body.encode('utf-8')
  235. else:
  236. body = request.body or b''
  237. try:
  238. ssl_param: Union[bool, ssl.SSLContext] = ssl_context if ssl_context is not None else bool(verify)
  239. async with s.request(request.method, url,
  240. data=body,
  241. headers=request.headers,
  242. ssl=ssl_param,
  243. proxy=proxy,
  244. timeout=timeout) as response:
  245. tea_resp: DaraResponse = DaraResponse()
  246. tea_resp.body = await response.read()
  247. tea_resp.headers = dict({k.lower(): v for k, v in response.headers.items()})
  248. tea_resp.status_code = response.status
  249. tea_resp.status_message = response.reason
  250. tea_resp.response = response
  251. except IOError as e:
  252. raise RetryError(str(e))
  253. return tea_resp
  254. @staticmethod
  255. def do_action(
  256. request: DaraRequest,
  257. runtime_option=None
  258. ) -> DaraResponse:
  259. url = DaraCore.compose_url(request)
  260. runtime_option = runtime_option or {}
  261. verify = not runtime_option.get('ignoreSSL', False)
  262. tls_min_version = runtime_option.get('tlsMinVersion')
  263. if isinstance(tls_min_version, Enum):
  264. tls_min_version = tls_min_version.value
  265. if verify:
  266. verify = runtime_option.get('ca', True) if runtime_option.get('ca', True) is not None else True
  267. cert = runtime_option.get('cert', None)
  268. timeout = runtime_option.get('timeout')
  269. connect_timeout = runtime_option.get('connectTimeout') or timeout or DEFAULT_CONNECT_TIMEOUT
  270. read_timeout = runtime_option.get('readTimeout') or timeout or DEFAULT_READ_TIMEOUT
  271. timeout = (int(connect_timeout) / 1000, int(read_timeout) / 1000)
  272. if isinstance(request.body, str):
  273. request.body = request.body.encode('utf-8')
  274. p = PreparedRequest()
  275. p.prepare(
  276. method=request.method.upper(),
  277. url=url,
  278. data=request.body,
  279. headers=request.headers,
  280. )
  281. proxies = {}
  282. http_proxy = runtime_option.get('httpProxy')
  283. https_proxy = runtime_option.get('httpsProxy')
  284. no_proxy = runtime_option.get('noProxy')
  285. if not http_proxy:
  286. http_proxy = os.environ.get('HTTP_PROXY') or os.environ.get('http_proxy')
  287. if not https_proxy:
  288. https_proxy = os.environ.get('HTTPS_PROXY') or os.environ.get('https_proxy')
  289. if http_proxy:
  290. proxies['http'] = http_proxy
  291. if https_proxy:
  292. proxies['https'] = https_proxy
  293. if no_proxy:
  294. proxies['no_proxy'] = no_proxy
  295. host = request.headers.get('host')
  296. host = host.rstrip('/')
  297. pool_maxsize = DaraCore._resolve_pool_maxsize(runtime_option)
  298. session_key = f'{request.protocol.lower()}://{host}:{request.port}:pool={pool_maxsize}'
  299. session = DaraCore._get_session(session_key=session_key, protocol=request.protocol,
  300. tls_min_version=tls_min_version, verify=verify,
  301. pool_size=pool_maxsize)
  302. try:
  303. resp = session.send(
  304. p,
  305. proxies=proxies,
  306. timeout=timeout,
  307. verify=verify,
  308. cert=cert,
  309. )
  310. except IOError as e:
  311. raise RetryError(str(e))
  312. debug = runtime_option.get('debug') or os.getenv('DEBUG')
  313. if debug and debug.lower() == 'sdk':
  314. DaraCore._do_http_debug(p, resp)
  315. response = DaraResponse()
  316. response.status_message = resp.reason
  317. response.status_code = resp.status_code
  318. response.headers = {k.lower(): v for k, v in resp.headers.items()}
  319. response.body = resp.content
  320. response.response = resp
  321. return response
  322. @staticmethod
  323. async def async_do_sse_action(
  324. request: DaraRequest,
  325. runtime_option=None
  326. ) -> DaraResponse:
  327. runtime_option = runtime_option or {}
  328. url = DaraCore.compose_url(request)
  329. ignore_ssl = runtime_option.get('ignoreSSL', False)
  330. verify: Union[bool, str] = not ignore_ssl
  331. tls_min_version = runtime_option.get('tlsMinVersion')
  332. if isinstance(tls_min_version, Enum):
  333. tls_min_version = tls_min_version.value
  334. if verify:
  335. ca = runtime_option.get('ca')
  336. if ca is not None:
  337. verify = ca
  338. cert = runtime_option.get('cert', None)
  339. timeout = runtime_option.get('timeout')
  340. connect_timeout = runtime_option.get('connectTimeout') or timeout or DEFAULT_CONNECT_TIMEOUT
  341. read_timeout = runtime_option.get('readTimeout') or timeout or DEFAULT_READ_TIMEOUT
  342. connect_timeout, read_timeout = (int(connect_timeout) / 1000, int(read_timeout) / 1000)
  343. proxy = None
  344. if request.protocol.upper() == 'HTTP':
  345. proxy = runtime_option.get('httpProxy')
  346. if not proxy:
  347. proxy = os.environ.get('HTTP_PROXY') or os.environ.get('http_proxy')
  348. elif request.protocol.upper() == 'HTTPS':
  349. proxy = runtime_option.get('httpsProxy')
  350. if not proxy:
  351. proxy = os.environ.get('HTTPS_PROXY') or os.environ.get('https_proxy')
  352. pool_maxsize = DaraCore._resolve_pool_maxsize(runtime_option)
  353. connector = None
  354. ca_cert = certifi.where()
  355. ssl_context = None
  356. if isinstance(verify, str) and request.protocol.upper() == 'HTTPS':
  357. ssl_context = ssl.create_default_context()
  358. ssl_context = DaraCore._set_tls_minimum_version(ssl_context, tls_min_version)
  359. ssl_context.load_verify_locations(verify)
  360. # Handle cert if provided
  361. if cert is not None:
  362. if isinstance(cert, (list, tuple)):
  363. ssl_context.load_cert_chain(certfile=cert[0], keyfile=cert[1] if len(cert) > 1 else None)
  364. else:
  365. ssl_context.load_cert_chain(certfile=cert, keyfile=None)
  366. connector = aiohttp.TCPConnector(
  367. ssl=ssl_context, limit=pool_maxsize, limit_per_host=pool_maxsize
  368. )
  369. elif ca_cert and request.protocol.upper() == 'HTTPS' and verify:
  370. ssl_context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
  371. ssl_context = DaraCore._set_tls_minimum_version(ssl_context, tls_min_version)
  372. ssl_context.load_verify_locations(ca_cert)
  373. # Handle cert if provided
  374. if cert is not None:
  375. if isinstance(cert, (list, tuple)):
  376. ssl_context.load_cert_chain(certfile=cert[0], keyfile=cert[1] if len(cert) > 1 else None)
  377. else:
  378. ssl_context.load_cert_chain(certfile=cert, keyfile=None)
  379. connector = aiohttp.TCPConnector(
  380. ssl=ssl_context, limit=pool_maxsize, limit_per_host=pool_maxsize
  381. )
  382. else:
  383. verify = False
  384. connector = aiohttp.TCPConnector(limit=pool_maxsize, limit_per_host=pool_maxsize)
  385. timeout = aiohttp.ClientTimeout(
  386. sock_read=read_timeout,
  387. sock_connect=connect_timeout
  388. )
  389. session = aiohttp.ClientSession(connector=connector)
  390. body = b''
  391. if isinstance(request.body, BaseStream):
  392. for content in request.body:
  393. body += content
  394. elif isinstance(request.body, str):
  395. body = request.body.encode('utf-8')
  396. else:
  397. body = request.body or b''
  398. try:
  399. headers = request.headers.copy()
  400. ssl_param: Union[bool, ssl.SSLContext] = ssl_context if ssl_context is not None else bool(verify)
  401. response = await session.request(
  402. request.method,
  403. url,
  404. data=body,
  405. headers=headers,
  406. ssl=ssl_param,
  407. proxy=proxy,
  408. timeout=timeout
  409. )
  410. tea_resp: DaraResponse = DaraResponse()
  411. tea_resp.status_code = response.status
  412. tea_resp.status_message = response.reason
  413. tea_resp.headers = dict({k.lower(): v for k, v in response.headers.items()})
  414. tea_resp.body = SSEResponseWrapper(session, response)
  415. return tea_resp
  416. except IOError as e:
  417. await session.close()
  418. raise RetryError(str(e))
  419. @staticmethod
  420. def do_sse_action(
  421. request: DaraRequest,
  422. runtime_option=None
  423. ) -> DaraResponse:
  424. url = DaraCore.compose_url(request)
  425. runtime_option = runtime_option or {}
  426. verify = not runtime_option.get('ignoreSSL', False)
  427. tls_min_version = runtime_option.get('tlsMinVersion')
  428. if isinstance(tls_min_version, Enum):
  429. tls_min_version = tls_min_version.value
  430. if verify:
  431. verify = runtime_option.get('ca', True) if runtime_option.get('ca', True) is not None else True
  432. cert = runtime_option.get('cert', None)
  433. timeout = runtime_option.get('timeout')
  434. connect_timeout = runtime_option.get('connectTimeout') or timeout or DEFAULT_CONNECT_TIMEOUT
  435. read_timeout = runtime_option.get('readTimeout') or timeout or DEFAULT_READ_TIMEOUT
  436. timeout = (int(connect_timeout) / 1000, int(read_timeout) / 1000)
  437. if isinstance(request.body, str):
  438. request.body = request.body.encode('utf-8')
  439. p = PreparedRequest()
  440. p.prepare(
  441. method=request.method.upper(),
  442. url=url,
  443. data=request.body,
  444. headers=request.headers,
  445. )
  446. proxies = {}
  447. http_proxy = runtime_option.get('httpProxy')
  448. https_proxy = runtime_option.get('httpsProxy')
  449. no_proxy = runtime_option.get('noProxy')
  450. if not http_proxy:
  451. http_proxy = os.environ.get('HTTP_PROXY') or os.environ.get('http_proxy')
  452. if not https_proxy:
  453. https_proxy = os.environ.get('HTTPS_PROXY') or os.environ.get('https_proxy')
  454. if http_proxy:
  455. proxies['http'] = http_proxy
  456. if https_proxy:
  457. proxies['https'] = https_proxy
  458. if no_proxy:
  459. proxies['no_proxy'] = no_proxy
  460. host = request.headers.get('host')
  461. host = host.rstrip('/') if host else ''
  462. pool_maxsize = DaraCore._resolve_pool_maxsize(runtime_option)
  463. session_key = f'{request.protocol.lower()}://{host}:{request.port}:pool={pool_maxsize}'
  464. session = DaraCore._get_session(session_key=session_key, protocol=request.protocol,
  465. tls_min_version=tls_min_version, verify=verify,
  466. pool_size=pool_maxsize)
  467. try:
  468. resp = session.send(
  469. p,
  470. proxies=proxies,
  471. timeout=timeout,
  472. verify=verify,
  473. cert=cert,
  474. stream=True
  475. )
  476. except IOError as e:
  477. raise RetryError(str(e))
  478. debug = runtime_option.get('debug') or os.getenv('DEBUG')
  479. if debug and debug.lower() == 'sdk':
  480. DaraCore._do_http_debug(p, resp)
  481. response = DaraResponse()
  482. response.status_message = resp.reason
  483. response.status_code = resp.status_code
  484. response.headers = {k.lower(): v for k, v in resp.headers.items()}
  485. response.body = SyncSSEResponseWrapper(session, resp)
  486. return response
  487. @staticmethod
  488. def get_response_body(resp) -> str:
  489. return resp.content.decode("utf-8")
  490. @staticmethod
  491. def allow_retry(dic, retry_times, now=None) -> bool:
  492. if retry_times == 0:
  493. return True
  494. if dic is None or not dic.__contains__("maxAttempts") or \
  495. dic.get('retryable') is not True and retry_times >= 1:
  496. return False
  497. else:
  498. retry = 0 if dic.get("maxAttempts") is None else int(
  499. dic.get("maxAttempts"))
  500. return retry >= retry_times
  501. @staticmethod
  502. def should_retry(options: RetryOptions, ctx: RetryPolicyContext) -> bool:
  503. if ctx.retries_attempted == 0:
  504. return True
  505. if not options or not options.retryable:
  506. return False
  507. retries_attempted = ctx.retries_attempted
  508. ex = ctx.exception
  509. for condition in options.no_retry_condition:
  510. if getattr(ex, 'name', None) in condition.exception or getattr(ex, 'code', None) in condition.error_code:
  511. return False
  512. for condition in options.retry_condition:
  513. if getattr(ex, 'name', None) not in condition.exception and getattr(ex, 'code', None) not in condition.error_code:
  514. continue
  515. if retries_attempted >= condition.max_attempts:
  516. return False
  517. return True
  518. return False
  519. @staticmethod
  520. def get_backoff_time(options: RetryOptions, ctx: RetryPolicyContext) -> int:
  521. ex = ctx.exception
  522. conditions = options.retry_condition
  523. for condition in conditions:
  524. if getattr(ex, 'name', None) not in condition.exception and getattr(ex, 'code', None) not in condition.error_code:
  525. continue
  526. max_delay = condition.max_delay or MAX_DELAY_TIME
  527. retry_after = getattr(ctx.exception, "retry_after", None)
  528. if retry_after is not None:
  529. return min(retry_after, max_delay)
  530. if not condition.backoff:
  531. return MIN_DELAY_TIME
  532. return min(condition.backoff.get_delay_time(ctx), max_delay)
  533. return MIN_DELAY_TIME
  534. @staticmethod
  535. async def sleep_async(millisecond: int):
  536. await asyncio.sleep(millisecond / 1000)
  537. @staticmethod
  538. def sleep(millisecond: int):
  539. time.sleep(millisecond / 1000)
  540. @staticmethod
  541. def is_retryable(ex) -> bool:
  542. return isinstance(ex, RetryError)
  543. @staticmethod
  544. def bytes_readable(body):
  545. return body
  546. @staticmethod
  547. def merge(*dic_list) -> dict:
  548. dic_result = {}
  549. for item in dic_list:
  550. if isinstance(item, dict):
  551. dic_result.update(item)
  552. elif isinstance(item, DaraModel):
  553. dic_result.update(item.to_map())
  554. return dic_result
  555. @staticmethod
  556. def is_null(value) -> bool:
  557. return value is None
  558. @staticmethod
  559. def to_readable_stream(data):
  560. if isinstance(data, str):
  561. return io.StringIO(data)
  562. elif isinstance(data, bytes):
  563. return io.BytesIO(data)
  564. else:
  565. raise TypeError("Input data must be of type str or bytes")
  566. @staticmethod
  567. def to_map(model: Optional[DaraModel]) -> Dict[str, Any]:
  568. if isinstance(model, DaraModel):
  569. return model.to_map()
  570. else:
  571. return dict()
  572. @staticmethod
  573. def to_number(model) -> int:
  574. if isinstance(model, int):
  575. return model
  576. if isinstance(model, str):
  577. if model == "":
  578. return 0
  579. return int(model)
  580. if isinstance(model, float):
  581. return int(model)
  582. return 0
  583. @staticmethod
  584. def from_map(
  585. model: DaraModel,
  586. dic: Dict[str, Any]
  587. ) -> DaraModel:
  588. if isinstance(model, DaraModel):
  589. try:
  590. return model.from_map(dic)
  591. except Exception:
  592. model._map = dic
  593. return model
  594. else:
  595. return model
  596. @staticmethod
  597. def _get_session(session_key: str, protocol: str, tls_min_version: str = None,
  598. verify: bool = True, pool_size: int = None):
  599. if session_key not in DaraCore._sessions:
  600. session = Session()
  601. adapter = DaraCore.get_adapter(protocol, tls_min_version, pool_size=pool_size)
  602. if protocol.upper() == 'HTTPS':
  603. if verify:
  604. session.mount('https://', adapter)
  605. else:
  606. # Honor configured pool size even when SSL verify is disabled.
  607. insecure_adapter = adapters.HTTPAdapter(
  608. pool_connections=DEFAULT_POOL_SIZE,
  609. pool_maxsize=pool_size if pool_size is not None and pool_size > 0 else DEFAULT_POOL_MAXSIZE,
  610. )
  611. session.mount('https://', insecure_adapter)
  612. else:
  613. session.mount('http://', adapter)
  614. DaraCore._sessions[session_key] = session
  615. return DaraCore._sessions[session_key]