scitex 2.7.3__py3-none-any.whl → 2.10.0__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 (563) hide show
  1. scitex/__init__.py +15 -7
  2. scitex/__version__.py +1 -2
  3. scitex/_install_guide.py +250 -0
  4. scitex/_optional_deps.py +206 -39
  5. scitex/ai/_gen_ai/_Groq.py +2 -4
  6. scitex/ai/_gen_ai/_OpenAI.py +5 -2
  7. scitex/ai/_gen_ai/_Perplexity.py +20 -6
  8. scitex/audio/__init__.py +24 -15
  9. scitex/audio/_cross_process_lock.py +139 -0
  10. scitex/audio/_mcp_handlers.py +256 -0
  11. scitex/audio/_mcp_tool_schemas.py +203 -0
  12. scitex/audio/engines/elevenlabs_engine.py +5 -2
  13. scitex/audio/mcp_server.py +98 -457
  14. scitex/bridge/__init__.py +30 -19
  15. scitex/bridge/_figrecipe.py +245 -0
  16. scitex/bridge/_helpers.py +2 -1
  17. scitex/bridge/_plt_vis.py +23 -10
  18. scitex/bridge/_stats_plt.py +18 -5
  19. scitex/bridge/_stats_vis.py +16 -2
  20. scitex/browser/__init__.py +84 -44
  21. scitex/browser/automation/__init__.py +5 -1
  22. scitex/browser/core/BrowserMixin.py +17 -4
  23. scitex/browser/core/__init__.py +11 -2
  24. scitex/browser/remote/CaptchaHandler.py +1 -1
  25. scitex/browser/remote/ZenRowsAPIClient.py +1 -1
  26. scitex/capture/grid.py +487 -0
  27. scitex/capture/mcp_handlers.py +401 -0
  28. scitex/capture/mcp_tool_defs.py +192 -0
  29. scitex/capture/mcp_tools.py +241 -0
  30. scitex/capture/mcp_utils.py +30 -0
  31. scitex/cli/convert.py +421 -0
  32. scitex/cli/main.py +6 -4
  33. scitex/datetime/__init__.py +46 -0
  34. scitex/datetime/_linspace.py +100 -0
  35. scitex/datetime/_normalize_timestamp.py +306 -0
  36. scitex/db/_delete_duplicates.py +4 -4
  37. scitex/db/_sqlite3/_delete_duplicates.py +11 -2
  38. scitex/dev/plt/__init__.py +61 -62
  39. scitex/dev/plt/demo_plotters/__init__.py +0 -0
  40. scitex/dev/plt/demo_plotters/plot_mpl_axhline.py +28 -0
  41. scitex/dev/plt/demo_plotters/plot_mpl_axhspan.py +28 -0
  42. scitex/dev/plt/demo_plotters/plot_mpl_axvline.py +28 -0
  43. scitex/dev/plt/demo_plotters/plot_mpl_axvspan.py +28 -0
  44. scitex/dev/plt/demo_plotters/plot_mpl_bar.py +29 -0
  45. scitex/dev/plt/demo_plotters/plot_mpl_barh.py +29 -0
  46. scitex/dev/plt/demo_plotters/plot_mpl_boxplot.py +28 -0
  47. scitex/dev/plt/demo_plotters/plot_mpl_contour.py +31 -0
  48. scitex/dev/plt/demo_plotters/plot_mpl_contourf.py +31 -0
  49. scitex/dev/plt/demo_plotters/plot_mpl_errorbar.py +30 -0
  50. scitex/dev/plt/demo_plotters/plot_mpl_eventplot.py +28 -0
  51. scitex/dev/plt/demo_plotters/plot_mpl_fill.py +30 -0
  52. scitex/dev/plt/demo_plotters/plot_mpl_fill_between.py +31 -0
  53. scitex/dev/plt/demo_plotters/plot_mpl_hexbin.py +28 -0
  54. scitex/dev/plt/demo_plotters/plot_mpl_hist.py +28 -0
  55. scitex/dev/plt/demo_plotters/plot_mpl_hist2d.py +28 -0
  56. scitex/dev/plt/demo_plotters/plot_mpl_imshow.py +29 -0
  57. scitex/dev/plt/demo_plotters/plot_mpl_pcolormesh.py +31 -0
  58. scitex/dev/plt/demo_plotters/plot_mpl_pie.py +29 -0
  59. scitex/dev/plt/demo_plotters/plot_mpl_plot.py +29 -0
  60. scitex/dev/plt/demo_plotters/plot_mpl_quiver.py +31 -0
  61. scitex/dev/plt/demo_plotters/plot_mpl_scatter.py +28 -0
  62. scitex/dev/plt/demo_plotters/plot_mpl_stackplot.py +31 -0
  63. scitex/dev/plt/demo_plotters/plot_mpl_stem.py +29 -0
  64. scitex/dev/plt/demo_plotters/plot_mpl_step.py +29 -0
  65. scitex/dev/plt/demo_plotters/plot_mpl_violinplot.py +28 -0
  66. scitex/dev/plt/demo_plotters/plot_sns_barplot.py +29 -0
  67. scitex/dev/plt/demo_plotters/plot_sns_boxplot.py +29 -0
  68. scitex/dev/plt/demo_plotters/plot_sns_heatmap.py +28 -0
  69. scitex/dev/plt/demo_plotters/plot_sns_histplot.py +29 -0
  70. scitex/dev/plt/demo_plotters/plot_sns_kdeplot.py +29 -0
  71. scitex/dev/plt/demo_plotters/plot_sns_lineplot.py +31 -0
  72. scitex/dev/plt/demo_plotters/plot_sns_scatterplot.py +29 -0
  73. scitex/dev/plt/demo_plotters/plot_sns_stripplot.py +29 -0
  74. scitex/dev/plt/demo_plotters/plot_sns_swarmplot.py +29 -0
  75. scitex/dev/plt/demo_plotters/plot_sns_violinplot.py +29 -0
  76. scitex/dev/plt/demo_plotters/plot_stx_bar.py +29 -0
  77. scitex/dev/plt/demo_plotters/plot_stx_barh.py +29 -0
  78. scitex/dev/plt/demo_plotters/plot_stx_box.py +28 -0
  79. scitex/dev/plt/demo_plotters/plot_stx_boxplot.py +28 -0
  80. scitex/dev/plt/demo_plotters/plot_stx_conf_mat.py +28 -0
  81. scitex/dev/plt/demo_plotters/plot_stx_contour.py +31 -0
  82. scitex/dev/plt/demo_plotters/plot_stx_ecdf.py +28 -0
  83. scitex/dev/plt/demo_plotters/plot_stx_errorbar.py +30 -0
  84. scitex/dev/plt/demo_plotters/plot_stx_fill_between.py +31 -0
  85. scitex/dev/plt/demo_plotters/plot_stx_fillv.py +28 -0
  86. scitex/dev/plt/demo_plotters/plot_stx_heatmap.py +28 -0
  87. scitex/dev/plt/demo_plotters/plot_stx_image.py +28 -0
  88. scitex/dev/plt/demo_plotters/plot_stx_imshow.py +28 -0
  89. scitex/dev/plt/demo_plotters/plot_stx_joyplot.py +28 -0
  90. scitex/dev/plt/demo_plotters/plot_stx_kde.py +28 -0
  91. scitex/dev/plt/demo_plotters/plot_stx_line.py +28 -0
  92. scitex/dev/plt/demo_plotters/plot_stx_mean_ci.py +28 -0
  93. scitex/dev/plt/demo_plotters/plot_stx_mean_std.py +28 -0
  94. scitex/dev/plt/demo_plotters/plot_stx_median_iqr.py +28 -0
  95. scitex/dev/plt/demo_plotters/plot_stx_raster.py +28 -0
  96. scitex/dev/plt/demo_plotters/plot_stx_rectangle.py +28 -0
  97. scitex/dev/plt/demo_plotters/plot_stx_scatter.py +29 -0
  98. scitex/dev/plt/demo_plotters/plot_stx_shaded_line.py +29 -0
  99. scitex/dev/plt/demo_plotters/plot_stx_violin.py +28 -0
  100. scitex/dev/plt/demo_plotters/plot_stx_violinplot.py +28 -0
  101. scitex/dev/plt/mpl/get_dir_ax.py +46 -0
  102. scitex/dev/plt/mpl/get_signatures.py +176 -0
  103. scitex/dev/plt/mpl/get_signatures_details.py +522 -0
  104. scitex/dev/plt/plot_mpl_axhline.py +0 -0
  105. scitex/dev/plt/plot_mpl_axhspan.py +0 -0
  106. scitex/dev/plt/plot_mpl_axvline.py +0 -0
  107. scitex/dev/plt/plot_mpl_axvspan.py +0 -0
  108. scitex/dev/plt/plot_mpl_bar.py +0 -0
  109. scitex/dev/plt/plot_mpl_barh.py +0 -0
  110. scitex/dev/plt/plot_mpl_boxplot.py +0 -0
  111. scitex/dev/plt/plot_mpl_contour.py +0 -0
  112. scitex/dev/plt/plot_mpl_contourf.py +0 -0
  113. scitex/dev/plt/plot_mpl_errorbar.py +0 -0
  114. scitex/dev/plt/plot_mpl_eventplot.py +0 -0
  115. scitex/dev/plt/plot_mpl_fill.py +0 -0
  116. scitex/dev/plt/plot_mpl_fill_between.py +0 -0
  117. scitex/dev/plt/plot_mpl_hexbin.py +0 -0
  118. scitex/dev/plt/plot_mpl_hist.py +0 -0
  119. scitex/dev/plt/plot_mpl_hist2d.py +0 -0
  120. scitex/dev/plt/plot_mpl_imshow.py +0 -0
  121. scitex/dev/plt/plot_mpl_pcolormesh.py +0 -0
  122. scitex/dev/plt/plot_mpl_pie.py +0 -0
  123. scitex/dev/plt/plot_mpl_plot.py +0 -0
  124. scitex/dev/plt/plot_mpl_quiver.py +0 -0
  125. scitex/dev/plt/plot_mpl_scatter.py +0 -0
  126. scitex/dev/plt/plot_mpl_stackplot.py +0 -0
  127. scitex/dev/plt/plot_mpl_stem.py +0 -0
  128. scitex/dev/plt/plot_mpl_step.py +0 -0
  129. scitex/dev/plt/plot_mpl_violinplot.py +0 -0
  130. scitex/dev/plt/plot_sns_barplot.py +0 -0
  131. scitex/dev/plt/plot_sns_boxplot.py +0 -0
  132. scitex/dev/plt/plot_sns_heatmap.py +0 -0
  133. scitex/dev/plt/plot_sns_histplot.py +0 -0
  134. scitex/dev/plt/plot_sns_kdeplot.py +0 -0
  135. scitex/dev/plt/plot_sns_lineplot.py +0 -0
  136. scitex/dev/plt/plot_sns_scatterplot.py +0 -0
  137. scitex/dev/plt/plot_sns_stripplot.py +0 -0
  138. scitex/dev/plt/plot_sns_swarmplot.py +0 -0
  139. scitex/dev/plt/plot_sns_violinplot.py +0 -0
  140. scitex/dev/plt/plot_stx_bar.py +0 -0
  141. scitex/dev/plt/plot_stx_barh.py +0 -0
  142. scitex/dev/plt/plot_stx_box.py +0 -0
  143. scitex/dev/plt/plot_stx_boxplot.py +0 -0
  144. scitex/dev/plt/plot_stx_conf_mat.py +0 -0
  145. scitex/dev/plt/plot_stx_contour.py +0 -0
  146. scitex/dev/plt/plot_stx_ecdf.py +0 -0
  147. scitex/dev/plt/plot_stx_errorbar.py +0 -0
  148. scitex/dev/plt/plot_stx_fill_between.py +0 -0
  149. scitex/dev/plt/plot_stx_fillv.py +0 -0
  150. scitex/dev/plt/plot_stx_heatmap.py +0 -0
  151. scitex/dev/plt/plot_stx_image.py +0 -0
  152. scitex/dev/plt/plot_stx_imshow.py +0 -0
  153. scitex/dev/plt/plot_stx_joyplot.py +0 -0
  154. scitex/dev/plt/plot_stx_kde.py +0 -0
  155. scitex/dev/plt/plot_stx_line.py +0 -0
  156. scitex/dev/plt/plot_stx_mean_ci.py +0 -0
  157. scitex/dev/plt/plot_stx_mean_std.py +0 -0
  158. scitex/dev/plt/plot_stx_median_iqr.py +0 -0
  159. scitex/dev/plt/plot_stx_raster.py +0 -0
  160. scitex/dev/plt/plot_stx_rectangle.py +0 -0
  161. scitex/dev/plt/plot_stx_scatter.py +0 -0
  162. scitex/dev/plt/plot_stx_shaded_line.py +0 -0
  163. scitex/dev/plt/plot_stx_violin.py +0 -0
  164. scitex/dev/plt/plot_stx_violinplot.py +0 -0
  165. scitex/diagram/README.md +197 -0
  166. scitex/diagram/__init__.py +48 -0
  167. scitex/diagram/_compile.py +312 -0
  168. scitex/diagram/_diagram.py +355 -0
  169. scitex/diagram/_presets.py +173 -0
  170. scitex/diagram/_schema.py +182 -0
  171. scitex/diagram/_split.py +278 -0
  172. scitex/dict/_pop_keys.py +1 -7
  173. scitex/dsp/__init__.py +15 -10
  174. scitex/dsp/add_noise.py +5 -2
  175. scitex/dsp/example.py +35 -22
  176. scitex/dsp/filt.py +8 -3
  177. scitex/dsp/reference.py +3 -2
  178. scitex/dsp/utils/__init__.py +2 -1
  179. scitex/dsp/utils/_differential_bandpass_filters.py +14 -4
  180. scitex/dt/__init__.py +39 -2
  181. scitex/errors.py +82 -521
  182. scitex/fig/__init__.py +4 -4
  183. scitex/fig/editor/__init__.py +5 -2
  184. scitex/fig/editor/_dearpygui_editor.py +1 -1
  185. scitex/fig/editor/_mpl_editor.py +1 -1
  186. scitex/fig/editor/_qt_editor.py +1 -1
  187. scitex/fig/editor/_tkinter_editor.py +1 -1
  188. scitex/fig/editor/edit/__init__.py +50 -0
  189. scitex/fig/editor/edit/backend_detector.py +109 -0
  190. scitex/fig/editor/edit/bundle_resolver.py +240 -0
  191. scitex/fig/editor/edit/editor_launcher.py +239 -0
  192. scitex/fig/editor/edit/manual_handler.py +53 -0
  193. scitex/fig/editor/edit/panel_loader.py +232 -0
  194. scitex/fig/editor/edit/path_resolver.py +67 -0
  195. scitex/fig/editor/flask_editor/_bbox.py +23 -0
  196. scitex/fig/editor/flask_editor/_core.py +908 -103
  197. scitex/fig/editor/flask_editor/_renderer.py +74 -0
  198. scitex/fig/editor/flask_editor/static/css/base/reset.css +41 -0
  199. scitex/fig/editor/flask_editor/static/css/base/typography.css +16 -0
  200. scitex/fig/editor/flask_editor/static/css/base/variables.css +85 -0
  201. scitex/fig/editor/flask_editor/static/css/components/buttons.css +217 -0
  202. scitex/fig/editor/flask_editor/static/css/components/context-menu.css +93 -0
  203. scitex/fig/editor/flask_editor/static/css/components/dropdown.css +57 -0
  204. scitex/fig/editor/flask_editor/static/css/components/forms.css +112 -0
  205. scitex/fig/editor/flask_editor/static/css/components/modal.css +59 -0
  206. scitex/fig/editor/flask_editor/static/css/components/sections.css +212 -0
  207. scitex/fig/editor/flask_editor/static/css/features/canvas.css +176 -0
  208. scitex/fig/editor/flask_editor/static/css/features/element-inspector.css +190 -0
  209. scitex/fig/editor/flask_editor/static/css/features/loading.css +59 -0
  210. scitex/fig/editor/flask_editor/static/css/features/overlay.css +45 -0
  211. scitex/fig/editor/flask_editor/static/css/features/panel-grid.css +95 -0
  212. scitex/fig/editor/flask_editor/static/css/features/selection.css +101 -0
  213. scitex/fig/editor/flask_editor/static/css/features/statistics.css +138 -0
  214. scitex/fig/editor/flask_editor/static/css/index.css +31 -0
  215. scitex/fig/editor/flask_editor/static/css/layout/container.css +7 -0
  216. scitex/fig/editor/flask_editor/static/css/layout/controls.css +56 -0
  217. scitex/fig/editor/flask_editor/static/css/layout/preview.css +78 -0
  218. scitex/fig/editor/flask_editor/static/js/alignment/axis.js +314 -0
  219. scitex/fig/editor/flask_editor/static/js/alignment/basic.js +107 -0
  220. scitex/fig/editor/flask_editor/static/js/alignment/distribute.js +54 -0
  221. scitex/fig/editor/flask_editor/static/js/canvas/canvas.js +172 -0
  222. scitex/fig/editor/flask_editor/static/js/canvas/dragging.js +258 -0
  223. scitex/fig/editor/flask_editor/static/js/canvas/resize.js +48 -0
  224. scitex/fig/editor/flask_editor/static/js/canvas/selection.js +71 -0
  225. scitex/fig/editor/flask_editor/static/js/core/api.js +288 -0
  226. scitex/fig/editor/flask_editor/static/js/core/state.js +143 -0
  227. scitex/fig/editor/flask_editor/static/js/core/utils.js +245 -0
  228. scitex/fig/editor/flask_editor/static/js/dev/element-inspector.js +992 -0
  229. scitex/fig/editor/flask_editor/static/js/editor/bbox.js +339 -0
  230. scitex/fig/editor/flask_editor/static/js/editor/element-drag.js +286 -0
  231. scitex/fig/editor/flask_editor/static/js/editor/overlay.js +371 -0
  232. scitex/fig/editor/flask_editor/static/js/editor/preview.js +293 -0
  233. scitex/fig/editor/flask_editor/static/js/main.js +426 -0
  234. scitex/fig/editor/flask_editor/static/js/shortcuts/context-menu.js +152 -0
  235. scitex/fig/editor/flask_editor/static/js/shortcuts/keyboard.js +265 -0
  236. scitex/fig/editor/flask_editor/static/js/ui/controls.js +184 -0
  237. scitex/fig/editor/flask_editor/static/js/ui/download.js +57 -0
  238. scitex/fig/editor/flask_editor/static/js/ui/help.js +100 -0
  239. scitex/fig/editor/flask_editor/static/js/ui/theme.js +34 -0
  240. scitex/fig/editor/flask_editor/templates/__init__.py +95 -5
  241. scitex/fig/editor/flask_editor/templates/_html.py +27 -9
  242. scitex/fig/editor/flask_editor/templates/_scripts.py +1928 -131
  243. scitex/fig/editor/flask_editor/templates/_styles.py +363 -51
  244. scitex/fig/io/_bundle.py +104 -19
  245. scitex/fts/README.md +262 -0
  246. scitex/fts/TODO.md +66 -0
  247. scitex/fts/__init__.py +90 -0
  248. scitex/fts/_bundle/README_IN_BUNDLE.md +102 -0
  249. scitex/fts/_bundle/_FTS.py +657 -0
  250. scitex/fts/_bundle/__init__.py +38 -0
  251. scitex/fts/_bundle/_children.py +216 -0
  252. scitex/fts/_bundle/_conversion/__init__.py +15 -0
  253. scitex/fts/_bundle/_conversion/_bundle2dict.py +44 -0
  254. scitex/fts/_bundle/_conversion/_dict2bundle.py +50 -0
  255. scitex/fts/_bundle/_dataclasses/_Axes.py +57 -0
  256. scitex/fts/_bundle/_dataclasses/_BBox.py +54 -0
  257. scitex/fts/_bundle/_dataclasses/_ColumnDef.py +72 -0
  258. scitex/fts/_bundle/_dataclasses/_DataFormat.py +40 -0
  259. scitex/fts/_bundle/_dataclasses/_DataInfo.py +135 -0
  260. scitex/fts/_bundle/_dataclasses/_DataSource.py +44 -0
  261. scitex/fts/_bundle/_dataclasses/_Node.py +319 -0
  262. scitex/fts/_bundle/_dataclasses/_NodeRefs.py +45 -0
  263. scitex/fts/_bundle/_dataclasses/_SizeMM.py +38 -0
  264. scitex/fts/_bundle/_dataclasses/__init__.py +35 -0
  265. scitex/fts/_bundle/_extractors/__init__.py +32 -0
  266. scitex/fts/_bundle/_extractors/_extract_bar.py +131 -0
  267. scitex/fts/_bundle/_extractors/_extract_line.py +71 -0
  268. scitex/fts/_bundle/_extractors/_extract_scatter.py +79 -0
  269. scitex/fts/_bundle/_loader.py +134 -0
  270. scitex/fts/_bundle/_mpl_helpers.py +389 -0
  271. scitex/fts/_bundle/_saver.py +269 -0
  272. scitex/fts/_bundle/_storage.py +200 -0
  273. scitex/fts/_bundle/_utils/__init__.py +55 -0
  274. scitex/fts/_bundle/_utils/_const.py +26 -0
  275. scitex/fts/_bundle/_utils/_errors.py +73 -0
  276. scitex/fts/_bundle/_utils/_generate.py +21 -0
  277. scitex/fts/_bundle/_utils/_types.py +76 -0
  278. scitex/fts/_bundle/_validation.py +434 -0
  279. scitex/fts/_bundle/_zipbundle.py +165 -0
  280. scitex/fts/_fig/__init__.py +22 -0
  281. scitex/fts/_fig/_backend/__init__.py +53 -0
  282. scitex/fts/_fig/_backend/_export.py +165 -0
  283. scitex/fts/_fig/_backend/_parser.py +188 -0
  284. scitex/fts/_fig/_backend/_render.py +538 -0
  285. scitex/fts/_fig/_composite.py +345 -0
  286. scitex/fts/_fig/_dataclasses/_ChannelEncoding.py +46 -0
  287. scitex/fts/_fig/_dataclasses/_Encoding.py +82 -0
  288. scitex/fts/_fig/_dataclasses/_Theme.py +441 -0
  289. scitex/fts/_fig/_dataclasses/_TraceEncoding.py +52 -0
  290. scitex/fts/_fig/_dataclasses/__init__.py +47 -0
  291. scitex/fts/_fig/_editor/__init__.py +14 -0
  292. scitex/fts/_fig/_editor/_cui/__init__.py +33 -0
  293. scitex/fts/_fig/_editor/_cui/_backend_detector.py +39 -0
  294. scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +366 -0
  295. scitex/fts/_fig/_editor/_cui/_editor_launcher.py +175 -0
  296. scitex/fts/_fig/_editor/_cui/_manual_handler.py +52 -0
  297. scitex/fts/_fig/_editor/_cui/_panel_loader.py +246 -0
  298. scitex/fts/_fig/_editor/_cui/_path_resolver.py +66 -0
  299. scitex/fts/_fig/_editor/_defaults.py +300 -0
  300. scitex/fts/_fig/_editor/_gui/__init__.py +11 -0
  301. scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +20 -0
  302. scitex/fts/_fig/_editor/_gui/_flask_editor/_bbox.py +1339 -0
  303. scitex/fts/_fig/_editor/_gui/_flask_editor/_core.py +1688 -0
  304. scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +664 -0
  305. scitex/fts/_fig/_editor/_gui/_flask_editor/_renderer.py +853 -0
  306. scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +79 -0
  307. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +41 -0
  308. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +16 -0
  309. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +85 -0
  310. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +217 -0
  311. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +93 -0
  312. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +57 -0
  313. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +112 -0
  314. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +59 -0
  315. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +212 -0
  316. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/canvas.css +176 -0
  317. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +190 -0
  318. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +59 -0
  319. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +45 -0
  320. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +95 -0
  321. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +101 -0
  322. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +138 -0
  323. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +31 -0
  324. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +7 -0
  325. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +56 -0
  326. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +78 -0
  327. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +314 -0
  328. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +107 -0
  329. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +54 -0
  330. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +172 -0
  331. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +258 -0
  332. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +48 -0
  333. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +71 -0
  334. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +288 -0
  335. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +143 -0
  336. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +245 -0
  337. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +992 -0
  338. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +339 -0
  339. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +286 -0
  340. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +371 -0
  341. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +293 -0
  342. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +426 -0
  343. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +152 -0
  344. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +265 -0
  345. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +184 -0
  346. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +57 -0
  347. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +100 -0
  348. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +34 -0
  349. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +124 -0
  350. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +851 -0
  351. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +4932 -0
  352. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +1657 -0
  353. scitex/fts/_fig/_editor/_gui/_flask_editor.py +36 -0
  354. scitex/fts/_fig/_models/_Annotations.py +115 -0
  355. scitex/fts/_fig/_models/_Axes.py +152 -0
  356. scitex/fts/_fig/_models/_Figure.py +138 -0
  357. scitex/fts/_fig/_models/_Guides.py +104 -0
  358. scitex/fts/_fig/_models/_Plot.py +123 -0
  359. scitex/fts/_fig/_models/_Styles.py +245 -0
  360. scitex/fts/_fig/_models/__init__.py +80 -0
  361. scitex/fts/_fig/_models/_plot_types/__init__.py +156 -0
  362. scitex/fts/_fig/_models/_plot_types/_bar.py +43 -0
  363. scitex/fts/_fig/_models/_plot_types/_box.py +38 -0
  364. scitex/fts/_fig/_models/_plot_types/_distribution.py +36 -0
  365. scitex/fts/_fig/_models/_plot_types/_errorbar.py +60 -0
  366. scitex/fts/_fig/_models/_plot_types/_histogram.py +30 -0
  367. scitex/fts/_fig/_models/_plot_types/_image.py +61 -0
  368. scitex/fts/_fig/_models/_plot_types/_line.py +57 -0
  369. scitex/fts/_fig/_models/_plot_types/_scatter.py +30 -0
  370. scitex/fts/_fig/_models/_plot_types/_seaborn.py +121 -0
  371. scitex/fts/_fig/_models/_plot_types/_violin.py +36 -0
  372. scitex/fts/_fig/_utils/__init__.py +129 -0
  373. scitex/fts/_fig/_utils/_auto_layout.py +127 -0
  374. scitex/fts/_fig/_utils/_calc_bounds.py +111 -0
  375. scitex/fts/_fig/_utils/_const_sizes.py +48 -0
  376. scitex/fts/_fig/_utils/_convert_coords.py +77 -0
  377. scitex/fts/_fig/_utils/_get_template.py +178 -0
  378. scitex/fts/_fig/_utils/_normalize.py +73 -0
  379. scitex/fts/_fig/_utils/_plot_layout.py +397 -0
  380. scitex/fts/_fig/_utils/_validate.py +197 -0
  381. scitex/fts/_kinds/__init__.py +45 -0
  382. scitex/fts/_kinds/_figure/__init__.py +19 -0
  383. scitex/fts/_kinds/_figure/_composite.py +345 -0
  384. scitex/fts/_kinds/_plot/__init__.py +25 -0
  385. scitex/fts/_kinds/_plot/_backend/__init__.py +53 -0
  386. scitex/fts/_kinds/_plot/_backend/_export.py +165 -0
  387. scitex/fts/_kinds/_plot/_backend/_parser.py +188 -0
  388. scitex/fts/_kinds/_plot/_backend/_render.py +538 -0
  389. scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +46 -0
  390. scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +82 -0
  391. scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +441 -0
  392. scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +52 -0
  393. scitex/fts/_kinds/_plot/_dataclasses/__init__.py +47 -0
  394. scitex/fts/_kinds/_plot/_models/_Annotations.py +115 -0
  395. scitex/fts/_kinds/_plot/_models/_Axes.py +152 -0
  396. scitex/fts/_kinds/_plot/_models/_Figure.py +138 -0
  397. scitex/fts/_kinds/_plot/_models/_Guides.py +104 -0
  398. scitex/fts/_kinds/_plot/_models/_Plot.py +123 -0
  399. scitex/fts/_kinds/_plot/_models/_Styles.py +245 -0
  400. scitex/fts/_kinds/_plot/_models/__init__.py +80 -0
  401. scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +156 -0
  402. scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +43 -0
  403. scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +38 -0
  404. scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +36 -0
  405. scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +60 -0
  406. scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +30 -0
  407. scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +61 -0
  408. scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +57 -0
  409. scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +30 -0
  410. scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +121 -0
  411. scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +36 -0
  412. scitex/fts/_kinds/_plot/_utils/__init__.py +129 -0
  413. scitex/fts/_kinds/_plot/_utils/_auto_layout.py +127 -0
  414. scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +111 -0
  415. scitex/fts/_kinds/_plot/_utils/_const_sizes.py +48 -0
  416. scitex/fts/_kinds/_plot/_utils/_convert_coords.py +77 -0
  417. scitex/fts/_kinds/_plot/_utils/_get_template.py +178 -0
  418. scitex/fts/_kinds/_plot/_utils/_normalize.py +73 -0
  419. scitex/fts/_kinds/_plot/_utils/_plot_layout.py +397 -0
  420. scitex/fts/_kinds/_plot/_utils/_validate.py +197 -0
  421. scitex/fts/_kinds/_shape/__init__.py +141 -0
  422. scitex/fts/_kinds/_stats/__init__.py +56 -0
  423. scitex/fts/_kinds/_stats/_dataclasses/_Stats.py +423 -0
  424. scitex/fts/_kinds/_stats/_dataclasses/__init__.py +48 -0
  425. scitex/fts/_kinds/_table/__init__.py +72 -0
  426. scitex/fts/_kinds/_table/_latex/__init__.py +93 -0
  427. scitex/fts/_kinds/_table/_latex/_editor/__init__.py +11 -0
  428. scitex/fts/_kinds/_table/_latex/_editor/_app.py +725 -0
  429. scitex/fts/_kinds/_table/_latex/_export.py +279 -0
  430. scitex/fts/_kinds/_table/_latex/_figure_exporter.py +153 -0
  431. scitex/fts/_kinds/_table/_latex/_stats_formatter.py +274 -0
  432. scitex/fts/_kinds/_table/_latex/_table_exporter.py +362 -0
  433. scitex/fts/_kinds/_table/_latex/_utils.py +369 -0
  434. scitex/fts/_kinds/_table/_latex/_validator.py +445 -0
  435. scitex/fts/_kinds/_text/__init__.py +77 -0
  436. scitex/fts/_schemas/data_info.schema.json +75 -0
  437. scitex/fts/_schemas/encoding.schema.json +90 -0
  438. scitex/fts/_schemas/node.schema.json +145 -0
  439. scitex/fts/_schemas/render_manifest.schema.json +62 -0
  440. scitex/fts/_schemas/stats.schema.json +132 -0
  441. scitex/fts/_schemas/theme.schema.json +141 -0
  442. scitex/fts/_stats/__init__.py +48 -0
  443. scitex/fts/_stats/_dataclasses/_Stats.py +423 -0
  444. scitex/fts/_stats/_dataclasses/__init__.py +48 -0
  445. scitex/fts/_tables/__init__.py +65 -0
  446. scitex/fts/_tables/_latex/__init__.py +93 -0
  447. scitex/fts/_tables/_latex/_editor/__init__.py +11 -0
  448. scitex/fts/_tables/_latex/_editor/_app.py +725 -0
  449. scitex/fts/_tables/_latex/_export.py +279 -0
  450. scitex/fts/_tables/_latex/_figure_exporter.py +153 -0
  451. scitex/fts/_tables/_latex/_stats_formatter.py +274 -0
  452. scitex/fts/_tables/_latex/_table_exporter.py +362 -0
  453. scitex/fts/_tables/_latex/_utils.py +369 -0
  454. scitex/fts/_tables/_latex/_validator.py +445 -0
  455. scitex/gen/__init__.py +66 -25
  456. scitex/gen/misc.py +28 -0
  457. scitex/io/__init__.py +47 -20
  458. scitex/io/_load.py +87 -36
  459. scitex/io/_load_modules/__init__.py +10 -7
  460. scitex/io/_load_modules/_pandas.py +6 -1
  461. scitex/io/_save.py +299 -1556
  462. scitex/io/_save_modules/__init__.py +76 -19
  463. scitex/io/_save_modules/_figure_utils.py +90 -0
  464. scitex/io/_save_modules/_image_csv.py +497 -0
  465. scitex/io/_save_modules/_legends.py +91 -0
  466. scitex/io/_save_modules/_pltz_bundle.py +356 -0
  467. scitex/io/_save_modules/_pltz_stx.py +536 -0
  468. scitex/io/_save_modules/_stx_bundle.py +104 -0
  469. scitex/io/_save_modules/_symlink.py +96 -0
  470. scitex/io/_save_modules/_yaml.py +1 -1
  471. scitex/io/_save_modules/_zarr.py +64 -18
  472. scitex/io/bundle/README.md +212 -0
  473. scitex/io/bundle/__init__.py +110 -0
  474. scitex/io/{_bundle.py → bundle/_core.py} +219 -89
  475. scitex/io/bundle/_nested.py +713 -0
  476. scitex/io/bundle/_types.py +74 -0
  477. scitex/io/bundle/_zip.py +487 -0
  478. scitex/io/utils/h5_to_zarr.py +1 -1
  479. scitex/logging/__init__.py +108 -13
  480. scitex/logging/_errors.py +508 -0
  481. scitex/logging/_formatters.py +30 -6
  482. scitex/logging/_warnings.py +261 -0
  483. scitex/plt/__init__.py +4 -1
  484. scitex/plt/_figrecipe.py +236 -0
  485. scitex/plt/_subplots/_AxisWrapper.py +6 -0
  486. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +0 -0
  487. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +0 -0
  488. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +0 -0
  489. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +0 -0
  490. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +0 -0
  491. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +0 -0
  492. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +0 -0
  493. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +0 -0
  494. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +0 -0
  495. scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +0 -0
  496. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +0 -0
  497. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +0 -0
  498. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +0 -0
  499. scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +112 -1
  500. scitex/plt/_subplots/_FigWrapper.py +15 -0
  501. scitex/plt/_subplots/_SubplotsWrapper.py +125 -489
  502. scitex/plt/_subplots/_export_as_csv.py +11 -0
  503. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +2 -0
  504. scitex/plt/_subplots/_export_as_csv_formatters/_format_pcolormesh.py +66 -0
  505. scitex/plt/_subplots/_export_as_csv_formatters/_format_stackplot.py +62 -0
  506. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +0 -0
  507. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +0 -0
  508. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +0 -0
  509. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +0 -0
  510. scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +208 -0
  511. scitex/plt/_subplots/_fonts.py +71 -0
  512. scitex/plt/_subplots/_mm_layout.py +282 -0
  513. scitex/plt/gallery/__init__.py +99 -2
  514. scitex/plt/io/_layered_bundle.py +0 -0
  515. scitex/plt/styles/_plot_postprocess.py +3 -1
  516. scitex/plt/utils/_configure_mpl.py +16 -19
  517. scitex/repro/_RandomStateManager.py +13 -8
  518. scitex/resource/__init__.py +19 -1
  519. scitex/resource/_utils/_get_env_info.py +13 -25
  520. scitex/schema/__init__.py +149 -160
  521. scitex/schema/_encoding.py +273 -0
  522. scitex/schema/_figure_elements.py +406 -0
  523. scitex/schema/_plot.py +0 -0
  524. scitex/schema/_theme.py +360 -0
  525. scitex/schema/_validation.py +0 -98
  526. scitex/scholar/__init__.py +56 -14
  527. scitex/scholar/auth/ScholarAuthManager.py +1 -1
  528. scitex/scholar/auth/__init__.py +11 -2
  529. scitex/scholar/auth/providers/BaseAuthenticator.py +1 -1
  530. scitex/scholar/auth/providers/EZProxyAuthenticator.py +1 -1
  531. scitex/scholar/auth/providers/OpenAthensAuthenticator.py +1 -1
  532. scitex/scholar/auth/providers/ShibbolethAuthenticator.py +1 -1
  533. scitex/scholar/config/ScholarConfig.py +1 -1
  534. scitex/scholar/core/Scholar.py +1 -1
  535. scitex/session/_decorator.py +18 -16
  536. scitex/session/_lifecycle.py +9 -11
  537. scitex/session/template.py +9 -8
  538. scitex/sh/test_sh.py +72 -0
  539. scitex/sh/test_sh_simple.py +61 -0
  540. scitex/stats/__init__.py +221 -97
  541. scitex/stats/_schema.py +21 -22
  542. scitex/stats/descriptive/_circular.py +212 -351
  543. scitex/stats/descriptive/_describe.py +81 -132
  544. scitex/stats/descriptive/_nan.py +205 -433
  545. scitex/stats/descriptive/_real.py +127 -141
  546. scitex/str/_format_plot_text.py +5 -5
  547. scitex/str/_latex.py +26 -84
  548. scitex/str/_latex_fallback.py +53 -47
  549. scitex/web/_search_pubmed.py +5 -4
  550. scitex/writer/tests/test_diff_between.py +451 -0
  551. scitex/writer/tests/test_document_section.py +311 -0
  552. scitex/writer/tests/test_document_workflow.py +393 -0
  553. scitex/writer/tests/test_writer.py +361 -0
  554. scitex/writer/tests/test_writer_integration.py +303 -0
  555. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/METADATA +364 -181
  556. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/RECORD +479 -108
  557. scitex/fig/editor/_edit.py +0 -751
  558. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/ARCHITECTURE_EXAMPLE.md +0 -905
  559. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/BULLETIN_BOARD_EXAMPLE.md +0 -99
  560. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/PROJECT_DESCRIPTION_EXAMPLE.md +0 -96
  561. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/WHEEL +0 -0
  562. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/entry_points.txt +0 -0
  563. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/licenses/LICENSE +0 -0
@@ -21,6 +21,10 @@ import sys
21
21
  # SCITEX_LOG_FORMAT=debug python script.py
22
22
  LOG_FORMAT = os.getenv("SCITEX_LOG_FORMAT", "default")
23
23
 
24
+ # Force color output even when stdout is not a TTY (e.g., when piping through tee)
25
+ # SCITEX_FORCE_COLOR=1 python script.py | tee output.log
26
+ FORCE_COLOR = os.getenv("SCITEX_FORCE_COLOR", "").lower() in ("1", "true", "yes")
27
+
24
28
  # Available format templates
25
29
  FORMAT_TEMPLATES = {
26
30
  "minimal": "%(levelname)s: %(message)s",
@@ -80,6 +84,14 @@ class SciTeXConsoleFormatter(logging.Formatter):
80
84
  self.indent_width = indent_width
81
85
 
82
86
  def format(self, record):
87
+ # Handle leading newlines: extract and preserve them
88
+ msg = str(record.msg) if record.msg else ""
89
+ leading_newlines = ""
90
+ while msg.startswith("\n"):
91
+ leading_newlines += "\n"
92
+ msg = msg[1:]
93
+ record.msg = msg
94
+
83
95
  # Apply indentation if specified in record
84
96
  indent_level = getattr(record, "indent", 0)
85
97
  if indent_level > 0:
@@ -89,12 +101,23 @@ class SciTeXConsoleFormatter(logging.Formatter):
89
101
  # Use parent formatter to apply template
90
102
  formatted = super().format(record)
91
103
 
92
- # Check if we can use colors (stdout is a tty and not closed)
104
+ # Handle internal newlines: each line gets the level prefix
105
+ if "\n" in formatted:
106
+ lines = formatted.split("\n")
107
+ # First line already has prefix from parent formatter
108
+ # Add prefix to each continuation line
109
+ prefix = f"{record.levelname}: "
110
+ formatted = lines[0] + "\n" + "\n".join(
111
+ prefix + line if line.strip() else line
112
+ for line in lines[1:]
113
+ )
114
+
115
+ # Check if we can use colors (stdout is a tty and not closed, or forced)
93
116
  try:
94
- use_colors = hasattr(sys.stdout, "isatty") and sys.stdout.isatty()
117
+ use_colors = FORCE_COLOR or (hasattr(sys.stdout, "isatty") and sys.stdout.isatty())
95
118
  except ValueError:
96
119
  # stdout/stderr is closed
97
- use_colors = False
120
+ use_colors = FORCE_COLOR
98
121
 
99
122
  if use_colors:
100
123
  # Check for custom color override
@@ -103,15 +126,15 @@ class SciTeXConsoleFormatter(logging.Formatter):
103
126
  if custom_color and custom_color in self.COLOR_NAMES:
104
127
  # Use custom color
105
128
  color = self.COLOR_NAMES[custom_color]
106
- return f"{color}{formatted}{self.RESET}"
129
+ return f"{leading_newlines}{color}{formatted}{self.RESET}"
107
130
  else:
108
131
  # Use default color for log level
109
132
  levelname = record.levelname
110
133
  if levelname in self.COLORS:
111
134
  color = self.COLORS[levelname]
112
- return f"{color}{formatted}{self.RESET}"
135
+ return f"{leading_newlines}{color}{formatted}{self.RESET}"
113
136
 
114
- return formatted
137
+ return f"{leading_newlines}{formatted}"
115
138
 
116
139
 
117
140
  class SciTeXFileFormatter(logging.Formatter):
@@ -129,6 +152,7 @@ __all__ = [
129
152
  "SciTeXFileFormatter",
130
153
  "LOG_FORMAT",
131
154
  "FORMAT_TEMPLATES",
155
+ "FORCE_COLOR",
132
156
  ]
133
157
 
134
158
  # EOF
@@ -0,0 +1,261 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-12-21"
4
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/logging/_warnings.py
5
+
6
+ """Warning system for SciTeX, mimicking Python's warnings module.
7
+
8
+ Usage:
9
+ import scitex.logging as logging
10
+ from scitex.logging import UnitWarning
11
+
12
+ # Emit a warning
13
+ logging.warn("Missing units on axis label", UnitWarning)
14
+
15
+ # Filter warnings (like warnings.filterwarnings)
16
+ logging.filterwarnings("ignore", category=UnitWarning)
17
+ logging.filterwarnings("error", category=UnitWarning) # Raise as exception
18
+ logging.filterwarnings("always", category=UnitWarning) # Always show
19
+ """
20
+
21
+ import logging as _logging
22
+ from typing import Dict, Optional, Type, Set
23
+
24
+ # =============================================================================
25
+ # Warning Categories (similar to Python's warning classes)
26
+ # =============================================================================
27
+
28
+
29
+ class SciTeXWarning(UserWarning):
30
+ """Base warning class for all SciTeX warnings."""
31
+
32
+ pass
33
+
34
+
35
+ class UnitWarning(SciTeXWarning):
36
+ """Warning for axis label unit issues (educational for SI conventions).
37
+
38
+ Raised when:
39
+ - Axis labels are missing units
40
+ - Units use parentheses instead of brackets (SI prefers [])
41
+ - Units use division instead of negative exponents (m/s vs m·s⁻¹)
42
+ """
43
+
44
+ pass
45
+
46
+
47
+ class StyleWarning(SciTeXWarning):
48
+ """Warning for style/formatting issues."""
49
+
50
+ pass
51
+
52
+
53
+ class SciTeXDeprecationWarning(SciTeXWarning):
54
+ """Warning for deprecated SciTeX features."""
55
+
56
+ pass
57
+
58
+
59
+ class PerformanceWarning(SciTeXWarning):
60
+ """Warning for performance issues."""
61
+
62
+ pass
63
+
64
+
65
+ class DataLossWarning(SciTeXWarning):
66
+ """Warning for potential data loss."""
67
+
68
+ pass
69
+
70
+
71
+ # =============================================================================
72
+ # Warning Filter Registry
73
+ # =============================================================================
74
+
75
+ # Actions: "ignore", "error", "always", "default", "once", "module"
76
+ _filters: Dict[Type[SciTeXWarning], str] = {}
77
+ _seen_warnings: Set[str] = set() # For "once" action
78
+
79
+
80
+ def filterwarnings(
81
+ action: str,
82
+ category: Type[SciTeXWarning] = SciTeXWarning,
83
+ message: Optional[str] = None,
84
+ ) -> None:
85
+ """Control warning behavior (like warnings.filterwarnings).
86
+
87
+ Parameters
88
+ ----------
89
+ action : str
90
+ One of:
91
+ - "ignore": Never show this warning
92
+ - "error": Raise as exception
93
+ - "always": Always show
94
+ - "default": Show first occurrence per location
95
+ - "once": Show only once total
96
+ - "module": Show once per module
97
+ category : type
98
+ Warning category (default: SciTeXWarning = all)
99
+ message : str, optional
100
+ Regex pattern to match warning message (not implemented yet)
101
+
102
+ Examples
103
+ --------
104
+ >>> import scitex.logging as logging
105
+ >>> from scitex.logging import UnitWarning
106
+ >>> logging.filterwarnings("ignore", category=UnitWarning)
107
+ """
108
+ valid_actions = {"ignore", "error", "always", "default", "once", "module"}
109
+ if action not in valid_actions:
110
+ raise ValueError(f"Invalid action '{action}'. Must be one of: {valid_actions}")
111
+
112
+ _filters[category] = action
113
+
114
+
115
+ def resetwarnings() -> None:
116
+ """Reset all warning filters to default behavior."""
117
+ global _filters, _seen_warnings
118
+ _filters = {}
119
+ _seen_warnings = set()
120
+
121
+
122
+ def _get_action(category: Type[SciTeXWarning]) -> str:
123
+ """Get the action for a warning category, checking inheritance."""
124
+ # Check exact match first
125
+ if category in _filters:
126
+ return _filters[category]
127
+
128
+ # Check parent classes
129
+ for filter_cat, action in _filters.items():
130
+ if issubclass(category, filter_cat):
131
+ return action
132
+
133
+ # Default action
134
+ return "default"
135
+
136
+
137
+ # =============================================================================
138
+ # Warning Emission
139
+ # =============================================================================
140
+
141
+
142
+ def warn(
143
+ message: str,
144
+ category: Type[SciTeXWarning] = SciTeXWarning,
145
+ stacklevel: int = 2,
146
+ ) -> None:
147
+ """Emit a warning (like warnings.warn but integrated with scitex.logging).
148
+
149
+ Parameters
150
+ ----------
151
+ message : str
152
+ Warning message
153
+ category : type
154
+ Warning category (default: SciTeXWarning)
155
+ stacklevel : int
156
+ Stack level for source location (default: 2 = caller)
157
+
158
+ Examples
159
+ --------
160
+ >>> import scitex.logging as logging
161
+ >>> from scitex.logging import UnitWarning
162
+ >>> logging.warn("X axis has no units", UnitWarning)
163
+ """
164
+ import inspect
165
+
166
+ action = _get_action(category)
167
+
168
+ # Handle action
169
+ if action == "ignore":
170
+ return
171
+
172
+ if action == "error":
173
+ raise category(message)
174
+
175
+ # Get source location for "once", "module", "default" actions
176
+ frame = inspect.currentframe()
177
+ for _ in range(stacklevel):
178
+ if frame is not None:
179
+ frame = frame.f_back
180
+
181
+ location = ""
182
+ if frame is not None:
183
+ filename = frame.f_code.co_filename
184
+ lineno = frame.f_lineno
185
+ location = f"{filename}:{lineno}"
186
+
187
+ # Check if already seen
188
+ warn_key = f"{category.__name__}:{message}:{location}"
189
+
190
+ if action == "once":
191
+ if warn_key in _seen_warnings:
192
+ return
193
+ _seen_warnings.add(warn_key)
194
+ elif action == "default":
195
+ # Show first per location
196
+ loc_key = f"{category.__name__}:{location}"
197
+ if loc_key in _seen_warnings:
198
+ return
199
+ _seen_warnings.add(loc_key)
200
+ elif action == "module":
201
+ # Show once per module
202
+ if frame is not None:
203
+ module_key = f"{category.__name__}:{frame.f_code.co_filename}"
204
+ if module_key in _seen_warnings:
205
+ return
206
+ _seen_warnings.add(module_key)
207
+
208
+ # Emit via scitex.logging
209
+ logger = _logging.getLogger("scitex.warnings")
210
+ category_name = category.__name__
211
+
212
+ # Format: "UnitWarning: message"
213
+ logger.warning(f"{category_name}: {message}")
214
+
215
+
216
+ # =============================================================================
217
+ # Convenience Warning Functions
218
+ # =============================================================================
219
+
220
+
221
+ def warn_deprecated(
222
+ old_name: str, new_name: str, version: Optional[str] = None
223
+ ) -> None:
224
+ """Issue a deprecation warning."""
225
+ message = f"{old_name} is deprecated. Use {new_name} instead."
226
+ if version:
227
+ message += f" Will be removed in version {version}."
228
+ warn(message, SciTeXDeprecationWarning, stacklevel=3)
229
+
230
+
231
+ def warn_performance(operation: str, suggestion: str) -> None:
232
+ """Issue a performance warning."""
233
+ message = f"Performance warning in {operation}: {suggestion}"
234
+ warn(message, PerformanceWarning, stacklevel=3)
235
+
236
+
237
+ def warn_data_loss(operation: str, detail: str) -> None:
238
+ """Issue a data loss warning."""
239
+ message = f"Potential data loss in {operation}: {detail}"
240
+ warn(message, DataLossWarning, stacklevel=3)
241
+
242
+
243
+ __all__ = [
244
+ # Warning categories
245
+ "SciTeXWarning",
246
+ "UnitWarning",
247
+ "StyleWarning",
248
+ "SciTeXDeprecationWarning",
249
+ "PerformanceWarning",
250
+ "DataLossWarning",
251
+ # Functions
252
+ "warn",
253
+ "filterwarnings",
254
+ "resetwarnings",
255
+ # Convenience functions
256
+ "warn_deprecated",
257
+ "warn_performance",
258
+ "warn_data_loss",
259
+ ]
260
+
261
+ # EOF
scitex/plt/__init__.py CHANGED
@@ -179,7 +179,10 @@ try:
179
179
  except Exception:
180
180
  pass # Use matplotlib default colors if color module fails
181
181
 
182
- from ._tpl import termplot
182
+ try:
183
+ from ._tpl import termplot
184
+ except ImportError:
185
+ termplot = None
183
186
  from . import color
184
187
  from . import utils
185
188
  from . import ax
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: "2026-01-01 (ywatanabe)"
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/plt/_figrecipe.py
4
+ # ----------------------------------------
5
+ """
6
+ Figrecipe integration for scitex.
7
+
8
+ This module provides integration with figrecipe for reproducible matplotlib figures.
9
+ Uses csv_format="single" by default for backward compatibility with scitex's
10
+ SigmaPlot-compatible CSV format.
11
+
12
+ Usage
13
+ -----
14
+ >>> import scitex.plt as splt
15
+ >>> fig, ax = splt.subplots()
16
+ >>> ax.plot([1, 2, 3], [4, 5, 6], id='data')
17
+ >>> splt.save_recipe(fig, 'figure.yaml') # Saves recipe with single CSV
18
+ """
19
+
20
+ from pathlib import Path
21
+ from typing import Any, Dict, Literal, Optional, Tuple, Union
22
+
23
+ from scitex import logging
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+ # Check if figrecipe is available
28
+ try:
29
+ import figrecipe as fr
30
+
31
+ FIGRECIPE_AVAILABLE = True
32
+ except ImportError:
33
+ FIGRECIPE_AVAILABLE = False
34
+ fr = None
35
+
36
+
37
+ def check_figrecipe_available() -> bool:
38
+ """Check if figrecipe is installed."""
39
+ return FIGRECIPE_AVAILABLE
40
+
41
+
42
+ def subplots(
43
+ nrows: int = 1,
44
+ ncols: int = 1,
45
+ **kwargs,
46
+ ) -> Tuple[Any, Any]:
47
+ """Create recording-enabled subplots using figrecipe.
48
+
49
+ This is a wrapper around figrecipe.subplots() that creates
50
+ figures with recording capabilities for reproducibility.
51
+
52
+ Parameters
53
+ ----------
54
+ nrows, ncols : int
55
+ Number of rows and columns.
56
+ **kwargs
57
+ Additional arguments passed to figrecipe.subplots().
58
+
59
+ Returns
60
+ -------
61
+ fig : RecordingFigure
62
+ Figrecipe's wrapped figure.
63
+ axes : RecordingAxes or ndarray
64
+ Wrapped axes.
65
+
66
+ Raises
67
+ ------
68
+ ImportError
69
+ If figrecipe is not installed.
70
+ """
71
+ if not FIGRECIPE_AVAILABLE:
72
+ raise ImportError(
73
+ "figrecipe is not installed. Install with: pip install figrecipe"
74
+ )
75
+
76
+ return fr.subplots(nrows, ncols, **kwargs)
77
+
78
+
79
+ def save_recipe(
80
+ fig,
81
+ path: Union[str, Path],
82
+ csv_format: Literal["single", "separate"] = "single",
83
+ data_format: Literal["csv", "npz", "inline"] = "csv",
84
+ validate: bool = True,
85
+ verbose: bool = True,
86
+ **kwargs,
87
+ ) -> Optional[Tuple[Path, Path]]:
88
+ """Save figure recipe using figrecipe with scitex-compatible CSV format.
89
+
90
+ This function saves a matplotlib figure as a reproducible recipe using
91
+ figrecipe. By default, uses csv_format="single" for backward compatibility
92
+ with scitex's SigmaPlot-compatible CSV format.
93
+
94
+ Parameters
95
+ ----------
96
+ fig : matplotlib.figure.Figure or RecordingFigure
97
+ The figure to save.
98
+ path : str or Path
99
+ Output path (.yaml for recipe, .png/.pdf for image+recipe).
100
+ csv_format : str
101
+ CSV format: 'single' (scitex-compatible, default) or 'separate'.
102
+ - 'single': All columns in one CSV with scitex naming convention
103
+ - 'separate': One CSV per variable (figrecipe default)
104
+ data_format : str
105
+ Data format: 'csv' (default), 'npz', or 'inline'.
106
+ validate : bool
107
+ If True (default), validate reproducibility after saving.
108
+ verbose : bool
109
+ If True (default), print save status.
110
+ **kwargs
111
+ Additional arguments passed to figrecipe.save().
112
+
113
+ Returns
114
+ -------
115
+ tuple or None
116
+ (image_path, yaml_path) if successful, None if figrecipe unavailable.
117
+
118
+ Examples
119
+ --------
120
+ >>> import scitex.plt as splt
121
+ >>> fig, ax = splt.subplots()
122
+ >>> ax.plot([1, 2, 3], [4, 5, 6])
123
+ >>> splt.save_recipe(fig, 'output.yaml') # Single CSV format (scitex-compatible)
124
+ >>> splt.save_recipe(fig, 'output.yaml', csv_format='separate') # Separate CSVs
125
+ """
126
+ if not FIGRECIPE_AVAILABLE:
127
+ logger.warning(
128
+ "figrecipe is not installed. Recipe not saved. "
129
+ "Install with: pip install figrecipe"
130
+ )
131
+ return None
132
+
133
+ path = Path(path)
134
+
135
+ # Handle different figure types
136
+ # If it's a scitex FigWrapper, extract the matplotlib figure
137
+ if hasattr(fig, "_fig_mpl"):
138
+ mpl_fig = fig._fig_mpl
139
+ elif hasattr(fig, "figure"):
140
+ mpl_fig = fig.figure
141
+ else:
142
+ mpl_fig = fig
143
+
144
+ # Check if fig is already a figrecipe RecordingFigure
145
+ if hasattr(fig, "_recorder"):
146
+ # Already a RecordingFigure, use figrecipe's save directly
147
+ return fr.save(
148
+ fig,
149
+ path,
150
+ data_format=data_format,
151
+ csv_format=csv_format,
152
+ validate=validate,
153
+ verbose=verbose,
154
+ **kwargs,
155
+ )
156
+
157
+ # For regular matplotlib figures, we need to wrap them first
158
+ # This requires re-creating the figure with figrecipe
159
+ logger.warning(
160
+ "Figure is not a RecordingFigure. For full recipe support, "
161
+ "create figures with fr.subplots() or splt.subplots_recipe()."
162
+ )
163
+ return None
164
+
165
+
166
+ def load_recipe(path: Union[str, Path]) -> Tuple[Any, Any]:
167
+ """Load and reproduce a figure from a recipe file.
168
+
169
+ Parameters
170
+ ----------
171
+ path : str or Path
172
+ Path to .yaml recipe file.
173
+
174
+ Returns
175
+ -------
176
+ fig : matplotlib.figure.Figure
177
+ Reproduced figure.
178
+ axes : Axes or list of Axes
179
+ Reproduced axes.
180
+
181
+ Raises
182
+ ------
183
+ ImportError
184
+ If figrecipe is not installed.
185
+ """
186
+ if not FIGRECIPE_AVAILABLE:
187
+ raise ImportError(
188
+ "figrecipe is not installed. Install with: pip install figrecipe"
189
+ )
190
+
191
+ return fr.reproduce(path)
192
+
193
+
194
+ def recipe_info(path: Union[str, Path]) -> Dict[str, Any]:
195
+ """Get information about a recipe without reproducing.
196
+
197
+ Parameters
198
+ ----------
199
+ path : str or Path
200
+ Path to .yaml recipe file.
201
+
202
+ Returns
203
+ -------
204
+ dict
205
+ Recipe information including figure settings, calls, etc.
206
+
207
+ Raises
208
+ ------
209
+ ImportError
210
+ If figrecipe is not installed.
211
+ """
212
+ if not FIGRECIPE_AVAILABLE:
213
+ raise ImportError(
214
+ "figrecipe is not installed. Install with: pip install figrecipe"
215
+ )
216
+
217
+ return fr.info(path)
218
+
219
+
220
+ # Convenience aliases
221
+ reproduce = load_recipe
222
+ info = recipe_info
223
+
224
+
225
+ __all__ = [
226
+ "FIGRECIPE_AVAILABLE",
227
+ "check_figrecipe_available",
228
+ "subplots",
229
+ "save_recipe",
230
+ "load_recipe",
231
+ "reproduce",
232
+ "recipe_info",
233
+ "info",
234
+ ]
235
+
236
+ # EOF
@@ -189,10 +189,14 @@ class AxisWrapper(
189
189
  "errorbar",
190
190
  "step",
191
191
  "stem",
192
+ # Fill and area plots
193
+ "fill",
194
+ "stackplot",
192
195
  # Statistical plots
193
196
  "hist2d",
194
197
  "hexbin",
195
198
  "pie",
199
+ "eventplot",
196
200
  # Contour plots
197
201
  "contour",
198
202
  "contourf",
@@ -202,6 +206,8 @@ class AxisWrapper(
202
206
  "imshow",
203
207
  "matshow",
204
208
  "spy",
209
+ "pcolormesh",
210
+ "pcolor",
205
211
  # Quiver plots
206
212
  "quiver",
207
213
  "streamplot",