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,635 @@
1
+ from datetime import datetime
2
+ from unittest.mock import MagicMock, patch
3
+
4
+ import pandas as pd
5
+ import pytest
6
+
7
+ from xfintech.data.common.cache import Cache
8
+ from xfintech.data.common.params import Params
9
+ from xfintech.data.source.tushare.session.session import Session
10
+ from xfintech.data.source.tushare.stock.companyprofit.companyprofit import CompanyProfit
11
+ from xfintech.data.source.tushare.stock.companyprofit.constant import (
12
+ KEY,
13
+ NAME,
14
+ PAGINATE,
15
+ SOURCE,
16
+ TARGET,
17
+ )
18
+
19
+ # ============================================================================
20
+ # Fixtures
21
+ # ============================================================================
22
+
23
+
24
+ @pytest.fixture
25
+ def mock_session():
26
+ """Create a mock Tushare session"""
27
+ session = MagicMock(spec=Session)
28
+ session._credential = "test_token"
29
+ session.id = "test1234"
30
+ session.mode = "direct"
31
+ session.relay_url = None
32
+ session.relay_secret = None
33
+ session.connected = True
34
+
35
+ # Mock the connection object
36
+ mock_connection = MagicMock()
37
+ mock_connection.income_vip = MagicMock()
38
+ session.connection = mock_connection
39
+
40
+ return session
41
+
42
+
43
+ @pytest.fixture
44
+ def sample_source_data():
45
+ """Create sample source data in Tushare format"""
46
+ return pd.DataFrame(
47
+ {
48
+ "ts_code": ["000001.SZ", "000001.SZ", "000001.SZ"],
49
+ "ann_date": ["20200425", "20200826", "20201030"],
50
+ "f_ann_date": ["20200425", "20200826", "20201030"],
51
+ "end_date": ["20200331", "20200630", "20200930"],
52
+ "report_type": ["1", "1", "1"],
53
+ "comp_type": ["1", "1", "1"],
54
+ "end_type": ["12", "12", "12"],
55
+ "update_flag": ["0", "0", "0"],
56
+ "basic_eps": [0.5, 1.2, 1.8],
57
+ "diluted_eps": [0.5, 1.2, 1.8],
58
+ "total_revenue": [100000000.0, 250000000.0, 380000000.0],
59
+ "revenue": [98000000.0, 245000000.0, 372000000.0],
60
+ "n_income": [12000000.0, 32000000.0, 48000000.0],
61
+ "n_income_attr_p": [11500000.0, 31000000.0, 46000000.0],
62
+ "total_profit": [15000000.0, 39000000.0, 58000000.0],
63
+ "income_tax": [3000000.0, 7000000.0, 10000000.0],
64
+ }
65
+ )
66
+
67
+
68
+ @pytest.fixture
69
+ def expected_transformed_data():
70
+ """Create expected transformed data"""
71
+ return pd.DataFrame(
72
+ {
73
+ "code": ["000001.SZ", "000001.SZ", "000001.SZ"],
74
+ "date": ["2020-03-31", "2020-06-30", "2020-09-30"],
75
+ "datecode": ["20200331", "20200630", "20200930"],
76
+ "ann_date": ["2020-04-25", "2020-08-26", "2020-10-30"],
77
+ "f_ann_date": ["2020-04-25", "2020-08-26", "2020-10-30"],
78
+ "end_date": ["2020-03-31", "2020-06-30", "2020-09-30"],
79
+ "report_type": ["1", "1", "1"],
80
+ "comp_type": ["1", "1", "1"],
81
+ "end_type": ["12", "12", "12"],
82
+ "update_flag": ["0", "0", "0"],
83
+ "basic_eps": [0.5, 1.2, 1.8],
84
+ "diluted_eps": [0.5, 1.2, 1.8],
85
+ "total_revenue": [100000000.0, 250000000.0, 380000000.0],
86
+ "revenue": [98000000.0, 245000000.0, 372000000.0],
87
+ "n_income": [12000000.0, 32000000.0, 48000000.0],
88
+ "n_income_attr_p": [11500000.0, 31000000.0, 46000000.0],
89
+ "total_profit": [15000000.0, 39000000.0, 58000000.0],
90
+ "income_tax": [3000000.0, 7000000.0, 10000000.0],
91
+ }
92
+ )
93
+
94
+
95
+ # ============================================================================
96
+ # Initialization Tests
97
+ # ============================================================================
98
+
99
+
100
+ def test_companyprofit_init_basic(mock_session):
101
+ """Test basic initialization with required session"""
102
+ profit = CompanyProfit(session=mock_session)
103
+ assert profit.name == NAME
104
+ assert profit.key == KEY
105
+ assert profit.source == SOURCE
106
+ assert profit.target == TARGET
107
+ assert profit.paginate == PAGINATE
108
+
109
+
110
+ def test_companyprofit_init_with_params_dict(mock_session):
111
+ """Test initialization with params as dict"""
112
+ params = {"ts_code": "000001.SZ", "start_date": "20200101", "end_date": "20201231"}
113
+ profit = CompanyProfit(session=mock_session, params=params)
114
+ assert profit.params.get("ts_code") == "000001.SZ"
115
+ assert profit.params.get("start_date") == "20200101"
116
+ assert profit.params.get("end_date") == "20201231"
117
+
118
+
119
+ def test_companyprofit_init_with_params_object(mock_session):
120
+ """Test initialization with params as Params object"""
121
+ params = Params(ts_code="000001.SZ", start_date="20200101")
122
+ profit = CompanyProfit(session=mock_session, params=params)
123
+ assert profit.params.get("ts_code") == "000001.SZ"
124
+
125
+
126
+ def test_companyprofit_init_with_year_param(mock_session):
127
+ """Test initialization with year parameter"""
128
+ params = {"ts_code": "000001.SZ", "year": "2020"}
129
+ profit = CompanyProfit(session=mock_session, params=params)
130
+ assert profit.params.get("year") == "2020"
131
+
132
+
133
+ def test_companyprofit_init_with_cache_bool_true(mock_session):
134
+ """Test initialization with cache as boolean True"""
135
+ profit = CompanyProfit(session=mock_session, cache=True)
136
+ assert profit.cache is not None
137
+ assert isinstance(profit.cache, Cache)
138
+
139
+
140
+ def test_companyprofit_init_with_cache_bool_false(mock_session):
141
+ """Test initialization with cache as boolean False"""
142
+ profit = CompanyProfit(session=mock_session, cache=False)
143
+ assert profit.cache is None
144
+
145
+
146
+ def test_companyprofit_init_with_cache_dict(mock_session):
147
+ """Test initialization with cache as dict"""
148
+ cache_config = {"dir": "/tmp/test_cache", "ttl": 3600}
149
+ profit = CompanyProfit(session=mock_session, cache=cache_config)
150
+ assert profit.cache is not None
151
+ assert isinstance(profit.cache, Cache)
152
+
153
+
154
+ def test_companyprofit_init_default_paginate_limit(mock_session):
155
+ """Test that default paginate settings are correctly set"""
156
+ profit = CompanyProfit(session=mock_session)
157
+ assert profit.paginate.pagesize == 1000
158
+ assert profit.paginate.pagelimit == 1000
159
+
160
+
161
+ def test_companyprofit_init_with_all_params(mock_session):
162
+ """Test initialization with all parameters"""
163
+ params = {"ts_code": "000001.SZ", "start_date": "20200101", "end_date": "20201231"}
164
+ coolant = {"interval": 0.5}
165
+ retry = {"max_attempts": 3}
166
+ cache = True
167
+
168
+ profit = CompanyProfit(session=mock_session, params=params, coolant=coolant, retry=retry, cache=cache)
169
+ assert profit.params.get("ts_code") == "000001.SZ"
170
+ assert profit.cache is not None
171
+
172
+
173
+ # ============================================================================
174
+ # Transform Tests
175
+ # ============================================================================
176
+
177
+
178
+ def test_companyprofit_transform_basic(mock_session, sample_source_data):
179
+ """Test basic transformation of source data"""
180
+ profit = CompanyProfit(session=mock_session)
181
+ result = profit.transform(sample_source_data)
182
+
183
+ assert not result.empty
184
+ assert len(result) == 3
185
+ assert "code" in result.columns
186
+ assert "date" in result.columns
187
+ assert "datecode" in result.columns
188
+
189
+
190
+ def test_companyprofit_transform_code_mapping(mock_session, sample_source_data):
191
+ """Test that ts_code is correctly mapped to code"""
192
+ profit = CompanyProfit(session=mock_session)
193
+ result = profit.transform(sample_source_data)
194
+
195
+ assert all(result["code"] == ["000001.SZ", "000001.SZ", "000001.SZ"])
196
+
197
+
198
+ def test_companyprofit_transform_date_format(mock_session, sample_source_data):
199
+ """Test that dates are formatted correctly (YYYY-MM-DD)"""
200
+ profit = CompanyProfit(session=mock_session)
201
+ result = profit.transform(sample_source_data)
202
+
203
+ # Check date format
204
+ assert result["date"].iloc[0] == "2020-03-31"
205
+ assert result["ann_date"].iloc[0] == "2020-04-25"
206
+ assert result["f_ann_date"].iloc[0] == "2020-04-25"
207
+ assert result["end_date"].iloc[0] == "2020-03-31"
208
+
209
+
210
+ def test_companyprofit_transform_datecode_preserved(mock_session, sample_source_data):
211
+ """Test that datecode is preserved in YYYYMMDD format"""
212
+ profit = CompanyProfit(session=mock_session)
213
+ result = profit.transform(sample_source_data)
214
+
215
+ assert result["datecode"].iloc[0] == "20200331"
216
+ assert result["datecode"].iloc[1] == "20200630"
217
+ assert result["datecode"].iloc[2] == "20200930"
218
+
219
+
220
+ def test_companyprofit_transform_numeric_conversions(mock_session, sample_source_data):
221
+ """Test that numeric fields are properly converted"""
222
+ profit = CompanyProfit(session=mock_session)
223
+ result = profit.transform(sample_source_data)
224
+
225
+ # Check numeric fields
226
+ assert result["basic_eps"].dtype == "float64"
227
+ assert result["diluted_eps"].dtype == "float64"
228
+ assert result["total_revenue"].dtype == "float64"
229
+ assert result["n_income"].dtype == "float64"
230
+
231
+
232
+ def test_companyprofit_transform_string_fields(mock_session, sample_source_data):
233
+ """Test that string fields are properly converted"""
234
+ profit = CompanyProfit(session=mock_session)
235
+ result = profit.transform(sample_source_data)
236
+
237
+ assert result["report_type"].dtype == "object"
238
+ assert result["comp_type"].dtype == "object"
239
+ assert result["end_type"].dtype == "object"
240
+ assert result["update_flag"].dtype == "object"
241
+
242
+
243
+ def test_companyprofit_transform_empty_dataframe(mock_session):
244
+ """Test transform with empty DataFrame"""
245
+ profit = CompanyProfit(session=mock_session)
246
+ empty_df = pd.DataFrame()
247
+ result = profit.transform(empty_df)
248
+
249
+ assert result.empty
250
+ assert len(result.columns) == len(TARGET.list_column_names())
251
+
252
+
253
+ def test_companyprofit_transform_none_input(mock_session):
254
+ """Test transform with None input"""
255
+ profit = CompanyProfit(session=mock_session)
256
+ result = profit.transform(None)
257
+
258
+ assert result.empty
259
+ assert len(result.columns) == len(TARGET.list_column_names())
260
+
261
+
262
+ def test_companyprofit_transform_handles_invalid_dates(mock_session):
263
+ """Test transform handles invalid dates gracefully"""
264
+ profit = CompanyProfit(session=mock_session)
265
+ data = pd.DataFrame(
266
+ {
267
+ "ts_code": ["000001.SZ"],
268
+ "end_date": ["invalid"],
269
+ "ann_date": ["20200101"],
270
+ "f_ann_date": ["20200101"],
271
+ "report_type": ["1"],
272
+ "comp_type": ["1"],
273
+ "end_type": ["12"],
274
+ "update_flag": ["0"],
275
+ }
276
+ )
277
+ result = profit.transform(data)
278
+
279
+ assert not result.empty
280
+ assert pd.isna(result["date"].iloc[0]) or result["date"].iloc[0] == "NaT"
281
+
282
+
283
+ def test_companyprofit_transform_removes_duplicates(mock_session):
284
+ """Test that duplicates are removed"""
285
+ profit = CompanyProfit(session=mock_session)
286
+ data = pd.DataFrame(
287
+ {
288
+ "ts_code": ["000001.SZ", "000001.SZ"],
289
+ "end_date": ["20200331", "20200331"],
290
+ "ann_date": ["20200425", "20200425"],
291
+ "f_ann_date": ["20200425", "20200425"],
292
+ "report_type": ["1", "1"],
293
+ "comp_type": ["1", "1"],
294
+ "end_type": ["12", "12"],
295
+ "update_flag": ["0", "0"],
296
+ "basic_eps": [0.5, 0.5],
297
+ }
298
+ )
299
+ result = profit.transform(data)
300
+
301
+ assert len(result) == 1
302
+
303
+
304
+ def test_companyprofit_transform_sorts_by_code_and_date(mock_session):
305
+ """Test that output is sorted by code and date"""
306
+ profit = CompanyProfit(session=mock_session)
307
+ data = pd.DataFrame(
308
+ {
309
+ "ts_code": ["000002.SZ", "000001.SZ", "000001.SZ"],
310
+ "end_date": ["20200331", "20200630", "20200331"],
311
+ "ann_date": ["20200425", "20200826", "20200425"],
312
+ "f_ann_date": ["20200425", "20200826", "20200425"],
313
+ "report_type": ["1", "1", "1"],
314
+ "comp_type": ["1", "1", "1"],
315
+ "end_type": ["12", "12", "12"],
316
+ "update_flag": ["0", "0", "0"],
317
+ }
318
+ )
319
+ result = profit.transform(data)
320
+
321
+ assert result["code"].iloc[0] == "000001.SZ"
322
+ assert result["code"].iloc[1] == "000001.SZ"
323
+ assert result["code"].iloc[2] == "000002.SZ"
324
+ assert result["date"].iloc[0] == "2020-03-31"
325
+ assert result["date"].iloc[1] == "2020-06-30"
326
+
327
+
328
+ def test_companyprofit_transform_resets_index(mock_session, sample_source_data):
329
+ """Test that index is reset after transformation"""
330
+ profit = CompanyProfit(session=mock_session)
331
+ result = profit.transform(sample_source_data)
332
+
333
+ assert list(result.index) == [0, 1, 2]
334
+
335
+
336
+ def test_companyprofit_transform_only_target_columns(mock_session, sample_source_data):
337
+ """Test that only target columns are returned"""
338
+ profit = CompanyProfit(session=mock_session)
339
+
340
+ # Add extra column to source data
341
+ data_with_extra = sample_source_data.copy()
342
+ data_with_extra["extra_column"] = "test"
343
+
344
+ result = profit.transform(data_with_extra)
345
+
346
+ assert "extra_column" not in result.columns
347
+ assert "ts_code" not in result.columns # Should be renamed to "code"
348
+ assert "code" in result.columns
349
+
350
+
351
+ # ============================================================================
352
+ # Run Tests
353
+ # ============================================================================
354
+
355
+
356
+ def test_companyprofit_run_with_cache_hit(mock_session, sample_source_data):
357
+ """Test run with cache hit (no API call)"""
358
+ profit = CompanyProfit(
359
+ session=mock_session,
360
+ params={"ts_code": "000001.SZ"},
361
+ cache=True,
362
+ )
363
+
364
+ # Mock cache load to return data
365
+ with patch.object(profit, "_load_cache", return_value=sample_source_data):
366
+ result = profit.run()
367
+
368
+ assert not result.empty
369
+ mock_session.connection.income_vip.assert_not_called()
370
+
371
+
372
+ def test_companyprofit_run_basic_date_range(mock_session, sample_source_data):
373
+ """Test run with start_date and end_date"""
374
+ profit = CompanyProfit(
375
+ session=mock_session,
376
+ params={"ts_code": "000001.SZ", "start_date": "20200101", "end_date": "20201231"},
377
+ )
378
+
379
+ with patch.object(profit, "_fetchall", return_value=sample_source_data):
380
+ with patch.object(profit, "_save_cache"):
381
+ result = profit.run()
382
+
383
+ assert not result.empty
384
+ assert "code" in result.columns
385
+
386
+
387
+ def test_companyprofit_run_with_year_param(mock_session, sample_source_data):
388
+ """Test that year parameter is converted to start_date and end_date"""
389
+ profit = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ", "year": "2020"})
390
+
391
+ with patch.object(profit, "_fetchall", return_value=sample_source_data) as mock_fetch:
392
+ with patch.object(profit, "_save_cache"):
393
+ profit.run()
394
+
395
+ # Verify that year was converted to date range
396
+ call_kwargs = mock_fetch.call_args[1]
397
+ assert call_kwargs["start_date"] == "20200101"
398
+ assert call_kwargs["end_date"] == "20201231"
399
+
400
+
401
+ def test_companyprofit_run_with_year_param_int(mock_session, sample_source_data):
402
+ """Test year parameter as integer"""
403
+ profit = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ", "year": 2020})
404
+
405
+ with patch.object(profit, "_fetchall", return_value=sample_source_data) as mock_fetch:
406
+ with patch.object(profit, "_save_cache"):
407
+ profit.run()
408
+
409
+ call_kwargs = mock_fetch.call_args[1]
410
+ assert call_kwargs["start_date"] == "20200101"
411
+ assert call_kwargs["end_date"] == "20201231"
412
+
413
+
414
+ def test_companyprofit_run_with_datetime_params(mock_session, sample_source_data):
415
+ """Test run with datetime objects as parameters"""
416
+ profit = CompanyProfit(
417
+ session=mock_session,
418
+ params={
419
+ "ts_code": "000001.SZ",
420
+ "start_date": datetime(2020, 1, 1),
421
+ "end_date": datetime(2020, 12, 31),
422
+ },
423
+ )
424
+
425
+ with patch.object(profit, "_fetchall", return_value=sample_source_data) as mock_fetch:
426
+ with patch.object(profit, "_save_cache"):
427
+ profit.run()
428
+
429
+ call_kwargs = mock_fetch.call_args[1]
430
+ assert call_kwargs["start_date"] == "20200101"
431
+ assert call_kwargs["end_date"] == "20201231"
432
+
433
+
434
+ def test_companyprofit_run_adds_fields_param(mock_session, sample_source_data):
435
+ """Test that fields parameter is automatically added"""
436
+ profit = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ"})
437
+
438
+ with patch.object(profit, "_fetchall", return_value=sample_source_data) as mock_fetch:
439
+ with patch.object(profit, "_save_cache"):
440
+ profit.run()
441
+
442
+ call_kwargs = mock_fetch.call_args[1]
443
+ assert "fields" in call_kwargs
444
+ assert isinstance(call_kwargs["fields"], str)
445
+ assert "ts_code" in call_kwargs["fields"]
446
+
447
+
448
+ def test_companyprofit_run_sets_cache(mock_session, sample_source_data):
449
+ """Test that result is saved to cache"""
450
+ profit = CompanyProfit(
451
+ session=mock_session,
452
+ params={"ts_code": "000001.SZ"},
453
+ cache=True,
454
+ )
455
+
456
+ with patch.object(profit, "_fetchall", return_value=sample_source_data):
457
+ with patch.object(profit, "_save_cache") as mock_save:
458
+ profit.run()
459
+
460
+ mock_save.assert_called_once()
461
+
462
+
463
+ def test_companyprofit_run_calls_transform(mock_session, sample_source_data):
464
+ """Test that run calls transform method"""
465
+ profit = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ"})
466
+
467
+ with patch.object(profit, "_fetchall", return_value=sample_source_data):
468
+ with patch.object(profit, "transform", return_value=pd.DataFrame()) as mock_transform:
469
+ with patch.object(profit, "_save_cache"):
470
+ profit.run()
471
+
472
+ mock_transform.assert_called_once()
473
+
474
+
475
+ def test_companyprofit_run_uses_income_vip_api(mock_session, sample_source_data):
476
+ """Test that run method uses the correct API endpoint"""
477
+ profit = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ"})
478
+
479
+ with patch.object(profit, "_fetchall", return_value=sample_source_data) as mock_fetch:
480
+ with patch.object(profit, "_save_cache"):
481
+ profit.run()
482
+
483
+ # Verify the correct API method is used
484
+ assert mock_fetch.call_args[1]["api"] == mock_session.connection.income_vip
485
+
486
+
487
+ # ============================================================================
488
+ # Integration Tests
489
+ # ============================================================================
490
+
491
+
492
+ def test_companyprofit_full_workflow(mock_session, sample_source_data):
493
+ """Test complete workflow from initialization to result"""
494
+ profit = CompanyProfit(
495
+ session=mock_session,
496
+ params={"ts_code": "000001.SZ", "start_date": "20200101", "end_date": "20201231"},
497
+ cache=True,
498
+ )
499
+
500
+ with patch.object(profit, "_fetchall", return_value=sample_source_data):
501
+ with patch.object(profit, "_save_cache"):
502
+ result = profit.run()
503
+
504
+ assert not result.empty
505
+ assert "code" in result.columns
506
+ assert "date" in result.columns
507
+ assert "datecode" in result.columns
508
+ assert all(result["code"] == "000001.SZ")
509
+
510
+
511
+ def test_companyprofit_cache_persistence(mock_session, sample_source_data):
512
+ """Test that cache persists across runs"""
513
+ profit = CompanyProfit(
514
+ session=mock_session,
515
+ params={"ts_code": "000001.SZ"},
516
+ cache=True,
517
+ )
518
+
519
+ # First run - should fetch data
520
+ with patch.object(profit, "_fetchall", return_value=sample_source_data) as mock_fetch:
521
+ with patch.object(profit, "_load_cache", return_value=None):
522
+ with patch.object(profit, "_save_cache"):
523
+ first_result = profit.run()
524
+ assert mock_fetch.called
525
+
526
+ # Second run - should use cache
527
+ with patch.object(profit, "_fetchall", return_value=sample_source_data) as mock_fetch:
528
+ with patch.object(profit, "_load_cache", return_value=first_result):
529
+ profit.run()
530
+ assert not mock_fetch.called
531
+
532
+
533
+ def test_companyprofit_params_identifier_uniqueness(mock_session):
534
+ """Test that different params create different cache identifiers"""
535
+ profit1 = CompanyProfit(
536
+ session=mock_session,
537
+ params={"ts_code": "000001.SZ", "start_date": "20200101"},
538
+ cache=True,
539
+ )
540
+ profit2 = CompanyProfit(
541
+ session=mock_session,
542
+ params={"ts_code": "000001.SZ", "start_date": "20210101"},
543
+ cache=True,
544
+ )
545
+
546
+ # Test that different params are properly stored
547
+ assert profit1.params.get("start_date") == "20200101"
548
+ assert profit2.params.get("start_date") == "20210101"
549
+ assert profit1.params.get("start_date") != profit2.params.get("start_date")
550
+
551
+
552
+ def test_companyprofit_different_stocks(mock_session, sample_source_data):
553
+ """Test that different stock codes are handled correctly"""
554
+ profit1 = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ"})
555
+ profit2 = CompanyProfit(session=mock_session, params={"ts_code": "000002.SZ"})
556
+
557
+ assert profit1.params.get("ts_code") != profit2.params.get("ts_code")
558
+
559
+
560
+ def test_companyprofit_empty_result_handling(mock_session):
561
+ """Test handling of empty result from API"""
562
+ profit = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ"})
563
+
564
+ with patch.object(profit, "_fetchall", return_value=pd.DataFrame()):
565
+ with patch.object(profit, "_save_cache"):
566
+ result = profit.run()
567
+
568
+ assert result.empty
569
+ assert len(result.columns) == len(TARGET.list_column_names())
570
+
571
+
572
+ def test_companyprofit_large_dataset_handling(mock_session):
573
+ """Test handling of large dataset"""
574
+ large_data = pd.DataFrame(
575
+ {
576
+ "ts_code": ["000001.SZ"] * 1000,
577
+ "end_date": ["20200331"] * 1000,
578
+ "ann_date": ["20200425"] * 1000,
579
+ "f_ann_date": ["20200425"] * 1000,
580
+ "report_type": ["1"] * 1000,
581
+ "comp_type": ["1"] * 1000,
582
+ "end_type": ["12"] * 1000,
583
+ "update_flag": ["0"] * 1000,
584
+ "basic_eps": [0.5] * 1000,
585
+ }
586
+ )
587
+
588
+ profit = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ"})
589
+
590
+ with patch.object(profit, "_fetchall", return_value=large_data):
591
+ with patch.object(profit, "_save_cache"):
592
+ result = profit.run()
593
+
594
+ # After deduplication, should have only 1 row
595
+ assert len(result) == 1
596
+
597
+
598
+ def test_companyprofit_without_cache(mock_session, sample_source_data):
599
+ """Test that data fetching works without cache"""
600
+ profit = CompanyProfit(
601
+ session=mock_session,
602
+ params={"ts_code": "000001.SZ"},
603
+ cache=False,
604
+ )
605
+
606
+ with patch.object(profit, "_fetchall", return_value=sample_source_data):
607
+ result = profit.run()
608
+
609
+ assert not result.empty
610
+ assert profit.cache is None
611
+
612
+
613
+ def test_companyprofit_handles_missing_numeric_fields(mock_session):
614
+ """Test handling of missing numeric values"""
615
+ data = pd.DataFrame(
616
+ {
617
+ "ts_code": ["000001.SZ"],
618
+ "end_date": ["20200331"],
619
+ "ann_date": ["20200425"],
620
+ "f_ann_date": ["20200425"],
621
+ "report_type": ["1"],
622
+ "comp_type": ["1"],
623
+ "end_type": ["12"],
624
+ "update_flag": ["0"],
625
+ "basic_eps": [None],
626
+ "n_income": [None],
627
+ }
628
+ )
629
+
630
+ profit = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ"})
631
+ result = profit.transform(data)
632
+
633
+ assert not result.empty
634
+ assert pd.isna(result["basic_eps"].iloc[0])
635
+ assert pd.isna(result["n_income"].iloc[0])
@@ -0,0 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ from xfintech.data.source.tushare.stock.conceptcapflowdc.conceptcapflowdc import (
4
+ ConceptCapflowDC,
5
+ )
6
+ from xfintech.data.source.tushare.stock.conceptcapflowdc.constant import (
7
+ KEY,
8
+ NAME,
9
+ PAGINATE,
10
+ SOURCE,
11
+ TARGET,
12
+ )
13
+
14
+ __all__ = [
15
+ "ConceptCapflowDC",
16
+ "KEY",
17
+ "NAME",
18
+ "PAGINATE",
19
+ "SOURCE",
20
+ "TARGET",
21
+ ]