scitex 2.8.1__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.
- scitex/__init__.py +15 -7
- scitex/__version__.py +1 -2
- scitex/_install_guide.py +250 -0
- scitex/_optional_deps.py +206 -39
- scitex/ai/_gen_ai/_Groq.py +2 -4
- scitex/ai/_gen_ai/_OpenAI.py +5 -2
- scitex/ai/_gen_ai/_Perplexity.py +20 -6
- scitex/audio/__init__.py +24 -15
- scitex/audio/_cross_process_lock.py +139 -0
- scitex/audio/_mcp_handlers.py +256 -0
- scitex/audio/_mcp_tool_schemas.py +203 -0
- scitex/audio/engines/elevenlabs_engine.py +5 -2
- scitex/audio/mcp_server.py +98 -457
- scitex/bridge/__init__.py +30 -19
- scitex/bridge/_figrecipe.py +245 -0
- scitex/bridge/_helpers.py +2 -1
- scitex/bridge/_plt_vis.py +23 -10
- scitex/bridge/_stats_plt.py +18 -5
- scitex/bridge/_stats_vis.py +16 -2
- scitex/browser/__init__.py +84 -44
- scitex/browser/automation/__init__.py +5 -1
- scitex/browser/core/BrowserMixin.py +17 -4
- scitex/browser/core/__init__.py +11 -2
- scitex/browser/remote/CaptchaHandler.py +1 -1
- scitex/browser/remote/ZenRowsAPIClient.py +1 -1
- scitex/capture/grid.py +487 -0
- scitex/capture/mcp_handlers.py +401 -0
- scitex/capture/mcp_tool_defs.py +192 -0
- scitex/capture/mcp_tools.py +241 -0
- scitex/capture/mcp_utils.py +30 -0
- scitex/cli/convert.py +421 -0
- scitex/cli/main.py +6 -4
- scitex/datetime/__init__.py +46 -0
- scitex/datetime/_linspace.py +100 -0
- scitex/datetime/_normalize_timestamp.py +306 -0
- scitex/db/_delete_duplicates.py +4 -4
- scitex/db/_sqlite3/_delete_duplicates.py +11 -2
- scitex/dev/plt/__init__.py +61 -62
- scitex/dev/plt/demo_plotters/__init__.py +0 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axhline.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axhspan.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axvline.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axvspan.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_bar.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_barh.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_boxplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_contour.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_contourf.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_errorbar.py +30 -0
- scitex/dev/plt/demo_plotters/plot_mpl_eventplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_fill.py +30 -0
- scitex/dev/plt/demo_plotters/plot_mpl_fill_between.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hexbin.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hist.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hist2d.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_imshow.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_pcolormesh.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_pie.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_plot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_quiver.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_scatter.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_stackplot.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_stem.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_step.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_violinplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_sns_barplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_boxplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_heatmap.py +28 -0
- scitex/dev/plt/demo_plotters/plot_sns_histplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_kdeplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_lineplot.py +31 -0
- scitex/dev/plt/demo_plotters/plot_sns_scatterplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_stripplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_swarmplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_violinplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_bar.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_barh.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_box.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_boxplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_conf_mat.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_contour.py +31 -0
- scitex/dev/plt/demo_plotters/plot_stx_ecdf.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_errorbar.py +30 -0
- scitex/dev/plt/demo_plotters/plot_stx_fill_between.py +31 -0
- scitex/dev/plt/demo_plotters/plot_stx_fillv.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_heatmap.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_image.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_imshow.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_joyplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_kde.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_line.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_mean_ci.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_mean_std.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_median_iqr.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_raster.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_rectangle.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_scatter.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_shaded_line.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_violin.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_violinplot.py +28 -0
- scitex/dev/plt/mpl/get_dir_ax.py +46 -0
- scitex/dev/plt/mpl/get_signatures.py +176 -0
- scitex/dev/plt/mpl/get_signatures_details.py +522 -0
- scitex/dict/_pop_keys.py +1 -7
- scitex/dsp/__init__.py +15 -10
- scitex/dsp/add_noise.py +5 -2
- scitex/dsp/example.py +35 -22
- scitex/dsp/filt.py +8 -3
- scitex/dsp/reference.py +3 -2
- scitex/dsp/utils/__init__.py +2 -1
- scitex/dsp/utils/_differential_bandpass_filters.py +14 -4
- scitex/dt/__init__.py +39 -2
- scitex/errors.py +82 -521
- scitex/fig/__init__.py +4 -4
- scitex/fig/editor/edit/panel_loader.py +1 -1
- scitex/fig/io/_bundle.py +7 -7
- scitex/fts/README.md +262 -0
- scitex/fts/TODO.md +66 -0
- scitex/fts/__init__.py +90 -0
- scitex/fts/_bundle/README_IN_BUNDLE.md +102 -0
- scitex/fts/_bundle/_FTS.py +657 -0
- scitex/fts/_bundle/__init__.py +38 -0
- scitex/fts/_bundle/_children.py +216 -0
- scitex/fts/_bundle/_conversion/__init__.py +15 -0
- scitex/fts/_bundle/_conversion/_bundle2dict.py +44 -0
- scitex/fts/_bundle/_conversion/_dict2bundle.py +50 -0
- scitex/fts/_bundle/_dataclasses/_Axes.py +57 -0
- scitex/fts/_bundle/_dataclasses/_BBox.py +54 -0
- scitex/fts/_bundle/_dataclasses/_ColumnDef.py +72 -0
- scitex/fts/_bundle/_dataclasses/_DataFormat.py +40 -0
- scitex/fts/_bundle/_dataclasses/_DataInfo.py +135 -0
- scitex/fts/_bundle/_dataclasses/_DataSource.py +44 -0
- scitex/fts/_bundle/_dataclasses/_Node.py +319 -0
- scitex/fts/_bundle/_dataclasses/_NodeRefs.py +45 -0
- scitex/fts/_bundle/_dataclasses/_SizeMM.py +38 -0
- scitex/fts/_bundle/_dataclasses/__init__.py +35 -0
- scitex/fts/_bundle/_extractors/__init__.py +32 -0
- scitex/fts/_bundle/_extractors/_extract_bar.py +131 -0
- scitex/fts/_bundle/_extractors/_extract_line.py +71 -0
- scitex/fts/_bundle/_extractors/_extract_scatter.py +79 -0
- scitex/fts/_bundle/_loader.py +134 -0
- scitex/fts/_bundle/_mpl_helpers.py +389 -0
- scitex/fts/_bundle/_saver.py +269 -0
- scitex/fts/_bundle/_storage.py +200 -0
- scitex/fts/_bundle/_utils/__init__.py +55 -0
- scitex/fts/_bundle/_utils/_const.py +26 -0
- scitex/fts/_bundle/_utils/_errors.py +73 -0
- scitex/fts/_bundle/_utils/_generate.py +21 -0
- scitex/fts/_bundle/_utils/_types.py +76 -0
- scitex/fts/_bundle/_validation.py +434 -0
- scitex/fts/_bundle/_zipbundle.py +165 -0
- scitex/fts/_fig/__init__.py +22 -0
- scitex/fts/_fig/_backend/__init__.py +53 -0
- scitex/fts/_fig/_backend/_export.py +165 -0
- scitex/fts/_fig/_backend/_parser.py +188 -0
- scitex/fts/_fig/_backend/_render.py +538 -0
- scitex/fts/_fig/_composite.py +345 -0
- scitex/fts/_fig/_dataclasses/_ChannelEncoding.py +46 -0
- scitex/fts/_fig/_dataclasses/_Encoding.py +82 -0
- scitex/fts/_fig/_dataclasses/_Theme.py +441 -0
- scitex/fts/_fig/_dataclasses/_TraceEncoding.py +52 -0
- scitex/fts/_fig/_dataclasses/__init__.py +47 -0
- scitex/fts/_fig/_editor/__init__.py +14 -0
- scitex/fts/_fig/_editor/_cui/__init__.py +33 -0
- scitex/fts/_fig/_editor/_cui/_backend_detector.py +39 -0
- scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +366 -0
- scitex/fts/_fig/_editor/_cui/_editor_launcher.py +175 -0
- scitex/fts/_fig/_editor/_cui/_manual_handler.py +52 -0
- scitex/fts/_fig/_editor/_cui/_panel_loader.py +246 -0
- scitex/fts/_fig/_editor/_cui/_path_resolver.py +66 -0
- scitex/fts/_fig/_editor/_defaults.py +300 -0
- scitex/fts/_fig/_editor/_gui/__init__.py +11 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +20 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_bbox.py +1339 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_core.py +1688 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +664 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_renderer.py +853 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +79 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +41 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +16 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +85 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +217 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +93 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +57 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +112 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +59 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +212 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/canvas.css +176 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +190 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +59 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +45 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +95 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +101 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +138 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +31 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +7 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +56 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +78 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +314 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +107 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +54 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +172 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +258 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +48 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +71 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +288 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +143 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +245 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +992 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +339 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +286 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +371 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +293 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +426 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +152 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +265 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +184 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +57 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +100 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +34 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +124 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +851 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +4932 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +1657 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor.py +36 -0
- scitex/fts/_fig/_models/_Annotations.py +115 -0
- scitex/fts/_fig/_models/_Axes.py +152 -0
- scitex/fts/_fig/_models/_Figure.py +138 -0
- scitex/fts/_fig/_models/_Guides.py +104 -0
- scitex/fts/_fig/_models/_Plot.py +123 -0
- scitex/fts/_fig/_models/_Styles.py +245 -0
- scitex/fts/_fig/_models/__init__.py +80 -0
- scitex/fts/_fig/_models/_plot_types/__init__.py +156 -0
- scitex/fts/_fig/_models/_plot_types/_bar.py +43 -0
- scitex/fts/_fig/_models/_plot_types/_box.py +38 -0
- scitex/fts/_fig/_models/_plot_types/_distribution.py +36 -0
- scitex/fts/_fig/_models/_plot_types/_errorbar.py +60 -0
- scitex/fts/_fig/_models/_plot_types/_histogram.py +30 -0
- scitex/fts/_fig/_models/_plot_types/_image.py +61 -0
- scitex/fts/_fig/_models/_plot_types/_line.py +57 -0
- scitex/fts/_fig/_models/_plot_types/_scatter.py +30 -0
- scitex/fts/_fig/_models/_plot_types/_seaborn.py +121 -0
- scitex/fts/_fig/_models/_plot_types/_violin.py +36 -0
- scitex/fts/_fig/_utils/__init__.py +129 -0
- scitex/fts/_fig/_utils/_auto_layout.py +127 -0
- scitex/fts/_fig/_utils/_calc_bounds.py +111 -0
- scitex/fts/_fig/_utils/_const_sizes.py +48 -0
- scitex/fts/_fig/_utils/_convert_coords.py +77 -0
- scitex/fts/_fig/_utils/_get_template.py +178 -0
- scitex/fts/_fig/_utils/_normalize.py +73 -0
- scitex/fts/_fig/_utils/_plot_layout.py +397 -0
- scitex/fts/_fig/_utils/_validate.py +197 -0
- scitex/fts/_kinds/__init__.py +45 -0
- scitex/fts/_kinds/_figure/__init__.py +19 -0
- scitex/fts/_kinds/_figure/_composite.py +345 -0
- scitex/fts/_kinds/_plot/__init__.py +25 -0
- scitex/fts/_kinds/_plot/_backend/__init__.py +53 -0
- scitex/fts/_kinds/_plot/_backend/_export.py +165 -0
- scitex/fts/_kinds/_plot/_backend/_parser.py +188 -0
- scitex/fts/_kinds/_plot/_backend/_render.py +538 -0
- scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +46 -0
- scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +82 -0
- scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +441 -0
- scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +52 -0
- scitex/fts/_kinds/_plot/_dataclasses/__init__.py +47 -0
- scitex/fts/_kinds/_plot/_models/_Annotations.py +115 -0
- scitex/fts/_kinds/_plot/_models/_Axes.py +152 -0
- scitex/fts/_kinds/_plot/_models/_Figure.py +138 -0
- scitex/fts/_kinds/_plot/_models/_Guides.py +104 -0
- scitex/fts/_kinds/_plot/_models/_Plot.py +123 -0
- scitex/fts/_kinds/_plot/_models/_Styles.py +245 -0
- scitex/fts/_kinds/_plot/_models/__init__.py +80 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +156 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +43 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +38 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +36 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +60 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +30 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +61 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +57 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +30 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +121 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +36 -0
- scitex/fts/_kinds/_plot/_utils/__init__.py +129 -0
- scitex/fts/_kinds/_plot/_utils/_auto_layout.py +127 -0
- scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +111 -0
- scitex/fts/_kinds/_plot/_utils/_const_sizes.py +48 -0
- scitex/fts/_kinds/_plot/_utils/_convert_coords.py +77 -0
- scitex/fts/_kinds/_plot/_utils/_get_template.py +178 -0
- scitex/fts/_kinds/_plot/_utils/_normalize.py +73 -0
- scitex/fts/_kinds/_plot/_utils/_plot_layout.py +397 -0
- scitex/fts/_kinds/_plot/_utils/_validate.py +197 -0
- scitex/fts/_kinds/_shape/__init__.py +141 -0
- scitex/fts/_kinds/_stats/__init__.py +56 -0
- scitex/fts/_kinds/_stats/_dataclasses/_Stats.py +423 -0
- scitex/fts/_kinds/_stats/_dataclasses/__init__.py +48 -0
- scitex/fts/_kinds/_table/__init__.py +72 -0
- scitex/fts/_kinds/_table/_latex/__init__.py +93 -0
- scitex/fts/_kinds/_table/_latex/_editor/__init__.py +11 -0
- scitex/fts/_kinds/_table/_latex/_editor/_app.py +725 -0
- scitex/fts/_kinds/_table/_latex/_export.py +279 -0
- scitex/fts/_kinds/_table/_latex/_figure_exporter.py +153 -0
- scitex/fts/_kinds/_table/_latex/_stats_formatter.py +274 -0
- scitex/fts/_kinds/_table/_latex/_table_exporter.py +362 -0
- scitex/fts/_kinds/_table/_latex/_utils.py +369 -0
- scitex/fts/_kinds/_table/_latex/_validator.py +445 -0
- scitex/fts/_kinds/_text/__init__.py +77 -0
- scitex/fts/_schemas/data_info.schema.json +75 -0
- scitex/fts/_schemas/encoding.schema.json +90 -0
- scitex/fts/_schemas/node.schema.json +145 -0
- scitex/fts/_schemas/render_manifest.schema.json +62 -0
- scitex/fts/_schemas/stats.schema.json +132 -0
- scitex/fts/_schemas/theme.schema.json +141 -0
- scitex/fts/_stats/__init__.py +48 -0
- scitex/fts/_stats/_dataclasses/_Stats.py +423 -0
- scitex/fts/_stats/_dataclasses/__init__.py +48 -0
- scitex/fts/_tables/__init__.py +65 -0
- scitex/fts/_tables/_latex/__init__.py +93 -0
- scitex/fts/_tables/_latex/_editor/__init__.py +11 -0
- scitex/fts/_tables/_latex/_editor/_app.py +725 -0
- scitex/fts/_tables/_latex/_export.py +279 -0
- scitex/fts/_tables/_latex/_figure_exporter.py +153 -0
- scitex/fts/_tables/_latex/_stats_formatter.py +274 -0
- scitex/fts/_tables/_latex/_table_exporter.py +362 -0
- scitex/fts/_tables/_latex/_utils.py +369 -0
- scitex/fts/_tables/_latex/_validator.py +445 -0
- scitex/gen/__init__.py +66 -25
- scitex/gen/misc.py +28 -0
- scitex/io/__init__.py +47 -32
- scitex/io/_load.py +87 -36
- scitex/io/_load_modules/__init__.py +10 -7
- scitex/io/_load_modules/_pandas.py +6 -1
- scitex/io/_save.py +299 -1556
- scitex/io/_save_modules/__init__.py +76 -19
- scitex/io/_save_modules/_figure_utils.py +90 -0
- scitex/io/_save_modules/_image_csv.py +497 -0
- scitex/io/_save_modules/_legends.py +91 -0
- scitex/io/_save_modules/_pltz_bundle.py +356 -0
- scitex/io/_save_modules/_pltz_stx.py +536 -0
- scitex/io/_save_modules/_stx_bundle.py +104 -0
- scitex/io/_save_modules/_symlink.py +96 -0
- scitex/io/_save_modules/_yaml.py +1 -1
- scitex/io/_save_modules/_zarr.py +64 -18
- scitex/io/bundle/README.md +212 -0
- scitex/io/bundle/__init__.py +110 -0
- scitex/io/{_bundle.py → bundle/_core.py} +168 -97
- scitex/io/bundle/_nested.py +713 -0
- scitex/io/bundle/_types.py +74 -0
- scitex/io/{_zip_bundle.py → bundle/_zip.py} +93 -45
- scitex/io/utils/h5_to_zarr.py +1 -1
- scitex/logging/__init__.py +108 -13
- scitex/logging/_errors.py +508 -0
- scitex/logging/_formatters.py +30 -6
- scitex/logging/_warnings.py +261 -0
- scitex/plt/__init__.py +4 -1
- scitex/plt/_figrecipe.py +236 -0
- scitex/plt/_subplots/_AxisWrapper.py +6 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +112 -1
- scitex/plt/_subplots/_FigWrapper.py +15 -0
- scitex/plt/_subplots/_SubplotsWrapper.py +125 -489
- scitex/plt/_subplots/_export_as_csv.py +11 -0
- scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +2 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_pcolormesh.py +66 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stackplot.py +62 -0
- scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +208 -0
- scitex/plt/_subplots/_fonts.py +71 -0
- scitex/plt/_subplots/_mm_layout.py +282 -0
- scitex/plt/gallery/__init__.py +99 -2
- scitex/plt/styles/_plot_postprocess.py +3 -1
- scitex/plt/utils/_configure_mpl.py +16 -19
- scitex/repro/_RandomStateManager.py +13 -8
- scitex/resource/__init__.py +19 -1
- scitex/resource/_utils/_get_env_info.py +13 -25
- scitex/schema/__init__.py +149 -160
- scitex/schema/_encoding.py +273 -0
- scitex/schema/_figure_elements.py +406 -0
- scitex/schema/_theme.py +360 -0
- scitex/schema/_validation.py +0 -98
- scitex/scholar/__init__.py +56 -14
- scitex/scholar/auth/ScholarAuthManager.py +1 -1
- scitex/scholar/auth/__init__.py +11 -2
- scitex/scholar/auth/providers/BaseAuthenticator.py +1 -1
- scitex/scholar/auth/providers/EZProxyAuthenticator.py +1 -1
- scitex/scholar/auth/providers/OpenAthensAuthenticator.py +1 -1
- scitex/scholar/auth/providers/ShibbolethAuthenticator.py +1 -1
- scitex/scholar/config/ScholarConfig.py +1 -1
- scitex/scholar/core/Scholar.py +1 -1
- scitex/session/_decorator.py +18 -16
- scitex/session/_lifecycle.py +9 -11
- scitex/session/template.py +9 -8
- scitex/sh/test_sh.py +72 -0
- scitex/sh/test_sh_simple.py +61 -0
- scitex/stats/__init__.py +221 -97
- scitex/stats/_schema.py +21 -22
- scitex/stats/descriptive/_circular.py +212 -351
- scitex/stats/descriptive/_describe.py +81 -132
- scitex/stats/descriptive/_nan.py +205 -433
- scitex/stats/descriptive/_real.py +127 -141
- scitex/str/_format_plot_text.py +5 -5
- scitex/str/_latex.py +26 -84
- scitex/str/_latex_fallback.py +53 -47
- scitex/web/_search_pubmed.py +5 -4
- scitex/writer/tests/test_diff_between.py +451 -0
- scitex/writer/tests/test_document_section.py +311 -0
- scitex/writer/tests/test_document_workflow.py +393 -0
- scitex/writer/tests/test_writer.py +361 -0
- scitex/writer/tests/test_writer_integration.py +303 -0
- {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/METADATA +364 -181
- {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/RECORD +412 -97
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/ARCHITECTURE_EXAMPLE.md +0 -905
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/BULLETIN_BOARD_EXAMPLE.md +0 -99
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/PROJECT_DESCRIPTION_EXAMPLE.md +0 -96
- {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/WHEEL +0 -0
- {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/entry_points.txt +0 -0
- {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,24 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
|
-
# Timestamp: "2025-12-
|
|
4
|
-
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/
|
|
3
|
+
# Timestamp: "2025-12-16 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/bundle/_core.py
|
|
5
5
|
|
|
6
6
|
"""
|
|
7
|
-
SciTeX Bundle
|
|
7
|
+
SciTeX Bundle Core Operations.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- scitex.fig.io._bundle (figz: panel composition, nested pltz)
|
|
12
|
-
- scitex.stats.io._bundle (statsz: comparison metadata)
|
|
9
|
+
Provides load, save, copy, pack, unpack, and validate operations for
|
|
10
|
+
.figz, .pltz, and .statsz bundle formats.
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
.statsz - Statistical Results Bundle (stats + metadata)
|
|
18
|
-
|
|
19
|
-
Each bundle can be:
|
|
20
|
-
- Directory form: Figure1.figz.d/
|
|
21
|
-
- ZIP archive form: Figure1.figz
|
|
12
|
+
Each bundle can exist in two forms:
|
|
13
|
+
- Directory: Figure1.figz.d/
|
|
14
|
+
- ZIP archive: Figure1.figz
|
|
22
15
|
"""
|
|
23
16
|
|
|
24
17
|
import json
|
|
@@ -27,59 +20,55 @@ import zipfile
|
|
|
27
20
|
from pathlib import Path
|
|
28
21
|
from typing import Any, Dict, List, Optional, Union
|
|
29
22
|
|
|
23
|
+
from ._types import (
|
|
24
|
+
EXTENSIONS,
|
|
25
|
+
BundleNotFoundError,
|
|
26
|
+
BundleType,
|
|
27
|
+
BundleValidationError,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
30
|
__all__ = [
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
31
|
+
"load",
|
|
32
|
+
"save",
|
|
33
|
+
"copy",
|
|
34
|
+
"pack",
|
|
35
|
+
"unpack",
|
|
36
|
+
"validate",
|
|
37
37
|
"validate_spec",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"BUNDLE_EXTENSIONS",
|
|
41
|
-
"get_bundle_type",
|
|
38
|
+
"is_bundle",
|
|
39
|
+
"get_type",
|
|
42
40
|
"dir_to_zip_path",
|
|
43
41
|
"zip_to_dir_path",
|
|
44
42
|
]
|
|
45
43
|
|
|
46
|
-
# Bundle extensions
|
|
47
|
-
BUNDLE_EXTENSIONS = (".figz", ".pltz", ".statsz")
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class BundleValidationError(ValueError):
|
|
51
|
-
"""Error raised when bundle validation fails."""
|
|
52
|
-
pass
|
|
53
44
|
|
|
54
|
-
|
|
55
|
-
class BundleType:
|
|
56
|
-
"""Bundle type constants."""
|
|
57
|
-
FIGZ = "figz"
|
|
58
|
-
PLTZ = "pltz"
|
|
59
|
-
STATSZ = "statsz"
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def get_bundle_type(path: Union[str, Path]) -> Optional[str]:
|
|
45
|
+
def get_type(path: Union[str, Path]) -> Optional[str]:
|
|
63
46
|
"""Get bundle type from path.
|
|
64
47
|
|
|
65
48
|
Args:
|
|
66
49
|
path: Path to bundle (directory or ZIP).
|
|
67
50
|
|
|
68
51
|
Returns:
|
|
69
|
-
Bundle type string or None if not a bundle.
|
|
52
|
+
Bundle type string ('figz', 'pltz', 'statsz') or None if not a bundle.
|
|
53
|
+
|
|
54
|
+
Example:
|
|
55
|
+
>>> get_type("Figure1.figz")
|
|
56
|
+
'figz'
|
|
57
|
+
>>> get_type("plot.pltz.d")
|
|
58
|
+
'pltz'
|
|
70
59
|
"""
|
|
71
60
|
p = Path(path)
|
|
72
61
|
|
|
73
62
|
# Directory bundle: ends with .figz.d, .pltz.d, .statsz.d
|
|
74
63
|
if p.is_dir() and p.suffix == ".d":
|
|
75
64
|
stem = p.stem # e.g., "Figure1.figz"
|
|
76
|
-
for ext in
|
|
65
|
+
for ext in EXTENSIONS:
|
|
77
66
|
if stem.endswith(ext):
|
|
78
67
|
return ext[1:] # Remove leading dot
|
|
79
68
|
return None
|
|
80
69
|
|
|
81
70
|
# ZIP bundle: ends with .figz, .pltz, .statsz
|
|
82
|
-
if p.suffix in
|
|
71
|
+
if p.suffix in EXTENSIONS:
|
|
83
72
|
return p.suffix[1:] # Remove leading dot
|
|
84
73
|
|
|
85
74
|
return None
|
|
@@ -93,14 +82,22 @@ def is_bundle(path: Union[str, Path]) -> bool:
|
|
|
93
82
|
|
|
94
83
|
Returns:
|
|
95
84
|
True if path is a bundle.
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
>>> is_bundle("Figure1.figz")
|
|
88
|
+
True
|
|
89
|
+
>>> is_bundle("data.csv")
|
|
90
|
+
False
|
|
96
91
|
"""
|
|
97
|
-
return
|
|
92
|
+
return get_type(path) is not None
|
|
98
93
|
|
|
99
94
|
|
|
100
95
|
def dir_to_zip_path(dir_path: Path) -> Path:
|
|
101
96
|
"""Convert directory path to ZIP path.
|
|
102
97
|
|
|
103
|
-
Example:
|
|
98
|
+
Example:
|
|
99
|
+
>>> dir_to_zip_path(Path("Figure1.figz.d"))
|
|
100
|
+
Path('Figure1.figz')
|
|
104
101
|
"""
|
|
105
102
|
if dir_path.suffix == ".d":
|
|
106
103
|
return dir_path.with_suffix("")
|
|
@@ -110,12 +107,14 @@ def dir_to_zip_path(dir_path: Path) -> Path:
|
|
|
110
107
|
def zip_to_dir_path(zip_path: Path) -> Path:
|
|
111
108
|
"""Convert ZIP path to directory path.
|
|
112
109
|
|
|
113
|
-
Example:
|
|
110
|
+
Example:
|
|
111
|
+
>>> zip_to_dir_path(Path("Figure1.figz"))
|
|
112
|
+
Path('Figure1.figz.d')
|
|
114
113
|
"""
|
|
115
114
|
return Path(str(zip_path) + ".d")
|
|
116
115
|
|
|
117
116
|
|
|
118
|
-
def
|
|
117
|
+
def pack(
|
|
119
118
|
dir_path: Union[str, Path], output_path: Optional[Union[str, Path]] = None
|
|
120
119
|
) -> Path:
|
|
121
120
|
"""Pack a bundle directory into a ZIP archive.
|
|
@@ -129,6 +128,10 @@ def pack_bundle(
|
|
|
129
128
|
|
|
130
129
|
Returns:
|
|
131
130
|
Path to created ZIP archive.
|
|
131
|
+
|
|
132
|
+
Example:
|
|
133
|
+
>>> pack("plot.pltz.d")
|
|
134
|
+
Path('plot.pltz')
|
|
132
135
|
"""
|
|
133
136
|
dir_path = Path(dir_path)
|
|
134
137
|
|
|
@@ -141,15 +144,12 @@ def pack_bundle(
|
|
|
141
144
|
output_path = Path(output_path)
|
|
142
145
|
|
|
143
146
|
# Get the directory name to use as top-level folder in ZIP
|
|
144
|
-
# e.g., "stx_line.pltz.d" for path "/path/to/stx_line.pltz.d"
|
|
145
147
|
dir_name = dir_path.name
|
|
146
148
|
|
|
147
149
|
# Create ZIP archive with directory structure preserved
|
|
148
150
|
with zipfile.ZipFile(output_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
|
149
151
|
for file_path in dir_path.rglob("*"):
|
|
150
152
|
if file_path.is_file():
|
|
151
|
-
# Include directory name in archive path
|
|
152
|
-
# e.g., "stx_line.pltz.d/stx_line.csv"
|
|
153
153
|
rel_path = file_path.relative_to(dir_path)
|
|
154
154
|
arcname = Path(dir_name) / rel_path
|
|
155
155
|
zf.write(file_path, arcname)
|
|
@@ -157,29 +157,28 @@ def pack_bundle(
|
|
|
157
157
|
return output_path
|
|
158
158
|
|
|
159
159
|
|
|
160
|
-
def
|
|
160
|
+
def unpack(
|
|
161
161
|
zip_path: Union[str, Path], output_path: Optional[Union[str, Path]] = None
|
|
162
162
|
) -> Path:
|
|
163
163
|
"""Unpack a bundle ZIP archive into a directory.
|
|
164
164
|
|
|
165
|
-
The ZIP archive contains a top-level .d directory, so extraction goes to
|
|
166
|
-
the parent directory. E.g., stx_line.pltz extracts to create stx_line.pltz.d/
|
|
167
|
-
|
|
168
165
|
Args:
|
|
169
166
|
zip_path: Path to bundle ZIP (e.g., Figure1.figz).
|
|
170
167
|
output_path: Output directory path. Auto-generated if None.
|
|
171
168
|
|
|
172
169
|
Returns:
|
|
173
170
|
Path to created directory.
|
|
171
|
+
|
|
172
|
+
Example:
|
|
173
|
+
>>> unpack("plot.pltz")
|
|
174
|
+
Path('plot.pltz.d')
|
|
174
175
|
"""
|
|
175
176
|
zip_path = Path(zip_path)
|
|
176
177
|
|
|
177
178
|
if not zip_path.is_file():
|
|
178
179
|
raise ValueError(f"Not a file: {zip_path}")
|
|
179
180
|
|
|
180
|
-
# Determine extraction target
|
|
181
181
|
if output_path is None:
|
|
182
|
-
# Extract to same directory as ZIP file (ZIP contains .d folder structure)
|
|
183
182
|
extract_to = zip_path.parent
|
|
184
183
|
expected_dir = zip_to_dir_path(zip_path)
|
|
185
184
|
else:
|
|
@@ -187,7 +186,6 @@ def unpack_bundle(
|
|
|
187
186
|
extract_to = output_path.parent
|
|
188
187
|
expected_dir = output_path
|
|
189
188
|
|
|
190
|
-
# Extract ZIP archive (contains .d directory structure)
|
|
191
189
|
with zipfile.ZipFile(zip_path, "r") as zf:
|
|
192
190
|
zf.extractall(extract_to)
|
|
193
191
|
|
|
@@ -227,12 +225,15 @@ def validate_spec(
|
|
|
227
225
|
# Delegate to domain-specific validators
|
|
228
226
|
if bundle_type == BundleType.FIGZ:
|
|
229
227
|
from scitex.fig.io._bundle import validate_figz_spec
|
|
228
|
+
|
|
230
229
|
errors.extend(validate_figz_spec(spec))
|
|
231
230
|
elif bundle_type == BundleType.PLTZ:
|
|
232
231
|
from scitex.plt.io._bundle import validate_pltz_spec
|
|
232
|
+
|
|
233
233
|
errors.extend(validate_pltz_spec(spec))
|
|
234
234
|
elif bundle_type == BundleType.STATSZ:
|
|
235
235
|
from scitex.stats.io._bundle import validate_statsz_spec
|
|
236
|
+
|
|
236
237
|
errors.extend(validate_statsz_spec(spec))
|
|
237
238
|
else:
|
|
238
239
|
errors.append(f"Unknown bundle type: {bundle_type}")
|
|
@@ -243,9 +244,7 @@ def validate_spec(
|
|
|
243
244
|
return errors
|
|
244
245
|
|
|
245
246
|
|
|
246
|
-
def
|
|
247
|
-
path: Union[str, Path], strict: bool = False
|
|
248
|
-
) -> Dict[str, Any]:
|
|
247
|
+
def validate(path: Union[str, Path], strict: bool = False) -> Dict[str, Any]:
|
|
249
248
|
"""Validate a bundle and return validation results.
|
|
250
249
|
|
|
251
250
|
Args:
|
|
@@ -271,8 +270,7 @@ def validate_bundle(
|
|
|
271
270
|
|
|
272
271
|
p = Path(path)
|
|
273
272
|
|
|
274
|
-
|
|
275
|
-
bundle_type = get_bundle_type(p)
|
|
273
|
+
bundle_type = get_type(p)
|
|
276
274
|
if bundle_type is None:
|
|
277
275
|
result["valid"] = False
|
|
278
276
|
result["errors"].append(f"Not a valid bundle path: {path}")
|
|
@@ -282,9 +280,8 @@ def validate_bundle(
|
|
|
282
280
|
|
|
283
281
|
result["bundle_type"] = bundle_type
|
|
284
282
|
|
|
285
|
-
# Try to load and validate
|
|
286
283
|
try:
|
|
287
|
-
bundle =
|
|
284
|
+
bundle = load(path)
|
|
288
285
|
spec = bundle.get("spec")
|
|
289
286
|
result["spec"] = spec
|
|
290
287
|
|
|
@@ -307,7 +304,7 @@ def validate_bundle(
|
|
|
307
304
|
return result
|
|
308
305
|
|
|
309
306
|
|
|
310
|
-
def
|
|
307
|
+
def load(path: Union[str, Path], in_memory: bool = True) -> Dict[str, Any]:
|
|
311
308
|
"""Load bundle from directory or ZIP transparently.
|
|
312
309
|
|
|
313
310
|
Args:
|
|
@@ -322,12 +319,19 @@ def load_bundle(path: Union[str, Path], in_memory: bool = True) -> Dict[str, Any
|
|
|
322
319
|
- 'path': Original path
|
|
323
320
|
- 'is_zip': Whether loaded from ZIP
|
|
324
321
|
- Additional fields based on bundle type
|
|
322
|
+
|
|
323
|
+
Example:
|
|
324
|
+
>>> bundle = load("Figure1.figz")
|
|
325
|
+
>>> bundle['type']
|
|
326
|
+
'figz'
|
|
327
|
+
>>> bundle['spec']['schema']['name']
|
|
328
|
+
'scitex.fig'
|
|
325
329
|
"""
|
|
326
330
|
p = Path(path)
|
|
327
|
-
bundle_type =
|
|
331
|
+
bundle_type = get_type(p)
|
|
328
332
|
|
|
329
333
|
if bundle_type is None:
|
|
330
|
-
raise
|
|
334
|
+
raise BundleNotFoundError(f"Not a valid bundle: {path}")
|
|
331
335
|
|
|
332
336
|
result = {
|
|
333
337
|
"type": bundle_type,
|
|
@@ -336,15 +340,14 @@ def load_bundle(path: Union[str, Path], in_memory: bool = True) -> Dict[str, Any
|
|
|
336
340
|
}
|
|
337
341
|
|
|
338
342
|
# Handle ZIP vs directory
|
|
339
|
-
if p.is_file() and p.suffix in
|
|
343
|
+
if p.is_file() and p.suffix in EXTENSIONS:
|
|
340
344
|
result["is_zip"] = True
|
|
341
345
|
|
|
342
346
|
if in_memory:
|
|
343
|
-
|
|
344
|
-
|
|
347
|
+
from ._zip import ZipBundle
|
|
348
|
+
|
|
345
349
|
with ZipBundle(p, mode="r") as zb:
|
|
346
350
|
result["_zip_bundle"] = zb
|
|
347
|
-
# Load common files in-memory
|
|
348
351
|
try:
|
|
349
352
|
result["spec"] = zb.read_json("spec.json")
|
|
350
353
|
except FileNotFoundError:
|
|
@@ -358,13 +361,12 @@ def load_bundle(path: Union[str, Path], in_memory: bool = True) -> Dict[str, Any
|
|
|
358
361
|
except FileNotFoundError:
|
|
359
362
|
result["data"] = None
|
|
360
363
|
|
|
361
|
-
# Get file list
|
|
362
364
|
result["files"] = zb.namelist()
|
|
363
365
|
|
|
364
366
|
return result
|
|
365
367
|
else:
|
|
366
|
-
# Legacy: extract to temp and load
|
|
367
368
|
import tempfile
|
|
369
|
+
|
|
368
370
|
temp_dir = Path(tempfile.mkdtemp())
|
|
369
371
|
with zipfile.ZipFile(p, "r") as zf:
|
|
370
372
|
zf.extractall(temp_dir)
|
|
@@ -372,21 +374,24 @@ def load_bundle(path: Union[str, Path], in_memory: bool = True) -> Dict[str, Any
|
|
|
372
374
|
else:
|
|
373
375
|
bundle_dir = p
|
|
374
376
|
|
|
375
|
-
# Delegate to domain-specific loaders
|
|
377
|
+
# Delegate to domain-specific loaders
|
|
376
378
|
if bundle_type == BundleType.FIGZ:
|
|
377
379
|
from scitex.fig.io._bundle import load_figz_bundle
|
|
380
|
+
|
|
378
381
|
result.update(load_figz_bundle(bundle_dir))
|
|
379
382
|
elif bundle_type == BundleType.PLTZ:
|
|
380
383
|
from scitex.plt.io import load_layered_pltz_bundle
|
|
384
|
+
|
|
381
385
|
result.update(load_layered_pltz_bundle(bundle_dir))
|
|
382
386
|
elif bundle_type == BundleType.STATSZ:
|
|
383
387
|
from scitex.stats.io._bundle import load_statsz_bundle
|
|
388
|
+
|
|
384
389
|
result.update(load_statsz_bundle(bundle_dir))
|
|
385
390
|
|
|
386
391
|
return result
|
|
387
392
|
|
|
388
393
|
|
|
389
|
-
def
|
|
394
|
+
def save(
|
|
390
395
|
data: Dict[str, Any],
|
|
391
396
|
path: Union[str, Path],
|
|
392
397
|
bundle_type: Optional[str] = None,
|
|
@@ -404,17 +409,20 @@ def save_bundle(
|
|
|
404
409
|
|
|
405
410
|
Returns:
|
|
406
411
|
Path to saved bundle.
|
|
412
|
+
|
|
413
|
+
Example:
|
|
414
|
+
>>> save({"spec": {...}, "data": df}, "plot.pltz", as_zip=True)
|
|
415
|
+
Path('plot.pltz')
|
|
407
416
|
"""
|
|
408
417
|
p = Path(path)
|
|
409
418
|
|
|
410
|
-
# Determine bundle type
|
|
411
419
|
if bundle_type is None:
|
|
412
|
-
bundle_type =
|
|
420
|
+
bundle_type = get_type(p)
|
|
413
421
|
if bundle_type is None:
|
|
414
422
|
raise ValueError(f"Cannot determine bundle type from path: {path}")
|
|
415
423
|
|
|
416
424
|
# Determine if saving as directory or ZIP
|
|
417
|
-
if as_zip or (p.suffix in
|
|
425
|
+
if as_zip or (p.suffix in EXTENSIONS and not str(p).endswith(".d")):
|
|
418
426
|
save_as_zip = True
|
|
419
427
|
if p.suffix == ".d":
|
|
420
428
|
zip_path = dir_to_zip_path(p)
|
|
@@ -428,28 +436,23 @@ def save_bundle(
|
|
|
428
436
|
else:
|
|
429
437
|
dir_path = p
|
|
430
438
|
|
|
431
|
-
# For direct ZIP saving with atomic writes
|
|
432
|
-
# Note: figz bundles need special handling for nested pltz panels,
|
|
433
|
-
# so they go through the directory-based save_figz_bundle path
|
|
439
|
+
# For direct ZIP saving with atomic writes
|
|
434
440
|
if save_as_zip and atomic and bundle_type != BundleType.FIGZ:
|
|
435
|
-
from .
|
|
441
|
+
from ._zip import ZipBundle
|
|
436
442
|
|
|
437
443
|
with ZipBundle(zip_path, mode="w") as zb:
|
|
438
|
-
# Write spec
|
|
439
444
|
if "spec" in data:
|
|
440
445
|
zb.write_json("spec.json", data["spec"])
|
|
441
446
|
|
|
442
|
-
# Write style
|
|
443
447
|
if "style" in data:
|
|
444
448
|
zb.write_json("style.json", data["style"])
|
|
445
449
|
|
|
446
|
-
# Write CSV data
|
|
447
450
|
if "data" in data and data["data"] is not None:
|
|
448
451
|
import pandas as pd
|
|
452
|
+
|
|
449
453
|
if isinstance(data["data"], pd.DataFrame):
|
|
450
454
|
zb.write_csv("data.csv", data["data"])
|
|
451
455
|
|
|
452
|
-
# Write exports (PNG, SVG, etc.)
|
|
453
456
|
for key in ["png", "svg", "pdf"]:
|
|
454
457
|
if key in data and data[key] is not None:
|
|
455
458
|
export_data = data[key]
|
|
@@ -458,36 +461,104 @@ def save_bundle(
|
|
|
458
461
|
|
|
459
462
|
return zip_path
|
|
460
463
|
|
|
461
|
-
# Create directory for non-ZIP or non-atomic saves
|
|
462
464
|
dir_path.mkdir(parents=True, exist_ok=True)
|
|
463
465
|
|
|
464
466
|
# Delegate to domain-specific savers
|
|
465
467
|
if bundle_type == BundleType.FIGZ:
|
|
466
468
|
from scitex.fig.io._bundle import save_figz_bundle
|
|
469
|
+
|
|
467
470
|
save_figz_bundle(data, dir_path)
|
|
468
471
|
elif bundle_type == BundleType.PLTZ:
|
|
469
|
-
# Note: This path is only reached when calling save_bundle() directly
|
|
470
|
-
# The main stx.io.save() flow uses _save_pltz_bundle() which handles layered format
|
|
471
472
|
from scitex.plt.io._bundle import save_pltz_bundle
|
|
473
|
+
|
|
472
474
|
save_pltz_bundle(data, dir_path)
|
|
473
475
|
elif bundle_type == BundleType.STATSZ:
|
|
474
476
|
from scitex.stats.io._bundle import save_statsz_bundle
|
|
477
|
+
|
|
475
478
|
save_statsz_bundle(data, dir_path)
|
|
476
479
|
else:
|
|
477
480
|
raise ValueError(f"Unknown bundle type: {bundle_type}")
|
|
478
481
|
|
|
479
|
-
# Pack to ZIP if requested (non-atomic path)
|
|
480
482
|
if save_as_zip:
|
|
481
|
-
|
|
482
|
-
shutil.rmtree(dir_path)
|
|
483
|
+
pack(dir_path, zip_path)
|
|
484
|
+
shutil.rmtree(dir_path)
|
|
483
485
|
return zip_path
|
|
484
486
|
|
|
485
487
|
return dir_path
|
|
486
488
|
|
|
487
489
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
490
|
+
def copy(
|
|
491
|
+
src: Union[str, Path],
|
|
492
|
+
dst: Union[str, Path],
|
|
493
|
+
overwrite: bool = False,
|
|
494
|
+
) -> Path:
|
|
495
|
+
"""Copy a bundle from source to destination.
|
|
496
|
+
|
|
497
|
+
Handles both directory (.d) and ZIP formats transparently.
|
|
498
|
+
If source is ZIP, extracts to destination directory.
|
|
499
|
+
If source is directory, copies to destination directory.
|
|
500
|
+
|
|
501
|
+
Args:
|
|
502
|
+
src: Source bundle path (directory or ZIP).
|
|
503
|
+
dst: Destination path (will be created as directory).
|
|
504
|
+
overwrite: If True, overwrite existing destination.
|
|
505
|
+
|
|
506
|
+
Returns:
|
|
507
|
+
Path to copied bundle (directory form).
|
|
508
|
+
|
|
509
|
+
Raises:
|
|
510
|
+
BundleNotFoundError: If source bundle doesn't exist.
|
|
511
|
+
FileExistsError: If destination exists and overwrite=False.
|
|
512
|
+
|
|
513
|
+
Example:
|
|
514
|
+
>>> copy("gallery/line/plot.pltz.d", "my_project/A.pltz.d")
|
|
515
|
+
>>> copy("template.pltz", "output/panel.pltz.d")
|
|
516
|
+
"""
|
|
517
|
+
src_path = Path(src)
|
|
518
|
+
dst_path = Path(dst)
|
|
519
|
+
|
|
520
|
+
# Validate source exists
|
|
521
|
+
if not src_path.exists():
|
|
522
|
+
if src_path.suffix in EXTENSIONS:
|
|
523
|
+
alt_path = Path(str(src_path) + ".d")
|
|
524
|
+
if alt_path.exists():
|
|
525
|
+
src_path = alt_path
|
|
526
|
+
else:
|
|
527
|
+
raise BundleNotFoundError(
|
|
528
|
+
f"Bundle not found: {src} (also tried {alt_path})"
|
|
529
|
+
)
|
|
530
|
+
elif str(src_path).endswith(".d"):
|
|
531
|
+
alt_path = Path(str(src_path)[:-2])
|
|
532
|
+
if alt_path.exists():
|
|
533
|
+
src_path = alt_path
|
|
534
|
+
else:
|
|
535
|
+
raise BundleNotFoundError(
|
|
536
|
+
f"Bundle not found: {src} (also tried {alt_path})"
|
|
537
|
+
)
|
|
538
|
+
else:
|
|
539
|
+
raise BundleNotFoundError(f"Bundle not found: {src}")
|
|
540
|
+
|
|
541
|
+
# Handle destination
|
|
542
|
+
if dst_path.exists():
|
|
543
|
+
if overwrite:
|
|
544
|
+
if dst_path.is_dir():
|
|
545
|
+
shutil.rmtree(dst_path)
|
|
546
|
+
else:
|
|
547
|
+
dst_path.unlink()
|
|
548
|
+
else:
|
|
549
|
+
raise FileExistsError(f"Destination exists: {dst}")
|
|
550
|
+
|
|
551
|
+
dst_path.parent.mkdir(parents=True, exist_ok=True)
|
|
552
|
+
|
|
553
|
+
# Copy based on source type
|
|
554
|
+
if src_path.is_dir():
|
|
555
|
+
shutil.copytree(src_path, dst_path)
|
|
556
|
+
elif src_path.suffix in EXTENSIONS or zipfile.is_zipfile(src_path):
|
|
557
|
+
unpack(src_path, dst_path)
|
|
558
|
+
else:
|
|
559
|
+
raise ValueError(f"Unknown bundle format: {src_path}")
|
|
560
|
+
|
|
561
|
+
return dst_path
|
|
562
|
+
|
|
492
563
|
|
|
493
564
|
# EOF
|