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,482 @@
1
+ """
2
+ Test suite for JobLike Protocol
3
+ Tests cover protocol definition, compliance checking, and structural typing
4
+ """
5
+
6
+ from typing import Any, Dict
7
+
8
+ from xfintech.data.job.joblike import JobLike
9
+
10
+ # ============================================================================
11
+ # Helper Classes for Testing
12
+ # ============================================================================
13
+
14
+
15
+ class CompliantJob:
16
+ """Class that implements JobLike protocol"""
17
+
18
+ def run(self) -> Any:
19
+ return self._run()
20
+
21
+ def _run(self) -> Any:
22
+ return {"result": "success"}
23
+
24
+ def describe(self) -> Dict[str, Any]:
25
+ return {"name": "CompliantJob", "type": "test"}
26
+
27
+ def to_dict(self) -> Dict[str, Any]:
28
+ return {"name": "CompliantJob", "status": "active"}
29
+
30
+
31
+ class PartialJob:
32
+ """Class that partially implements JobLike protocol"""
33
+
34
+ def run(self) -> Any:
35
+ return "result"
36
+
37
+ def _run(self) -> Any:
38
+ return "internal"
39
+
40
+ def describe(self) -> Dict[str, Any]:
41
+ return {"name": "PartialJob"}
42
+
43
+ # Missing to_dict method
44
+
45
+
46
+ class NonCompliantJob:
47
+ """Class that doesn't implement JobLike protocol"""
48
+
49
+ def execute(self) -> Any:
50
+ return "result"
51
+
52
+
53
+ class MinimalJob:
54
+ """Minimal implementation of JobLike"""
55
+
56
+ def run(self):
57
+ return None
58
+
59
+ def _run(self):
60
+ return None
61
+
62
+ def describe(self):
63
+ return {}
64
+
65
+ def to_dict(self):
66
+ return {}
67
+
68
+
69
+ # ============================================================================
70
+ # Protocol Compliance Tests
71
+ # ============================================================================
72
+
73
+
74
+ def test_joblike_compliant_job():
75
+ """Test that compliant class is recognized as JobLike"""
76
+ job = CompliantJob()
77
+ assert isinstance(job, JobLike)
78
+
79
+
80
+ def test_joblike_partial_job():
81
+ """Test that partial implementation is not recognized as JobLike"""
82
+ job = PartialJob()
83
+ # Missing to_dict, so should not be fully compliant
84
+ # Note: runtime_checkable only checks for method existence
85
+ assert isinstance(job, JobLike) is False
86
+
87
+
88
+ def test_joblike_non_compliant_job():
89
+ """Test that non-compliant class is not recognized as JobLike"""
90
+ job = NonCompliantJob()
91
+ assert isinstance(job, JobLike) is False
92
+
93
+
94
+ def test_joblike_minimal_job():
95
+ """Test minimal implementation is recognized as JobLike"""
96
+ job = MinimalJob()
97
+ assert isinstance(job, JobLike)
98
+
99
+
100
+ # ============================================================================
101
+ # Protocol Method Tests
102
+ # ============================================================================
103
+
104
+
105
+ def test_joblike_has_run_method():
106
+ """Test JobLike protocol requires run method"""
107
+ job = CompliantJob()
108
+ assert hasattr(job, "run")
109
+ assert callable(job.run)
110
+
111
+
112
+ def test_joblike_has_private_run_method():
113
+ """Test JobLike protocol requires _run method"""
114
+ job = CompliantJob()
115
+ assert hasattr(job, "_run")
116
+ assert callable(job._run)
117
+
118
+
119
+ def test_joblike_has_describe_method():
120
+ """Test JobLike protocol requires describe method"""
121
+ job = CompliantJob()
122
+ assert hasattr(job, "describe")
123
+ assert callable(job.describe)
124
+
125
+
126
+ def test_joblike_has_to_dict_method():
127
+ """Test JobLike protocol requires to_dict method"""
128
+ job = CompliantJob()
129
+ assert hasattr(job, "to_dict")
130
+ assert callable(job.to_dict)
131
+
132
+
133
+ # ============================================================================
134
+ # Method Signature Tests
135
+ # ============================================================================
136
+
137
+
138
+ def test_joblike_run_returns_any():
139
+ """Test run method can return any type"""
140
+ job = CompliantJob()
141
+ result = job.run()
142
+ assert result is not None
143
+
144
+
145
+ def test_joblike_private_run_returns_any():
146
+ """Test _run method can return any type"""
147
+ job = CompliantJob()
148
+ result = job._run()
149
+ assert result == {"result": "success"}
150
+
151
+
152
+ def test_joblike_describe_returns_dict():
153
+ """Test describe method returns dict"""
154
+ job = CompliantJob()
155
+ result = job.describe()
156
+ assert isinstance(result, dict)
157
+ assert "name" in result
158
+
159
+
160
+ def test_joblike_to_dict_returns_dict():
161
+ """Test to_dict method returns dict"""
162
+ job = CompliantJob()
163
+ result = job.to_dict()
164
+ assert isinstance(result, dict)
165
+ assert "name" in result
166
+
167
+
168
+ # ============================================================================
169
+ # Protocol Usage Tests
170
+ # ============================================================================
171
+
172
+
173
+ def test_joblike_as_type_annotation():
174
+ """Test JobLike can be used as type annotation"""
175
+
176
+ def process_job(job: JobLike) -> Dict[str, Any]:
177
+ return job.describe()
178
+
179
+ job = CompliantJob()
180
+ result = process_job(job)
181
+
182
+ assert isinstance(result, dict)
183
+ assert result["name"] == "CompliantJob"
184
+
185
+
186
+ def test_joblike_type_checking():
187
+ """Test JobLike enables structural type checking"""
188
+
189
+ def execute_job(job: JobLike) -> Any:
190
+ return job.run()
191
+
192
+ compliant = CompliantJob()
193
+ result = execute_job(compliant)
194
+
195
+ assert result == {"result": "success"}
196
+
197
+
198
+ def test_joblike_duck_typing():
199
+ """Test JobLike supports duck typing"""
200
+
201
+ class DuckTypedJob:
202
+ def run(self):
203
+ return "duck result"
204
+
205
+ def _run(self):
206
+ return "internal duck"
207
+
208
+ def describe(self):
209
+ return {"type": "duck"}
210
+
211
+ def to_dict(self):
212
+ return {"duck": True}
213
+
214
+ job = DuckTypedJob()
215
+ assert isinstance(job, JobLike)
216
+
217
+
218
+ # ============================================================================
219
+ # Protocol Instance Tests
220
+ # ============================================================================
221
+
222
+
223
+ def test_joblike_multiple_implementations():
224
+ """Test multiple classes can implement JobLike"""
225
+
226
+ class Job1:
227
+ def run(self):
228
+ return 1
229
+
230
+ def _run(self):
231
+ return 1
232
+
233
+ def describe(self):
234
+ return {}
235
+
236
+ def to_dict(self):
237
+ return {}
238
+
239
+ class Job2:
240
+ def run(self):
241
+ return 2
242
+
243
+ def _run(self):
244
+ return 2
245
+
246
+ def describe(self):
247
+ return {}
248
+
249
+ def to_dict(self):
250
+ return {}
251
+
252
+ job1 = Job1()
253
+ job2 = Job2()
254
+
255
+ assert isinstance(job1, JobLike)
256
+ assert isinstance(job2, JobLike)
257
+
258
+
259
+ def test_joblike_inheritance_not_required():
260
+ """Test classes don't need to inherit from JobLike"""
261
+
262
+ class IndependentJob:
263
+ def run(self):
264
+ return "independent"
265
+
266
+ def _run(self):
267
+ return "internal"
268
+
269
+ def describe(self):
270
+ return {"independent": True}
271
+
272
+ def to_dict(self):
273
+ return {"independent": True}
274
+
275
+ job = IndependentJob()
276
+ # Should be recognized as JobLike due to structural typing
277
+ assert isinstance(job, JobLike)
278
+
279
+
280
+ # ============================================================================
281
+ # Edge Case Tests
282
+ # ============================================================================
283
+
284
+
285
+ def test_joblike_with_extra_methods():
286
+ """Test class with extra methods is still JobLike compliant"""
287
+
288
+ class ExtendedJob:
289
+ def run(self):
290
+ return "extended"
291
+
292
+ def _run(self):
293
+ return "internal"
294
+
295
+ def describe(self):
296
+ return {}
297
+
298
+ def to_dict(self):
299
+ return {}
300
+
301
+ def extra_method(self):
302
+ return "extra"
303
+
304
+ def another_method(self):
305
+ return "another"
306
+
307
+ job = ExtendedJob()
308
+ assert isinstance(job, JobLike)
309
+ assert hasattr(job, "extra_method")
310
+
311
+
312
+ def test_joblike_with_properties():
313
+ """Test class with properties can be JobLike compliant"""
314
+
315
+ class PropertyJob:
316
+ def __init__(self):
317
+ self.name = "PropertyJob"
318
+
319
+ def run(self):
320
+ return self.name
321
+
322
+ def _run(self):
323
+ return self.name
324
+
325
+ def describe(self):
326
+ return {"name": self.name}
327
+
328
+ def to_dict(self):
329
+ return {"name": self.name}
330
+
331
+ job = PropertyJob()
332
+ assert isinstance(job, JobLike)
333
+
334
+
335
+ def test_joblike_method_with_arguments():
336
+ """Test JobLike methods can accept arguments"""
337
+
338
+ class ArgJob:
339
+ def run(self, *args, **kwargs):
340
+ return "with args"
341
+
342
+ def _run(self, *args, **kwargs):
343
+ return "internal with args"
344
+
345
+ def describe(self, verbose=False):
346
+ return {"verbose": verbose}
347
+
348
+ def to_dict(self, include_meta=True):
349
+ return {"meta": include_meta}
350
+
351
+ job = ArgJob()
352
+ assert isinstance(job, JobLike)
353
+
354
+
355
+ # ============================================================================
356
+ # Protocol Validation Tests
357
+ # ============================================================================
358
+
359
+
360
+ def test_joblike_missing_run():
361
+ """Test class missing run method is not JobLike"""
362
+
363
+ class MissingRun:
364
+ def _run(self):
365
+ return None
366
+
367
+ def describe(self):
368
+ return {}
369
+
370
+ def to_dict(self):
371
+ return {}
372
+
373
+ job = MissingRun()
374
+ assert not isinstance(job, JobLike)
375
+
376
+
377
+ def test_joblike_missing_private_run():
378
+ """Test class missing _run method is not JobLike"""
379
+
380
+ class MissingPrivateRun:
381
+ def run(self):
382
+ return None
383
+
384
+ def describe(self):
385
+ return {}
386
+
387
+ def to_dict(self):
388
+ return {}
389
+
390
+ job = MissingPrivateRun()
391
+ assert not isinstance(job, JobLike)
392
+
393
+
394
+ def test_joblike_missing_describe():
395
+ """Test class missing describe method is not JobLike"""
396
+
397
+ class MissingDescribe:
398
+ def run(self):
399
+ return None
400
+
401
+ def _run(self):
402
+ return None
403
+
404
+ def to_dict(self):
405
+ return {}
406
+
407
+ job = MissingDescribe()
408
+ assert not isinstance(job, JobLike)
409
+
410
+
411
+ def test_joblike_missing_to_dict():
412
+ """Test class missing to_dict method is not JobLike"""
413
+
414
+ class MissingToDict:
415
+ def run(self):
416
+ return None
417
+
418
+ def _run(self):
419
+ return None
420
+
421
+ def describe(self):
422
+ return {}
423
+
424
+ job = MissingToDict()
425
+ assert not isinstance(job, JobLike)
426
+
427
+
428
+ # ============================================================================
429
+ # Integration Tests
430
+ # ============================================================================
431
+
432
+
433
+ def test_joblike_function_parameter():
434
+ """Test JobLike as function parameter type"""
435
+
436
+ def execute_and_describe(job: JobLike) -> tuple:
437
+ result = job.run()
438
+ description = job.describe()
439
+ return result, description
440
+
441
+ job = CompliantJob()
442
+ result, desc = execute_and_describe(job)
443
+
444
+ assert result == {"result": "success"}
445
+ assert desc["name"] == "CompliantJob"
446
+
447
+
448
+ def test_joblike_list_of_jobs():
449
+ """Test list of JobLike objects"""
450
+ jobs = [CompliantJob(), MinimalJob()]
451
+
452
+ for job in jobs:
453
+ assert isinstance(job, JobLike)
454
+ assert callable(job.run)
455
+
456
+
457
+ def test_joblike_runtime_check():
458
+ """Test runtime_checkable allows isinstance checks"""
459
+
460
+ class RuntimeJob:
461
+ def run(self):
462
+ return "runtime"
463
+
464
+ def _run(self):
465
+ return "internal"
466
+
467
+ def describe(self):
468
+ return {"runtime": True}
469
+
470
+ def to_dict(self):
471
+ return {"runtime": True}
472
+
473
+ job = RuntimeJob()
474
+
475
+ # This works because Protocol is runtime_checkable
476
+ assert isinstance(job, JobLike)
477
+
478
+
479
+ def test_joblike_protocol_documentation():
480
+ """Test JobLike protocol has documentation"""
481
+ assert JobLike.__doc__ is not None
482
+ assert "描述" in JobLike.__doc__
@@ -0,0 +1,7 @@
1
+ from .client import RelayClient
2
+ from .clientlike import RelayClientLike
3
+
4
+ __all__ = [
5
+ "RelayClient",
6
+ "RelayClientLike",
7
+ ]
@@ -0,0 +1,114 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from typing import Any, Optional
5
+
6
+ from xfintech.data.relay.clientlike import RelayClientLike
7
+
8
+
9
+ class RelayClient(RelayClientLike):
10
+ """
11
+ 描述:
12
+ - 中继客户端基类,提供通用的中继服务器连接功能。
13
+ - 实现 URL、密钥和超时时间的验证与解析。
14
+ - 提供规范化 JSON 序列化方法,确保数据格式一致性。
15
+ - 作为具体中继客户端实现的基础类,子类需实现 call() 方法。
16
+
17
+ 属性:
18
+ - url: str, 中继服务器 URL 地址(自动移除尾部斜杠)。
19
+ - secret: str, 用于认证的密钥(自动去除首尾空格)。
20
+ - timeout: int, 请求超时时间(秒),默认 180 秒。
21
+ - DEFAULT_TIMEOUT: int = 180, 默认超时时间常量。
22
+
23
+ 方法:
24
+ - canonical_json(value): 将数据转换为规范化的 JSON 字节串(键排序、无空格)。
25
+ - call(): 抽象方法,由子类实现具体的 API 调用逻辑。
26
+
27
+ 例子:
28
+ ```python
29
+ from xfintech.data.relay.client import RelayClient
30
+
31
+ # 创建子类实现
32
+ class MyRelayClient(RelayClient):
33
+ def call(self):
34
+ # 实现具体的调用逻辑
35
+ return {"result": "success"}
36
+
37
+ # 使用客户端
38
+ client = MyRelayClient(
39
+ url="https://relay.example.com",
40
+ secret="my-secret-key",
41
+ timeout=120
42
+ )
43
+
44
+ # URL 自动处理尾部斜杠
45
+ print(client.url) # 输出: https://relay.example.com
46
+
47
+ # 规范化 JSON 序列化
48
+ data = {"name": "test", "value": 42}
49
+ json_bytes = client.canonical_json(data)
50
+ print(json_bytes) # 输出: b'{"name":"test","value":42}'
51
+ ```
52
+ """
53
+
54
+ DEFAULT_TIMEOUT = 180
55
+
56
+ def __init__(
57
+ self,
58
+ url: str,
59
+ secret: str,
60
+ timeout: Optional[int] = None,
61
+ **kwargs: Any,
62
+ ) -> None:
63
+ self.url = self._resolve_url(url)
64
+ self.secret = self._resolve_secret(secret)
65
+ self.timeout = self._resolve_timeout(timeout)
66
+
67
+ def _resolve_url(
68
+ self,
69
+ url: str,
70
+ ) -> str:
71
+ if not url:
72
+ msg = "Relay URL must be provided."
73
+ raise ValueError(msg)
74
+ return url.rstrip("/")
75
+
76
+ def _resolve_secret(
77
+ self,
78
+ secret: str,
79
+ ) -> str:
80
+ if not secret:
81
+ msg = "Relay secret must be provided."
82
+ raise ValueError(msg)
83
+ secret = secret.strip()
84
+ if not secret:
85
+ msg = "Relay secret must be provided."
86
+ raise ValueError(msg)
87
+ return secret
88
+
89
+ def _resolve_timeout(
90
+ self,
91
+ timeout: Optional[int],
92
+ ) -> int:
93
+ if not timeout:
94
+ return self.DEFAULT_TIMEOUT
95
+ if not isinstance(timeout, int):
96
+ msg = "Timeout must be an integer."
97
+ raise ValueError(msg)
98
+ if timeout <= 0:
99
+ return self.DEFAULT_TIMEOUT
100
+ return timeout
101
+
102
+ def canonical_json(
103
+ self,
104
+ value: Any,
105
+ ) -> bytes:
106
+ return json.dumps(
107
+ value,
108
+ ensure_ascii=False,
109
+ separators=(",", ":"),
110
+ sort_keys=True,
111
+ ).encode("utf-8")
112
+
113
+ def call(self) -> Any:
114
+ raise NotImplementedError()
@@ -0,0 +1,45 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Protocol, runtime_checkable
4
+
5
+
6
+ @runtime_checkable
7
+ class RelayClientLike(Protocol):
8
+ """
9
+ 描述:
10
+ - 中继客户端协议接口,定义中继客户端必须实现的核心属性和方法。
11
+ - 使用 Protocol 实现结构化类型检查(structural typing)。
12
+ - 任何实现这些属性和方法的类都会被视为符合 RelayClientLike 协议。
13
+
14
+ 属性:
15
+ - url: str, 中继服务器 URL 地址。
16
+ - secret: str, 用于认证的密钥。
17
+ - timeout: int, 请求超时时间(秒)。
18
+
19
+ 方法:
20
+ - call(): 执行 API 调用并返回结果。
21
+
22
+ 例子:
23
+ ```python
24
+ from xfintech.data.relay.clientlike import RelayClientLike
25
+
26
+ # 任何实现这些属性和方法的类都符合 RelayClientLike 协议
27
+ class MyClient:
28
+ def __init__(self):
29
+ self.url = "https://api.example.com"
30
+ self.secret = "my-secret"
31
+ self.timeout = 180
32
+
33
+ def call(self):
34
+ return {"result": "success"}
35
+
36
+ # 类型检查会通过
37
+ client: RelayClientLike = MyClient()
38
+ ```
39
+ """
40
+
41
+ url: str
42
+ secret: str
43
+ timeout: int
44
+
45
+ def call(self) -> Any: ...