cfquant 0.2.64__tar.gz → 0.2.65__tar.gz

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 (278) hide show
  1. {cfquant-0.2.64 → cfquant-0.2.65}/PKG-INFO +1 -1
  2. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/__init__.py +5 -0
  3. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/_editable_install.py +9 -1
  4. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/algo.py +2 -2
  5. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/algo_broker.py +3 -1
  6. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/batch_orders.py +10 -2
  7. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/callback_dispatcher.py +5 -5
  8. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/client.py +38 -4
  9. cfquant-0.2.65/cfquant/diagnostic_context.py +35 -0
  10. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/flow_control.py +2 -2
  11. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/hub.py +2 -1
  12. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/management.py +3 -1
  13. cfquant-0.2.65/cfquant/monitor_notifications.py +593 -0
  14. cfquant-0.2.65/cfquant/monitor_performance.py +81 -0
  15. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/normal_bridge.py +145 -10
  16. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/pipe_bridge.py +1 -0
  17. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/pipe_hub.py +132 -9
  18. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/pipe_transport.py +14 -1
  19. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/protocol.py +10 -5
  20. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_bridge.py +5 -1
  21. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_component_login.py +19 -13
  22. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_dependencies.py +3 -1
  23. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_inner.py +47 -14
  24. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_native_worker.py +25 -13
  25. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_pipe_runtime.py +4 -2
  26. cfquant-0.2.65/cfquant/qmt_python_runtime.py +641 -0
  27. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_strategy_deploy.py +89 -4
  28. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_strategy_recovery.py +9 -5
  29. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_strategy_runtime.py +14 -2
  30. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_update.py +3 -1
  31. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/quote_service.py +20 -2
  32. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/remote_management.py +3 -1
  33. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/shared_quotes.py +16 -3
  34. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/smart_orders.py +2 -2
  35. cfquant-0.2.65/cfquant/subscription_bridge.py +167 -0
  36. cfquant-0.2.65/cfquant/subscription_client.py +190 -0
  37. cfquant-0.2.65/cfquant/subscription_manager.py +214 -0
  38. cfquant-0.2.65/cfquant/subscription_store.py +181 -0
  39. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/13_/347/256/241/351/201/223/344/270/255/345/277/203/350/277/236/346/216/245/346/265/213/350/257/225.py +42 -0
  40. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/24_/350/264/246/346/210/267/347/274/223/345/255/230/346/214/211/351/234/200/345/210/267/346/226/260/346/265/213/350/257/225.py +23 -0
  41. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/26_/345/205/250/346/216/250/350/256/242/351/230/205/346/265/213/350/257/225.py +58 -0
  42. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/28_Level2/346/216/245/345/217/243/351/200/202/351/205/215/346/265/213/350/257/225.py +5 -0
  43. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_account_query_timing.py +40 -0
  44. cfquant-0.2.65/cfquant/tests/test_activity_log_browser.py +256 -0
  45. cfquant-0.2.65/cfquant/tests/test_auto_login_app.py +166 -0
  46. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_cftrader_tutorial.py +7 -29
  47. cfquant-0.2.65/cfquant/tests/test_diagnostic_context.py +30 -0
  48. cfquant-0.2.65/cfquant/tests/test_monitor_notifications.py +63 -0
  49. cfquant-0.2.65/cfquant/tests/test_monitor_performance.py +49 -0
  50. cfquant-0.2.65/cfquant/tests/test_navigation_browser.py +96 -0
  51. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_binding_settings.py +2 -1
  52. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_inner.py +18 -0
  53. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_inner_web.py +37 -0
  54. cfquant-0.2.65/cfquant/tests/test_qmt_python_runtime.py +133 -0
  55. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_strategy_deploy.py +19 -0
  56. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_quote_service.py +77 -0
  57. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_smart_orders_docs.py +10 -9
  58. cfquant-0.2.65/cfquant/tests/test_subscription_persistence.py +107 -0
  59. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_trading_monitor.py +96 -1
  60. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_trading_monitor_browser.py +40 -0
  61. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_tx_shutdown.py +2 -1
  62. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/trading_monitor.py +203 -7
  63. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tx_trade_bridge.py +7 -2
  64. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/version.py +2 -2
  65. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/xttrader.py +20 -8
  66. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant.egg-info/PKG-INFO +1 -1
  67. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant.egg-info/SOURCES.txt +19 -0
  68. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant_web_server.py +572 -13
  69. {cfquant-0.2.64 → cfquant-0.2.65}/pyproject.toml +1 -1
  70. {cfquant-0.2.64 → cfquant-0.2.65}/qmt_scripts/CFQUANT_LITE.py +420 -30
  71. {cfquant-0.2.64 → cfquant-0.2.65}/qmt_scripts//345/220/214/350/264/246/345/217/267/347/213/254/347/253/213/345/270/202/345/234/272/CFQUANT_LITE_SH.py +420 -30
  72. {cfquant-0.2.64 → cfquant-0.2.65}/qmt_scripts//345/220/214/350/264/246/345/217/267/347/213/254/347/253/213/345/270/202/345/234/272/CFQUANT_LITE_SZ.py +420 -30
  73. cfquant-0.2.65/web_dashboard/activity-log.js +162 -0
  74. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/app.js +498 -89
  75. cfquant-0.2.65/web_dashboard/cfquant-architecture.html +14837 -0
  76. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/index.html +161 -114
  77. cfquant-0.2.65/web_dashboard/navigation.js +151 -0
  78. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/python-api.js +6 -0
  79. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/qmt-inner-api-data.js +123 -105
  80. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/styles.css +791 -23
  81. cfquant-0.2.65/web_dashboard/trading-monitor.js +718 -0
  82. cfquant-0.2.64/web_dashboard/trading-monitor.js +0 -282
  83. {cfquant-0.2.64 → cfquant-0.2.65}/LICENSE +0 -0
  84. {cfquant-0.2.64 → cfquant-0.2.65}/LTtx/__init__.py +0 -0
  85. {cfquant-0.2.64 → cfquant-0.2.65}/LTtx/tx/Config.txt +0 -0
  86. {cfquant-0.2.64 → cfquant-0.2.65}/LTtx/tx/LTtx_server.py +0 -0
  87. {cfquant-0.2.64 → cfquant-0.2.65}/LTtx/tx/__init__.py +0 -0
  88. {cfquant-0.2.64 → cfquant-0.2.65}/LTtx/tx/tx.py +0 -0
  89. {cfquant-0.2.64 → cfquant-0.2.65}/MANIFEST.in +0 -0
  90. {cfquant-0.2.64 → cfquant-0.2.65}/README.md +0 -0
  91. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/account_routing.py +0 -0
  92. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/build_info.py +0 -0
  93. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/cftrader.py +0 -0
  94. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/channels.py +0 -0
  95. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/cli.py +0 -0
  96. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/conditional_orders.py +0 -0
  97. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/config.py +0 -0
  98. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/diagnostics.py +0 -0
  99. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/level2.py +0 -0
  100. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/log_management.py +0 -0
  101. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/logging_i18n.py +0 -0
  102. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/order_identity.py +0 -0
  103. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/order_meta.py +0 -0
  104. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/pipe_client.py +0 -0
  105. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/planning_broker.py +0 -0
  106. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/position_sizing.py +0 -0
  107. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_binding_defaults.py +0 -0
  108. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_binding_settings.py +0 -0
  109. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_component_agent.js +0 -0
  110. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_credentials.py +0 -0
  111. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_inner_catalog.py +0 -0
  112. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_inner_runtime.py +0 -0
  113. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_native_files.py +0 -0
  114. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_strategy_agent.js +0 -0
  115. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/qmt_strategy_package.py +0 -0
  116. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/quote_history.py +0 -0
  117. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/quote_leases.py +0 -0
  118. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/remote_adapter.py +0 -0
  119. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/remote_supervisor.py +0 -0
  120. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/rpc_timing.py +0 -0
  121. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/runtime_report.py +0 -0
  122. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/stock_connect.py +0 -0
  123. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/10_/345/221/275/344/273/244/350/241/214/346/216/245/345/217/243/346/265/213/350/257/225.py" +0 -0
  124. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/11_/346/231/256/351/200/232/346/241/245/346/216/245/346/264/276/345/217/221/346/265/213/350/257/225.py" +0 -0
  125. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/12_/350/256/242/345/215/225/345/244/207/346/263/250/346/265/213/350/257/225.py" +0 -0
  126. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/14_/350/277/220/350/241/214/346/227/266/346/212/245/345/221/212/346/265/213/350/257/225.py" +0 -0
  127. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/15_/347/275/221/351/241/265/347/212/266/346/200/201/347/274/223/345/255/230/346/265/213/350/257/225.py" +0 -0
  128. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/16_/345/220/257/345/212/250/345/256/211/350/243/205/346/265/213/350/257/225.py" +0 -0
  129. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/17_/345/244/247QMT/346/216/245/345/217/243/351/200/202/351/205/215/346/265/213/350/257/225.py" +0 -0
  130. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/18_/351/253/230/347/272/247/346/250/241/345/274/217/345/256/236/346/234/272/350/201/224/350/260/203.py" +0 -0
  131. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/19_/345/256/236/346/234/272/350/201/224/350/260/203/345/210/244/345/256/232/346/265/213/350/257/225.py" +0 -0
  132. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/1_/350/241/214/346/203/205/346/216/245/346/224/266/346/265/213/350/257/225.py" +0 -0
  133. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/20_/350/201/224/350/260/203/351/227/256/351/242/230/344/277/256/345/244/215/346/265/213/350/257/225.py" +0 -0
  134. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/21_/345/215/225/350/202/241/350/241/214/346/203/205/350/256/242/351/230/205/346/265/213/350/257/225.py" +0 -0
  135. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/22_/350/277/236/346/216/245/350/257/212/346/226/255/346/265/213/350/257/225.py" +0 -0
  136. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/23_/347/244/272/344/276/213/351/205/215/347/275/256/346/265/213/350/257/225.py" +0 -0
  137. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/25_/350/264/246/346/210/267/347/274/223/345/255/230/351/241/265/351/235/242/346/265/213/350/257/225.py" +0 -0
  138. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/27_/345/205/250/346/216/250/350/256/242/351/230/205/351/241/265/351/235/242/346/265/213/350/257/225.py" +0 -0
  139. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/29_Level2/350/241/214/346/203/205/346/265/213/350/257/225.py" +0 -0
  140. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/2_/346/225/260/346/215/256/350/216/267/345/217/226/346/265/213/350/257/225.py" +0 -0
  141. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/30_cftrader/346/211/271/351/207/217/344/270/213/345/215/225/346/200/247/350/203/275/345/237/272/345/207/206.py" +0 -0
  142. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/31_cftrader/346/250/241/346/213/237/350/264/246/345/217/267/346/211/271/351/207/217/344/270/213/345/215/225/346/265/213/350/257/225.py" +0 -0
  143. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/32_/345/274/202/346/255/245/344/270/213/345/215/225/346/265/213/350/257/225.py" +0 -0
  144. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/33_/345/220/214/346/255/245/344/270/213/345/215/225/345/233/236/350/260/203/346/265/213/350/257/225.py" +0 -0
  145. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/34_/345/205/250/346/216/250/345/206/205/351/203/250/345/273/266/350/277/237/346/265/213/350/257/225.py" +0 -0
  146. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/3_/346/225/260/346/215/256/344/270/213/350/275/275/346/265/213/350/257/225.py" +0 -0
  147. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/4_/344/272/244/346/230/223/345/247/224/346/211/230/346/237/245/350/257/242/346/265/213/350/257/225.py" +0 -0
  148. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/5_/347/234/237/345/256/236/344/270/213/345/215/225/346/265/213/350/257/225.py" +0 -0
  149. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/6_/345/233/236/350/260/203/346/265/213/350/257/225.py" +0 -0
  150. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/7_/345/220/214/346/255/245/345/274/202/346/255/245/344/270/213/345/215/225/346/265/213/350/257/225.py" +0 -0
  151. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/8_/346/210/220/344/272/244/350/256/242/345/215/225/345/222/214/345/247/224/346/211/230/350/256/242/345/215/225/346/237/245/350/257/242/346/265/213/350/257/225.py" +0 -0
  152. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/9_/345/217/257/346/222/244/350/256/242/345/215/225/346/265/213/350/257/225.py" +0 -0
  153. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/_helpers.py +0 -0
  154. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/readme.md +0 -0
  155. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_account_subscription_probe.py +0 -0
  156. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_advanced_parity.py +0 -0
  157. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_algo.py +0 -0
  158. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_algo_broker.py +0 -0
  159. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_api_tester_browser.py +0 -0
  160. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_api_tester_web.py +0 -0
  161. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_binding_delete_confirmation.py +0 -0
  162. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_binding_live_refresh_browser.py +0 -0
  163. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_binding_runtime_browser.py +0 -0
  164. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_callback_recovery.py +0 -0
  165. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_callback_requests.py +0 -0
  166. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_cftrader.py +0 -0
  167. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_cftrader_web.py +0 -0
  168. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_credit_compat.py +0 -0
  169. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_dataframe_encoding.py +0 -0
  170. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_json_serialization.py +0 -0
  171. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_latency_flow_control.py +0 -0
  172. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_lite_account_routing.py +0 -0
  173. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_lite_deployment_paths.py +0 -0
  174. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_lite_parity.py +0 -0
  175. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_lite_qmt_dispatch.py +0 -0
  176. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_local_trading_calendar.py +0 -0
  177. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_log_management.py +0 -0
  178. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_management.py +0 -0
  179. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_management_batch.py +0 -0
  180. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_market_data_fallback.py +0 -0
  181. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_market_timing.py +0 -0
  182. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_order_identity.py +0 -0
  183. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_order_time_callback.py +0 -0
  184. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_pipe_shutdown.py +0 -0
  185. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_project_updater.py +0 -0
  186. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_python_api_reference.py +0 -0
  187. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_auto_login_browser.py +0 -0
  188. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_auto_login_integration.py +0 -0
  189. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_binding_defaults.py +0 -0
  190. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_component_login.py +0 -0
  191. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_dependencies.py +0 -0
  192. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_fileless.py +0 -0
  193. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_inner_reference.py +0 -0
  194. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_native_files.py +0 -0
  195. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_native_reuse.py +0 -0
  196. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_native_worker.py +0 -0
  197. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_strategy_browser.py +0 -0
  198. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_strategy_capabilities.py +0 -0
  199. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_strategy_recovery.py +0 -0
  200. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_strategy_web.py +0 -0
  201. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_qmt_update.py +0 -0
  202. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_quote_latency.py +0 -0
  203. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_quote_latency_encoding.py +0 -0
  204. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_remote_management.py +0 -0
  205. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_remote_management_browser.py +0 -0
  206. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_remote_version_check.py +0 -0
  207. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_setup_qmt_prompt.py +0 -0
  208. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_shared_quotes.py +0 -0
  209. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_shared_quotes_generated.py +0 -0
  210. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_smart_orders.py +0 -0
  211. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_smart_orders_api.py +0 -0
  212. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_startup_recovery.py +0 -0
  213. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_stock_connect.py +0 -0
  214. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_stock_connect_browser.py +0 -0
  215. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_strategy_recovery_api.py +0 -0
  216. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_subscription_congestion.py +0 -0
  217. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_table_conversion.py +0 -0
  218. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_trade_strategy_callback.py +0 -0
  219. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_trader_subscription_recovery.py +0 -0
  220. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_trading_dates.py +0 -0
  221. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_tutorial_reader.py +0 -0
  222. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tests/test_tx_auto_import.py +0 -0
  223. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/trading_calendar.py +0 -0
  224. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/tx.py +0 -0
  225. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/xtconstant.py +0 -0
  226. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/xtdata.py +0 -0
  227. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant/xttype.py +0 -0
  228. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant.egg-info/dependency_links.txt +0 -0
  229. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant.egg-info/entry_points.txt +0 -0
  230. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant.egg-info/requires.txt +0 -0
  231. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant.egg-info/top_level.txt +0 -0
  232. {cfquant-0.2.64 → cfquant-0.2.65}/cfquant_pipe_hub.py +0 -0
  233. {cfquant-0.2.64 → cfquant-0.2.65}/qmt_scripts/CFQUANT.py +0 -0
  234. {cfquant-0.2.64 → cfquant-0.2.65}/qmt_scripts/CFQUANT_CTYPE_ALL_LOWLAT.py +0 -0
  235. {cfquant-0.2.64 → cfquant-0.2.65}/qmt_scripts/CFQUANT_TRADE_LOWLAT.py +0 -0
  236. {cfquant-0.2.64 → cfquant-0.2.65}/qmt_scripts/__init__.py +0 -0
  237. {cfquant-0.2.64 → cfquant-0.2.65}/qmt_scripts//345/220/214/350/264/246/345/217/267/347/213/254/347/253/213/345/270/202/345/234/272/CFQUANT_CTYPE_ALL_LOWLAT_SH.py" +0 -0
  238. {cfquant-0.2.64 → cfquant-0.2.65}/qmt_scripts//345/220/214/350/264/246/345/217/267/347/213/254/347/253/213/345/270/202/345/234/272/CFQUANT_CTYPE_ALL_LOWLAT_SZ.py" +0 -0
  239. {cfquant-0.2.64 → cfquant-0.2.65}/qmt_scripts//345/220/214/350/264/246/345/217/267/347/213/254/347/253/213/345/270/202/345/234/272/CFQUANT_TRADE_LOWLAT_SH.py" +0 -0
  240. {cfquant-0.2.64 → cfquant-0.2.65}/qmt_scripts//345/220/214/350/264/246/345/217/267/347/213/254/347/253/213/345/270/202/345/234/272/CFQUANT_TRADE_LOWLAT_SZ.py" +0 -0
  241. {cfquant-0.2.64 → cfquant-0.2.65}/qmt_scripts//345/220/214/350/264/246/345/217/267/347/213/254/347/253/213/345/270/202/345/234/272/readme.md" +0 -0
  242. {cfquant-0.2.64 → cfquant-0.2.65}/requirements.txt +0 -0
  243. {cfquant-0.2.64 → cfquant-0.2.65}/setup.cfg +0 -0
  244. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/__init__.py +0 -0
  245. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/api-tester.js +0 -0
  246. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/avatars/amber-pulse.svg +0 -0
  247. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/avatars/copper-grid.svg +0 -0
  248. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/avatars/market-blue.svg +0 -0
  249. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/avatars/rose-circuit.svg +0 -0
  250. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/avatars/signal-green.svg +0 -0
  251. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/avatars/slate-wave.svg +0 -0
  252. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/avatars/teal-orbit.svg +0 -0
  253. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/avatars/violet-node.svg +0 -0
  254. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/cftrader-api-data.js +0 -0
  255. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/docs.js +0 -0
  256. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/favicon.svg +0 -0
  257. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-10.png +0 -0
  258. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-11.png +0 -0
  259. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-12.png +0 -0
  260. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-13.png +0 -0
  261. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-14.png +0 -0
  262. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-15.png +0 -0
  263. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-16.png +0 -0
  264. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-17.png +0 -0
  265. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-18.png +0 -0
  266. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-19.png +0 -0
  267. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-20.png +0 -0
  268. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-21.png +0 -0
  269. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-4.png +0 -0
  270. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-5.png +0 -0
  271. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-6.png +0 -0
  272. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-7.png +0 -0
  273. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-8.png +0 -0
  274. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/image/qmt_deploy/image-9.png +0 -0
  275. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/python-api-data.js +0 -0
  276. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/remote-management.js +0 -0
  277. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/smart-order-api-data.js +0 -0
  278. {cfquant-0.2.64 → cfquant-0.2.65}/web_dashboard/vendor/mermaid.min.js +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cfquant
3
- Version: 0.2.64
3
+ Version: 0.2.65
4
4
  Summary: QMT local bridge and xtquant-compatible Python client.
5
5
  License: MIT
6
6
  Requires-Python: >=3.8
@@ -12,6 +12,7 @@ cfquant: 面向 QMT 的 xtquant 兼容层。
12
12
  from . import xtconstant, xtdata, xttrader, xttype
13
13
  from . import cftrader
14
14
  from .qmt_inner import QmtInnerApi
15
+ from .qmt_python_runtime import QmtPythonRuntime, QmtPythonRuntimeError, QmtPythonPackage, QmtPythonProgress
15
16
  from .client import CfquantError, CfquantTimeout, configure, get_client
16
17
  from .version import __version__
17
18
  from .build_info import build_identity, get_git_commit
@@ -27,6 +28,10 @@ __all__ = [
27
28
  "xttrader",
28
29
  "cftrader",
29
30
  "QmtInnerApi",
31
+ "QmtPythonRuntime",
32
+ "QmtPythonRuntimeError",
33
+ "QmtPythonPackage",
34
+ "QmtPythonProgress",
30
35
  "xttype",
31
36
  "configure",
32
37
  "get_client",
@@ -12,7 +12,15 @@ from pathlib import Path
12
12
  if __package__:
13
13
  from .qmt_dependencies import FRIDA_REQUIREMENT, FRIDA_VERSION, report_startup_dependencies
14
14
  else:
15
- from qmt_dependencies import FRIDA_REQUIREMENT, FRIDA_VERSION, report_startup_dependencies
15
+ # The Windows launcher executes this file by path (``python
16
+ # cfquant\\_editable_install.py``), so Python puts the package directory on
17
+ # sys.path and leaves ``__package__`` empty. Import through the package in
18
+ # that mode as well; qmt_dependencies uses relative imports and cannot be
19
+ # loaded as a top-level module.
20
+ _PROJECT_ROOT = Path(__file__).resolve().parent.parent
21
+ if str(_PROJECT_ROOT) not in sys.path:
22
+ sys.path.insert(0, str(_PROJECT_ROOT))
23
+ from cfquant.qmt_dependencies import FRIDA_REQUIREMENT, FRIDA_VERSION, report_startup_dependencies
16
24
 
17
25
 
18
26
  PACKAGE_NAME = "cfquant"
@@ -2,7 +2,7 @@
2
2
  import copy
3
3
  import datetime as dt
4
4
  import json
5
- import logging
5
+ from .diagnostic_context import get_logger
6
6
  import math
7
7
  import os
8
8
  import sqlite3
@@ -488,7 +488,7 @@ class AlgoEngine:
488
488
  except Exception:
489
489
  # Persistence is a prerequisite for further order submission.
490
490
  task["status"] = "PAUSED"
491
- logging.getLogger(__name__).exception("algorithm persistence failed")
491
+ get_logger(__name__).exception("algorithm persistence failed")
492
492
  finally:
493
493
  with self._lock:
494
494
  self._busy.discard(algo_id)
@@ -1,4 +1,6 @@
1
1
  """Existing cfquant account/market routes adapted to the execution engine."""
2
+
3
+ from .diagnostic_context import VersionedErrorMixin
2
4
  import datetime as dt
3
5
  import math
4
6
  import threading
@@ -10,7 +12,7 @@ from .xttype import XtAsset, XtOrder, XtPosition
10
12
  from .batch_orders import prepare_batch_orders, prepare_batch_cancels
11
13
 
12
14
 
13
- class AlgoOrderRejected(RuntimeError):
15
+ class AlgoOrderRejected(VersionedErrorMixin, RuntimeError):
14
16
  dispatched = False
15
17
 
16
18
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  import math
4
4
  import os
5
+ import re
5
6
  import time
6
7
  from collections.abc import Mapping
7
8
  from numbers import Integral, Real
@@ -19,6 +20,13 @@ _BATCH_ORDER_FIELDS = {"stock_code", "order_type", "order_volume", "price_type",
19
20
  "strategy_name", "order_remark", "cfquant_order_remark"}
20
21
  _BATCH_REQUIRED_FIELDS = {"stock_code", "order_type", "order_volume", "price_type", "price"}
21
22
  _BATCH_CANCEL_FIELDS = {"order_id", "stock_code", "market", "order_remark"}
23
+ _VERSION_SUFFIX = re.compile(r"\s+\[cfquant\s+[^\]]+\]\s*$")
24
+
25
+
26
+ def _batch_error_text(error):
27
+ """Keep row-level compatibility while request errors retain diagnostics."""
28
+ text = str(error) or type(error).__name__
29
+ return _VERSION_SUFFIX.sub("", text)
22
30
 
23
31
 
24
32
  def batch_positive_id(value):
@@ -206,14 +214,14 @@ def batch_result(account, batch_id, asynchronous, results, operation="order"):
206
214
 
207
215
  def batch_unknown(rows, error):
208
216
  for row in rows:
209
- row.update(status="unknown", ok=None, error=str(error) or type(error).__name__)
217
+ row.update(status="unknown", ok=None, error=_batch_error_text(error))
210
218
 
211
219
 
212
220
  def batch_rejected(rows, error):
213
221
  """Mark a rejection known to happen before QMT dispatch."""
214
222
  details = getattr(error, "error_details", None)
215
223
  details = details if isinstance(details, dict) else {}
216
- message = str(error) or type(error).__name__
224
+ message = _batch_error_text(error)
217
225
  for row in rows:
218
226
  row.update(status="failed", ok=False, error=message, dispatched=False,
219
227
  safe_to_retry=details.get("safe_to_retry") is True,
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """Serial user callbacks, independent of the RPC receive thread."""
3
3
  from collections import deque
4
- import logging
4
+ from .diagnostic_context import get_logger
5
5
  import os
6
6
  import tempfile
7
7
  import threading
@@ -90,7 +90,7 @@ class CallbackDispatcher(object):
90
90
  item[1] = item[3] = None
91
91
  if self._queue and len(self._queue) >= 1000 and time.monotonic() - self._last_warning >= 5:
92
92
  self._last_warning = time.monotonic()
93
- logging.getLogger(__name__).warning("callback backlog count=%s oldest_ms=%.1f spooled_bytes=%s",
93
+ get_logger(__name__).warning("callback backlog count=%s oldest_ms=%.1f spooled_bytes=%s",
94
94
  len(self._queue), (time.monotonic() - self._queue[0][2]) * 1000, self._spool_live)
95
95
 
96
96
  def _take_message(self, item):
@@ -168,7 +168,7 @@ class CallbackDispatcher(object):
168
168
  if message is None:
169
169
  raise ValueError("invalid preserved callback message")
170
170
  except Exception as error:
171
- logging.getLogger(__name__).exception("callback backlog failed; connection must be restarted")
171
+ get_logger(__name__).exception("callback backlog failed; connection must be restarted")
172
172
  with self._condition:
173
173
  if generation != self._generation:
174
174
  continue # An old connection must not close its replacement.
@@ -185,7 +185,7 @@ class CallbackDispatcher(object):
185
185
  if self.is_current_callback():
186
186
  self._dispatch(message)
187
187
  except Exception:
188
- logging.getLogger(__name__).exception("callback dispatch failed event=%s", message.get("event"))
188
+ get_logger(__name__).exception("callback dispatch failed event=%s", message.get("event"))
189
189
  finally:
190
190
  self._last_callback_ms = (time.monotonic() - started) * 1000
191
191
  del self._context.generation
@@ -215,4 +215,4 @@ def dispatch_callbacks(callbacks, message, is_current):
215
215
  try:
216
216
  callback(payload)
217
217
  except Exception:
218
- logging.getLogger(__name__).exception("user callback failed event=%s", event)
218
+ get_logger(__name__).exception("user callback failed event=%s", event)
@@ -10,14 +10,16 @@ from .config import get_config
10
10
  from .callback_dispatcher import CallbackDispatcher, dispatch_callbacks
11
11
  from .protocol import decode_value, dumps_message, loads_message, new_id, pack_request
12
12
  from .rpc_timing import RpcTiming
13
+ from .diagnostic_context import CFQUANT_VERSION, VersionedErrorMixin
13
14
 
14
15
 
15
- class CfquantError(RuntimeError):
16
+ class CfquantError(VersionedErrorMixin, RuntimeError):
16
17
  """RPC failure retaining the server's structured rejection details."""
17
18
 
18
19
  def __init__(self, message="", details=None):
19
20
  super(CfquantError, self).__init__(message)
20
21
  self.error_details = dict(details) if isinstance(details, dict) else {}
22
+ self.error_details.setdefault("cfquant_version", CFQUANT_VERSION)
21
23
  self.code = self.error_details.get("code", "")
22
24
  self.stage = self.error_details.get("stage", "")
23
25
  self.dispatched = self.error_details.get("dispatched")
@@ -25,7 +27,7 @@ class CfquantError(RuntimeError):
25
27
  self.retry_when = self.error_details.get("retry_when", "")
26
28
 
27
29
 
28
- class CfquantTimeout(TimeoutError):
30
+ class CfquantTimeout(VersionedErrorMixin, TimeoutError):
29
31
  pass
30
32
 
31
33
 
@@ -157,7 +159,7 @@ class LTtxRpcClient(object):
157
159
  if not getattr(self._tx, "run_on", True):
158
160
  raise CfquantError("cfquant client closed during connection startup")
159
161
  except BaseException:
160
- self.close()
162
+ LTtxRpcClient.close(self)
161
163
  raise
162
164
  self._started = True
163
165
  self._closed = False
@@ -286,7 +288,7 @@ class LTtxRpcClient(object):
286
288
  break
287
289
  with self._lock:
288
290
  if self._tx is expected_tx:
289
- self.close()
291
+ LTtxRpcClient.close(self)
290
292
 
291
293
  def _dispatch_event(self, msg):
292
294
  dispatch_callbacks(self._callbacks, msg, self._event_dispatcher.is_current_callback)
@@ -355,6 +357,38 @@ class WebLttxRpcClient(LTtxRpcClient):
355
357
  timeout=timeout,
356
358
  client_id=client_id,
357
359
  )
360
+ self._subscription_session = None
361
+ self._subscription_protocol = None
362
+ self._subscription_init_lock = threading.Lock()
363
+
364
+ def _subscription_raw_request(self, action, params, timeout=None):
365
+ return super(WebLttxRpcClient, self).request(action, params, timeout=timeout)
366
+
367
+ def request(self, action, params=None, timeout=None, request_channel=None, response_meta=None):
368
+ from .subscription_client import managed_request
369
+ handled, result = managed_request(self, self._subscription_raw_request, action, params, timeout)
370
+ if handled:
371
+ return result
372
+ return super(WebLttxRpcClient, self).request(action, params, timeout, request_channel, response_meta)
373
+
374
+ def _dispatch_event(self, msg):
375
+ if msg.get('event') == 'cfquant.managed_subscription':
376
+ data = msg.get('data')
377
+ if isinstance(data, dict):
378
+ if self._subscription_session is None or data.get('key') not in self._subscription_session.specs:
379
+ return
380
+ msg = dict(msg, event=data.get('event'), data=data.get('payload'))
381
+ sid = (msg.get('meta') or {}).get('managed_subscription_id')
382
+ if sid is not None and (self._subscription_session is None or str(sid) not in self._subscription_session.specs):
383
+ return
384
+ super(WebLttxRpcClient, self)._dispatch_event(msg)
385
+
386
+ def close(self):
387
+ session = getattr(self, '_subscription_session', None)
388
+ if session is not None:
389
+ self._subscription_session = None
390
+ session.close()
391
+ super(WebLttxRpcClient, self).close()
358
392
 
359
393
 
360
394
  def _registry_has_web_route(registry):
@@ -0,0 +1,35 @@
1
+ """Version context for cfquant diagnostics without changing application logging."""
2
+ import logging
3
+
4
+ from .version import __version__ as CFQUANT_VERSION
5
+
6
+
7
+ def versioned_message(message):
8
+ text = str(message)
9
+ tag = "[cfquant %s]" % CFQUANT_VERSION
10
+ return text if tag in text else "%s %s" % (text, tag)
11
+
12
+
13
+ class VersionedErrorMixin:
14
+ cfquant_version = CFQUANT_VERSION
15
+
16
+ def __str__(self):
17
+ # Keep .args and structured error codes intact for existing callers.
18
+ return versioned_message(super(VersionedErrorMixin, self).__str__())
19
+
20
+
21
+ class _VersionFilter(logging.Filter):
22
+ def filter(self, record):
23
+ record.msg = versioned_message(record.getMessage())
24
+ record.args = ()
25
+ record.cfquant_version = CFQUANT_VERSION
26
+ return True
27
+
28
+
29
+ _VERSION_FILTER = _VersionFilter()
30
+
31
+
32
+ def get_logger(name):
33
+ logger = logging.getLogger(name)
34
+ logger.addFilter(_VERSION_FILTER)
35
+ return logger
@@ -1,6 +1,6 @@
1
1
  """Ordered, bounded background work. No QMT API calls run in these workers."""
2
2
  from collections import deque
3
- import logging
3
+ from .diagnostic_context import get_logger
4
4
  import threading
5
5
  import time
6
6
 
@@ -163,7 +163,7 @@ class KeyedExecutor(object):
163
163
  try:
164
164
  item[2](error)
165
165
  except Exception:
166
- logging.getLogger(__name__).exception("Web route could not deliver request failure")
166
+ get_logger(__name__).exception("Web route could not deliver request failure")
167
167
 
168
168
  def status(self):
169
169
  with self._condition:
@@ -7,6 +7,7 @@ import threading
7
7
  import time
8
8
 
9
9
  from .protocol import loads_message
10
+ from .diagnostic_context import versioned_message
10
11
 
11
12
 
12
13
  def default_status_file(filename):
@@ -263,7 +264,7 @@ class CfquantHub(object):
263
264
 
264
265
  def _log(self, msg):
265
266
  if self.show:
266
- print("%s %s" % (self._timestamp_ms(), msg), flush=True)
267
+ print("%s %s" % (self._timestamp_ms(), versioned_message(msg)), flush=True)
267
268
 
268
269
  def _timestamp_ms(self):
269
270
  now = time.time()
@@ -5,6 +5,8 @@ The service has its own source directory. Updating it never installs packages
5
5
  into, or reloads modules in, the calling application's Python environment.
6
6
  """
7
7
 
8
+ from .diagnostic_context import VersionedErrorMixin
9
+
8
10
  import importlib.util
9
11
  import json
10
12
  import os
@@ -23,7 +25,7 @@ import urllib.request
23
25
  _UNSET = object()
24
26
 
25
27
 
26
- class ManagementError(RuntimeError):
28
+ class ManagementError(VersionedErrorMixin, RuntimeError):
27
29
  """Management failure, including the server's status and response details."""
28
30
 
29
31
  def __init__(self, message, code="management_error", status=None, details=None):