jarvisplot 2.1.6__tar.gz → 2.1.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/JarvisPLOT.egg-info/PKG-INFO +49 -2
  2. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/JarvisPLOT.egg-info/SOURCES.txt +22 -0
  3. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/PKG-INFO +49 -2
  4. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/README.md +48 -1
  5. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/adapters_rect.py +0 -1
  6. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/config_runtime.py +5 -1
  7. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/figure.py +76 -28
  8. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/layer_runtime.py +1 -1
  9. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/layout_runtime.py +0 -2
  10. jarvisplot-2.1.8/jarvisplot/Figure/legend_runtime.py +94 -0
  11. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/preprocessor.py +28 -1
  12. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/preprocessor_runtime.py +27 -5
  13. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/style_runtime.py +14 -0
  14. jarvisplot-2.1.8/jarvisplot/cache_registry.py +346 -0
  15. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cache_store.py +78 -8
  16. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/capabilities.py +6 -0
  17. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/a4paper/2x1/dynesty_runplot.json +5 -0
  18. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/a4paper/2x1/rect.json +1 -0
  19. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/a4paper/2x1/rect5x1.json +5 -0
  20. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/a4paper/2x1/rect_cmap.json +1 -0
  21. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/a4paper/2x1/rect_marginal.json +3 -0
  22. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/a4paper/2x1/rectratio.json +2 -0
  23. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/a4paper/2x1/ternary.json +1 -0
  24. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/a4paper/2x1/ternary_cmap.json +1 -0
  25. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/a4paper/4x1/rect_cmap.json +1 -0
  26. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/args.json +13 -2
  27. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/corrplot/diamond.json +1 -0
  28. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/corrplot/matrix.json +1 -0
  29. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/gambit/2x1/rect_cmap.json +1 -0
  30. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/gambit/2x1/ternary.json +1 -0
  31. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/gambit/2x1/ternary_cmap.json +1 -0
  32. jarvisplot-2.1.8/jarvisplot/cards/legends/compact/legend-card.json +15 -0
  33. jarvisplot-2.1.8/jarvisplot/cards/legends/default/legend-card.json +68 -0
  34. jarvisplot-2.1.8/jarvisplot/cards/legends/paper/legend-card.json +11 -0
  35. jarvisplot-2.1.8/jarvisplot/cards/legends/presentation/legend-card.json +16 -0
  36. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/column_demand.py +35 -0
  37. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/core.py +50 -6
  38. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/core_runtime.py +189 -43
  39. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/data_access.py +15 -15
  40. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/data_loader.py +37 -2
  41. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/data_loader_hdf5.py +0 -2
  42. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/data_loader_runtime.py +60 -16
  43. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/diagnostic_guidance.py +15 -6
  44. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/dryrun_runtime.py +32 -0
  45. jarvisplot-2.1.8/jarvisplot/legend_cards.py +250 -0
  46. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/man_render_agent.py +4 -0
  47. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/man_render_human.py +13 -0
  48. jarvisplot-2.1.8/jarvisplot/manual_cards/cache.yaml +57 -0
  49. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/manual_cards/cli-map.yaml +3 -1
  50. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/manual_cards/layer-method.yaml +5 -1
  51. jarvisplot-2.1.8/jarvisplot/manual_cards/legend.yaml +117 -0
  52. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/manual_cards/manifest.yaml +5 -0
  53. jarvisplot-2.1.8/jarvisplot/manual_cards/style-axes.yaml +58 -0
  54. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/render_health.py +9 -50
  55. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/core/figure.json +13 -5
  56. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/core/layer.json +13 -1
  57. jarvisplot-2.1.8/jarvisplot/schema/core/legend-card.json +106 -0
  58. jarvisplot-2.1.8/jarvisplot/schema/core/legend.json +53 -0
  59. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/manifest.json +2 -0
  60. jarvisplot-2.1.8/jarvisplot/utils/expression.py +425 -0
  61. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/validation.py +62 -2
  62. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/verbs/__init__.py +1 -0
  63. jarvisplot-2.1.8/jarvisplot/verbs/cache.py +179 -0
  64. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/pyproject.toml +1 -1
  65. jarvisplot-2.1.8/tests/test_add_column.py +58 -0
  66. jarvisplot-2.1.8/tests/test_cache_cleanup.py +75 -0
  67. jarvisplot-2.1.8/tests/test_cache_registry.py +455 -0
  68. jarvisplot-2.1.8/tests/test_column_pruning.py +251 -0
  69. jarvisplot-2.1.8/tests/test_examples_validate.py +29 -0
  70. jarvisplot-2.1.8/tests/test_expression_semantics.py +185 -0
  71. jarvisplot-2.1.8/tests/test_layer_legend.py +186 -0
  72. jarvisplot-2.1.8/tests/test_legend_cards.py +185 -0
  73. jarvisplot-2.1.8/tests/test_legend_runtime.py +138 -0
  74. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_parse_data_metadata.py +75 -0
  75. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_render_health.py +15 -13
  76. jarvisplot-2.1.8/tests/test_render_lifecycle.py +175 -0
  77. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_schema_catalog.py +5 -7
  78. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_template_catalog.py +7 -0
  79. jarvisplot-2.1.6/jarvisplot/manual_cards/style-axes.yaml +0 -35
  80. jarvisplot-2.1.6/jarvisplot/utils/expression.py +0 -125
  81. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/JarvisPLOT.egg-info/dependency_links.txt +0 -0
  82. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/JarvisPLOT.egg-info/entry_points.txt +0 -0
  83. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/JarvisPLOT.egg-info/requires.txt +0 -0
  84. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/JarvisPLOT.egg-info/top_level.txt +0 -0
  85. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/__init__.py +0 -0
  86. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/adapters_ternary.py +0 -0
  87. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/adaptive_cdf_runtime.py +0 -0
  88. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/bin_stat_runtime.py +0 -0
  89. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/cards/std_axes_adapter_config.json +0 -0
  90. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/colorbar_runtime.py +0 -0
  91. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/corr_layout.py +0 -0
  92. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/corr_layout_diamond.py +0 -0
  93. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/corr_order.py +0 -0
  94. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/correlation_runtime.py +0 -0
  95. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/corrplot_runtime.py +0 -0
  96. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/data_pipelines.py +0 -0
  97. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/debug_config.py +0 -0
  98. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/density_cell_runtime.py +0 -0
  99. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/design_runtime.py +0 -0
  100. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/distribution_1d_runtime.py +0 -0
  101. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/dynesty_runtime.py +0 -0
  102. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/figure_types.py +0 -0
  103. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/helper.py +0 -0
  104. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/interp_2d_runtime.py +0 -0
  105. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/interp_natural_neighbor.py +0 -0
  106. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/interp_natural_neighbor_exact.py +0 -0
  107. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/method_registry.py +0 -0
  108. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/posterior_density_runtime.py +0 -0
  109. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/posterior_hpd.py +0 -0
  110. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/posterior_mesh.py +0 -0
  111. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/profile_runtime.py +0 -0
  112. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/Figure/significance_runtime.py +0 -0
  113. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/__init__.py +0 -0
  114. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/agent_digest.py +0 -0
  115. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/agent_io.py +0 -0
  116. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/a4paper/1x1/ternary.json +0 -0
  117. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/a4paper/4x1/rect.json +0 -0
  118. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/a4paper/4x1/ternary.json +0 -0
  119. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/a4paper/4x1/ternary_cmap.json +0 -0
  120. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/colors/colormaps.json +0 -0
  121. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/default/output.json +0 -0
  122. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/flowchart/default.json +0 -0
  123. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/gambit/1x1/ternary.json +0 -0
  124. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/icons/JarvisHEP.png +0 -0
  125. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/icons/flowchart/JarvisHEP.png +0 -0
  126. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/icons/flowchart/calculator.png +0 -0
  127. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/icons/flowchart/inputfile.png +0 -0
  128. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/icons/flowchart/opera.png +0 -0
  129. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/icons/flowchart/outputfile.png +0 -0
  130. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/icons/flowchart/sampler.png +0 -0
  131. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/icons/gambit.png +0 -0
  132. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/icons/gambit_small.png +0 -0
  133. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cards/style_preference.json +0 -0
  134. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cli.py +0 -0
  135. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/cli_help.py +0 -0
  136. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/client.py +0 -0
  137. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/column_probe.py +0 -0
  138. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/config.py +0 -0
  139. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/config_address.py +0 -0
  140. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/core_assets.py +0 -0
  141. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/data_generation_contracts.py +0 -0
  142. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/data_loader_summary.py +0 -0
  143. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/dataset_types.py +0 -0
  144. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/diagnostics.py +0 -0
  145. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/distribution_1d_config.py +0 -0
  146. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/expr_names.py +0 -0
  147. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/fix_apply.py +0 -0
  148. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/flowchart.py +0 -0
  149. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/generated_data.py +0 -0
  150. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/inner_func.py +0 -0
  151. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/man_catalog.py +0 -0
  152. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/man_methods.py +0 -0
  153. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/man_transforms.py +0 -0
  154. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/manual_cards/agent-output.yaml +0 -0
  155. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/manual_cards/data-columns.yaml +0 -0
  156. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/manual_cards/debug-overlay.yaml +0 -0
  157. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/manual_cards/diagnostics.yaml +0 -0
  158. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/manual_cards/dryrun-viz.yaml +0 -0
  159. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/manual_cards/field-interp.yaml +0 -0
  160. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/manual_cards/generated-data.yaml +0 -0
  161. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/manual_cards/method_notes.yaml +0 -0
  162. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/manual_cards/type-correlation-matrix.yaml +0 -0
  163. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/manual_cards/type-posterior-2d.yaml +0 -0
  164. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/manual_cards/type-profile-2d.yaml +0 -0
  165. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/manual_cards/validate.yaml +0 -0
  166. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/manual_cards/workflow.yaml +0 -0
  167. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/manual_cards/yaml-root.yaml +0 -0
  168. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/memtrace.py +0 -0
  169. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/method_contracts.py +0 -0
  170. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/core/common.json +0 -0
  171. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/core/dataset.json +0 -0
  172. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/core/distribution-1d.json +0 -0
  173. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/core/generated-data.json +0 -0
  174. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/core/transform.json +0 -0
  175. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/bar.json +0 -0
  176. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/barh.json +0 -0
  177. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/contour.json +0 -0
  178. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/contourf.json +0 -0
  179. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/corrplot.json +0 -0
  180. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/dynesty_runplot.json +0 -0
  181. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/errorbar.json +0 -0
  182. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/fill.json +0 -0
  183. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/fill_between.json +0 -0
  184. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/fill_betweenx.json +0 -0
  185. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/hist.json +0 -0
  186. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/hist2d.json +0 -0
  187. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/imshow.json +0 -0
  188. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/jpcontour.json +0 -0
  189. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/jpcontourf.json +0 -0
  190. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/jpfield.json +0 -0
  191. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/pcolor.json +0 -0
  192. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/pcolormesh.json +0 -0
  193. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/plot.json +0 -0
  194. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/quiver.json +0 -0
  195. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/scatter.json +0 -0
  196. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/stairs.json +0 -0
  197. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/step.json +0 -0
  198. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/tricontour.json +0 -0
  199. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/tricontourf.json +0 -0
  200. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/tripcolor.json +0 -0
  201. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/tripcolor_axes.json +0 -0
  202. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/triplot.json +0 -0
  203. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/voronoi.json +0 -0
  204. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/methods/voronoif.json +0 -0
  205. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema/plot-config.json +0 -0
  206. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema_catalog.py +0 -0
  207. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/schema_diagnostics.py +0 -0
  208. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/templates_catalog.py +0 -0
  209. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/transform_contracts.py +0 -0
  210. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/utils/__init__.py +0 -0
  211. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/utils/cmaps.py +0 -0
  212. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/utils/dataframes.py +0 -0
  213. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/utils/interpolator.py +0 -0
  214. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/utils/pathing.py +0 -0
  215. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/verbs/cap.py +0 -0
  216. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/verbs/config_cmd.py +0 -0
  217. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/verbs/data.py +0 -0
  218. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/verbs/doctor.py +0 -0
  219. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/verbs/dryrun.py +0 -0
  220. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/verbs/explain.py +0 -0
  221. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/verbs/man.py +0 -0
  222. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/verbs/suggest.py +0 -0
  223. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/verbs/template.py +0 -0
  224. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/verbs/validate.py +0 -0
  225. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/jarvisplot/yaml_io.py +0 -0
  226. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/setup.cfg +0 -0
  227. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_adaptive_cdf.py +0 -0
  228. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_agent_digest.py +0 -0
  229. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_agent_envelope.py +0 -0
  230. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_axis_title.py +0 -0
  231. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_capabilities.py +0 -0
  232. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_card_debug_shape.py +0 -0
  233. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_cli_help.py +0 -0
  234. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_cli_verbs.py +0 -0
  235. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_cmaps.py +0 -0
  236. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_column_existence.py +0 -0
  237. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_config_addressing.py +0 -0
  238. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_config_edit.py +0 -0
  239. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_config_expand.py +0 -0
  240. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_correlation_runtime.py +0 -0
  241. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_corrplot.py +0 -0
  242. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_data_access.py +0 -0
  243. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_data_describe.py +0 -0
  244. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_data_head_eval.py +0 -0
  245. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_data_loader_contracts.py +0 -0
  246. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_data_pipelines.py +0 -0
  247. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_debug_config.py +0 -0
  248. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_debug_switches.py +0 -0
  249. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_debug_yaml_override.py +0 -0
  250. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_density_cell_transform.py +0 -0
  251. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_design_runtime.py +0 -0
  252. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_diagnostic_guidance.py +0 -0
  253. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_distribution_1d_transform.py +0 -0
  254. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_docs_status_labels.py +0 -0
  255. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_doctor_partial.py +0 -0
  256. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_explain.py +0 -0
  257. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_figure_contracts.py +0 -0
  258. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_figure_type_expander.py +0 -0
  259. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_fix_application.py +0 -0
  260. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_flowchart_renderer.py +0 -0
  261. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_generated_data.py +0 -0
  262. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_imshow_adapter.py +0 -0
  263. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_interp_2d_transform.py +0 -0
  264. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_interp_natural_neighbor.py +0 -0
  265. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_interpolator_contracts.py +0 -0
  266. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_jpcontour_methods.py +0 -0
  267. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_man_cli.py +0 -0
  268. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_method_contracts.py +0 -0
  269. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_pathing_contracts.py +0 -0
  270. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_posterior_hpd.py +0 -0
  271. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_profile_runtime.py +0 -0
  272. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_row_ledger.py +0 -0
  273. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_suggest.py +0 -0
  274. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_suggest_axes.py +0 -0
  275. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_template_json.py +0 -0
  276. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_ternary_debug.py +0 -0
  277. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_transform_contracts.py +0 -0
  278. {jarvisplot-2.1.6 → jarvisplot-2.1.8}/tests/test_validate_no_matplotlib.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: JarvisPLOT
3
- Version: 2.1.6
3
+ Version: 2.1.8
4
4
  Summary: Jarvis-PLOT: YAML-driven plotting engine
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -66,6 +66,7 @@ Render with a bare path — there is no `jplot run`:
66
66
  ```bash
67
67
  jplot path/to/config.yaml
68
68
  jplot path/to/config.yaml --rebuild-cache
69
+ jplot path/to/config.yaml --print
69
70
  ```
70
71
 
71
72
  Everything else is discovery and judgement, in three groups.
@@ -160,12 +161,22 @@ The loop `jplot man` prints is:
160
161
  carry `JP-*` codes; `jplot explain JP-XXX-NNN` expands any of them.
161
162
  5. `jplot <file>` — render, only when the figure itself is needed.
162
163
 
163
- `jplot man --json` is the machine-readable manual: 15 topics, plus a
164
+ `jplot man --json` is the machine-readable manual: a topic index, plus
164
165
  live pages for every registered layer method and transform step
165
166
  (`jplot man scatter`, `jplot man transform.profile`, `jplot man PDF1D`). Method contracts come from
166
167
  the same registry `jplot cap methods` reads, so a man page cannot drift from the
167
168
  code.
168
169
 
170
+ For compact legends and keys composed from real layers, use
171
+ `jplot man legend --json`. Declare `legend: {object, label, role}` on each drawing layer; components
172
+ with the same object regroup automatically using card templates such as
173
+ `band_line`, `line_errorbar` and `error_bounds`. The role is optional for a
174
+ single-component object. `frame.<axes>.legend` owns card selection, `order`,
175
+ `ncols`, position and spacing. JSON cards provide
176
+ reusable defaults, and YAML can override them. See
177
+ [the legend contract](docs/specs/LEGEND.md) and the
178
+ [self-contained example](Example/compact_legend.yaml).
179
+
169
180
  For continuous one-dimensional distributions from raw samples, use `PDF1D`
170
181
  (CDF plus PDF) or `CDF1D` in a data transform. `coordinates.x` owns `expr`,
171
182
  `lim`, and `grid`; optional `repeat` computes independent batch means and
@@ -208,9 +219,45 @@ from jarvisplot import render_flowchart, render_flowchart_file
208
219
  - You can set `project.workdir` in YAML.
209
220
  - If `output.dir` is omitted, Jarvis-PLOT defaults to `<workdir>/plots/`.
210
221
  - Data cache is stored in `<workdir>/.cache/`.
222
+ - `--print` accepts the current settings and renders final figures without the logo. It announces that, after all enabled figures are saved successfully, cache payloads used or created by this run will be deleted (pipeline data and metadata, named/shared data, summaries, and HDF5 materialization slots). Rendering failures or a run with no enabled figures keep the cache. Unrelated cache entries and the lightweight fingerprint manifest are retained; later renders rebuild deleted entries as needed.
211
223
  - Profiling pipelines are prebuilt once and reused from cache when source fingerprint and profile settings are unchanged.
212
224
  - Profiling uses a fast two-stage grid reduction (`pregrid` + render `bin`) for large datasets.
213
225
 
226
+ Plot caches are automatically registered in `~/.jarvis/plot.json` (or
227
+ `$JARVIS_HOME/plot.json`). The registry groups caches by workdir and records
228
+ associated YAML paths and last use; payloads stay in their original directories.
229
+ This includes caches created by `data describe` and default `dryrun --with-data`
230
+ exports. Sizes are calculated when listing, rather than stored in the registry.
231
+
232
+ ```bash
233
+ jplot cache list
234
+ jplot cache scan ~/Jarvis-Workshop # register existing plot caches
235
+ jplot cache clean --workdir /path/to/project # this directory and all nested projects; repeatable
236
+ jplot cache clean --all --older-than 30 # unused for at least 30 days
237
+ jplot cache clean --all --dry-run # preview; no deletion/index changes
238
+ jplot cache clean --all # clean all registered inactive caches
239
+ jplot cache -h
240
+ jplot man cache
241
+ ```
242
+
243
+ `--workdir PATH` selects registered caches in PATH itself and all of its
244
+ subdirectories, even when PATH has no cache of its own. Repeat it to select
245
+ multiple directory trees; overlapping selections are deduplicated. Use
246
+ `cache scan PATH` first for old caches that have not been registered.
247
+
248
+ All cache subcommands accept `--json` and use the existing CLI envelope and
249
+ exit codes (0 success/partial, 1 failure, 2 usage). Non-TTY stdout defaults to
250
+ JSON; human results go to stderr. Active caches are skipped (`ok: null` when
251
+ the remaining operation succeeds), and records for missing paths are pruned
252
+ during cleanup. Only marked Jarvis-PLOT components are removed; source data,
253
+ plots, and unrelated `.cache` content are retained. Failed deletions retain
254
+ their registry entries. Scanning adopts legacy caches only when the four
255
+ pipeline directories and a compatible `manifest.json` identify them; old
256
+ unmarked twin-only directories need a new `dryrun --with-data` run to register.
257
+ An explicitly supplied `--out-dir` is an export directory and is not registered.
258
+ Concurrent registry writes use a lock and atomic replacement. Registration
259
+ failures warn without preventing rendering.
260
+
214
261
  ### Example: SUSYRun2 Ternary Plots
215
262
 
216
263
  ```bash
@@ -9,6 +9,7 @@ JarvisPLOT.egg-info/top_level.txt
9
9
  jarvisplot/__init__.py
10
10
  jarvisplot/agent_digest.py
11
11
  jarvisplot/agent_io.py
12
+ jarvisplot/cache_registry.py
12
13
  jarvisplot/cache_store.py
13
14
  jarvisplot/capabilities.py
14
15
  jarvisplot/cli.py
@@ -37,6 +38,7 @@ jarvisplot/fix_apply.py
37
38
  jarvisplot/flowchart.py
38
39
  jarvisplot/generated_data.py
39
40
  jarvisplot/inner_func.py
41
+ jarvisplot/legend_cards.py
40
42
  jarvisplot/man_catalog.py
41
43
  jarvisplot/man_methods.py
42
44
  jarvisplot/man_render_agent.py
@@ -77,6 +79,7 @@ jarvisplot/Figure/interp_natural_neighbor.py
77
79
  jarvisplot/Figure/interp_natural_neighbor_exact.py
78
80
  jarvisplot/Figure/layer_runtime.py
79
81
  jarvisplot/Figure/layout_runtime.py
82
+ jarvisplot/Figure/legend_runtime.py
80
83
  jarvisplot/Figure/method_registry.py
81
84
  jarvisplot/Figure/posterior_density_runtime.py
82
85
  jarvisplot/Figure/posterior_hpd.py
@@ -120,7 +123,12 @@ jarvisplot/cards/icons/flowchart/inputfile.png
120
123
  jarvisplot/cards/icons/flowchart/opera.png
121
124
  jarvisplot/cards/icons/flowchart/outputfile.png
122
125
  jarvisplot/cards/icons/flowchart/sampler.png
126
+ jarvisplot/cards/legends/compact/legend-card.json
127
+ jarvisplot/cards/legends/default/legend-card.json
128
+ jarvisplot/cards/legends/paper/legend-card.json
129
+ jarvisplot/cards/legends/presentation/legend-card.json
123
130
  jarvisplot/manual_cards/agent-output.yaml
131
+ jarvisplot/manual_cards/cache.yaml
124
132
  jarvisplot/manual_cards/cli-map.yaml
125
133
  jarvisplot/manual_cards/data-columns.yaml
126
134
  jarvisplot/manual_cards/debug-overlay.yaml
@@ -129,6 +137,7 @@ jarvisplot/manual_cards/dryrun-viz.yaml
129
137
  jarvisplot/manual_cards/field-interp.yaml
130
138
  jarvisplot/manual_cards/generated-data.yaml
131
139
  jarvisplot/manual_cards/layer-method.yaml
140
+ jarvisplot/manual_cards/legend.yaml
132
141
  jarvisplot/manual_cards/manifest.yaml
133
142
  jarvisplot/manual_cards/method_notes.yaml
134
143
  jarvisplot/manual_cards/style-axes.yaml
@@ -146,6 +155,8 @@ jarvisplot/schema/core/distribution-1d.json
146
155
  jarvisplot/schema/core/figure.json
147
156
  jarvisplot/schema/core/generated-data.json
148
157
  jarvisplot/schema/core/layer.json
158
+ jarvisplot/schema/core/legend-card.json
159
+ jarvisplot/schema/core/legend.json
149
160
  jarvisplot/schema/core/transform.json
150
161
  jarvisplot/schema/methods/bar.json
151
162
  jarvisplot/schema/methods/barh.json
@@ -184,6 +195,7 @@ jarvisplot/utils/expression.py
184
195
  jarvisplot/utils/interpolator.py
185
196
  jarvisplot/utils/pathing.py
186
197
  jarvisplot/verbs/__init__.py
198
+ jarvisplot/verbs/cache.py
187
199
  jarvisplot/verbs/cap.py
188
200
  jarvisplot/verbs/config_cmd.py
189
201
  jarvisplot/verbs/data.py
@@ -195,15 +207,19 @@ jarvisplot/verbs/suggest.py
195
207
  jarvisplot/verbs/template.py
196
208
  jarvisplot/verbs/validate.py
197
209
  tests/test_adaptive_cdf.py
210
+ tests/test_add_column.py
198
211
  tests/test_agent_digest.py
199
212
  tests/test_agent_envelope.py
200
213
  tests/test_axis_title.py
214
+ tests/test_cache_cleanup.py
215
+ tests/test_cache_registry.py
201
216
  tests/test_capabilities.py
202
217
  tests/test_card_debug_shape.py
203
218
  tests/test_cli_help.py
204
219
  tests/test_cli_verbs.py
205
220
  tests/test_cmaps.py
206
221
  tests/test_column_existence.py
222
+ tests/test_column_pruning.py
207
223
  tests/test_config_addressing.py
208
224
  tests/test_config_edit.py
209
225
  tests/test_config_expand.py
@@ -223,7 +239,9 @@ tests/test_diagnostic_guidance.py
223
239
  tests/test_distribution_1d_transform.py
224
240
  tests/test_docs_status_labels.py
225
241
  tests/test_doctor_partial.py
242
+ tests/test_examples_validate.py
226
243
  tests/test_explain.py
244
+ tests/test_expression_semantics.py
227
245
  tests/test_figure_contracts.py
228
246
  tests/test_figure_type_expander.py
229
247
  tests/test_fix_application.py
@@ -234,6 +252,9 @@ tests/test_interp_2d_transform.py
234
252
  tests/test_interp_natural_neighbor.py
235
253
  tests/test_interpolator_contracts.py
236
254
  tests/test_jpcontour_methods.py
255
+ tests/test_layer_legend.py
256
+ tests/test_legend_cards.py
257
+ tests/test_legend_runtime.py
237
258
  tests/test_man_cli.py
238
259
  tests/test_method_contracts.py
239
260
  tests/test_parse_data_metadata.py
@@ -241,6 +262,7 @@ tests/test_pathing_contracts.py
241
262
  tests/test_posterior_hpd.py
242
263
  tests/test_profile_runtime.py
243
264
  tests/test_render_health.py
265
+ tests/test_render_lifecycle.py
244
266
  tests/test_row_ledger.py
245
267
  tests/test_schema_catalog.py
246
268
  tests/test_suggest.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: JarvisPLOT
3
- Version: 2.1.6
3
+ Version: 2.1.8
4
4
  Summary: Jarvis-PLOT: YAML-driven plotting engine
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -66,6 +66,7 @@ Render with a bare path — there is no `jplot run`:
66
66
  ```bash
67
67
  jplot path/to/config.yaml
68
68
  jplot path/to/config.yaml --rebuild-cache
69
+ jplot path/to/config.yaml --print
69
70
  ```
70
71
 
71
72
  Everything else is discovery and judgement, in three groups.
@@ -160,12 +161,22 @@ The loop `jplot man` prints is:
160
161
  carry `JP-*` codes; `jplot explain JP-XXX-NNN` expands any of them.
161
162
  5. `jplot <file>` — render, only when the figure itself is needed.
162
163
 
163
- `jplot man --json` is the machine-readable manual: 15 topics, plus a
164
+ `jplot man --json` is the machine-readable manual: a topic index, plus
164
165
  live pages for every registered layer method and transform step
165
166
  (`jplot man scatter`, `jplot man transform.profile`, `jplot man PDF1D`). Method contracts come from
166
167
  the same registry `jplot cap methods` reads, so a man page cannot drift from the
167
168
  code.
168
169
 
170
+ For compact legends and keys composed from real layers, use
171
+ `jplot man legend --json`. Declare `legend: {object, label, role}` on each drawing layer; components
172
+ with the same object regroup automatically using card templates such as
173
+ `band_line`, `line_errorbar` and `error_bounds`. The role is optional for a
174
+ single-component object. `frame.<axes>.legend` owns card selection, `order`,
175
+ `ncols`, position and spacing. JSON cards provide
176
+ reusable defaults, and YAML can override them. See
177
+ [the legend contract](docs/specs/LEGEND.md) and the
178
+ [self-contained example](Example/compact_legend.yaml).
179
+
169
180
  For continuous one-dimensional distributions from raw samples, use `PDF1D`
170
181
  (CDF plus PDF) or `CDF1D` in a data transform. `coordinates.x` owns `expr`,
171
182
  `lim`, and `grid`; optional `repeat` computes independent batch means and
@@ -208,9 +219,45 @@ from jarvisplot import render_flowchart, render_flowchart_file
208
219
  - You can set `project.workdir` in YAML.
209
220
  - If `output.dir` is omitted, Jarvis-PLOT defaults to `<workdir>/plots/`.
210
221
  - Data cache is stored in `<workdir>/.cache/`.
222
+ - `--print` accepts the current settings and renders final figures without the logo. It announces that, after all enabled figures are saved successfully, cache payloads used or created by this run will be deleted (pipeline data and metadata, named/shared data, summaries, and HDF5 materialization slots). Rendering failures or a run with no enabled figures keep the cache. Unrelated cache entries and the lightweight fingerprint manifest are retained; later renders rebuild deleted entries as needed.
211
223
  - Profiling pipelines are prebuilt once and reused from cache when source fingerprint and profile settings are unchanged.
212
224
  - Profiling uses a fast two-stage grid reduction (`pregrid` + render `bin`) for large datasets.
213
225
 
226
+ Plot caches are automatically registered in `~/.jarvis/plot.json` (or
227
+ `$JARVIS_HOME/plot.json`). The registry groups caches by workdir and records
228
+ associated YAML paths and last use; payloads stay in their original directories.
229
+ This includes caches created by `data describe` and default `dryrun --with-data`
230
+ exports. Sizes are calculated when listing, rather than stored in the registry.
231
+
232
+ ```bash
233
+ jplot cache list
234
+ jplot cache scan ~/Jarvis-Workshop # register existing plot caches
235
+ jplot cache clean --workdir /path/to/project # this directory and all nested projects; repeatable
236
+ jplot cache clean --all --older-than 30 # unused for at least 30 days
237
+ jplot cache clean --all --dry-run # preview; no deletion/index changes
238
+ jplot cache clean --all # clean all registered inactive caches
239
+ jplot cache -h
240
+ jplot man cache
241
+ ```
242
+
243
+ `--workdir PATH` selects registered caches in PATH itself and all of its
244
+ subdirectories, even when PATH has no cache of its own. Repeat it to select
245
+ multiple directory trees; overlapping selections are deduplicated. Use
246
+ `cache scan PATH` first for old caches that have not been registered.
247
+
248
+ All cache subcommands accept `--json` and use the existing CLI envelope and
249
+ exit codes (0 success/partial, 1 failure, 2 usage). Non-TTY stdout defaults to
250
+ JSON; human results go to stderr. Active caches are skipped (`ok: null` when
251
+ the remaining operation succeeds), and records for missing paths are pruned
252
+ during cleanup. Only marked Jarvis-PLOT components are removed; source data,
253
+ plots, and unrelated `.cache` content are retained. Failed deletions retain
254
+ their registry entries. Scanning adopts legacy caches only when the four
255
+ pipeline directories and a compatible `manifest.json` identify them; old
256
+ unmarked twin-only directories need a new `dryrun --with-data` run to register.
257
+ An explicitly supplied `--out-dir` is an export directory and is not registered.
258
+ Concurrent registry writes use a lock and atomic replacement. Registration
259
+ failures warn without preventing rendering.
260
+
214
261
  ### Example: SUSYRun2 Ternary Plots
215
262
 
216
263
  ```bash
@@ -44,6 +44,7 @@ Render with a bare path — there is no `jplot run`:
44
44
  ```bash
45
45
  jplot path/to/config.yaml
46
46
  jplot path/to/config.yaml --rebuild-cache
47
+ jplot path/to/config.yaml --print
47
48
  ```
48
49
 
49
50
  Everything else is discovery and judgement, in three groups.
@@ -138,12 +139,22 @@ The loop `jplot man` prints is:
138
139
  carry `JP-*` codes; `jplot explain JP-XXX-NNN` expands any of them.
139
140
  5. `jplot <file>` — render, only when the figure itself is needed.
140
141
 
141
- `jplot man --json` is the machine-readable manual: 15 topics, plus a
142
+ `jplot man --json` is the machine-readable manual: a topic index, plus
142
143
  live pages for every registered layer method and transform step
143
144
  (`jplot man scatter`, `jplot man transform.profile`, `jplot man PDF1D`). Method contracts come from
144
145
  the same registry `jplot cap methods` reads, so a man page cannot drift from the
145
146
  code.
146
147
 
148
+ For compact legends and keys composed from real layers, use
149
+ `jplot man legend --json`. Declare `legend: {object, label, role}` on each drawing layer; components
150
+ with the same object regroup automatically using card templates such as
151
+ `band_line`, `line_errorbar` and `error_bounds`. The role is optional for a
152
+ single-component object. `frame.<axes>.legend` owns card selection, `order`,
153
+ `ncols`, position and spacing. JSON cards provide
154
+ reusable defaults, and YAML can override them. See
155
+ [the legend contract](docs/specs/LEGEND.md) and the
156
+ [self-contained example](Example/compact_legend.yaml).
157
+
147
158
  For continuous one-dimensional distributions from raw samples, use `PDF1D`
148
159
  (CDF plus PDF) or `CDF1D` in a data transform. `coordinates.x` owns `expr`,
149
160
  `lim`, and `grid`; optional `repeat` computes independent batch means and
@@ -186,9 +197,45 @@ from jarvisplot import render_flowchart, render_flowchart_file
186
197
  - You can set `project.workdir` in YAML.
187
198
  - If `output.dir` is omitted, Jarvis-PLOT defaults to `<workdir>/plots/`.
188
199
  - Data cache is stored in `<workdir>/.cache/`.
200
+ - `--print` accepts the current settings and renders final figures without the logo. It announces that, after all enabled figures are saved successfully, cache payloads used or created by this run will be deleted (pipeline data and metadata, named/shared data, summaries, and HDF5 materialization slots). Rendering failures or a run with no enabled figures keep the cache. Unrelated cache entries and the lightweight fingerprint manifest are retained; later renders rebuild deleted entries as needed.
189
201
  - Profiling pipelines are prebuilt once and reused from cache when source fingerprint and profile settings are unchanged.
190
202
  - Profiling uses a fast two-stage grid reduction (`pregrid` + render `bin`) for large datasets.
191
203
 
204
+ Plot caches are automatically registered in `~/.jarvis/plot.json` (or
205
+ `$JARVIS_HOME/plot.json`). The registry groups caches by workdir and records
206
+ associated YAML paths and last use; payloads stay in their original directories.
207
+ This includes caches created by `data describe` and default `dryrun --with-data`
208
+ exports. Sizes are calculated when listing, rather than stored in the registry.
209
+
210
+ ```bash
211
+ jplot cache list
212
+ jplot cache scan ~/Jarvis-Workshop # register existing plot caches
213
+ jplot cache clean --workdir /path/to/project # this directory and all nested projects; repeatable
214
+ jplot cache clean --all --older-than 30 # unused for at least 30 days
215
+ jplot cache clean --all --dry-run # preview; no deletion/index changes
216
+ jplot cache clean --all # clean all registered inactive caches
217
+ jplot cache -h
218
+ jplot man cache
219
+ ```
220
+
221
+ `--workdir PATH` selects registered caches in PATH itself and all of its
222
+ subdirectories, even when PATH has no cache of its own. Repeat it to select
223
+ multiple directory trees; overlapping selections are deduplicated. Use
224
+ `cache scan PATH` first for old caches that have not been registered.
225
+
226
+ All cache subcommands accept `--json` and use the existing CLI envelope and
227
+ exit codes (0 success/partial, 1 failure, 2 usage). Non-TTY stdout defaults to
228
+ JSON; human results go to stderr. Active caches are skipped (`ok: null` when
229
+ the remaining operation succeeds), and records for missing paths are pruned
230
+ during cleanup. Only marked Jarvis-PLOT components are removed; source data,
231
+ plots, and unrelated `.cache` content are retained. Failed deletions retain
232
+ their registry entries. Scanning adopts legacy caches only when the four
233
+ pipeline directories and a compatible `manifest.json` identify them; old
234
+ unmarked twin-only directories need a new `dryrun --with-data` run to register.
235
+ An explicitly supplied `--out-dir` is an export directory and is not registered.
236
+ Concurrent registry writes use a lock and atomic replacement. Registration
237
+ failures warn without preventing rendering.
238
+
192
239
  ### Example: SUSYRun2 Ternary Plots
193
240
 
194
241
  ```bash
@@ -36,7 +36,6 @@ class StdAxesAdapter:
36
36
  self._defaults = defaults or {}
37
37
  self._clip_path = clip_path # None means no cropping
38
38
  self.config = self._load_internal_config()
39
- self._legend = False
40
39
  self.status = "init" # lifecycle: init -> configured -> drawn -> finalized
41
40
  self.needs_finalize = True # allow some axes (e.g., logo) to opt out
42
41
 
@@ -3,6 +3,8 @@ from __future__ import annotations
3
3
  from pathlib import Path
4
4
  from typing import Mapping
5
5
 
6
+ from .style_runtime import normalize_style_tokens
7
+
6
8
 
7
9
  def apply_figure_config(fig, info: Mapping) -> bool:
8
10
  """Apply a YAML figure block to a Figure instance."""
@@ -13,6 +15,8 @@ def apply_figure_config(fig, info: Mapping) -> bool:
13
15
  fig._setup_error = None
14
16
 
15
17
  try:
18
+ if "legend" in info:
19
+ raise ValueError("Figure-level legend is not supported; declare items on layers and overall layout under frame.<axes>.legend")
16
20
  if "name" in info:
17
21
  fig.name = info["name"]
18
22
 
@@ -53,7 +57,7 @@ def apply_figure_config(fig, info: Mapping) -> bool:
53
57
  )
54
58
 
55
59
  if "style" in info:
56
- style_tokens = info["style"]
60
+ style_tokens = normalize_style_tokens(info["style"])
57
61
  else:
58
62
  style_tokens = ["a4paper_2x1"]
59
63
  fig.style = style_tokens
@@ -11,9 +11,11 @@ from types import MethodType
11
11
  from .adapters_rect import StdAxesAdapter
12
12
  from .adapters_ternary import TernaryAxesAdapter
13
13
  from .method_registry import resolve_callable
14
- from .style_runtime import resolve_style_bundle_payload
14
+ from .style_runtime import normalize_style_tokens, resolve_style_bundle_payload
15
15
  from .config_runtime import apply_figure_config
16
16
  from .design_runtime import draw_design_reference
17
+ from .legend_runtime import apply_legend
18
+ from ..legend_cards import merge_axes_legend, effective_axes_legend
17
19
  from .layer_runtime import (
18
20
  load_layer_runtime_data as runtime_load_layer_runtime_data,
19
21
  release_layer_runtime_data as runtime_release_layer_runtime_data,
@@ -356,15 +358,35 @@ class Figure:
356
358
 
357
359
  @frame.setter
358
360
  def frame(self, value) -> None:
361
+ self._legend_disabled_axes = getattr(self, "_legend_disabled_axes", set())
362
+ for name, node in (value or {}).items():
363
+ if isinstance(node, dict) and "legend" in node:
364
+ declared = node["legend"]
365
+ if declared is False or (isinstance(declared, dict) and declared.get("enabled") is False):
366
+ self._legend_disabled_axes.add(name)
367
+ else:
368
+ self._legend_disabled_axes.discard(name)
359
369
  if self._frame is None:
360
370
  self._frame = value
361
371
  else:
372
+ # Legend order/roles are replacement lists, not deepmerge's
373
+ # default append lists. Explicit YAML also enables a disabled card
374
+ # default, unless enabled:false was supplied by the user.
375
+ value = deepcopy(value)
376
+ legends = {}
377
+ for name, node in (value or {}).items():
378
+ if isinstance(node, dict) and "legend" in node:
379
+ legends[name] = merge_axes_legend(
380
+ self._frame.get(name, {}).get("legend", False), node.pop("legend")
381
+ )
362
382
  for _name, _node in (value or {}).items():
363
383
  _color = _node.get("color") if isinstance(_node, dict) else None
364
384
  if isinstance(_color, dict) and "cmap" in _color:
365
385
  self._yaml_axc_cmaps.add(str(_name))
366
386
  from deepmerge import always_merger
367
387
  self._frame = always_merger.merge(self._frame, value)
388
+ for name, legend in legends.items():
389
+ self._frame.setdefault(name, {})["legend"] = legend
368
390
 
369
391
  @property
370
392
  def yaml_colorbar_cmaps(self) -> set:
@@ -377,12 +399,16 @@ class Figure:
377
399
 
378
400
 
379
401
  @style.setter
380
- def style(self, value) -> None:
381
- if len(value) not in (1, 2):
382
- self.logger.error("Undefined style -> {}".format(value))
383
- raise TypeError
402
+ def style(self, value) -> None:
403
+ value = normalize_style_tokens(value)
404
+ if not isinstance(value, list) or len(value) not in (1, 2):
405
+ raise TypeError(
406
+ "style must name a card family or [family, variant], e.g. "
407
+ "a4paper_2x1 or [a4paper_2x1, rect]; got {!r}".format(value)
408
+ )
384
409
  family, selected, bundle = resolve_style_bundle_payload(self.jpstyles, value)
385
410
  self._frame = deepcopy(bundle["Frame"])
411
+ self._legend_disabled_axes = set()
386
412
  self._yaml_axc_cmaps = set()
387
413
  self._style = deepcopy(bundle["Style"])
388
414
  self._debug_config = deepcopy(bundle.get("Debug", {}))
@@ -485,7 +511,7 @@ class Figure:
485
511
  return {kk: self._ensure_pandas_data(vv, reason=reason) for kk, vv in data.items()}
486
512
  return data
487
513
 
488
- def _concat_loaded_data(self, items):
514
+ def _concat_loaded_data(self, items, layer_name: str = ""):
489
515
  if len(items) == 0:
490
516
  return None
491
517
  if len(items) == 1:
@@ -496,7 +522,28 @@ class Figure:
496
522
  pandas_items = [self._ensure_pandas_data(item, reason="concat-layer") for item in items]
497
523
  try:
498
524
  return pd.concat(pandas_items, ignore_index=False)
499
- except Exception:
525
+ except Exception as exc:
526
+ # Keep drawing what can be drawn, but never drop data silently.
527
+ if self.logger:
528
+ shapes = ", ".join(
529
+ str(getattr(item, "shape", type(item).__name__)) for item in pandas_items
530
+ )
531
+ self.logger.warning(
532
+ "Layer '{}': the {} data blocks could not be concatenated, so only "
533
+ "the first block is drawn and the other {} are dropped.\n"
534
+ "\t reason \t-> {}: {}\n"
535
+ "\t blocks \t-> {}\n"
536
+ "\t hint \t\t-> blocks combined with `combine: concat` must be tables "
537
+ "with compatible columns (no duplicated column names); use "
538
+ "`combine: separate` to draw them independently.".format(
539
+ layer_name or "<unnamed>",
540
+ len(pandas_items),
541
+ len(pandas_items) - 1,
542
+ type(exc).__name__,
543
+ exc,
544
+ shapes,
545
+ )
546
+ )
500
547
  return pandas_items[0]
501
548
 
502
549
  def _store_share_data_if_needed(self, layer, data, cache_ref: str | None = None):
@@ -616,7 +663,6 @@ class Figure:
616
663
  clip_path=Path(vertices, codes) # 用 path 做 clip,transform 使用 ax.transData 已在适配器里处理
617
664
  )
618
665
  adapter._type = 'tri'
619
- adapter._legend = False
620
666
  adapter.layers = []
621
667
  adapter.status = 'configured'
622
668
  self.axes["axtri"] = adapter
@@ -1075,7 +1121,6 @@ class Figure:
1075
1121
  adapter = StdAxesAdapter(raw_ax)
1076
1122
  adapter._type = "rect"
1077
1123
  adapter.layers = []
1078
- adapter._legend = self.frame['ax'].get("legend", False)
1079
1124
  self.axes['ax'] = adapter
1080
1125
  adapter.status = 'configured'
1081
1126
 
@@ -1185,19 +1230,13 @@ class Figure:
1185
1230
  self.logger.debug("Loaded main rectangle axes -> ax")
1186
1231
 
1187
1232
  def _apply_legend_on_axes(self, ax_name: str, ax_obj, leg_cfg: dict):
1188
- """Apply a legend on a specific axes using a YAML dict stored under frame['axes'][ax_name]['legend'].
1189
- Supports an optional 'enabled' key (default True). Any 'axes' key will be ignored here."""
1190
- if not isinstance(leg_cfg, dict):
1191
- return
1192
- if leg_cfg.get("enabled", True) is False:
1193
- return
1194
- kw = dict(leg_cfg)
1195
- kw.pop("axes", None) # per-axes legend doesn't need this
1196
- try:
1197
- (ax_obj.ax if hasattr(ax_obj, "ax") else ax_obj).legend(**kw)
1198
- except Exception as e:
1199
- if self.logger:
1200
- self.logger.warning(f"Legend apply failed on '{ax_name}': {e}")
1233
+ """Apply frame.<axes>.legend after all real layer handles are available."""
1234
+ target_ax = ax_obj.ax if hasattr(ax_obj, "ax") else ax_obj
1235
+ layer_specs = getattr(self, "_legend_layers", {}).get(ax_name, [])
1236
+ return apply_legend(
1237
+ target_ax, leg_cfg, getattr(self, "_legend_layer_handles", {}).get(ax_name, {}),
1238
+ base_dir=self._yaml_dir, layer_specs=layer_specs,
1239
+ )
1201
1240
 
1202
1241
 
1203
1242
  def _install_tri_auto_clip(self, ax):
@@ -1326,10 +1365,17 @@ class Figure:
1326
1365
  # determined by frame config + data — never by render order.
1327
1366
  self._prescan_colorbar_ranges()
1328
1367
 
1368
+ self._legend_layer_handles = {}
1369
+ self._legend_layers = {}
1370
+ axes_names = {id(ax): name for name, ax in self.axes.items()}
1329
1371
  for ax, ly in self._render_queue:
1330
1372
  runtime_load_layer_runtime_data(self, ly)
1331
1373
  try:
1332
- runtime_render_layer(self, ax, ly)
1374
+ rendered = runtime_render_layer(self, ax, ly)
1375
+ name = axes_names[id(ax)]
1376
+ specs = self._legend_layers.setdefault(name, [])
1377
+ self._legend_layer_handles.setdefault(name, {})[f"@layer:{len(specs)}"] = rendered
1378
+ specs.append(ly.get("layer_spec", {}))
1333
1379
  finally:
1334
1380
  runtime_release_layer_runtime_data(self, ly)
1335
1381
 
@@ -1340,12 +1386,14 @@ class Figure:
1340
1386
 
1341
1387
  for name, ax in self.axes.items():
1342
1388
  try:
1343
- if hasattr(ax, "_legend") and ax._legend:
1344
- target_ax = ax.ax if hasattr(ax, "ax") else ax
1345
- target_ax.legend(**ax._legend)
1389
+ legend_config = self.frame.get(name, {}).get("legend", False)
1390
+ legend_config = effective_axes_legend(
1391
+ legend_config, self._legend_layers.get(name, []),
1392
+ explicitly_disabled=name in getattr(self, "_legend_disabled_axes", set()),
1393
+ )
1394
+ self._apply_legend_on_axes(name, ax, legend_config)
1346
1395
  except Exception as e:
1347
- if self.logger:
1348
- self.logger.warning(f"Legend draw failed on axes '{name}': {e}")
1396
+ raise ValueError(f"Legend draw failed on axes '{name}': {e}") from e
1349
1397
 
1350
1398
  # Finalize all colorbar axes (axc and any named axc*)
1351
1399
  for name in list(self.axes.keys()):
@@ -642,7 +642,7 @@ def _load_layer_data(fig, layer):
642
642
  fig.logger.error("DataSet -> {} not specified".format(src))
643
643
  if len(dts) == 0:
644
644
  return None, None
645
- combined = fig._concat_loaded_data(dts)
645
+ combined = fig._concat_loaded_data(dts, layer_name=str(layer.get("name", "") or ""))
646
646
  cache_ref = None
647
647
  if len(dts) == 1 and combined is dts[0] and len(cache_keys) == 1:
648
648
  cache_ref = cache_keys[0]
@@ -164,7 +164,6 @@ def ensure_rect_axes(fig, ax_name: str, kwgs: dict):
164
164
  adapter = StdAxesAdapter(raw_ax)
165
165
  adapter._type = "rect"
166
166
  adapter.layers = []
167
- adapter._legend = fig.frame.get(ax_name, {}).get("legend", False)
168
167
  fig.axes[ax_name] = adapter
169
168
  adapter.status = "configured"
170
169
 
@@ -309,7 +308,6 @@ def ensure_corr_axes(fig, kwgs: dict):
309
308
  adapter = StdAxesAdapter(raw_ax)
310
309
  adapter._type = "rect"
311
310
  adapter.layers = []
312
- adapter._legend = False
313
311
  fig.axes[name] = adapter
314
312
  adapter.status = "configured"
315
313