lib-x17-fintech 2.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (282) hide show
  1. lib_x17_fintech-2.1.3.dist-info/METADATA +633 -0
  2. lib_x17_fintech-2.1.3.dist-info/RECORD +282 -0
  3. lib_x17_fintech-2.1.3.dist-info/WHEEL +5 -0
  4. lib_x17_fintech-2.1.3.dist-info/licenses/LICENSE +1 -0
  5. lib_x17_fintech-2.1.3.dist-info/top_level.txt +1 -0
  6. xfintech/__init__.py +0 -0
  7. xfintech/connect/__init__.py +18 -0
  8. xfintech/connect/artifact/__init__.py +5 -0
  9. xfintech/connect/artifact/artifact.py +168 -0
  10. xfintech/connect/artifact/tests/__init__.py +3 -0
  11. xfintech/connect/artifact/tests/test_class_artifact_all.py +564 -0
  12. xfintech/connect/common/__init__.py +12 -0
  13. xfintech/connect/common/connect.py +49 -0
  14. xfintech/connect/common/connectref.py +119 -0
  15. xfintech/connect/common/error.py +62 -0
  16. xfintech/connect/common/tests/__init__.py +1 -0
  17. xfintech/connect/common/tests/test_class_connectlike_all.py +544 -0
  18. xfintech/connect/common/tests/test_class_connectref_all.py +586 -0
  19. xfintech/connect/common/tests/test_class_errors_all.py +524 -0
  20. xfintech/connect/instance/__init__.py +7 -0
  21. xfintech/connect/instance/macos.py +121 -0
  22. xfintech/connect/instance/s3.py +176 -0
  23. xfintech/connect/instance/tests/__init__.py +1 -0
  24. xfintech/connect/instance/tests/test_class_macosconnect_all.py +692 -0
  25. xfintech/connect/instance/tests/test_class_s3connect_all.py +603 -0
  26. xfintech/data/__init__.py +20 -0
  27. xfintech/data/common/__init__.py +15 -0
  28. xfintech/data/common/cache.py +186 -0
  29. xfintech/data/common/coolant.py +171 -0
  30. xfintech/data/common/metric.py +138 -0
  31. xfintech/data/common/paginate.py +132 -0
  32. xfintech/data/common/params.py +162 -0
  33. xfintech/data/common/retry.py +201 -0
  34. xfintech/data/common/tests/__init__.py +1 -0
  35. xfintech/data/common/tests/test_class_cache_all.py +681 -0
  36. xfintech/data/common/tests/test_class_coolant_all.py +534 -0
  37. xfintech/data/common/tests/test_class_metric_all.py +705 -0
  38. xfintech/data/common/tests/test_class_paginate_all.py +508 -0
  39. xfintech/data/common/tests/test_class_params_all.py +891 -0
  40. xfintech/data/common/tests/test_class_retry_all.py +714 -0
  41. xfintech/data/job/__init__.py +17 -0
  42. xfintech/data/job/errors.py +112 -0
  43. xfintech/data/job/house.py +156 -0
  44. xfintech/data/job/job.py +247 -0
  45. xfintech/data/job/joblike.py +47 -0
  46. xfintech/data/job/tests/__init__.py +1 -0
  47. xfintech/data/job/tests/test_class_errors_all.py +275 -0
  48. xfintech/data/job/tests/test_class_house_all.py +801 -0
  49. xfintech/data/job/tests/test_class_job_all.py +684 -0
  50. xfintech/data/job/tests/test_class_joblike_all.py +482 -0
  51. xfintech/data/relay/__init__.py +7 -0
  52. xfintech/data/relay/client.py +114 -0
  53. xfintech/data/relay/clientlike.py +45 -0
  54. xfintech/data/relay/tests/test_class_relayclient_all.py +484 -0
  55. xfintech/data/relay/tests/test_class_relayclientlike_all.py +500 -0
  56. xfintech/data/source/__init__.py +7 -0
  57. xfintech/data/source/baostock/__init__.py +21 -0
  58. xfintech/data/source/baostock/job/__init__.py +5 -0
  59. xfintech/data/source/baostock/job/job.py +217 -0
  60. xfintech/data/source/baostock/job/tests/__init__.py +0 -0
  61. xfintech/data/source/baostock/job/tests/test_class_baostockjob_all.py +547 -0
  62. xfintech/data/source/baostock/session/__init__.py +8 -0
  63. xfintech/data/source/baostock/session/relay.py +223 -0
  64. xfintech/data/source/baostock/session/session.py +241 -0
  65. xfintech/data/source/baostock/session/tests/__init__.py +0 -0
  66. xfintech/data/source/baostock/session/tests/test_class_relay_all.py +694 -0
  67. xfintech/data/source/baostock/session/tests/test_class_session_all.py +505 -0
  68. xfintech/data/source/baostock/stock/__init__.py +0 -0
  69. xfintech/data/source/baostock/stock/hs300stock/__init__.py +3 -0
  70. xfintech/data/source/baostock/stock/hs300stock/constant.py +49 -0
  71. xfintech/data/source/baostock/stock/hs300stock/hs300stock.py +133 -0
  72. xfintech/data/source/baostock/stock/hs300stock/tests/__init__.py +1 -0
  73. xfintech/data/source/baostock/stock/hs300stock/tests/test_class_hs300index_all.py +413 -0
  74. xfintech/data/source/baostock/stock/minuteline/__init__.py +19 -0
  75. xfintech/data/source/baostock/stock/minuteline/constant.py +89 -0
  76. xfintech/data/source/baostock/stock/minuteline/minuteline.py +163 -0
  77. xfintech/data/source/baostock/stock/minuteline/tests/__init__.py +0 -0
  78. xfintech/data/source/baostock/stock/minuteline/tests/test_class_minuteline_all.py +582 -0
  79. xfintech/data/source/baostock/stock/stock/__init__.py +19 -0
  80. xfintech/data/source/baostock/stock/stock/constant.py +55 -0
  81. xfintech/data/source/baostock/stock/stock/stock.py +149 -0
  82. xfintech/data/source/baostock/stock/stock/tests/__init__.py +0 -0
  83. xfintech/data/source/baostock/stock/stock/tests/test_class_stock_all.py +508 -0
  84. xfintech/data/source/baostock/stock/stockinfo/__init__.py +5 -0
  85. xfintech/data/source/baostock/stock/stockinfo/constant.py +66 -0
  86. xfintech/data/source/baostock/stock/stockinfo/stockinfo.py +176 -0
  87. xfintech/data/source/baostock/stock/stockinfo/tests/__init__.py +1 -0
  88. xfintech/data/source/baostock/stock/stockinfo/tests/test_class_stockinfo_all.py +617 -0
  89. xfintech/data/source/baostock/stock/sz50stock/__init__.py +3 -0
  90. xfintech/data/source/baostock/stock/sz50stock/constant.py +49 -0
  91. xfintech/data/source/baostock/stock/sz50stock/sz50stock.py +133 -0
  92. xfintech/data/source/baostock/stock/sz50stock/tests/__init__.py +1 -0
  93. xfintech/data/source/baostock/stock/sz50stock/tests/test_class_sz50stock_all.py +397 -0
  94. xfintech/data/source/baostock/stock/tradedate/__init__.py +19 -0
  95. xfintech/data/source/baostock/stock/tradedate/constant.py +72 -0
  96. xfintech/data/source/baostock/stock/tradedate/tests/__init__.py +0 -0
  97. xfintech/data/source/baostock/stock/tradedate/tests/test_class_tradedate_all.py +695 -0
  98. xfintech/data/source/baostock/stock/tradedate/tradedate.py +208 -0
  99. xfintech/data/source/baostock/stock/zz500stock/__init__.py +3 -0
  100. xfintech/data/source/baostock/stock/zz500stock/constant.py +55 -0
  101. xfintech/data/source/baostock/stock/zz500stock/tests/__init__.py +1 -0
  102. xfintech/data/source/baostock/stock/zz500stock/tests/test_class_zz500stock_all.py +421 -0
  103. xfintech/data/source/baostock/stock/zz500stock/zz500stock.py +133 -0
  104. xfintech/data/source/tushare/__init__.py +61 -0
  105. xfintech/data/source/tushare/job/__init__.py +5 -0
  106. xfintech/data/source/tushare/job/job.py +257 -0
  107. xfintech/data/source/tushare/job/tests/test_class_tusharejob_all.py +589 -0
  108. xfintech/data/source/tushare/session/__init__.py +5 -0
  109. xfintech/data/source/tushare/session/relay.py +231 -0
  110. xfintech/data/source/tushare/session/session.py +239 -0
  111. xfintech/data/source/tushare/session/tests/test_class_relay_all.py +719 -0
  112. xfintech/data/source/tushare/session/tests/test_class_session_all.py +705 -0
  113. xfintech/data/source/tushare/stock/__init__.py +55 -0
  114. xfintech/data/source/tushare/stock/adjfactor/__init__.py +19 -0
  115. xfintech/data/source/tushare/stock/adjfactor/adjfactor.py +150 -0
  116. xfintech/data/source/tushare/stock/adjfactor/constant.py +71 -0
  117. xfintech/data/source/tushare/stock/adjfactor/tests/__init__.py +0 -0
  118. xfintech/data/source/tushare/stock/adjfactor/tests/test_class_adjfactor_all.py +372 -0
  119. xfintech/data/source/tushare/stock/capflow/__init__.py +19 -0
  120. xfintech/data/source/tushare/stock/capflow/capflow.py +171 -0
  121. xfintech/data/source/tushare/stock/capflow/constant.py +105 -0
  122. xfintech/data/source/tushare/stock/capflow/tests/__init__.py +0 -0
  123. xfintech/data/source/tushare/stock/capflow/tests/test_class_capflow_all.py +589 -0
  124. xfintech/data/source/tushare/stock/capflowdc/__init__.py +19 -0
  125. xfintech/data/source/tushare/stock/capflowdc/capflowdc.py +167 -0
  126. xfintech/data/source/tushare/stock/capflowdc/constant.py +95 -0
  127. xfintech/data/source/tushare/stock/capflowdc/tests/__init__.py +0 -0
  128. xfintech/data/source/tushare/stock/capflowdc/tests/test_class_capflowdc_all.py +814 -0
  129. xfintech/data/source/tushare/stock/capflowths/__init__.py +19 -0
  130. xfintech/data/source/tushare/stock/capflowths/capflowths.py +173 -0
  131. xfintech/data/source/tushare/stock/capflowths/constant.py +92 -0
  132. xfintech/data/source/tushare/stock/capflowths/tests/__init__.py +0 -0
  133. xfintech/data/source/tushare/stock/capflowths/tests/test_class_capflowths_all.py +551 -0
  134. xfintech/data/source/tushare/stock/company/__init__.py +19 -0
  135. xfintech/data/source/tushare/stock/company/company.py +188 -0
  136. xfintech/data/source/tushare/stock/company/constant.py +92 -0
  137. xfintech/data/source/tushare/stock/company/tests/__init__.py +1 -0
  138. xfintech/data/source/tushare/stock/company/tests/test_class_company_all.py +829 -0
  139. xfintech/data/source/tushare/stock/companybusiness/__init__.py +21 -0
  140. xfintech/data/source/tushare/stock/companybusiness/companybusiness.py +183 -0
  141. xfintech/data/source/tushare/stock/companybusiness/constant.py +91 -0
  142. xfintech/data/source/tushare/stock/companybusiness/tests/__init__.py +0 -0
  143. xfintech/data/source/tushare/stock/companybusiness/tests/test_class_companybusiness_all.py +633 -0
  144. xfintech/data/source/tushare/stock/companycashflow/__init__.py +21 -0
  145. xfintech/data/source/tushare/stock/companycashflow/companycashflow.py +277 -0
  146. xfintech/data/source/tushare/stock/companycashflow/constant.py +293 -0
  147. xfintech/data/source/tushare/stock/companycashflow/tests/__init__.py +0 -0
  148. xfintech/data/source/tushare/stock/companycashflow/tests/test_class_companycashflow_all.py +619 -0
  149. xfintech/data/source/tushare/stock/companydebt/__init__.py +19 -0
  150. xfintech/data/source/tushare/stock/companydebt/companydebt.py +339 -0
  151. xfintech/data/source/tushare/stock/companydebt/constant.py +403 -0
  152. xfintech/data/source/tushare/stock/companydebt/tests/__init__.py +0 -0
  153. xfintech/data/source/tushare/stock/companydebt/tests/test_class_companydebt_all.py +655 -0
  154. xfintech/data/source/tushare/stock/companyoverview/__init__.py +21 -0
  155. xfintech/data/source/tushare/stock/companyoverview/companyoverview.py +214 -0
  156. xfintech/data/source/tushare/stock/companyoverview/constant.py +152 -0
  157. xfintech/data/source/tushare/stock/companyoverview/tests/__init__.py +0 -0
  158. xfintech/data/source/tushare/stock/companyoverview/tests/test_class_companyoverview_all.py +647 -0
  159. xfintech/data/source/tushare/stock/companyprofit/__init__.py +21 -0
  160. xfintech/data/source/tushare/stock/companyprofit/companyprofit.py +272 -0
  161. xfintech/data/source/tushare/stock/companyprofit/constant.py +259 -0
  162. xfintech/data/source/tushare/stock/companyprofit/tests/__init__.py +0 -0
  163. xfintech/data/source/tushare/stock/companyprofit/tests/test_class_companyprofit_all.py +635 -0
  164. xfintech/data/source/tushare/stock/conceptcapflowdc/__init__.py +21 -0
  165. xfintech/data/source/tushare/stock/conceptcapflowdc/conceptcapflowdc.py +175 -0
  166. xfintech/data/source/tushare/stock/conceptcapflowdc/constant.py +106 -0
  167. xfintech/data/source/tushare/stock/conceptcapflowdc/tests/__init__.py +0 -0
  168. xfintech/data/source/tushare/stock/conceptcapflowdc/tests/test_class_conceptcapflowdc_all.py +568 -0
  169. xfintech/data/source/tushare/stock/conceptcapflowths/__init__.py +21 -0
  170. xfintech/data/source/tushare/stock/conceptcapflowths/conceptcapflowths.py +188 -0
  171. xfintech/data/source/tushare/stock/conceptcapflowths/constant.py +89 -0
  172. xfintech/data/source/tushare/stock/conceptcapflowths/tests/__init__.py +0 -0
  173. xfintech/data/source/tushare/stock/conceptcapflowths/tests/test_class_conceptcapflowths_all.py +516 -0
  174. xfintech/data/source/tushare/stock/dayline/__init__.py +19 -0
  175. xfintech/data/source/tushare/stock/dayline/constant.py +87 -0
  176. xfintech/data/source/tushare/stock/dayline/dayline.py +177 -0
  177. xfintech/data/source/tushare/stock/dayline/tests/__init__.py +0 -0
  178. xfintech/data/source/tushare/stock/dayline/tests/test_class_dayline_all.py +585 -0
  179. xfintech/data/source/tushare/stock/industrycapflowths/__init__.py +21 -0
  180. xfintech/data/source/tushare/stock/industrycapflowths/constant.py +89 -0
  181. xfintech/data/source/tushare/stock/industrycapflowths/industrycapflowths.py +192 -0
  182. xfintech/data/source/tushare/stock/industrycapflowths/tests/__init__.py +0 -0
  183. xfintech/data/source/tushare/stock/industrycapflowths/tests/test_class_industrycapflowths_all.py +683 -0
  184. xfintech/data/source/tushare/stock/marketindexcapflowdc/__init__.py +21 -0
  185. xfintech/data/source/tushare/stock/marketindexcapflowdc/constant.py +90 -0
  186. xfintech/data/source/tushare/stock/marketindexcapflowdc/marketindexcapflowdc.py +173 -0
  187. xfintech/data/source/tushare/stock/marketindexcapflowdc/tests/__init__.py +0 -0
  188. xfintech/data/source/tushare/stock/marketindexcapflowdc/tests/test_class_marketindexcapflowdc_all.py +793 -0
  189. xfintech/data/source/tushare/stock/monthline/__init__.py +19 -0
  190. xfintech/data/source/tushare/stock/monthline/constant.py +87 -0
  191. xfintech/data/source/tushare/stock/monthline/monthline.py +180 -0
  192. xfintech/data/source/tushare/stock/monthline/tests/__init__.py +0 -0
  193. xfintech/data/source/tushare/stock/monthline/tests/test_class_monthline_all.py +574 -0
  194. xfintech/data/source/tushare/stock/stock/__init__.py +19 -0
  195. xfintech/data/source/tushare/stock/stock/constant.py +105 -0
  196. xfintech/data/source/tushare/stock/stock/stock.py +193 -0
  197. xfintech/data/source/tushare/stock/stock/tests/__init__.py +0 -0
  198. xfintech/data/source/tushare/stock/stock/tests/test_class_stock_all.py +788 -0
  199. xfintech/data/source/tushare/stock/stockdividend/__init__.py +21 -0
  200. xfintech/data/source/tushare/stock/stockdividend/constant.py +111 -0
  201. xfintech/data/source/tushare/stock/stockdividend/stockdividend.py +180 -0
  202. xfintech/data/source/tushare/stock/stockdividend/tests/__init__.py +0 -0
  203. xfintech/data/source/tushare/stock/stockdividend/tests/test_class_stockdividend_all.py +725 -0
  204. xfintech/data/source/tushare/stock/stockinfo/__init__.py +19 -0
  205. xfintech/data/source/tushare/stock/stockinfo/constant.py +104 -0
  206. xfintech/data/source/tushare/stock/stockinfo/stockinfo.py +208 -0
  207. xfintech/data/source/tushare/stock/stockinfo/tests/__init__.py +0 -0
  208. xfintech/data/source/tushare/stock/stockinfo/tests/test_class_stockinfo_all.py +881 -0
  209. xfintech/data/source/tushare/stock/stockipo/__init__.py +19 -0
  210. xfintech/data/source/tushare/stock/stockipo/constant.py +90 -0
  211. xfintech/data/source/tushare/stock/stockipo/stockipo.py +234 -0
  212. xfintech/data/source/tushare/stock/stockipo/tests/__init__.py +1 -0
  213. xfintech/data/source/tushare/stock/stockipo/tests/test_class_stockipo_all.py +750 -0
  214. xfintech/data/source/tushare/stock/stockpledge/__init__.py +19 -0
  215. xfintech/data/source/tushare/stock/stockpledge/constant.py +72 -0
  216. xfintech/data/source/tushare/stock/stockpledge/stockpledge.py +158 -0
  217. xfintech/data/source/tushare/stock/stockpledge/tests/__init__.py +0 -0
  218. xfintech/data/source/tushare/stock/stockpledge/tests/test_class_stockpledge_all.py +664 -0
  219. xfintech/data/source/tushare/stock/stockpledgedetail/__init__.py +21 -0
  220. xfintech/data/source/tushare/stock/stockpledgedetail/constant.py +85 -0
  221. xfintech/data/source/tushare/stock/stockpledgedetail/stockpledgedetail.py +171 -0
  222. xfintech/data/source/tushare/stock/stockpledgedetail/tests/__init__.py +0 -0
  223. xfintech/data/source/tushare/stock/stockpledgedetail/tests/test_class_stockpledgedetail_all.py +112 -0
  224. xfintech/data/source/tushare/stock/stockst/__init__.py +19 -0
  225. xfintech/data/source/tushare/stock/stockst/constant.py +80 -0
  226. xfintech/data/source/tushare/stock/stockst/stockst.py +189 -0
  227. xfintech/data/source/tushare/stock/stockst/tests/__init__.py +0 -0
  228. xfintech/data/source/tushare/stock/stockst/tests/test_class_stockst_all.py +693 -0
  229. xfintech/data/source/tushare/stock/stocksuspend/__init__.py +21 -0
  230. xfintech/data/source/tushare/stock/stocksuspend/constant.py +75 -0
  231. xfintech/data/source/tushare/stock/stocksuspend/stocksuspend.py +151 -0
  232. xfintech/data/source/tushare/stock/stocksuspend/tests/__init__.py +0 -0
  233. xfintech/data/source/tushare/stock/stocksuspend/tests/test_class_stocksuspend_all.py +626 -0
  234. xfintech/data/source/tushare/stock/techindex/__init__.py +19 -0
  235. xfintech/data/source/tushare/stock/techindex/constant.py +600 -0
  236. xfintech/data/source/tushare/stock/techindex/techindex.py +314 -0
  237. xfintech/data/source/tushare/stock/techindex/tests/__init__.py +0 -0
  238. xfintech/data/source/tushare/stock/techindex/tests/test_class_techindex_all.py +576 -0
  239. xfintech/data/source/tushare/stock/tradedate/__init__.py +19 -0
  240. xfintech/data/source/tushare/stock/tradedate/constant.py +93 -0
  241. xfintech/data/source/tushare/stock/tradedate/tests/__init__.py +0 -0
  242. xfintech/data/source/tushare/stock/tradedate/tests/test_class_tradedate_all.py +947 -0
  243. xfintech/data/source/tushare/stock/tradedate/tradedate.py +234 -0
  244. xfintech/data/source/tushare/stock/weekline/__init__.py +19 -0
  245. xfintech/data/source/tushare/stock/weekline/constant.py +87 -0
  246. xfintech/data/source/tushare/stock/weekline/tests/__init__.py +0 -0
  247. xfintech/data/source/tushare/stock/weekline/tests/test_class_weekline_all.py +575 -0
  248. xfintech/data/source/tushare/stock/weekline/weekline.py +182 -0
  249. xfintech/fabric/__init__.py +18 -0
  250. xfintech/fabric/column/__init__.py +7 -0
  251. xfintech/fabric/column/info.py +202 -0
  252. xfintech/fabric/column/kind.py +102 -0
  253. xfintech/fabric/column/tests/__init__.py +0 -0
  254. xfintech/fabric/column/tests/test_class_info_all.py +207 -0
  255. xfintech/fabric/column/tests/test_class_kind_all.py +80 -0
  256. xfintech/fabric/table/__init__.py +5 -0
  257. xfintech/fabric/table/info.py +263 -0
  258. xfintech/fabric/table/tests/__init__.py +0 -0
  259. xfintech/fabric/table/tests/test_class_info_all.py +547 -0
  260. xfintech/serde/__init__.py +35 -0
  261. xfintech/serde/common/__init__.py +9 -0
  262. xfintech/serde/common/dataformat.py +78 -0
  263. xfintech/serde/common/deserialiserlike.py +38 -0
  264. xfintech/serde/common/error.py +182 -0
  265. xfintech/serde/common/serialiserlike.py +38 -0
  266. xfintech/serde/common/tests/__init__.py +1 -0
  267. xfintech/serde/common/tests/test_class_dataformat_all.py +694 -0
  268. xfintech/serde/common/tests/test_class_deserialiserlike_all.py +500 -0
  269. xfintech/serde/common/tests/test_class_errors_all.py +518 -0
  270. xfintech/serde/common/tests/test_class_serialiserlike_all.py +401 -0
  271. xfintech/serde/deserialiser/__init__.py +7 -0
  272. xfintech/serde/deserialiser/pandas.py +113 -0
  273. xfintech/serde/deserialiser/python.py +68 -0
  274. xfintech/serde/deserialiser/tests/__init__.py +1 -0
  275. xfintech/serde/deserialiser/tests/test_class_pandasdeserialiser_all.py +503 -0
  276. xfintech/serde/deserialiser/tests/test_class_pythondeserialiser_all.py +570 -0
  277. xfintech/serde/serialiser/__init__.py +7 -0
  278. xfintech/serde/serialiser/pandas.py +116 -0
  279. xfintech/serde/serialiser/python.py +71 -0
  280. xfintech/serde/serialiser/tests/__init__.py +1 -0
  281. xfintech/serde/serialiser/tests/test_class_pandasserialiser_all.py +474 -0
  282. xfintech/serde/serialiser/tests/test_class_pythonserialiser_all.py +508 -0
@@ -0,0 +1,714 @@
1
+ import time
2
+
3
+ import backoff
4
+ import pytest
5
+
6
+ from xfintech.data.common.retry import Retry
7
+
8
+ # ==================== Retry Initialization Tests ====================
9
+
10
+
11
+ def test_retry_init_defaults():
12
+ """Test Retry initialization with default values"""
13
+ retry = Retry()
14
+
15
+ assert retry.retry == 0
16
+ assert retry.wait == 0
17
+ assert retry.rate == 1.0
18
+ assert retry.exceptions == (Exception,)
19
+ assert retry.jitter is True
20
+ assert retry.jitter_fn is not None
21
+
22
+
23
+ def test_retry_init_custom_values():
24
+ """Test Retry initialization with custom values"""
25
+ retry = Retry(retry=5, wait=2.0, rate=1.5, jitter=False)
26
+
27
+ assert retry.retry == 5
28
+ assert retry.wait == 2.0
29
+ assert retry.rate == 1.5
30
+ assert retry.jitter is False
31
+ assert retry.jitter_fn is None
32
+
33
+
34
+ def test_retry_init_custom_exceptions():
35
+ """Test Retry initialization with custom exceptions"""
36
+ retry = Retry(exceptions=[ValueError, TypeError])
37
+
38
+ assert retry.exceptions == (ValueError, TypeError)
39
+
40
+
41
+ def test_retry_init_single_exception():
42
+ """Test Retry initialization with single exception"""
43
+ retry = Retry(exceptions=[ConnectionError])
44
+
45
+ assert retry.exceptions == (ConnectionError,)
46
+
47
+
48
+ def test_retry_init_none_exceptions():
49
+ """Test Retry initialization with None exceptions defaults to Exception"""
50
+ retry = Retry(exceptions=None)
51
+
52
+ assert retry.exceptions == (Exception,)
53
+
54
+
55
+ # ==================== Jitter Function Tests ====================
56
+
57
+
58
+ def test_retry_jitter_enabled():
59
+ """Test jitter_fn is set when jitter is True"""
60
+ retry = Retry(jitter=True)
61
+
62
+ assert retry.jitter_fn is not None
63
+ assert retry.jitter_fn == backoff.full_jitter
64
+
65
+
66
+ def test_retry_jitter_disabled():
67
+ """Test jitter_fn is None when jitter is False"""
68
+ retry = Retry(jitter=False)
69
+
70
+ assert retry.jitter_fn is None
71
+
72
+
73
+ def test_retry_resolve_jitter_fn():
74
+ """Test _resolve_jitter_fn method"""
75
+ retry = Retry(jitter=True)
76
+ jitter_fn = retry._resolve_jitter_fn()
77
+
78
+ assert jitter_fn is not None
79
+ assert callable(jitter_fn)
80
+
81
+
82
+ # ==================== No Retry Tests ====================
83
+
84
+
85
+ def test_retry_zero_returns_original():
86
+ """Test retry=0 returns original function without decoration"""
87
+ retry = Retry(retry=0)
88
+
89
+ @retry
90
+ def sample_func():
91
+ return "result"
92
+
93
+ assert sample_func() == "result"
94
+ # Function should be the original, not wrapped
95
+ assert sample_func.__name__ == "sample_func"
96
+
97
+
98
+ def test_retry_negative_returns_original():
99
+ """Test negative retry returns original function"""
100
+ retry = Retry(retry=-1)
101
+
102
+ call_count = 0
103
+
104
+ @retry
105
+ def sample_func():
106
+ nonlocal call_count
107
+ call_count += 1
108
+ raise ValueError("Error")
109
+
110
+ with pytest.raises(ValueError):
111
+ sample_func()
112
+
113
+ # Should fail immediately without retry
114
+ assert call_count == 1
115
+
116
+
117
+ # ==================== Constant Interval Tests ====================
118
+
119
+
120
+ def test_retry_constant_interval():
121
+ """Test retry with constant interval (rate=1.0)"""
122
+ retry = Retry(retry=3, wait=0.01, rate=1.0, jitter=False)
123
+
124
+ call_count = 0
125
+
126
+ @retry
127
+ def failing_func():
128
+ nonlocal call_count
129
+ call_count += 1
130
+ if call_count < 3:
131
+ raise ValueError("Failed")
132
+ return "success"
133
+
134
+ result = failing_func()
135
+
136
+ assert result == "success"
137
+ assert call_count == 3
138
+
139
+
140
+ def test_retry_constant_interval_all_fail():
141
+ """Test retry with constant interval when all attempts fail"""
142
+ retry = Retry(retry=3, wait=0.01, rate=1.0, jitter=False)
143
+
144
+ call_count = 0
145
+
146
+ @retry
147
+ def always_fail():
148
+ nonlocal call_count
149
+ call_count += 1
150
+ raise ValueError("Always fails")
151
+
152
+ with pytest.raises(ValueError, match="Always fails"):
153
+ always_fail()
154
+
155
+ assert call_count == 3
156
+
157
+
158
+ # ==================== Exponential Backoff Tests ====================
159
+
160
+
161
+ def test_retry_exponential_backoff():
162
+ """Test retry with exponential backoff (rate>1.0)"""
163
+ retry = Retry(retry=4, wait=0.01, rate=2.0, jitter=False)
164
+
165
+ call_count = 0
166
+
167
+ @retry
168
+ def failing_func():
169
+ nonlocal call_count
170
+ call_count += 1
171
+ if call_count < 3:
172
+ raise ConnectionError("Connection failed")
173
+ return "connected"
174
+
175
+ result = failing_func()
176
+
177
+ assert result == "connected"
178
+ assert call_count == 3
179
+
180
+
181
+ def test_retry_exponential_backoff_all_fail():
182
+ """Test exponential backoff when all attempts fail"""
183
+ retry = Retry(retry=3, wait=0.01, rate=2.0, jitter=False)
184
+
185
+ call_count = 0
186
+
187
+ @retry
188
+ def always_fail():
189
+ nonlocal call_count
190
+ call_count += 1
191
+ raise TimeoutError("Timeout")
192
+
193
+ with pytest.raises(TimeoutError, match="Timeout"):
194
+ always_fail()
195
+
196
+ assert call_count == 3
197
+
198
+
199
+ # ==================== Exception Handling Tests ====================
200
+
201
+
202
+ def test_retry_specific_exception():
203
+ """Test retry only catches specified exceptions"""
204
+ retry = Retry(retry=3, wait=0.01, exceptions=[ValueError])
205
+
206
+ call_count = 0
207
+
208
+ @retry
209
+ def mixed_exceptions():
210
+ nonlocal call_count
211
+ call_count += 1
212
+ if call_count == 1:
213
+ raise ValueError("Retryable")
214
+ raise TypeError("Not retryable")
215
+
216
+ with pytest.raises(TypeError, match="Not retryable"):
217
+ mixed_exceptions()
218
+
219
+ # Should retry once for ValueError, then fail on TypeError
220
+ assert call_count == 2
221
+
222
+
223
+ def test_retry_multiple_exceptions():
224
+ """Test retry with multiple exception types"""
225
+ retry = Retry(retry=3, wait=0.01, exceptions=[ValueError, TypeError])
226
+
227
+ call_count = 0
228
+
229
+ @retry
230
+ def multiple_errors():
231
+ nonlocal call_count
232
+ call_count += 1
233
+ if call_count == 1:
234
+ raise ValueError("First error")
235
+ if call_count == 2:
236
+ raise TypeError("Second error")
237
+ return "success"
238
+
239
+ result = multiple_errors()
240
+
241
+ assert result == "success"
242
+ assert call_count == 3
243
+
244
+
245
+ def test_retry_non_matching_exception():
246
+ """Test retry doesn't catch non-matching exceptions"""
247
+ retry = Retry(retry=3, wait=0.01, exceptions=[ValueError])
248
+
249
+ call_count = 0
250
+
251
+ @retry
252
+ def wrong_exception():
253
+ nonlocal call_count
254
+ call_count += 1
255
+ raise KeyError("Not handled")
256
+
257
+ with pytest.raises(KeyError, match="Not handled"):
258
+ wrong_exception()
259
+
260
+ # Should fail immediately without retry
261
+ assert call_count == 1
262
+
263
+
264
+ def test_retry_success_on_first_try():
265
+ """Test function that succeeds on first try"""
266
+ retry = Retry(retry=3, wait=0.01)
267
+
268
+ call_count = 0
269
+
270
+ @retry
271
+ def success_func():
272
+ nonlocal call_count
273
+ call_count += 1
274
+ return "success"
275
+
276
+ result = success_func()
277
+
278
+ assert result == "success"
279
+ assert call_count == 1
280
+
281
+
282
+ # ==================== Function Decoration Tests ====================
283
+
284
+
285
+ def test_retry_preserves_function_name():
286
+ """Test retry decorator preserves function name"""
287
+ retry = Retry(retry=3, wait=0.01)
288
+
289
+ @retry
290
+ def my_function():
291
+ return "result"
292
+
293
+ assert my_function.__name__ == "my_function"
294
+
295
+
296
+ def test_retry_with_arguments():
297
+ """Test retry works with functions that have arguments"""
298
+ retry = Retry(retry=3, wait=0.01)
299
+
300
+ call_count = 0
301
+
302
+ @retry
303
+ def func_with_args(a, b, c=10):
304
+ nonlocal call_count
305
+ call_count += 1
306
+ if call_count < 2:
307
+ raise ValueError("Fail")
308
+ return a + b + c
309
+
310
+ result = func_with_args(1, 2, c=3)
311
+
312
+ assert result == 6
313
+ assert call_count == 2
314
+
315
+
316
+ def test_retry_with_kwargs():
317
+ """Test retry works with keyword arguments"""
318
+ retry = Retry(retry=3, wait=0.01)
319
+
320
+ call_count = 0
321
+
322
+ @retry
323
+ def func_with_kwargs(**kwargs):
324
+ nonlocal call_count
325
+ call_count += 1
326
+ if call_count < 2:
327
+ raise ValueError("Fail")
328
+ return kwargs
329
+
330
+ result = func_with_kwargs(x=1, y=2, z=3)
331
+
332
+ assert result == {"x": 1, "y": 2, "z": 3}
333
+ assert call_count == 2
334
+
335
+
336
+ def test_retry_with_mixed_args():
337
+ """Test retry with positional and keyword arguments"""
338
+ retry = Retry(retry=3, wait=0.01)
339
+
340
+ call_count = 0
341
+
342
+ @retry
343
+ def mixed_args(a, b, *args, x=10, **kwargs):
344
+ nonlocal call_count
345
+ call_count += 1
346
+ if call_count < 2:
347
+ raise ValueError("Fail")
348
+ return a, b, args, x, kwargs
349
+
350
+ result = mixed_args(1, 2, 3, 4, x=5, y=6)
351
+
352
+ assert result == (1, 2, (3, 4), 5, {"y": 6})
353
+ assert call_count == 2
354
+
355
+
356
+ def test_retry_decorator_as_variable():
357
+ """Test using retry as a variable instead of decorator"""
358
+ retry = Retry(retry=3, wait=0.01)
359
+
360
+ call_count = 0
361
+
362
+ def original_func():
363
+ nonlocal call_count
364
+ call_count += 1
365
+ if call_count < 2:
366
+ raise ValueError("Fail")
367
+ return "success"
368
+
369
+ wrapped_func = retry(original_func)
370
+ result = wrapped_func()
371
+
372
+ assert result == "success"
373
+ assert call_count == 2
374
+
375
+
376
+ # ==================== String Representation Tests ====================
377
+
378
+
379
+ def test_retry_str():
380
+ """Test __str__ returns retry count"""
381
+ retry = Retry(retry=5)
382
+
383
+ assert str(retry) == "5"
384
+
385
+
386
+ def test_retry_str_zero():
387
+ """Test __str__ with zero retry"""
388
+ retry = Retry(retry=0)
389
+
390
+ assert str(retry) == "0"
391
+
392
+
393
+ def test_retry_repr():
394
+ """Test __repr__ returns detailed representation"""
395
+ retry = Retry(retry=3, wait=1.5, rate=2.0)
396
+
397
+ repr_str = repr(retry)
398
+
399
+ assert "Retry" in repr_str
400
+ assert "retry=3" in repr_str
401
+ assert "wait=1.5" in repr_str
402
+ assert "rate=2.0" in repr_str
403
+ assert "exceptions=" in repr_str
404
+
405
+
406
+ def test_retry_repr_custom_exceptions():
407
+ """Test __repr__ with custom exceptions"""
408
+ retry = Retry(retry=3, exceptions=[ValueError, TypeError])
409
+
410
+ repr_str = repr(retry)
411
+
412
+ assert "ValueError" in repr_str
413
+ assert "TypeError" in repr_str
414
+
415
+
416
+ # ==================== To Dict / Describe Tests ====================
417
+
418
+
419
+ def test_retry_to_dict():
420
+ """Test to_dict returns configuration dictionary"""
421
+ retry = Retry(retry=5, wait=2.0, rate=1.5)
422
+
423
+ result = retry.to_dict()
424
+
425
+ assert isinstance(result, dict)
426
+ assert result["retry"] == 5
427
+ assert result["wait"] == 2.0
428
+ assert result["rate"] == 1.5
429
+ assert "exceptions" in result
430
+
431
+
432
+ def test_retry_to_dict_exception_names():
433
+ """Test to_dict returns exception names as strings"""
434
+ retry = Retry(retry=3, exceptions=[ValueError, TypeError, ConnectionError])
435
+
436
+ result = retry.to_dict()
437
+
438
+ assert result["exceptions"] == ["ValueError", "TypeError", "ConnectionError"]
439
+
440
+
441
+ def test_retry_describe():
442
+ """Test describe returns same as to_dict"""
443
+ retry = Retry(retry=3, wait=1.0, rate=2.0)
444
+
445
+ describe_result = retry.describe()
446
+ to_dict_result = retry.to_dict()
447
+
448
+ assert describe_result == to_dict_result
449
+
450
+
451
+ def test_retry_to_dict_default_exception():
452
+ """Test to_dict with default Exception"""
453
+ retry = Retry()
454
+
455
+ result = retry.to_dict()
456
+
457
+ assert result["exceptions"] == ["Exception"]
458
+
459
+
460
+ # ==================== Integration Tests ====================
461
+
462
+
463
+ def test_retry_real_world_api_call():
464
+ """Test retry with simulated API call scenario"""
465
+ retry = Retry(retry=4, wait=0.01, rate=2.0, exceptions=[ConnectionError, TimeoutError])
466
+
467
+ call_count = 0
468
+ errors = []
469
+
470
+ @retry
471
+ def api_call(endpoint):
472
+ nonlocal call_count
473
+ call_count += 1
474
+
475
+ if call_count == 1:
476
+ errors.append("connection_error")
477
+ raise ConnectionError("Network unreachable")
478
+ elif call_count == 2:
479
+ errors.append("timeout")
480
+ raise TimeoutError("Request timeout")
481
+ else:
482
+ return f"Data from {endpoint}"
483
+
484
+ result = api_call("/api/data")
485
+
486
+ assert result == "Data from /api/data"
487
+ assert call_count == 3
488
+ assert len(errors) == 2
489
+
490
+
491
+ def test_retry_database_connection():
492
+ """Test retry with simulated database connection scenario"""
493
+ retry = Retry(retry=3, wait=0.01, exceptions=[ConnectionError])
494
+
495
+ attempts = []
496
+
497
+ @retry
498
+ def connect_db(host, port):
499
+ attempts.append({"host": host, "port": port})
500
+ if len(attempts) < 3:
501
+ raise ConnectionError("Connection refused")
502
+ return f"Connected to {host}:{port}"
503
+
504
+ result = connect_db("localhost", 5432)
505
+
506
+ assert result == "Connected to localhost:5432"
507
+ assert len(attempts) == 3
508
+
509
+
510
+ def test_retry_with_cleanup():
511
+ """Test retry preserves function behavior with cleanup logic"""
512
+ retry = Retry(retry=3, wait=0.01)
513
+
514
+ resources = []
515
+
516
+ @retry
517
+ def process_with_cleanup():
518
+ resources.append("acquired")
519
+ try:
520
+ if len(resources) < 2:
521
+ raise ValueError("Processing failed")
522
+ return "success"
523
+ finally:
524
+ # Cleanup happens regardless
525
+ resources.append("released")
526
+
527
+ result = process_with_cleanup()
528
+
529
+ assert result == "success"
530
+ # Should have acquired and released for each attempt
531
+ assert len(resources) == 4 # 2 attempts * (acquire + release)
532
+
533
+
534
+ def test_retry_timing_constant():
535
+ """Test retry timing with constant interval"""
536
+ retry = Retry(retry=3, wait=0.05, rate=1.0, jitter=False)
537
+
538
+ call_times = []
539
+
540
+ @retry
541
+ def timed_func():
542
+ call_times.append(time.time())
543
+ if len(call_times) < 3:
544
+ raise ValueError("Not yet")
545
+ return "done"
546
+
547
+ result = timed_func()
548
+
549
+ assert result == "done"
550
+ assert len(call_times) == 3
551
+
552
+ # Check intervals are approximately constant
553
+ interval1 = call_times[1] - call_times[0]
554
+ interval2 = call_times[2] - call_times[1]
555
+
556
+ assert 0.04 < interval1 < 0.15 # Allow some tolerance
557
+ assert 0.04 < interval2 < 0.15
558
+
559
+
560
+ def test_retry_max_retries_exhausted():
561
+ """Test behavior when max retries are exhausted"""
562
+ retry = Retry(retry=3, wait=0.01)
563
+
564
+ call_count = 0
565
+
566
+ @retry
567
+ def always_fails():
568
+ nonlocal call_count
569
+ call_count += 1
570
+ raise ValueError(f"Attempt {call_count}")
571
+
572
+ with pytest.raises(ValueError, match="Attempt 3"):
573
+ always_fails()
574
+
575
+ assert call_count == 3
576
+
577
+
578
+ def test_retry_with_return_values():
579
+ """Test retry with different return values"""
580
+ retry = Retry(retry=3, wait=0.01)
581
+
582
+ results = []
583
+
584
+ @retry
585
+ def generate_result():
586
+ results.append(len(results) + 1)
587
+ if len(results) < 2:
588
+ raise ValueError("Not ready")
589
+ return {"attempt": results[-1], "data": "success"}
590
+
591
+ result = generate_result()
592
+
593
+ assert result == {"attempt": 2, "data": "success"}
594
+ assert len(results) == 2
595
+
596
+
597
+ def test_retry_class_method():
598
+ """Test retry works with class methods"""
599
+
600
+ class Service:
601
+ def __init__(self):
602
+ self.attempts = 0
603
+
604
+ @Retry(retry=3, wait=0.01)
605
+ def fetch(self):
606
+ self.attempts += 1
607
+ if self.attempts < 2:
608
+ raise ConnectionError("Failed")
609
+ return "data"
610
+
611
+ service = Service()
612
+ result = service.fetch()
613
+
614
+ assert result == "data"
615
+ assert service.attempts == 2
616
+
617
+
618
+ def test_retry_static_method():
619
+ """Test retry works with static methods"""
620
+
621
+ call_count = 0
622
+
623
+ class Utils:
624
+ @staticmethod
625
+ @Retry(retry=3, wait=0.01)
626
+ def process():
627
+ nonlocal call_count
628
+ call_count += 1
629
+ if call_count < 2:
630
+ raise ValueError("Fail")
631
+ return "processed"
632
+
633
+ result = Utils.process()
634
+
635
+ assert result == "processed"
636
+ assert call_count == 2
637
+
638
+
639
+ def test_retry_multiple_decorators():
640
+ """Test retry can be combined with other decorators"""
641
+ retry = Retry(retry=3, wait=0.01)
642
+
643
+ call_log = []
644
+ attempt_count = 0
645
+
646
+ def log_calls(func):
647
+ def wrapper(*args, **kwargs):
648
+ call_log.append("outer_called")
649
+ return func(*args, **kwargs)
650
+
651
+ return wrapper
652
+
653
+ @log_calls
654
+ @retry
655
+ def decorated_func():
656
+ nonlocal attempt_count
657
+ attempt_count += 1
658
+ if attempt_count < 2:
659
+ raise ValueError("Fail")
660
+ return "success"
661
+
662
+ result = decorated_func()
663
+
664
+ assert result == "success"
665
+ # Log should be called once (outer decorator)
666
+ # but retry happens inside with 2 attempts
667
+ assert len(call_log) == 1
668
+ assert attempt_count == 2
669
+
670
+
671
+ def test_retry_different_exception_types():
672
+ """Test retry behavior with different exception types in sequence"""
673
+ retry = Retry(retry=4, wait=0.01, exceptions=[ValueError, TypeError, KeyError])
674
+
675
+ call_count = 0
676
+
677
+ @retry
678
+ def multi_exception_func():
679
+ nonlocal call_count
680
+ call_count += 1
681
+
682
+ if call_count == 1:
683
+ raise ValueError("Error 1")
684
+ elif call_count == 2:
685
+ raise TypeError("Error 2")
686
+ elif call_count == 3:
687
+ raise KeyError("Error 3")
688
+ return "finally succeeded"
689
+
690
+ result = multi_exception_func()
691
+
692
+ assert result == "finally succeeded"
693
+ assert call_count == 4
694
+
695
+
696
+ def test_retry_configuration_immutability():
697
+ """Test retry configuration doesn't change after decoration"""
698
+ retry = Retry(retry=3, wait=0.5, rate=2.0)
699
+
700
+ @retry
701
+ def some_func():
702
+ return "result"
703
+
704
+ # Configuration should remain unchanged
705
+ assert retry.retry == 3
706
+ assert retry.wait == 0.5
707
+ assert retry.rate == 2.0
708
+
709
+ # Calling function shouldn't affect configuration
710
+ some_func()
711
+
712
+ assert retry.retry == 3
713
+ assert retry.wait == 0.5
714
+ assert retry.rate == 2.0