scitex 2.8.1__py3-none-any.whl → 2.10.2__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 (415) 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/dict/_pop_keys.py +1 -7
  105. scitex/dsp/__init__.py +15 -10
  106. scitex/dsp/add_noise.py +5 -2
  107. scitex/dsp/example.py +35 -22
  108. scitex/dsp/filt.py +8 -3
  109. scitex/dsp/reference.py +3 -2
  110. scitex/dsp/utils/__init__.py +2 -1
  111. scitex/dsp/utils/_differential_bandpass_filters.py +14 -4
  112. scitex/dt/__init__.py +39 -2
  113. scitex/errors.py +82 -521
  114. scitex/fig/__init__.py +4 -4
  115. scitex/fig/editor/edit/panel_loader.py +1 -1
  116. scitex/fig/io/_bundle.py +7 -7
  117. scitex/fts/README.md +262 -0
  118. scitex/fts/TODO.md +66 -0
  119. scitex/fts/__init__.py +90 -0
  120. scitex/fts/_bundle/README_IN_BUNDLE.md +102 -0
  121. scitex/fts/_bundle/_FTS.py +657 -0
  122. scitex/fts/_bundle/__init__.py +38 -0
  123. scitex/fts/_bundle/_children.py +216 -0
  124. scitex/fts/_bundle/_conversion/__init__.py +15 -0
  125. scitex/fts/_bundle/_conversion/_bundle2dict.py +44 -0
  126. scitex/fts/_bundle/_conversion/_dict2bundle.py +50 -0
  127. scitex/fts/_bundle/_dataclasses/_Axes.py +57 -0
  128. scitex/fts/_bundle/_dataclasses/_BBox.py +54 -0
  129. scitex/fts/_bundle/_dataclasses/_ColumnDef.py +72 -0
  130. scitex/fts/_bundle/_dataclasses/_DataFormat.py +40 -0
  131. scitex/fts/_bundle/_dataclasses/_DataInfo.py +135 -0
  132. scitex/fts/_bundle/_dataclasses/_DataSource.py +44 -0
  133. scitex/fts/_bundle/_dataclasses/_Node.py +319 -0
  134. scitex/fts/_bundle/_dataclasses/_NodeRefs.py +45 -0
  135. scitex/fts/_bundle/_dataclasses/_SizeMM.py +38 -0
  136. scitex/fts/_bundle/_dataclasses/__init__.py +35 -0
  137. scitex/fts/_bundle/_extractors/__init__.py +32 -0
  138. scitex/fts/_bundle/_extractors/_extract_bar.py +131 -0
  139. scitex/fts/_bundle/_extractors/_extract_line.py +71 -0
  140. scitex/fts/_bundle/_extractors/_extract_scatter.py +79 -0
  141. scitex/fts/_bundle/_loader.py +134 -0
  142. scitex/fts/_bundle/_mpl_helpers.py +389 -0
  143. scitex/fts/_bundle/_saver.py +269 -0
  144. scitex/fts/_bundle/_storage.py +200 -0
  145. scitex/fts/_bundle/_utils/__init__.py +55 -0
  146. scitex/fts/_bundle/_utils/_const.py +26 -0
  147. scitex/fts/_bundle/_utils/_errors.py +73 -0
  148. scitex/fts/_bundle/_utils/_generate.py +21 -0
  149. scitex/fts/_bundle/_utils/_types.py +76 -0
  150. scitex/fts/_bundle/_validation.py +434 -0
  151. scitex/fts/_bundle/_zipbundle.py +165 -0
  152. scitex/fts/_fig/__init__.py +22 -0
  153. scitex/fts/_fig/_backend/__init__.py +53 -0
  154. scitex/fts/_fig/_backend/_export.py +165 -0
  155. scitex/fts/_fig/_backend/_parser.py +188 -0
  156. scitex/fts/_fig/_backend/_render.py +538 -0
  157. scitex/fts/_fig/_composite.py +345 -0
  158. scitex/fts/_fig/_dataclasses/_ChannelEncoding.py +46 -0
  159. scitex/fts/_fig/_dataclasses/_Encoding.py +82 -0
  160. scitex/fts/_fig/_dataclasses/_Theme.py +441 -0
  161. scitex/fts/_fig/_dataclasses/_TraceEncoding.py +52 -0
  162. scitex/fts/_fig/_dataclasses/__init__.py +47 -0
  163. scitex/fts/_fig/_editor/__init__.py +14 -0
  164. scitex/fts/_fig/_editor/_cui/__init__.py +33 -0
  165. scitex/fts/_fig/_editor/_cui/_backend_detector.py +39 -0
  166. scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +366 -0
  167. scitex/fts/_fig/_editor/_cui/_editor_launcher.py +175 -0
  168. scitex/fts/_fig/_editor/_cui/_manual_handler.py +52 -0
  169. scitex/fts/_fig/_editor/_cui/_panel_loader.py +246 -0
  170. scitex/fts/_fig/_editor/_cui/_path_resolver.py +66 -0
  171. scitex/fts/_fig/_editor/_defaults.py +300 -0
  172. scitex/fts/_fig/_editor/_gui/__init__.py +11 -0
  173. scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +20 -0
  174. scitex/fts/_fig/_editor/_gui/_flask_editor/_bbox.py +1339 -0
  175. scitex/fts/_fig/_editor/_gui/_flask_editor/_core.py +1688 -0
  176. scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +664 -0
  177. scitex/fts/_fig/_editor/_gui/_flask_editor/_renderer.py +853 -0
  178. scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +79 -0
  179. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +41 -0
  180. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +16 -0
  181. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +85 -0
  182. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +217 -0
  183. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +93 -0
  184. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +57 -0
  185. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +112 -0
  186. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +59 -0
  187. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +212 -0
  188. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/canvas.css +176 -0
  189. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +190 -0
  190. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +59 -0
  191. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +45 -0
  192. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +95 -0
  193. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +101 -0
  194. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +138 -0
  195. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +31 -0
  196. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +7 -0
  197. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +56 -0
  198. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +78 -0
  199. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +314 -0
  200. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +107 -0
  201. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +54 -0
  202. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +172 -0
  203. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +258 -0
  204. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +48 -0
  205. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +71 -0
  206. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +288 -0
  207. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +143 -0
  208. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +245 -0
  209. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +992 -0
  210. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +339 -0
  211. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +286 -0
  212. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +371 -0
  213. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +293 -0
  214. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +426 -0
  215. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +152 -0
  216. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +265 -0
  217. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +184 -0
  218. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +57 -0
  219. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +100 -0
  220. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +34 -0
  221. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +124 -0
  222. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +851 -0
  223. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +4932 -0
  224. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +1657 -0
  225. scitex/fts/_fig/_editor/_gui/_flask_editor.py +36 -0
  226. scitex/fts/_fig/_models/_Annotations.py +115 -0
  227. scitex/fts/_fig/_models/_Axes.py +152 -0
  228. scitex/fts/_fig/_models/_Figure.py +138 -0
  229. scitex/fts/_fig/_models/_Guides.py +104 -0
  230. scitex/fts/_fig/_models/_Plot.py +123 -0
  231. scitex/fts/_fig/_models/_Styles.py +245 -0
  232. scitex/fts/_fig/_models/__init__.py +80 -0
  233. scitex/fts/_fig/_models/_plot_types/__init__.py +156 -0
  234. scitex/fts/_fig/_models/_plot_types/_bar.py +43 -0
  235. scitex/fts/_fig/_models/_plot_types/_box.py +38 -0
  236. scitex/fts/_fig/_models/_plot_types/_distribution.py +36 -0
  237. scitex/fts/_fig/_models/_plot_types/_errorbar.py +60 -0
  238. scitex/fts/_fig/_models/_plot_types/_histogram.py +30 -0
  239. scitex/fts/_fig/_models/_plot_types/_image.py +61 -0
  240. scitex/fts/_fig/_models/_plot_types/_line.py +57 -0
  241. scitex/fts/_fig/_models/_plot_types/_scatter.py +30 -0
  242. scitex/fts/_fig/_models/_plot_types/_seaborn.py +121 -0
  243. scitex/fts/_fig/_models/_plot_types/_violin.py +36 -0
  244. scitex/fts/_fig/_utils/__init__.py +129 -0
  245. scitex/fts/_fig/_utils/_auto_layout.py +127 -0
  246. scitex/fts/_fig/_utils/_calc_bounds.py +111 -0
  247. scitex/fts/_fig/_utils/_const_sizes.py +48 -0
  248. scitex/fts/_fig/_utils/_convert_coords.py +77 -0
  249. scitex/fts/_fig/_utils/_get_template.py +178 -0
  250. scitex/fts/_fig/_utils/_normalize.py +73 -0
  251. scitex/fts/_fig/_utils/_plot_layout.py +397 -0
  252. scitex/fts/_fig/_utils/_validate.py +197 -0
  253. scitex/fts/_kinds/__init__.py +45 -0
  254. scitex/fts/_kinds/_figure/__init__.py +19 -0
  255. scitex/fts/_kinds/_figure/_composite.py +345 -0
  256. scitex/fts/_kinds/_plot/__init__.py +25 -0
  257. scitex/fts/_kinds/_plot/_backend/__init__.py +53 -0
  258. scitex/fts/_kinds/_plot/_backend/_export.py +165 -0
  259. scitex/fts/_kinds/_plot/_backend/_parser.py +188 -0
  260. scitex/fts/_kinds/_plot/_backend/_render.py +538 -0
  261. scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +46 -0
  262. scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +82 -0
  263. scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +441 -0
  264. scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +52 -0
  265. scitex/fts/_kinds/_plot/_dataclasses/__init__.py +47 -0
  266. scitex/fts/_kinds/_plot/_models/_Annotations.py +115 -0
  267. scitex/fts/_kinds/_plot/_models/_Axes.py +152 -0
  268. scitex/fts/_kinds/_plot/_models/_Figure.py +138 -0
  269. scitex/fts/_kinds/_plot/_models/_Guides.py +104 -0
  270. scitex/fts/_kinds/_plot/_models/_Plot.py +123 -0
  271. scitex/fts/_kinds/_plot/_models/_Styles.py +245 -0
  272. scitex/fts/_kinds/_plot/_models/__init__.py +80 -0
  273. scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +156 -0
  274. scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +43 -0
  275. scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +38 -0
  276. scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +36 -0
  277. scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +60 -0
  278. scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +30 -0
  279. scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +61 -0
  280. scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +57 -0
  281. scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +30 -0
  282. scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +121 -0
  283. scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +36 -0
  284. scitex/fts/_kinds/_plot/_utils/__init__.py +129 -0
  285. scitex/fts/_kinds/_plot/_utils/_auto_layout.py +127 -0
  286. scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +111 -0
  287. scitex/fts/_kinds/_plot/_utils/_const_sizes.py +48 -0
  288. scitex/fts/_kinds/_plot/_utils/_convert_coords.py +77 -0
  289. scitex/fts/_kinds/_plot/_utils/_get_template.py +178 -0
  290. scitex/fts/_kinds/_plot/_utils/_normalize.py +73 -0
  291. scitex/fts/_kinds/_plot/_utils/_plot_layout.py +397 -0
  292. scitex/fts/_kinds/_plot/_utils/_validate.py +197 -0
  293. scitex/fts/_kinds/_shape/__init__.py +141 -0
  294. scitex/fts/_kinds/_stats/__init__.py +56 -0
  295. scitex/fts/_kinds/_stats/_dataclasses/_Stats.py +423 -0
  296. scitex/fts/_kinds/_stats/_dataclasses/__init__.py +48 -0
  297. scitex/fts/_kinds/_table/__init__.py +72 -0
  298. scitex/fts/_kinds/_table/_latex/__init__.py +93 -0
  299. scitex/fts/_kinds/_table/_latex/_editor/__init__.py +11 -0
  300. scitex/fts/_kinds/_table/_latex/_editor/_app.py +725 -0
  301. scitex/fts/_kinds/_table/_latex/_export.py +279 -0
  302. scitex/fts/_kinds/_table/_latex/_figure_exporter.py +153 -0
  303. scitex/fts/_kinds/_table/_latex/_stats_formatter.py +274 -0
  304. scitex/fts/_kinds/_table/_latex/_table_exporter.py +362 -0
  305. scitex/fts/_kinds/_table/_latex/_utils.py +369 -0
  306. scitex/fts/_kinds/_table/_latex/_validator.py +445 -0
  307. scitex/fts/_kinds/_text/__init__.py +77 -0
  308. scitex/fts/_schemas/data_info.schema.json +75 -0
  309. scitex/fts/_schemas/encoding.schema.json +90 -0
  310. scitex/fts/_schemas/node.schema.json +145 -0
  311. scitex/fts/_schemas/render_manifest.schema.json +62 -0
  312. scitex/fts/_schemas/stats.schema.json +132 -0
  313. scitex/fts/_schemas/theme.schema.json +141 -0
  314. scitex/fts/_stats/__init__.py +48 -0
  315. scitex/fts/_stats/_dataclasses/_Stats.py +423 -0
  316. scitex/fts/_stats/_dataclasses/__init__.py +48 -0
  317. scitex/fts/_tables/__init__.py +65 -0
  318. scitex/fts/_tables/_latex/__init__.py +93 -0
  319. scitex/fts/_tables/_latex/_editor/__init__.py +11 -0
  320. scitex/fts/_tables/_latex/_editor/_app.py +725 -0
  321. scitex/fts/_tables/_latex/_export.py +279 -0
  322. scitex/fts/_tables/_latex/_figure_exporter.py +153 -0
  323. scitex/fts/_tables/_latex/_stats_formatter.py +274 -0
  324. scitex/fts/_tables/_latex/_table_exporter.py +362 -0
  325. scitex/fts/_tables/_latex/_utils.py +369 -0
  326. scitex/fts/_tables/_latex/_validator.py +445 -0
  327. scitex/gen/__init__.py +66 -25
  328. scitex/gen/misc.py +28 -0
  329. scitex/io/__init__.py +47 -32
  330. scitex/io/_load.py +87 -36
  331. scitex/io/_load_modules/__init__.py +10 -7
  332. scitex/io/_load_modules/_pandas.py +6 -1
  333. scitex/io/_save.py +299 -1556
  334. scitex/io/_save_modules/__init__.py +76 -19
  335. scitex/io/_save_modules/_figure_utils.py +90 -0
  336. scitex/io/_save_modules/_image_csv.py +497 -0
  337. scitex/io/_save_modules/_legends.py +91 -0
  338. scitex/io/_save_modules/_pltz_bundle.py +356 -0
  339. scitex/io/_save_modules/_pltz_stx.py +536 -0
  340. scitex/io/_save_modules/_stx_bundle.py +104 -0
  341. scitex/io/_save_modules/_symlink.py +96 -0
  342. scitex/io/_save_modules/_yaml.py +1 -1
  343. scitex/io/_save_modules/_zarr.py +64 -18
  344. scitex/io/bundle/README.md +212 -0
  345. scitex/io/bundle/__init__.py +110 -0
  346. scitex/io/{_bundle.py → bundle/_core.py} +168 -97
  347. scitex/io/bundle/_nested.py +713 -0
  348. scitex/io/bundle/_types.py +74 -0
  349. scitex/io/{_zip_bundle.py → bundle/_zip.py} +93 -45
  350. scitex/io/utils/h5_to_zarr.py +1 -1
  351. scitex/logging/__init__.py +108 -13
  352. scitex/logging/_errors.py +508 -0
  353. scitex/logging/_formatters.py +30 -6
  354. scitex/logging/_warnings.py +261 -0
  355. scitex/plt/__init__.py +4 -1
  356. scitex/plt/_figrecipe.py +236 -0
  357. scitex/plt/_subplots/_AxisWrapper.py +6 -0
  358. scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +112 -1
  359. scitex/plt/_subplots/_FigWrapper.py +15 -0
  360. scitex/plt/_subplots/_SubplotsWrapper.py +125 -489
  361. scitex/plt/_subplots/_export_as_csv.py +11 -0
  362. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +2 -0
  363. scitex/plt/_subplots/_export_as_csv_formatters/_format_pcolormesh.py +66 -0
  364. scitex/plt/_subplots/_export_as_csv_formatters/_format_stackplot.py +62 -0
  365. scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +208 -0
  366. scitex/plt/_subplots/_fonts.py +71 -0
  367. scitex/plt/_subplots/_mm_layout.py +282 -0
  368. scitex/plt/gallery/__init__.py +99 -2
  369. scitex/plt/styles/_plot_postprocess.py +3 -1
  370. scitex/plt/utils/_configure_mpl.py +16 -19
  371. scitex/repro/_RandomStateManager.py +13 -8
  372. scitex/resource/__init__.py +19 -1
  373. scitex/resource/_utils/_get_env_info.py +13 -25
  374. scitex/schema/__init__.py +149 -160
  375. scitex/schema/_encoding.py +273 -0
  376. scitex/schema/_figure_elements.py +406 -0
  377. scitex/schema/_theme.py +360 -0
  378. scitex/schema/_validation.py +0 -98
  379. scitex/scholar/__init__.py +56 -14
  380. scitex/scholar/auth/ScholarAuthManager.py +1 -1
  381. scitex/scholar/auth/__init__.py +11 -2
  382. scitex/scholar/auth/providers/BaseAuthenticator.py +1 -1
  383. scitex/scholar/auth/providers/EZProxyAuthenticator.py +1 -1
  384. scitex/scholar/auth/providers/OpenAthensAuthenticator.py +1 -1
  385. scitex/scholar/auth/providers/ShibbolethAuthenticator.py +1 -1
  386. scitex/scholar/config/ScholarConfig.py +1 -1
  387. scitex/scholar/core/Scholar.py +1 -1
  388. scitex/session/_decorator.py +18 -16
  389. scitex/session/_lifecycle.py +9 -11
  390. scitex/session/template.py +9 -8
  391. scitex/sh/test_sh.py +72 -0
  392. scitex/sh/test_sh_simple.py +61 -0
  393. scitex/stats/__init__.py +221 -97
  394. scitex/stats/_schema.py +21 -22
  395. scitex/stats/descriptive/_circular.py +212 -351
  396. scitex/stats/descriptive/_describe.py +81 -132
  397. scitex/stats/descriptive/_nan.py +205 -433
  398. scitex/stats/descriptive/_real.py +127 -141
  399. scitex/str/_format_plot_text.py +5 -5
  400. scitex/str/_latex.py +26 -84
  401. scitex/str/_latex_fallback.py +53 -47
  402. scitex/web/_search_pubmed.py +5 -4
  403. scitex/writer/tests/test_diff_between.py +451 -0
  404. scitex/writer/tests/test_document_section.py +311 -0
  405. scitex/writer/tests/test_document_workflow.py +393 -0
  406. scitex/writer/tests/test_writer.py +361 -0
  407. scitex/writer/tests/test_writer_integration.py +303 -0
  408. {scitex-2.8.1.dist-info → scitex-2.10.2.dist-info}/METADATA +368 -183
  409. {scitex-2.8.1.dist-info → scitex-2.10.2.dist-info}/RECORD +412 -97
  410. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/ARCHITECTURE_EXAMPLE.md +0 -905
  411. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/BULLETIN_BOARD_EXAMPLE.md +0 -99
  412. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/PROJECT_DESCRIPTION_EXAMPLE.md +0 -96
  413. {scitex-2.8.1.dist-info → scitex-2.10.2.dist-info}/WHEEL +0 -0
  414. {scitex-2.8.1.dist-info → scitex-2.10.2.dist-info}/entry_points.txt +0 -0
  415. {scitex-2.8.1.dist-info → scitex-2.10.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-12-21 13:11:56 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_save_modules/_symlink.py
5
+
6
+
7
+ # Timestamp: 2025-12-19
8
+
9
+ """Symlink creation utilities for save operations."""
10
+
11
+ import os
12
+ from pathlib import Path
13
+
14
+ from scitex import logging
15
+ from scitex.path._clean import clean
16
+ from scitex.sh import sh
17
+ from scitex.str._color_text import color_text
18
+
19
+ logger = logging.getLogger()
20
+
21
+
22
+ def symlink(spath, spath_cwd, symlink_from_cwd, verbose):
23
+ """Create a symbolic link from the current working directory."""
24
+ if symlink_from_cwd and (spath != spath_cwd):
25
+ os.makedirs(os.path.dirname(spath_cwd), exist_ok=True)
26
+ sh(["rm", "-f", f"{spath_cwd}"], verbose=False)
27
+ sh(["ln", "-sfr", f"{spath}", f"{spath_cwd}"], verbose=False)
28
+ if verbose:
29
+ # Get file extension to provide more informative message
30
+ ext = os.path.splitext(spath_cwd)[1].lower()
31
+ logger.success(color_text(f"(Symlinked to: {spath_cwd})"))
32
+
33
+
34
+ # def symlink_to(spath_final, symlink_to_path, verbose):
35
+ # """Create a symbolic link at the specified path pointing to the saved file."""
36
+ # if symlink_to_path:
37
+ # # Convert Path objects to strings for consistency
38
+ # if isinstance(symlink_to_path, Path):
39
+ # symlink_to_path = str(symlink_to_path)
40
+
41
+ # # Clean the symlink path
42
+ # symlink_to_path = clean(symlink_to_path)
43
+
44
+ # # Ensure the symlink directory exists (only if there is a directory component)
45
+ # symlink_dir = os.path.dirname(symlink_to_path)
46
+ # if symlink_dir: # Only create directory if there's a directory component
47
+ # os.makedirs(symlink_dir, exist_ok=True)
48
+
49
+ # # Remove existing symlink or file
50
+ # sh(["rm", "-f", f"{symlink_to_path}"], verbose=False)
51
+
52
+ # # Create the symlink using relative path for robustness
53
+ # sh(["ln", "-sfr", f"{spath_final}", f"{symlink_to_path}"], verbose=False)
54
+
55
+
56
+ # if verbose:
57
+ # symlink_to_full = (
58
+ # os.path.realpath(symlink_to_path) + "/" + os.path.basename(spath_final)
59
+ # )
60
+ # logger.success(f"Symlinked: {spath_final} ->\n {symlink_to_full}")
61
+ def symlink_to(spath_final, symlink_to_path, verbose):
62
+ """Create a symbolic link at the specified path pointing to the saved file."""
63
+ if symlink_to_path:
64
+ if isinstance(symlink_to_path, Path):
65
+ symlink_to_path = str(symlink_to_path)
66
+
67
+ symlink_to_path = clean(symlink_to_path)
68
+
69
+ if not os.path.isabs(symlink_to_path):
70
+ symlink_to_path = os.path.abspath(symlink_to_path)
71
+
72
+ if os.path.isdir(symlink_to_path) or (
73
+ not os.path.exists(symlink_to_path)
74
+ and not os.path.splitext(symlink_to_path)[1]
75
+ ):
76
+ symlink_to_path = os.path.join(
77
+ symlink_to_path, os.path.basename(spath_final)
78
+ )
79
+
80
+ symlink_dir = os.path.dirname(symlink_to_path)
81
+ if symlink_dir:
82
+ os.makedirs(symlink_dir, exist_ok=True)
83
+
84
+ sh(["rm", "-f", f"{symlink_to_path}"], verbose=False)
85
+ sh(
86
+ ["ln", "-sfr", f"{spath_final}", f"{symlink_to_path}"],
87
+ verbose=False,
88
+ )
89
+
90
+ if verbose:
91
+ logger.success(
92
+ f"Symlinked: {spath_final} ->\n"
93
+ f" {symlink_to_path}"
94
+ )
95
+
96
+ # EOF
@@ -62,7 +62,7 @@ def _save_yaml(obj, spath):
62
62
 
63
63
  yaml = YAML()
64
64
  yaml.preserve_quotes = True
65
- yaml.indent(mapping=4, sequence=4, offset=4)
65
+ yaml.indent(mapping=4, sequence=4, offset=2)
66
66
 
67
67
  with open(spath, "w") as f:
68
68
  yaml.dump(obj_with_strings, f)
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
2
  # Timestamp: "2025-07-11 15:44:00 (ywatanabe)"
4
3
  # File: /ssh:sp:/home/ywatanabe/proj/scitex_repo/src/scitex/io/_save_modules/_zarr.py
5
4
  # ----------------------------------------
@@ -13,7 +12,55 @@ from typing import Any, Optional
13
12
 
14
13
  import numpy as np
15
14
  import zarr
16
- from numcodecs import Zstd, LZ4, GZip
15
+
16
+ # Detect Zarr version for API compatibility
17
+ ZARR_V3 = int(zarr.__version__.split(".")[0]) >= 3
18
+
19
+
20
+ def _get_compressor(compressor_name):
21
+ """Get compressor based on Zarr version."""
22
+ if ZARR_V3:
23
+ from zarr.codecs import GzipCodec, ZstdCodec
24
+
25
+ compressor_map = {
26
+ "zstd": ZstdCodec(level=3),
27
+ "gzip": GzipCodec(level=5),
28
+ }
29
+ return compressor_map.get(compressor_name.lower(), ZstdCodec(level=3))
30
+ else:
31
+ from numcodecs import LZ4, GZip, Zstd
32
+
33
+ compressor_map = {
34
+ "zstd": Zstd(level=3),
35
+ "lz4": LZ4(acceleration=1),
36
+ "gzip": GZip(level=5),
37
+ }
38
+ return compressor_map.get(compressor_name.lower(), Zstd(level=3))
39
+
40
+
41
+ def _create_array(group, name, data, chunks=True, compressor=None, **kwargs):
42
+ """Create array with version-appropriate API."""
43
+ if ZARR_V3:
44
+ # Zarr v3 API: use create_array with compressors list
45
+ comp_kwargs = {}
46
+ if compressor is not None:
47
+ comp_kwargs["compressors"] = [compressor]
48
+
49
+ # Handle chunks parameter: v3 doesn't accept True/None
50
+ if chunks is True:
51
+ chunks = "auto"
52
+ elif chunks is None or chunks is False:
53
+ # Don't pass chunks, let zarr use defaults
54
+ return group.create_array(name, data=data, **comp_kwargs, **kwargs)
55
+
56
+ return group.create_array(
57
+ name, data=data, chunks=chunks, **comp_kwargs, **kwargs
58
+ )
59
+ else:
60
+ # Zarr v2 API: use create_dataset with compressor
61
+ return group.create_dataset(
62
+ name, data=data, chunks=chunks, compressor=compressor, **kwargs
63
+ )
17
64
 
18
65
 
19
66
  def _save_zarr(
@@ -38,7 +85,7 @@ def _save_zarr(
38
85
  key : str, optional
39
86
  Key/group path within Zarr store
40
87
  compressor : str
41
- Compression algorithm ('zstd', 'lz4', 'gzip')
88
+ Compression algorithm ('zstd', 'gzip')
42
89
  chunks : bool or tuple
43
90
  Chunking strategy
44
91
  store_type : str
@@ -60,7 +107,7 @@ def _save_zarr(
60
107
  # Create appropriate store
61
108
  if store_type == "zip":
62
109
  # Single file ZIP store
63
- store = zarr.ZipStore(spath, mode="w")
110
+ store = zarr.storage.ZipStore(spath, mode="w")
64
111
  root = zarr.open(store, mode="w")
65
112
  else:
66
113
  # Directory store
@@ -71,17 +118,12 @@ def _save_zarr(
71
118
  # Open or create Zarr store
72
119
  try:
73
120
  root = zarr.open(spath, mode="a")
74
- except:
121
+ except Exception:
75
122
  root = zarr.open(spath, mode="w")
76
123
 
77
124
  # Handle compressor configuration
78
125
  if isinstance(compressor, str):
79
- compressor_map = {
80
- "zstd": Zstd(level=3),
81
- "lz4": LZ4(acceleration=1),
82
- "gzip": GZip(level=5),
83
- }
84
- compressor = compressor_map.get(compressor.lower(), Zstd(level=3))
126
+ compressor = _get_compressor(compressor)
85
127
 
86
128
  # Navigate to target group
87
129
  if key:
@@ -109,12 +151,14 @@ def _save_zarr(
109
151
  for dataset_name, data in obj.items():
110
152
  if isinstance(data, str):
111
153
  # String data
112
- target_group.create_dataset(
113
- dataset_name, data=np.array(data), compressor=None
154
+ _create_array(
155
+ target_group, dataset_name, data=np.array(data), compressor=None
114
156
  )
115
157
  elif isinstance(data, (int, float, bool)):
116
- # Scalar data - no compression needed
117
- target_group.create_dataset(dataset_name, data=data)
158
+ # Scalar data - convert to 0-d array
159
+ _create_array(
160
+ target_group, dataset_name, data=np.array(data), compressor=None
161
+ )
118
162
  else:
119
163
  # Array data
120
164
  data_array = np.asarray(data)
@@ -124,7 +168,8 @@ def _save_zarr(
124
168
  import pickle
125
169
 
126
170
  pickled_data = pickle.dumps(data)
127
- dataset = target_group.create_dataset(
171
+ dataset = _create_array(
172
+ target_group,
128
173
  dataset_name,
129
174
  data=np.frombuffer(pickled_data, dtype=np.uint8),
130
175
  compressor=compressor,
@@ -132,7 +177,8 @@ def _save_zarr(
132
177
  dataset.attrs["_type"] = "pickled"
133
178
  else:
134
179
  # Regular array data
135
- target_group.create_dataset(
180
+ _create_array(
181
+ target_group,
136
182
  dataset_name,
137
183
  data=data_array,
138
184
  chunks=chunks,
@@ -151,7 +197,7 @@ def _save_zarr(
151
197
  print(
152
198
  f"✅ Saved to Zarr (consolidated): {spath}" + (f"/{key}" if key else "")
153
199
  )
154
- except:
200
+ except Exception:
155
201
  print(f"✅ Saved to Zarr: {spath}" + (f"/{key}" if key else ""))
156
202
  else:
157
203
  print(f"✅ Saved to Zarr ({store_type}): {spath}" + (f"/{key}" if key else ""))
@@ -0,0 +1,212 @@
1
+ <!-- ---
2
+ !-- Timestamp: 2025-12-16 20:36:14
3
+ !-- Author: ywatanabe
4
+ !-- File: /home/ywatanabe/proj/scitex-code/src/scitex/io/bundle/README.md
5
+ !-- --- -->
6
+
7
+ # SciTeX Bundle I/O
8
+
9
+ ## Overview
10
+
11
+ SciTeX uses bundle formats for reproducible scientific figures:
12
+
13
+ | Format | Purpose | Contents |
14
+ |-----------|---------------------|---------------------------------------------|
15
+ | `.pltz` | Single plot bundle | spec.json, style.json, data.csv, exports/ |
16
+ | `.figz` | Multi-panel figure | spec.json, style.json, nested .pltz bundles |
17
+ | `.statsz` | Statistical results | spec.json, stats data, comparison metadata |
18
+
19
+ ## Format Variants
20
+
21
+ Each bundle can exist in two forms:
22
+
23
+ | Suffix | Type | Use Case |
24
+ |-------------------------------------|-------------|-----------------------------|
25
+ | `.pltz` / `.figz` / `.statsz` | ZIP archive | Storage, transfer, download |
26
+ | `.pltz.d` / `.figz.d` / `.statsz.d` | Directory | Editing, development |
27
+
28
+ ## Quick Start
29
+
30
+ ```python
31
+ import scitex.io.bundle as bundle
32
+
33
+ # Load a bundle (works with both ZIP and directory)
34
+ data = bundle.load("Figure1.figz")
35
+ print(data['spec'])
36
+ print(data['type']) # 'figz'
37
+
38
+ # Save a bundle
39
+ bundle.save(data, "output.pltz", as_zip=True)
40
+
41
+ # Copy a bundle
42
+ bundle.copy("template.pltz", "my_plot.pltz.d")
43
+
44
+ # Pack/unpack between formats
45
+ bundle.pack("plot.pltz.d") # -> plot.pltz
46
+ bundle.unpack("plot.pltz") # -> plot.pltz.d/
47
+ ```
48
+
49
+ ## ZipBundle Class
50
+
51
+ In-memory access to ZIP bundles without extraction:
52
+
53
+ ```python
54
+ from scitex.io.bundle import ZipBundle
55
+
56
+ # Reading
57
+ with ZipBundle("figure.figz") as zb:
58
+ spec = zb.read_json("spec.json")
59
+ data = zb.read_csv("data.csv")
60
+ png = zb.read_bytes("exports/figure.png")
61
+
62
+ # Writing (atomic)
63
+ with ZipBundle("output.pltz", mode="w") as zb:
64
+ zb.write_json("spec.json", spec_dict)
65
+ zb.write_csv("data.csv", dataframe)
66
+ zb.write_bytes("exports/plot.png", png_bytes)
67
+
68
+ # Modifying (read + write atomically)
69
+ with ZipBundle("figure.figz", mode="a") as zb:
70
+ spec = zb.read_json("spec.json")
71
+ spec["title"] = "Updated"
72
+ zb.write_json("spec.json", spec)
73
+ ```
74
+
75
+ ## Nested Bundle Access
76
+
77
+ Access pltz bundles nested inside figz:
78
+
79
+ ```python
80
+ from scitex.io.bundle import nested
81
+
82
+ # Get preview image
83
+ preview = nested.get_preview("Figure1.figz/A.pltz.d")
84
+
85
+ # Get JSON
86
+ spec = nested.get_json("Figure1.figz/A.pltz.d/spec.json")
87
+
88
+ # Get any file
89
+ png = nested.get_file("Figure1.figz/A.pltz.d/exports/plot.png")
90
+
91
+ # Write to nested bundle
92
+ nested.put_json("Figure1.figz/A.pltz.d/spec.json", updated_spec)
93
+
94
+ # List files
95
+ files = nested.list_files("Figure1.figz/A.pltz.d")
96
+
97
+ # Resolve full bundle data
98
+ data = nested.resolve("Figure1.figz/A.pltz.d")
99
+ ```
100
+
101
+ ## Bundle Structure
102
+
103
+ ### .pltz Bundle
104
+
105
+ ```
106
+ plot.pltz.d/
107
+ ├── spec.json # Plot specification (traces, axes, data refs)
108
+ ├── style.json # Visual styling (colors, fonts, sizes)
109
+ ├── data.csv # Source data
110
+ ├── exports/
111
+ │ ├── plot.png # Rendered preview
112
+ │ ├── plot_hitmap.png
113
+ │ └── plot.svg
114
+ └── cache/
115
+ └── geometry_px.json
116
+ ```
117
+
118
+ ### .figz Bundle
119
+
120
+ ```
121
+ Figure1.figz.d/
122
+ ├── spec.json # Figure layout, panel positions
123
+ ├── style.json # Figure-level styling
124
+ ├── A.pltz.d/ # Panel A (nested pltz)
125
+ ├── B.pltz.d/ # Panel B (nested pltz)
126
+ └── exports/
127
+ └── Figure1.png # Composed figure
128
+ ```
129
+
130
+ ## API Reference
131
+
132
+ ### Core Operations
133
+
134
+ | Function | Description |
135
+ |----------|-------------|
136
+ | `load(path)` | Load bundle from ZIP or directory |
137
+ | `save(data, path)` | Save bundle to ZIP or directory |
138
+ | `copy(src, dst)` | Copy bundle between locations |
139
+ | `pack(dir_path)` | Convert directory to ZIP |
140
+ | `unpack(zip_path)` | Convert ZIP to directory |
141
+ | `validate(path)` | Validate bundle structure |
142
+ | `is_bundle(path)` | Check if path is a bundle |
143
+ | `get_type(path)` | Get bundle type ('figz', 'pltz', 'statsz') |
144
+
145
+ ### ZipBundle Methods
146
+
147
+ | Method | Description |
148
+ |--------|-------------|
149
+ | `read_bytes(name)` | Read file as bytes |
150
+ | `read_text(name)` | Read file as string |
151
+ | `read_json(name)` | Read and parse JSON |
152
+ | `read_csv(name)` | Read CSV as DataFrame |
153
+ | `write_bytes(name, data)` | Write bytes |
154
+ | `write_text(name, text)` | Write string |
155
+ | `write_json(name, data)` | Write JSON |
156
+ | `write_csv(name, df)` | Write DataFrame as CSV |
157
+ | `namelist()` | List files in bundle |
158
+
159
+ ### Nested Access (bundle.nested)
160
+
161
+ | Function | Description |
162
+ |----------|-------------|
163
+ | `nested.get_file(path)` | Get file from nested bundle |
164
+ | `nested.get_json(path)` | Get JSON from nested bundle |
165
+ | `nested.get_preview(path)` | Get preview PNG |
166
+ | `nested.put_file(path, data)` | Write file to nested bundle |
167
+ | `nested.put_json(path, data)` | Write JSON to nested bundle |
168
+ | `nested.list_files(path)` | List files in nested bundle |
169
+ | `nested.resolve(path)` | Load full nested bundle data |
170
+ | `nested.parse_path(path)` | Parse nested path components |
171
+
172
+ ## Migration from Old API
173
+
174
+ | Old Import | New Import |
175
+ |------------|------------|
176
+ | `from scitex.io._bundle import load_bundle` | `from scitex.io.bundle import load` |
177
+ | `from scitex.io._bundle import save_bundle` | `from scitex.io.bundle import save` |
178
+ | `from scitex.io._bundle import copy_bundle` | `from scitex.io.bundle import copy` |
179
+ | `from scitex.io._bundle import pack_bundle` | `from scitex.io.bundle import pack` |
180
+ | `from scitex.io._bundle import unpack_bundle` | `from scitex.io.bundle import unpack` |
181
+ | `from scitex.io._bundle import validate_bundle` | `from scitex.io.bundle import validate` |
182
+ | `from scitex.io._bundle import BundleType` | `from scitex.io.bundle import BundleType` |
183
+ | `from scitex.io._bundle import BundleValidationError` | `from scitex.io.bundle import BundleValidationError` |
184
+ | `from scitex.io._bundle import BUNDLE_EXTENSIONS` | `from scitex.io.bundle import EXTENSIONS` |
185
+ | `from scitex.io._bundle import get_bundle_type` | `from scitex.io.bundle import get_type` |
186
+ | `from scitex.io._zip_bundle import ZipBundle` | `from scitex.io.bundle import ZipBundle` |
187
+ | `from scitex.io._zip_bundle import open_bundle` | `from scitex.io.bundle import open_zip` |
188
+ | `from scitex.io._zip_bundle import create_bundle` | `from scitex.io.bundle import create_zip` |
189
+ | `from scitex.io._zip_bundle import zip_directory_bundle` | `from scitex.io.bundle import zip_directory` |
190
+ | `from scitex.io._nested_bundle import resolve_nested_bundle` | `from scitex.io.bundle import nested; nested.resolve` |
191
+ | `from scitex.io._nested_bundle import get_nested_file` | `from scitex.io.bundle import nested; nested.get_file` |
192
+ | `from scitex.io._nested_bundle import get_nested_json` | `from scitex.io.bundle import nested; nested.get_json` |
193
+ | `from scitex.io._nested_bundle import get_nested_preview` | `from scitex.io.bundle import nested; nested.get_preview` |
194
+ | `from scitex.io._nested_bundle import put_nested_file` | `from scitex.io.bundle import nested; nested.put_file` |
195
+ | `from scitex.io._nested_bundle import put_nested_json` | `from scitex.io.bundle import nested; nested.put_json` |
196
+ | `from scitex.io._nested_bundle import list_nested_files` | `from scitex.io.bundle import nested; nested.list_files` |
197
+ | `from scitex.io._nested_bundle import parse_nested_path` | `from scitex.io.bundle import nested; nested.parse_path` |
198
+ | `from scitex.io._nested_bundle import NestedBundleNotFoundError` | `from scitex.io.bundle import NestedBundleNotFoundError` |
199
+
200
+ ## Module Structure
201
+
202
+ ```
203
+ scitex/io/bundle/
204
+ ├── __init__.py # Public API exports
205
+ ├── _types.py # BundleType, errors, constants
206
+ ├── _core.py # load, save, copy, pack, unpack, validate
207
+ ├── _zip.py # ZipBundle class and functions
208
+ ├── _nested.py # Nested bundle access
209
+ └── README.md # This documentation
210
+ ```
211
+
212
+ <!-- EOF -->
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-12-16 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/io/bundle/__init__.py
5
+
6
+ """
7
+ SciTeX Bundle I/O - Unified bundle handling for .figz, .pltz, .statsz formats.
8
+
9
+ This module provides a clean API for working with SciTeX bundles:
10
+ - .figz - Publication Figure Bundle (panels + layout)
11
+ - .pltz - Reproducible Plot Bundle (data + spec + exports)
12
+ - .statsz - Statistical Results Bundle (stats + metadata)
13
+
14
+ Each bundle can exist in two forms:
15
+ - ZIP archive: Figure1.figz, plot.pltz
16
+ - Directory: Figure1.figz.d/, plot.pltz.d/
17
+
18
+ Usage:
19
+ import scitex.io.bundle as bundle
20
+
21
+ # Load a bundle
22
+ data = bundle.load("Figure1.figz")
23
+
24
+ # Save a bundle
25
+ bundle.save(data, "output.pltz", as_zip=True)
26
+
27
+ # Copy a bundle
28
+ bundle.copy("template.pltz", "my_plot.pltz.d")
29
+
30
+ # Access ZIP bundles in-memory
31
+ with bundle.ZipBundle("figure.figz") as zb:
32
+ spec = zb.read_json("spec.json")
33
+ data = zb.read_csv("data.csv")
34
+
35
+ # Access nested bundles (pltz inside figz)
36
+ preview = bundle.nested.get_preview("Figure1.figz/A.pltz.d")
37
+ spec = bundle.nested.get_json("Figure1.figz/A.pltz.d/spec.json")
38
+ """
39
+
40
+ # Types and constants
41
+ from ._types import (
42
+ EXTENSIONS,
43
+ FIGZ,
44
+ PLTZ,
45
+ STATSZ,
46
+ BundleError,
47
+ BundleNotFoundError,
48
+ BundleType,
49
+ BundleValidationError,
50
+ NestedBundleNotFoundError,
51
+ )
52
+
53
+ # Core operations
54
+ from ._core import (
55
+ copy,
56
+ dir_to_zip_path,
57
+ get_type,
58
+ is_bundle,
59
+ load,
60
+ pack,
61
+ save,
62
+ unpack,
63
+ validate,
64
+ validate_spec,
65
+ zip_to_dir_path,
66
+ )
67
+
68
+ # ZipBundle class and functions
69
+ from ._zip import ZipBundle
70
+ from ._zip import create as create_zip
71
+ from ._zip import open as open_zip
72
+ from ._zip import zip_directory
73
+
74
+ # Nested bundle access as namespace
75
+ from . import _nested as nested
76
+
77
+ __all__ = [
78
+ # Types
79
+ "BundleType",
80
+ "BundleError",
81
+ "BundleValidationError",
82
+ "BundleNotFoundError",
83
+ "NestedBundleNotFoundError",
84
+ # Constants
85
+ "EXTENSIONS",
86
+ "FIGZ",
87
+ "PLTZ",
88
+ "STATSZ",
89
+ # Core operations
90
+ "load",
91
+ "save",
92
+ "copy",
93
+ "pack",
94
+ "unpack",
95
+ "validate",
96
+ "validate_spec",
97
+ "is_bundle",
98
+ "get_type",
99
+ "dir_to_zip_path",
100
+ "zip_to_dir_path",
101
+ # ZipBundle
102
+ "ZipBundle",
103
+ "open_zip",
104
+ "create_zip",
105
+ "zip_directory",
106
+ # Nested access namespace
107
+ "nested",
108
+ ]
109
+
110
+ # EOF