jarvisplot 2.0.4__tar.gz → 2.0.6__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 (241) hide show
  1. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/JarvisPLOT.egg-info/PKG-INFO +1 -1
  2. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/JarvisPLOT.egg-info/SOURCES.txt +10 -0
  3. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/PKG-INFO +1 -1
  4. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/colorbar_runtime.py +81 -3
  5. jarvisplot-2.0.6/jarvisplot/Figure/debug_config.py +466 -0
  6. jarvisplot-2.0.6/jarvisplot/Figure/design_runtime.py +637 -0
  7. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/figure.py +100 -15
  8. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/layout_runtime.py +84 -12
  9. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/method_registry.py +1 -0
  10. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/capabilities.py +4 -0
  11. jarvisplot-2.0.6/jarvisplot/cards/a4paper/1x1/ternary.json +298 -0
  12. jarvisplot-2.0.6/jarvisplot/cards/a4paper/2x1/dynesty_runplot.json +686 -0
  13. jarvisplot-2.0.6/jarvisplot/cards/a4paper/2x1/rect.json +435 -0
  14. jarvisplot-2.0.6/jarvisplot/cards/a4paper/2x1/rect5x1.json +687 -0
  15. jarvisplot-2.0.6/jarvisplot/cards/a4paper/2x1/rect_cmap.json +495 -0
  16. jarvisplot-2.0.6/jarvisplot/cards/a4paper/2x1/rect_marginal.json +660 -0
  17. jarvisplot-2.0.6/jarvisplot/cards/a4paper/2x1/rectratio.json +511 -0
  18. jarvisplot-2.0.6/jarvisplot/cards/a4paper/2x1/ternary.json +462 -0
  19. jarvisplot-2.0.6/jarvisplot/cards/a4paper/2x1/ternary_cmap.json +517 -0
  20. jarvisplot-2.0.6/jarvisplot/cards/a4paper/4x1/rect_cmap.json +480 -0
  21. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cards/a4paper/4x1/ternary_cmap.json +3 -0
  22. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cards/colors/colormaps.json +13 -0
  23. jarvisplot-2.0.6/jarvisplot/cards/gambit/1x1/ternary.json +298 -0
  24. jarvisplot-2.0.6/jarvisplot/cards/gambit/2x1/rect_cmap.json +506 -0
  25. jarvisplot-2.0.6/jarvisplot/cards/gambit/2x1/ternary.json +476 -0
  26. jarvisplot-2.0.6/jarvisplot/cards/gambit/2x1/ternary_cmap.json +533 -0
  27. jarvisplot-2.0.6/jarvisplot/cards/icons/JarvisHEP.png +0 -0
  28. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cards/style_preference.json +3 -1
  29. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/column_demand.py +6 -8
  30. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/data_access.py +2 -3
  31. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/expr_names.py +28 -1
  32. jarvisplot-2.0.6/jarvisplot/manual_cards/debug-overlay.yaml +65 -0
  33. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/manual_cards/manifest.yaml +5 -0
  34. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/manual_cards/method_notes.yaml +12 -0
  35. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/manual_cards/style-axes.yaml +2 -0
  36. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/method_contracts.py +1 -0
  37. jarvisplot-2.0.6/jarvisplot/schema/core/figure.json +230 -0
  38. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/core/layer.json +1 -0
  39. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/manifest.json +2 -0
  40. jarvisplot-2.0.6/jarvisplot/schema/methods/hist2d.json +37 -0
  41. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/pyproject.toml +1 -1
  42. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_axis_title.py +111 -0
  43. jarvisplot-2.0.6/tests/test_card_debug_shape.py +75 -0
  44. jarvisplot-2.0.6/tests/test_debug_config.py +147 -0
  45. jarvisplot-2.0.6/tests/test_debug_switches.py +180 -0
  46. jarvisplot-2.0.6/tests/test_debug_yaml_override.py +147 -0
  47. jarvisplot-2.0.6/tests/test_design_runtime.py +227 -0
  48. jarvisplot-2.0.6/tests/test_ternary_debug.py +151 -0
  49. jarvisplot-2.0.4/jarvisplot/Figure/design_runtime.py +0 -342
  50. jarvisplot-2.0.4/jarvisplot/cards/a4paper/1x1/ternary.json +0 -6
  51. jarvisplot-2.0.4/jarvisplot/cards/a4paper/2x1/dynesty_runplot.json +0 -165
  52. jarvisplot-2.0.4/jarvisplot/cards/a4paper/2x1/rect.json +0 -143
  53. jarvisplot-2.0.4/jarvisplot/cards/a4paper/2x1/rect5x1.json +0 -352
  54. jarvisplot-2.0.4/jarvisplot/cards/a4paper/2x1/rect_cmap.json +0 -189
  55. jarvisplot-2.0.4/jarvisplot/cards/a4paper/2x1/ternary.json +0 -142
  56. jarvisplot-2.0.4/jarvisplot/cards/a4paper/2x1/ternary_cmap.json +0 -189
  57. jarvisplot-2.0.4/jarvisplot/cards/a4paper/4x1/rect_cmap.json +0 -174
  58. jarvisplot-2.0.4/jarvisplot/cards/gambit/1x1/ternary.json +0 -6
  59. jarvisplot-2.0.4/jarvisplot/cards/gambit/2x1/rect_cmap.json +0 -200
  60. jarvisplot-2.0.4/jarvisplot/cards/gambit/2x1/ternary.json +0 -156
  61. jarvisplot-2.0.4/jarvisplot/cards/gambit/2x1/ternary_cmap.json +0 -205
  62. jarvisplot-2.0.4/jarvisplot/cards/icons/flowchart/JarvisHEP.png +0 -0
  63. jarvisplot-2.0.4/jarvisplot/schema/core/figure.json +0 -127
  64. jarvisplot-2.0.4/tests/test_design_runtime.py +0 -85
  65. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/JarvisPLOT.egg-info/dependency_links.txt +0 -0
  66. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/JarvisPLOT.egg-info/entry_points.txt +0 -0
  67. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/JarvisPLOT.egg-info/requires.txt +0 -0
  68. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/JarvisPLOT.egg-info/top_level.txt +0 -0
  69. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/README.md +0 -0
  70. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/__init__.py +0 -0
  71. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/adapters_rect.py +0 -0
  72. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/adapters_ternary.py +0 -0
  73. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/cards/std_axes_adapter_config.json +0 -0
  74. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/config_runtime.py +0 -0
  75. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/data_pipelines.py +0 -0
  76. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/density_cell_runtime.py +0 -0
  77. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/dynesty_runtime.py +0 -0
  78. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/figure_types.py +0 -0
  79. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/helper.py +0 -0
  80. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/interp_2d_runtime.py +0 -0
  81. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/interp_natural_neighbor.py +0 -0
  82. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/interp_natural_neighbor_exact.py +0 -0
  83. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/layer_runtime.py +0 -0
  84. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/posterior_density_runtime.py +0 -0
  85. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/posterior_hpd.py +0 -0
  86. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/posterior_mesh.py +0 -0
  87. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/preprocessor.py +0 -0
  88. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/preprocessor_runtime.py +0 -0
  89. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/profile_runtime.py +0 -0
  90. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/Figure/style_runtime.py +0 -0
  91. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/__init__.py +0 -0
  92. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/agent_digest.py +0 -0
  93. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/agent_io.py +0 -0
  94. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cache_store.py +0 -0
  95. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cards/a4paper/4x1/rect.json +0 -0
  96. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cards/a4paper/4x1/ternary.json +0 -0
  97. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cards/args.json +0 -0
  98. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cards/default/output.json +0 -0
  99. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cards/flowchart/default.json +0 -0
  100. {jarvisplot-2.0.4/jarvisplot/cards/icons → jarvisplot-2.0.6/jarvisplot/cards/icons/flowchart}/JarvisHEP.png +0 -0
  101. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cards/icons/flowchart/calculator.png +0 -0
  102. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cards/icons/flowchart/inputfile.png +0 -0
  103. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cards/icons/flowchart/opera.png +0 -0
  104. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cards/icons/flowchart/outputfile.png +0 -0
  105. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cards/icons/flowchart/sampler.png +0 -0
  106. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cards/icons/gambit.png +0 -0
  107. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cards/icons/gambit_small.png +0 -0
  108. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cli.py +0 -0
  109. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/cli_help.py +0 -0
  110. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/client.py +0 -0
  111. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/column_probe.py +0 -0
  112. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/config.py +0 -0
  113. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/config_address.py +0 -0
  114. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/core.py +0 -0
  115. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/core_assets.py +0 -0
  116. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/core_runtime.py +0 -0
  117. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/data_loader.py +0 -0
  118. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/data_loader_hdf5.py +0 -0
  119. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/data_loader_runtime.py +0 -0
  120. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/data_loader_summary.py +0 -0
  121. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/diagnostic_guidance.py +0 -0
  122. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/diagnostics.py +0 -0
  123. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/dryrun_runtime.py +0 -0
  124. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/fix_apply.py +0 -0
  125. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/flowchart.py +0 -0
  126. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/inner_func.py +0 -0
  127. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/man_catalog.py +0 -0
  128. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/man_methods.py +0 -0
  129. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/man_render_agent.py +0 -0
  130. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/man_render_human.py +0 -0
  131. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/man_transforms.py +0 -0
  132. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/manual_cards/agent-output.yaml +0 -0
  133. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/manual_cards/cli-map.yaml +0 -0
  134. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/manual_cards/data-columns.yaml +0 -0
  135. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/manual_cards/diagnostics.yaml +0 -0
  136. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/manual_cards/dryrun-viz.yaml +0 -0
  137. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/manual_cards/layer-method.yaml +0 -0
  138. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/manual_cards/type-posterior-2d.yaml +0 -0
  139. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/manual_cards/type-profile-2d.yaml +0 -0
  140. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/manual_cards/validate.yaml +0 -0
  141. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/manual_cards/workflow.yaml +0 -0
  142. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/manual_cards/yaml-root.yaml +0 -0
  143. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/memtrace.py +0 -0
  144. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/render_health.py +0 -0
  145. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/core/common.json +0 -0
  146. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/core/dataset.json +0 -0
  147. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/core/transform.json +0 -0
  148. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/bar.json +0 -0
  149. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/barh.json +0 -0
  150. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/contour.json +0 -0
  151. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/contourf.json +0 -0
  152. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/dynesty_runplot.json +0 -0
  153. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/errorbar.json +0 -0
  154. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/fill.json +0 -0
  155. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/fill_between.json +0 -0
  156. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/fill_betweenx.json +0 -0
  157. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/hist.json +0 -0
  158. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/imshow.json +0 -0
  159. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/jpcontour.json +0 -0
  160. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/jpcontourf.json +0 -0
  161. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/jpfield.json +0 -0
  162. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/pcolor.json +0 -0
  163. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/pcolormesh.json +0 -0
  164. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/plot.json +0 -0
  165. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/quiver.json +0 -0
  166. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/scatter.json +0 -0
  167. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/step.json +0 -0
  168. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/tricontour.json +0 -0
  169. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/tricontourf.json +0 -0
  170. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/tripcolor.json +0 -0
  171. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/tripcolor_axes.json +0 -0
  172. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/triplot.json +0 -0
  173. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/voronoi.json +0 -0
  174. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/methods/voronoif.json +0 -0
  175. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema/plot-config.json +0 -0
  176. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema_catalog.py +0 -0
  177. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/schema_diagnostics.py +0 -0
  178. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/templates_catalog.py +0 -0
  179. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/transform_contracts.py +0 -0
  180. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/utils/__init__.py +0 -0
  181. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/utils/cmaps.py +0 -0
  182. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/utils/dataframes.py +0 -0
  183. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/utils/expression.py +0 -0
  184. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/utils/interpolator.py +0 -0
  185. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/utils/pathing.py +0 -0
  186. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/validation.py +0 -0
  187. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/verbs/__init__.py +0 -0
  188. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/verbs/cap.py +0 -0
  189. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/verbs/config_cmd.py +0 -0
  190. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/verbs/data.py +0 -0
  191. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/verbs/doctor.py +0 -0
  192. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/verbs/dryrun.py +0 -0
  193. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/verbs/explain.py +0 -0
  194. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/verbs/man.py +0 -0
  195. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/verbs/suggest.py +0 -0
  196. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/verbs/template.py +0 -0
  197. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/verbs/validate.py +0 -0
  198. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/jarvisplot/yaml_io.py +0 -0
  199. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/setup.cfg +0 -0
  200. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_agent_digest.py +0 -0
  201. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_agent_envelope.py +0 -0
  202. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_capabilities.py +0 -0
  203. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_cli_help.py +0 -0
  204. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_cli_verbs.py +0 -0
  205. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_cmaps.py +0 -0
  206. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_column_existence.py +0 -0
  207. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_config_addressing.py +0 -0
  208. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_config_edit.py +0 -0
  209. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_config_expand.py +0 -0
  210. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_data_access.py +0 -0
  211. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_data_describe.py +0 -0
  212. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_data_head_eval.py +0 -0
  213. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_data_loader_contracts.py +0 -0
  214. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_data_pipelines.py +0 -0
  215. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_density_cell_transform.py +0 -0
  216. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_diagnostic_guidance.py +0 -0
  217. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_docs_status_labels.py +0 -0
  218. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_doctor_partial.py +0 -0
  219. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_explain.py +0 -0
  220. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_figure_contracts.py +0 -0
  221. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_figure_type_expander.py +0 -0
  222. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_fix_application.py +0 -0
  223. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_flowchart_renderer.py +0 -0
  224. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_interp_2d_transform.py +0 -0
  225. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_interp_natural_neighbor.py +0 -0
  226. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_interpolator_contracts.py +0 -0
  227. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_jpcontour_methods.py +0 -0
  228. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_man_cli.py +0 -0
  229. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_method_contracts.py +0 -0
  230. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_parse_data_metadata.py +0 -0
  231. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_pathing_contracts.py +0 -0
  232. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_posterior_hpd.py +0 -0
  233. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_render_health.py +0 -0
  234. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_row_ledger.py +0 -0
  235. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_schema_catalog.py +0 -0
  236. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_suggest.py +0 -0
  237. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_suggest_axes.py +0 -0
  238. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_template_catalog.py +0 -0
  239. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_template_json.py +0 -0
  240. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/tests/test_transform_contracts.py +0 -0
  241. {jarvisplot-2.0.4 → jarvisplot-2.0.6}/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.0.4
3
+ Version: 2.0.6
4
4
  Summary: Jarvis-PLOT: YAML-driven plotting engine
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -53,6 +53,7 @@ jarvisplot/Figure/adapters_ternary.py
53
53
  jarvisplot/Figure/colorbar_runtime.py
54
54
  jarvisplot/Figure/config_runtime.py
55
55
  jarvisplot/Figure/data_pipelines.py
56
+ jarvisplot/Figure/debug_config.py
56
57
  jarvisplot/Figure/density_cell_runtime.py
57
58
  jarvisplot/Figure/design_runtime.py
58
59
  jarvisplot/Figure/dynesty_runtime.py
@@ -80,6 +81,8 @@ jarvisplot/cards/a4paper/2x1/dynesty_runplot.json
80
81
  jarvisplot/cards/a4paper/2x1/rect.json
81
82
  jarvisplot/cards/a4paper/2x1/rect5x1.json
82
83
  jarvisplot/cards/a4paper/2x1/rect_cmap.json
84
+ jarvisplot/cards/a4paper/2x1/rect_marginal.json
85
+ jarvisplot/cards/a4paper/2x1/rectratio.json
83
86
  jarvisplot/cards/a4paper/2x1/ternary.json
84
87
  jarvisplot/cards/a4paper/2x1/ternary_cmap.json
85
88
  jarvisplot/cards/a4paper/4x1/rect.json
@@ -105,6 +108,7 @@ jarvisplot/cards/icons/flowchart/sampler.png
105
108
  jarvisplot/manual_cards/agent-output.yaml
106
109
  jarvisplot/manual_cards/cli-map.yaml
107
110
  jarvisplot/manual_cards/data-columns.yaml
111
+ jarvisplot/manual_cards/debug-overlay.yaml
108
112
  jarvisplot/manual_cards/diagnostics.yaml
109
113
  jarvisplot/manual_cards/dryrun-viz.yaml
110
114
  jarvisplot/manual_cards/layer-method.yaml
@@ -133,6 +137,7 @@ jarvisplot/schema/methods/fill.json
133
137
  jarvisplot/schema/methods/fill_between.json
134
138
  jarvisplot/schema/methods/fill_betweenx.json
135
139
  jarvisplot/schema/methods/hist.json
140
+ jarvisplot/schema/methods/hist2d.json
136
141
  jarvisplot/schema/methods/imshow.json
137
142
  jarvisplot/schema/methods/jpcontour.json
138
143
  jarvisplot/schema/methods/jpcontourf.json
@@ -171,6 +176,7 @@ tests/test_agent_digest.py
171
176
  tests/test_agent_envelope.py
172
177
  tests/test_axis_title.py
173
178
  tests/test_capabilities.py
179
+ tests/test_card_debug_shape.py
174
180
  tests/test_cli_help.py
175
181
  tests/test_cli_verbs.py
176
182
  tests/test_cmaps.py
@@ -183,6 +189,9 @@ tests/test_data_describe.py
183
189
  tests/test_data_head_eval.py
184
190
  tests/test_data_loader_contracts.py
185
191
  tests/test_data_pipelines.py
192
+ tests/test_debug_config.py
193
+ tests/test_debug_switches.py
194
+ tests/test_debug_yaml_override.py
186
195
  tests/test_density_cell_transform.py
187
196
  tests/test_design_runtime.py
188
197
  tests/test_diagnostic_guidance.py
@@ -209,5 +218,6 @@ tests/test_suggest.py
209
218
  tests/test_suggest_axes.py
210
219
  tests/test_template_catalog.py
211
220
  tests/test_template_json.py
221
+ tests/test_ternary_debug.py
212
222
  tests/test_transform_contracts.py
213
223
  tests/test_validate_no_matplotlib.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: JarvisPLOT
3
- Version: 2.0.4
3
+ Version: 2.0.6
4
4
  Summary: Jarvis-PLOT: YAML-driven plotting engine
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -56,6 +56,20 @@ def axc_color_config(frame: Mapping[str, Any], cb_name: str = "axc") -> dict:
56
56
  return out
57
57
 
58
58
 
59
+ def layer_cmap_wins(fig, cb_name: str, color_cfg: Mapping[str, Any]) -> bool:
60
+ """Whether a layer's own ``cmap`` may override ``frame[cb_name].color.cmap``.
61
+
62
+ A cmap that reached the frame from the **style card** is a preset: the
63
+ card says what this colorbar normally looks like, and a layer naming its
64
+ own is the more specific statement, so the layer wins. A cmap written in
65
+ the **project YAML** is the author talking about this one figure, so it
66
+ wins instead. Same defaults < card < YAML order the Debug block uses.
67
+ """
68
+ if color_cfg.get("cmap") is None:
69
+ return True
70
+ return str(cb_name) not in (getattr(fig, "yaml_colorbar_cmaps", None) or set())
71
+
72
+
59
73
  # ---------------------------------------------------------------------------
60
74
  # Scalar helpers
61
75
  # ---------------------------------------------------------------------------
@@ -151,6 +165,55 @@ _CONTOUR_LIKE_METHODS = frozenset({
151
165
  "jpcontour", "jpcontourf",
152
166
  })
153
167
 
168
+ #: Methods that bin their own data: the colour channel is the bin content, so
169
+ #: there is no ``z`` column for the pre-scan to read. It has to run the same
170
+ #: binning matplotlib will run.
171
+ _BINNED_COLOR_METHODS = frozenset({"hist2d"})
172
+
173
+
174
+ def _hist2d_bin_values(df, coor: dict, style: dict):
175
+ """Reproduce the bin contents ``Axes.hist2d`` will colour.
176
+
177
+ ``Axes.hist2d`` is ``np.histogram2d`` followed by ``pcolormesh``, so the
178
+ same call with the same ``bins`` / ``range`` / ``density`` / ``weights``
179
+ gives the pre-scan the colour range without drawing anything.
180
+ """
181
+ if df is None:
182
+ return None
183
+
184
+ def _channel(key):
185
+ src = coor.get(key)
186
+ if isinstance(src, dict) and "expr" in src:
187
+ return eval_dataframe_expression(df, src["expr"], logger=None)
188
+ if src is None:
189
+ src = style.get(key)
190
+ if src is None or isinstance(src, str):
191
+ return None
192
+ return np.asarray(src, dtype=float)
193
+
194
+ x = _channel("x")
195
+ y = _channel("y")
196
+ if x is None or y is None:
197
+ return None
198
+
199
+ values, _, _ = np.histogram2d(
200
+ np.asarray(x, dtype=float),
201
+ np.asarray(y, dtype=float),
202
+ bins=style.get("bins", 10),
203
+ range=style.get("range"),
204
+ density=bool(style.get("density", False)),
205
+ weights=_channel("weights"),
206
+ )
207
+ # `cmin` / `cmax` blank a bin out entirely; a blanked bin must not drag the
208
+ # colorbar range with it.
209
+ cmin = style.get("cmin")
210
+ cmax = style.get("cmax")
211
+ if cmin is not None:
212
+ values = values[values >= float(cmin)]
213
+ if cmax is not None:
214
+ values = values[values <= float(cmax)]
215
+ return values
216
+
154
217
 
155
218
  def layer_uses_color(style: dict, coor: dict, method_key: str) -> bool:
156
219
  """Return True if this layer will need a colorbar."""
@@ -169,16 +232,30 @@ def layer_uses_color(style: dict, coor: dict, method_key: str) -> bool:
169
232
  if method_key in _COLORED_Z_METHODS and _is_numeric_color_source(style_z):
170
233
  return True
171
234
 
235
+ # A self-binning method always has a colour channel -- its own bin contents.
236
+ if method_key in _BINNED_COLOR_METHODS:
237
+ return True
238
+
172
239
  return False
173
240
 
174
241
 
175
- def collect_layer_color_range(df, coor: dict, style: dict, *, scale: str | None = None):
242
+ def collect_layer_color_range(
243
+ df, coor: dict, style: dict, *, scale: str | None = None, method_key: str | None = None
244
+ ):
176
245
  """Extract (data_min, data_max) for the colour channel of a single layer.
177
246
 
178
247
  Returns (None, None) when no finite data is available.
179
248
  """
180
249
  arr = None
181
- if isinstance(coor.get("z"), dict) and "expr" in coor["z"]:
250
+ if str(method_key or "").strip().lower() in _BINNED_COLOR_METHODS:
251
+ try:
252
+ arr = _hist2d_bin_values(df, coor, style)
253
+ except Exception:
254
+ # A malformed bins/range only stops the colorbar from being
255
+ # pre-sized. Let the real draw call raise it, where the error
256
+ # names the layer instead of surfacing out of the pre-scan.
257
+ arr = None
258
+ elif isinstance(coor.get("z"), dict) and "expr" in coor["z"]:
182
259
  arr = eval_dataframe_expression(df, coor["z"]["expr"], logger=None)
183
260
  elif isinstance(coor.get("c"), dict) and "expr" in coor["c"]:
184
261
  arr = eval_dataframe_expression(df, coor["c"]["expr"], logger=None)
@@ -394,13 +471,14 @@ def collect_and_attach_colorbar(
394
471
  if not axc._cb.get("used"):
395
472
  frame = getattr(fig, "frame", {})
396
473
  color_cfg = axc_color_config(frame, cb_name)
397
- if color_cfg.get("cmap") is None and style.get("cmap") is not None:
474
+ if style.get("cmap") is not None and layer_cmap_wins(fig, cb_name, color_cfg):
398
475
  color_cfg["cmap"] = style.get("cmap")
399
476
  data_range = collect_layer_color_range(
400
477
  df,
401
478
  coor,
402
479
  style,
403
480
  scale=color_cfg.get("scale"),
481
+ method_key=method_key,
404
482
  )
405
483
  if data_range != (None, None):
406
484
  axc._cb.update(
@@ -0,0 +1,466 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """Defaults and merge rules for the design-reference debug overlay.
4
+
5
+ Split out of :mod:`design_runtime` so the configuration surface has one owner
6
+ and can be tested without importing matplotlib. The drawing code reads only
7
+ what this module hands it -- there are no literals below the unpack.
8
+
9
+ **Every appearance value lives in a block named after the matplotlib callable
10
+ that consumes it, and is splatted straight into that call:**
11
+
12
+ .. code-block:: python
13
+
14
+ ov.plot(xs, ys, **cfg["cap"]["plot"])
15
+ ov.add_patch(mpl.patches.Rectangle((l, b), w, h, **cfg["outline"]["framed"]["Rectangle"]))
16
+
17
+ So a block's keys are matplotlib's vocabulary, not Jarvis-PLOT's, and one
18
+ drawn thing owns one complete kwargs table -- no shared palette to chase, no
19
+ partial style merged with hidden Python defaults. What stays outside those
20
+ blocks is geometry: positions, spans, thresholds and the autoscale constants
21
+ that Python computes with before it draws.
22
+
23
+ Precedence, lowest to highest:
24
+
25
+ 1. :data:`DEFAULT_DEBUG` -- ships with the package, always present.
26
+ 2. A style card's root ``Debug`` block (``jarvisplot/cards/**/*.json``).
27
+ 3. A YAML ``Figures[].debug`` mapping, for one figure only.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ from copy import deepcopy
33
+ from typing import Mapping
34
+
35
+ from ..diagnostics import did_you_mean
36
+
37
+ __all__ = [
38
+ "CALL_BLOCKS",
39
+ "DEFAULT_DEBUG",
40
+ "deep_update",
41
+ "merge_debug_config",
42
+ "resolve_debug_config",
43
+ ]
44
+
45
+
46
+ # Colours are written out in full inside every call block below; these names
47
+ # exist only to keep that table readable and to make the roles obvious. They
48
+ # are not a runtime indirection -- a card overrides the block, not a palette.
49
+ _DIM = "#1F21E9" # dimension lines, arrowheads and their labels
50
+ _BOX = "#111111" # outline of a framed axes
51
+ _MARGIN = "#FF3FA4" # figure border, caption, frameless-axes outline
52
+ _NAME = "#1A237E" # text inside the axes-layout panel
53
+ _PANEL = "#FFEC73" # the panel's own background
54
+ _TERNARY = "#FF42A1" # ternary tick anchors and label leaders
55
+
56
+ # The overlay sits above every real artist; text above its own artists.
57
+ _Z_OVERLAY, _Z_ARTIST, _Z_TEXT = 10000, 10001, 10002
58
+
59
+ # Background box shared by the dimension labels and the caption. Written into
60
+ # each ``text`` block in full; the alpha differs between them.
61
+ _LABEL_BBOX = {"boxstyle": "square,pad=0.12", "fc": "white", "ec": "none"}
62
+
63
+ # Typography shared by the dimension labels, likewise written out in full.
64
+ _LABEL_FONT = {
65
+ "fontsize": 5.0,
66
+ "fontweight": "normal",
67
+ "family": "monospace",
68
+ "clip_on": False,
69
+ "zorder": _Z_TEXT,
70
+ }
71
+
72
+
73
+ DEFAULT_DEBUG = {
74
+ # Which axes gets fully dimensioned and hosts the info panel. First match wins.
75
+ "primary_order": ["ax", "ax0", "ax1", "axtri"],
76
+ "units": {"cm_per_inch": 2.54, "pt_per_inch": 72.0},
77
+ # The transparent full-figure axes every annotation is drawn onto.
78
+ "overlay": {"add_axes": {"zorder": _Z_OVERLAY}},
79
+ "figure": {
80
+ "border": {
81
+ "show": True,
82
+ "Rectangle": {"fill": False, "ec": _MARGIN, "lw": 0.9, "zorder": _Z_ARTIST},
83
+ },
84
+ "caption": {
85
+ "show": True,
86
+ "x": 0.5,
87
+ "y": 0.995,
88
+ "template": "design reference · figure %.2f cm × %.2f cm",
89
+ "text": {
90
+ "color": _MARGIN,
91
+ "fontsize": 6.0,
92
+ "ha": "center",
93
+ "va": "top",
94
+ "rotation": 0,
95
+ "fontweight": "bold",
96
+ "family": "DejaVu Sans",
97
+ "clip_on": False,
98
+ "zorder": _Z_TEXT,
99
+ "bbox": {**_LABEL_BBOX, "alpha": 0.82},
100
+ },
101
+ },
102
+ # Drawn through the shared dimension machinery, so it has no call block
103
+ # of its own -- only where it sits and how its number reads.
104
+ "height_marker": {"show": True, "x": 0.035, "template": "%.2f cm"},
105
+ },
106
+ "axes": {
107
+ "frameless_extension_max": 0.018,
108
+ "frameless_extension_min": 0.006,
109
+ "frameless_extension_factor": 0.09,
110
+ "outline": {
111
+ "show": True,
112
+ # A framed axes keeps the neutral reference outline; a frameless
113
+ # one (axlogo, say) is marked as a design box instead.
114
+ "framed": {
115
+ "Rectangle": {"fill": False, "ec": _BOX, "lw": 0.45, "zorder": _Z_ARTIST},
116
+ },
117
+ "frameless": {
118
+ "Rectangle": {"fill": False, "ec": _MARGIN, "lw": 0.45, "zorder": _Z_ARTIST},
119
+ },
120
+ },
121
+ # Hash marks past the corners, drawn for frameless axes only.
122
+ "corner_ticks": {
123
+ "show": True,
124
+ "plot": {
125
+ "color": _MARGIN,
126
+ "lw": 0.45,
127
+ "solid_capstyle": "projecting",
128
+ "clip_on": False,
129
+ "zorder": _Z_ARTIST,
130
+ },
131
+ },
132
+ },
133
+ # One dimension line: two terminal cap bars, an arrow, and a label. Every
134
+ # measured distance in the overlay is drawn with these four calls.
135
+ "dimension": {
136
+ "cap_half_pt": 5.0,
137
+ "narrow_threshold_pt": 24.0,
138
+ "narrow_length_pt": 8.0,
139
+ "narrow_scale_factor": 0.45,
140
+ "narrow_scale_min": 3.0,
141
+ "narrow_scale_max": 6.0,
142
+ "label_gap": 0.008,
143
+ "corner_gap": 0.018,
144
+ "colorbar_line_offset": 0.024,
145
+ "cap": {
146
+ "plot": {
147
+ "color": _DIM,
148
+ "lw": 0.7,
149
+ "solid_capstyle": "projecting",
150
+ "clip_on": False,
151
+ "zorder": _Z_ARTIST,
152
+ },
153
+ },
154
+ # Short gaps get two inward-pointing arrows whose tails extend outside
155
+ # the interval; ``mutation_scale`` is computed from the span, between
156
+ # ``narrow_scale_min`` and ``narrow_scale_max``.
157
+ "narrow_arrow": {
158
+ "annotate": {
159
+ "zorder": _Z_ARTIST,
160
+ "arrowprops": {
161
+ "arrowstyle": "-|>",
162
+ "shrinkA": 0,
163
+ "shrinkB": 0,
164
+ "color": _DIM,
165
+ "lw": 0.7,
166
+ },
167
+ },
168
+ },
169
+ "wide_arrow": {
170
+ "annotate": {
171
+ "zorder": _Z_ARTIST,
172
+ "arrowprops": {
173
+ "arrowstyle": "<|-|>",
174
+ "shrinkA": 0,
175
+ "shrinkB": 0,
176
+ "color": _DIM,
177
+ "lw": 0.7,
178
+ "mutation_scale": 6.0,
179
+ },
180
+ },
181
+ },
182
+ "hlabel": {
183
+ "text": {
184
+ **_LABEL_FONT,
185
+ "color": _DIM,
186
+ "ha": "center",
187
+ "va": "top",
188
+ "rotation": 0,
189
+ "bbox": {**_LABEL_BBOX, "alpha": 0.5},
190
+ },
191
+ },
192
+ "vlabel_left": {
193
+ "text": {
194
+ **_LABEL_FONT,
195
+ "color": _DIM,
196
+ "ha": "right",
197
+ "va": "center",
198
+ "rotation": 90,
199
+ "bbox": {**_LABEL_BBOX, "alpha": 0.5},
200
+ },
201
+ },
202
+ "vlabel_right": {
203
+ "text": {
204
+ **_LABEL_FONT,
205
+ "color": _DIM,
206
+ "ha": "left",
207
+ "va": "center",
208
+ "rotation": 90,
209
+ "bbox": {**_LABEL_BBOX, "alpha": 0.5},
210
+ },
211
+ },
212
+ },
213
+ "numbered_axes": {
214
+ "show": True,
215
+ "right_edge": 1.0,
216
+ "marker_step": 0.040,
217
+ "label_side": "left",
218
+ "template": "%.3f cm",
219
+ "top": {"show": True},
220
+ "bottom": {"show": True},
221
+ },
222
+ # Primary-axes margin dimensions. The top/bottom pair rides the primary
223
+ # axes' right border -- just inside it, mirroring where ``left`` sits --
224
+ # so it tracks the axes it measures and leaves the layout panel the whole
225
+ # left side. ``left`` measures along the top edge and keeps its own place.
226
+ "margins": {
227
+ "template": "%.3f cm",
228
+ "label_side": "right",
229
+ # Axes that get no margin dimensions at all. A logo plate is placed by
230
+ # eye in a corner, not designed against the page edges, so measuring it
231
+ # only adds a column that collides with the ones that matter.
232
+ "exclude": ["axlogo"],
233
+ # Axes that end at the same x would draw on top of each other, so each
234
+ # one after the first steps this far further inside the border.
235
+ "marker_step": 0.040,
236
+ "left": {"show": True},
237
+ "top": {"show": True},
238
+ "bottom": {"show": True},
239
+ },
240
+ "colorbar_gap": {"show": True, "template": "%.3f cm"},
241
+ # A layout reference has no layers, so nothing feeds the colorbar and it
242
+ # stays an empty box -- the one part of the page the reference cannot
243
+ # show. Fill it with the card's own colormap. A colorbar a layer did
244
+ # feed is a real result and is left alone.
245
+ "colorbar_preview": {
246
+ "show": True,
247
+ "samples": 256,
248
+ "fallback_cmap": "viridis",
249
+ "imshow": {
250
+ "aspect": "auto",
251
+ "interpolation": "bilinear",
252
+ "origin": "lower",
253
+ "zorder": 1,
254
+ },
255
+ },
256
+ # The layout panel. One type scale for every figure: the box is sized to
257
+ # its content and may run past the axes, rather than shrinking its text to
258
+ # fit a short axes and printing the same numbers at a different size on
259
+ # every card.
260
+ "panel": {
261
+ "show": True,
262
+ "pad_x_max": 0.018,
263
+ "pad_x_min": 0.004,
264
+ "pad_x_factor": 0.025,
265
+ "pad_y_max": 0.018,
266
+ "pad_y_min": 0.004,
267
+ "pad_y_factor": 0.035,
268
+ "entry_gap": 5.0,
269
+ "char_width_factor": 0.62,
270
+ "detail_lines_per_entry": 2.0,
271
+ "text_inset_x": 0.008,
272
+ "text_inset_y": 0.006,
273
+ "box": {
274
+ "FancyBboxPatch": {
275
+ "boxstyle": "round,pad=0.004",
276
+ "facecolor": _PANEL,
277
+ "alpha": 0.5,
278
+ "edgecolor": "none",
279
+ "linewidth": 0.0,
280
+ "zorder": _Z_ARTIST,
281
+ },
282
+ },
283
+ # ``size`` is used as written on every figure; ``fontsize`` is the one
284
+ # text kwarg Python supplies, and it supplies exactly this.
285
+ "header": {
286
+ "label": "axes layout",
287
+ "size": 6.0,
288
+ "leading": 1.20,
289
+ "text": {
290
+ "color": _NAME,
291
+ "fontweight": "bold",
292
+ "ha": "left",
293
+ "va": "top",
294
+ "family": "monospace",
295
+ "clip_on": True,
296
+ "zorder": _Z_TEXT,
297
+ },
298
+ },
299
+ "name": {
300
+ "size": 7.0,
301
+ "leading": 1.15,
302
+ "text": {
303
+ "color": _NAME,
304
+ "fontweight": "bold",
305
+ "ha": "left",
306
+ "va": "top",
307
+ "family": "monospace",
308
+ "clip_on": True,
309
+ "zorder": _Z_TEXT,
310
+ },
311
+ },
312
+ "detail": {
313
+ "size": 4.6,
314
+ "leading": 1.08,
315
+ "rect_template": " rect=[%.3f, %.3f, %.3f, %.3f]",
316
+ "size_template": " width: %.3f cm height: %.3f cm",
317
+ "text": {
318
+ "color": _NAME,
319
+ "fontweight": "normal",
320
+ "ha": "left",
321
+ "va": "top",
322
+ "family": "monospace",
323
+ "clip_on": True,
324
+ "zorder": _Z_TEXT,
325
+ },
326
+ },
327
+ },
328
+ # Ternary tick anchors and label leader lines, drawn during axtri build.
329
+ "ternary": {
330
+ "tick_anchors": {
331
+ "show": True,
332
+ "scatter": {"s": 1.0, "marker": ".", "c": _TERNARY, "clip_on": False},
333
+ },
334
+ "label_leaders": {
335
+ "show": True,
336
+ "plot": {
337
+ "marker": ".",
338
+ "linestyle": "-",
339
+ "lw": 0.3,
340
+ "markersize": 1,
341
+ "c": _TERNARY,
342
+ "clip_on": False,
343
+ },
344
+ },
345
+ },
346
+ }
347
+
348
+
349
+ #: Blocks named after the matplotlib callable that receives them. Their keys
350
+ #: are matplotlib's vocabulary, so the merge stops validating inside them --
351
+ #: the same way ``layers[].style`` is a delegated zone in the YAML schema.
352
+ #: Jarvis-PLOT does not enumerate what matplotlib accepts.
353
+ CALL_BLOCKS = (
354
+ "add_axes",
355
+ "annotate",
356
+ "plot",
357
+ "scatter",
358
+ "text",
359
+ "FancyBboxPatch",
360
+ "Rectangle",
361
+ )
362
+
363
+
364
+ def deep_update(base: Mapping, override: Mapping | None) -> dict:
365
+ """Layer one override mapping over another, without validating either.
366
+
367
+ Used to combine a style card's ``Debug`` block with a figure's YAML
368
+ ``debug:`` mapping. Validation is deliberately not done here: both are
369
+ partial by design, so checking them against each other would report every
370
+ key the card happens not to mention. :func:`merge_debug_config` validates
371
+ the combined result against :data:`DEFAULT_DEBUG` once, later.
372
+
373
+ Non-mapping values replace rather than merge -- notably lists, so a card
374
+ overriding ``primary_order`` sets it instead of appending to it.
375
+ """
376
+ merged = deepcopy(dict(base))
377
+ if not isinstance(override, Mapping):
378
+ return merged
379
+ for key, value in override.items():
380
+ if isinstance(value, Mapping) and isinstance(merged.get(key), Mapping):
381
+ merged[key] = deep_update(merged[key], value)
382
+ else:
383
+ merged[key] = deepcopy(value)
384
+ return merged
385
+
386
+
387
+ def _is_delegated(key: str) -> bool:
388
+ return key in CALL_BLOCKS
389
+
390
+
391
+ def merge_debug_config(
392
+ base: Mapping,
393
+ override: Mapping | None,
394
+ *,
395
+ path: str = "",
396
+ problems: list[str] | None = None,
397
+ ) -> tuple[dict, list[str]]:
398
+ """Merge a ``Debug`` block over the defaults, reporting what it had to drop.
399
+
400
+ Returns ``(merged, problems)``. Two things used to fail in silence here and
401
+ now surface instead:
402
+
403
+ - a key the defaults do not define was carried through as inert junk, so a
404
+ typo looked exactly like a working override;
405
+ - a scalar written where the defaults hold a mapping was discarded outright.
406
+
407
+ Inside a call block (see :data:`CALL_BLOCKS`) neither check applies: those
408
+ keys belong to matplotlib. Overrides there are layered key by key, so a
409
+ card may change one arrow property without restating the rest.
410
+
411
+ Neither ever raises. A bad ``Debug`` block degrades to the defaults and says
412
+ so once, because a debug overlay must never be able to break a plot.
413
+ """
414
+ problems = [] if problems is None else problems
415
+ merged = deepcopy(dict(base))
416
+ if not isinstance(override, Mapping):
417
+ return merged, problems
418
+
419
+ for key, value in override.items():
420
+ where = f"{path}{key}"
421
+ if key not in merged:
422
+ hint = did_you_mean(str(key), [str(k) for k in merged])
423
+ suffix = f" Did you mean {hint[0]!r}?" if hint else ""
424
+ problems.append(f"{where}: unknown Debug key, ignored.{suffix}")
425
+ continue
426
+
427
+ if _is_delegated(str(key)):
428
+ # matplotlib kwargs: take the override wholesale, validate nothing.
429
+ if isinstance(merged[key], Mapping) and isinstance(value, Mapping):
430
+ merged[key] = deep_update(merged[key], value)
431
+ else:
432
+ merged[key] = deepcopy(value)
433
+ continue
434
+
435
+ if isinstance(merged[key], Mapping):
436
+ if isinstance(value, Mapping):
437
+ merged[key], _ = merge_debug_config(
438
+ merged[key], value, path=f"{where}.", problems=problems
439
+ )
440
+ else:
441
+ problems.append(
442
+ f"{where}: expected a mapping, got "
443
+ f"{type(value).__name__}; kept the default."
444
+ )
445
+ continue
446
+
447
+ merged[key] = deepcopy(value)
448
+
449
+ return merged, problems
450
+
451
+
452
+ def resolve_debug_config(fig) -> dict:
453
+ """Return the merged debug settings, logging anything the merge dropped."""
454
+ merged, problems = merge_debug_config(
455
+ DEFAULT_DEBUG,
456
+ getattr(fig, "debug_config", getattr(fig, "_debug_config", None)),
457
+ )
458
+ if problems:
459
+ logger = getattr(fig, "logger", None)
460
+ warn = getattr(logger, "warning", None)
461
+ if callable(warn):
462
+ try:
463
+ warn("Debug config:\n\t" + "\n\t".join(problems))
464
+ except Exception:
465
+ pass
466
+ return merged