xy 0.0.2__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.
- xy-0.0.2/.github/workflows/ci.yml +796 -0
- xy-0.0.2/.github/workflows/codspeed.yml +76 -0
- xy-0.0.2/.github/workflows/release.yml +357 -0
- xy-0.0.2/.gitignore +17 -0
- xy-0.0.2/CHANGELOG.md +570 -0
- xy-0.0.2/CONTRIBUTING.md +31 -0
- xy-0.0.2/Cargo.lock +85 -0
- xy-0.0.2/Cargo.toml +21 -0
- xy-0.0.2/LICENSE +202 -0
- xy-0.0.2/Makefile +169 -0
- xy-0.0.2/PKG-INFO +239 -0
- xy-0.0.2/README.md +212 -0
- xy-0.0.2/SECURITY.md +36 -0
- xy-0.0.2/benchmarks/README.md +221 -0
- xy-0.0.2/benchmarks/__init__.py +1 -0
- xy-0.0.2/benchmarks/_browser.py +180 -0
- xy-0.0.2/benchmarks/_cdp.py +326 -0
- xy-0.0.2/benchmarks/_launch_interactive.py +559 -0
- xy-0.0.2/benchmarks/_launch_static.py +271 -0
- xy-0.0.2/benchmarks/_launch_webagg.py +402 -0
- xy-0.0.2/benchmarks/_xy_browser.py +176 -0
- xy-0.0.2/benchmarks/baseline.json +52 -0
- xy-0.0.2/benchmarks/bench.py +94 -0
- xy-0.0.2/benchmarks/bench_2d_charts.py +898 -0
- xy-0.0.2/benchmarks/bench_animation.py +158 -0
- xy-0.0.2/benchmarks/bench_dashboard.py +503 -0
- xy-0.0.2/benchmarks/bench_heatmap_native.py +167 -0
- xy-0.0.2/benchmarks/bench_install.py +291 -0
- xy-0.0.2/benchmarks/bench_interaction.py +1045 -0
- xy-0.0.2/benchmarks/bench_launch_scatter.py +190 -0
- xy-0.0.2/benchmarks/bench_line.py +290 -0
- xy-0.0.2/benchmarks/bench_native.py +233 -0
- xy-0.0.2/benchmarks/bench_native_vs_agg.py +230 -0
- xy-0.0.2/benchmarks/bench_pyplot_vs_matplotlib.py +587 -0
- xy-0.0.2/benchmarks/bench_raw_scatter.py +233 -0
- xy-0.0.2/benchmarks/bench_scatter_native.py +521 -0
- xy-0.0.2/benchmarks/bench_transport.py +541 -0
- xy-0.0.2/benchmarks/bench_vs.py +952 -0
- xy-0.0.2/benchmarks/bench_workflows.py +413 -0
- xy-0.0.2/benchmarks/categories.py +158 -0
- xy-0.0.2/benchmarks/environment.py +147 -0
- xy-0.0.2/benchmarks/launch_baselines/xy-0.1.0/macos-arm64-m5-pro/cpu-fallback-results.json +1162 -0
- xy-0.0.2/benchmarks/launch_baselines/xy-0.1.0/macos-arm64-m5-pro/default-results.json +2021 -0
- xy-0.0.2/benchmarks/launch_baselines/xy-0.1.0/macos-arm64-m5-pro/environment.json +24 -0
- xy-0.0.2/benchmarks/launch_baselines/xy-0.1.0/macos-arm64-m5-pro/manifest.json +48 -0
- xy-0.0.2/benchmarks/launch_baselines/xy-0.1.0/macos-arm64-m5-pro/pyproject.toml +19 -0
- xy-0.0.2/benchmarks/launch_baselines/xy-0.1.0/macos-arm64-m5-pro/report.md +256 -0
- xy-0.0.2/benchmarks/launch_baselines/xy-0.1.0/macos-arm64-m5-pro/uv.lock +772 -0
- xy-0.0.2/benchmarks/requirements-ci.in +20 -0
- xy-0.0.2/benchmarks/requirements-ci.lock +315 -0
- xy-0.0.2/benchmarks/test_codspeed_animation.py +95 -0
- xy-0.0.2/benchmarks/test_codspeed_kernels.py +1238 -0
- xy-0.0.2/benchmarks/test_codspeed_pyplot.py +395 -0
- xy-0.0.2/benchmarks/test_codspeed_selection.py +147 -0
- xy-0.0.2/benchmarks/test_codspeed_transport.py +100 -0
- xy-0.0.2/docs/advanced/index.md +139 -0
- xy-0.0.2/docs/advanced/runtime-and-deployment.md +134 -0
- xy-0.0.2/docs/api-reference/changelog.md +43 -0
- xy-0.0.2/docs/api-reference/chart-factories.md +54 -0
- xy-0.0.2/docs/api-reference/contributing.md +73 -0
- xy-0.0.2/docs/api-reference/events-and-callbacks.md +81 -0
- xy-0.0.2/docs/api-reference/figure-methods.md +132 -0
- xy-0.0.2/docs/api-reference/index.md +31 -0
- xy-0.0.2/docs/api-reference/limitations-and-alpha-status.md +92 -0
- xy-0.0.2/docs/api-reference/marks-and-components.md +57 -0
- xy-0.0.2/docs/api-reference/public-types.md +68 -0
- xy-0.0.2/docs/charts/area-chart.md +195 -0
- xy-0.0.2/docs/charts/bar-chart.md +197 -0
- xy-0.0.2/docs/charts/box-plot.md +189 -0
- xy-0.0.2/docs/charts/contour-plot.md +186 -0
- xy-0.0.2/docs/charts/ecdf.md +172 -0
- xy-0.0.2/docs/charts/heatmap.md +172 -0
- xy-0.0.2/docs/charts/hexbin.md +180 -0
- xy-0.0.2/docs/charts/histogram.md +178 -0
- xy-0.0.2/docs/charts/line-chart.md +160 -0
- xy-0.0.2/docs/charts/scatter.md +195 -0
- xy-0.0.2/docs/charts/segments.md +179 -0
- xy-0.0.2/docs/charts/stem-plot.md +169 -0
- xy-0.0.2/docs/charts/uncertainty.md +185 -0
- xy-0.0.2/docs/charts/violin-plot.md +191 -0
- xy-0.0.2/docs/components/annotations.md +257 -0
- xy-0.0.2/docs/components/axes.md +237 -0
- xy-0.0.2/docs/components/colorbars.md +245 -0
- xy-0.0.2/docs/components/facets-and-layers.md +165 -0
- xy-0.0.2/docs/components/index.md +41 -0
- xy-0.0.2/docs/components/legends.md +178 -0
- xy-0.0.2/docs/components/marks.md +258 -0
- xy-0.0.2/docs/components/modebars-and-interaction-controls.md +312 -0
- xy-0.0.2/docs/components/tooltips.md +179 -0
- xy-0.0.2/docs/components/triangle-mesh.md +232 -0
- xy-0.0.2/docs/core-concepts/axes-and-scales.md +149 -0
- xy-0.0.2/docs/core-concepts/configuration.md +162 -0
- xy-0.0.2/docs/core-concepts/data.md +302 -0
- xy-0.0.2/docs/core-concepts/index.md +203 -0
- xy-0.0.2/docs/core-concepts/interactions.md +279 -0
- xy-0.0.2/docs/core-concepts/large-data-and-performance.md +107 -0
- xy-0.0.2/docs/guides/dashboards-and-linked-views.md +65 -0
- xy-0.0.2/docs/guides/dataframes-and-real-data.md +169 -0
- xy-0.0.2/docs/guides/deployment-recipes.md +177 -0
- xy-0.0.2/docs/guides/display-and-export.md +201 -0
- xy-0.0.2/docs/guides/getting-help.md +135 -0
- xy-0.0.2/docs/guides/index.md +23 -0
- xy-0.0.2/docs/guides/real-time-and-streaming-data.md +72 -0
- xy-0.0.2/docs/guides/serving-csp-and-offline-use.md +66 -0
- xy-0.0.2/docs/guides/troubleshooting.md +108 -0
- xy-0.0.2/docs/index.md +91 -0
- xy-0.0.2/docs/integrations/index.md +20 -0
- xy-0.0.2/docs/integrations/matplotlib.md +66 -0
- xy-0.0.2/docs/integrations/notebooks.md +86 -0
- xy-0.0.2/docs/integrations/reflex.md +236 -0
- xy-0.0.2/docs/overview/benchmarks.md +156 -0
- xy-0.0.2/docs/overview/first-chart.md +164 -0
- xy-0.0.2/docs/overview/gallery.md +48 -0
- xy-0.0.2/docs/overview/installation.md +121 -0
- xy-0.0.2/docs/styling/animations.md +720 -0
- xy-0.0.2/docs/styling/chrome-slots.md +261 -0
- xy-0.0.2/docs/styling/component-variations.md +623 -0
- xy-0.0.2/docs/styling/customize.md +782 -0
- xy-0.0.2/docs/styling/examples.md +837 -0
- xy-0.0.2/docs/styling/gallery.md +719 -0
- xy-0.0.2/docs/styling/index.md +271 -0
- xy-0.0.2/docs/styling/mark-styles.md +239 -0
- xy-0.0.2/docs/styling/playground.md +16 -0
- xy-0.0.2/docs/styling/recipes.md +520 -0
- xy-0.0.2/docs/styling/themes-and-tokens.md +551 -0
- xy-0.0.2/examples/bad_apple/README.md +92 -0
- xy-0.0.2/examples/fastapi/README.md +55 -0
- xy-0.0.2/examples/fastapi/app.py +281 -0
- xy-0.0.2/examples/fastapi/charts.py +601 -0
- xy-0.0.2/examples/fastapi/live_drilldown.py +339 -0
- xy-0.0.2/examples/fastapi/pyproject.toml +16 -0
- xy-0.0.2/examples/osm/README.md +99 -0
- xy-0.0.2/examples/osm/osmium-rs/Cargo.lock +105 -0
- xy-0.0.2/examples/osm/osmium-rs/Cargo.toml +30 -0
- xy-0.0.2/examples/osm/osmium-rs/README.md +66 -0
- xy-0.0.2/examples/osm/osmium-rs/src/lib.rs +576 -0
- xy-0.0.2/examples/osm/osmium-rs/src/main.rs +88 -0
- xy-0.0.2/examples/osm/osmium-rs/src/sort.rs +369 -0
- xy-0.0.2/examples/osm/osmium-rs/src/sort_main.rs +105 -0
- xy-0.0.2/examples/pdsh/README.md +130 -0
- xy-0.0.2/examples/reflex/README.md +80 -0
- xy-0.0.2/examples/reflex/pyproject.toml +16 -0
- xy-0.0.2/examples/reflex/rxconfig.py +10 -0
- xy-0.0.2/examples/reflex/xy_reflex_demo/__init__.py +1 -0
- xy-0.0.2/examples/reflex/xy_reflex_demo/xy_reflex_demo.py +722 -0
- xy-0.0.2/hatch_build.py +326 -0
- xy-0.0.2/js/build.mjs +127 -0
- xy-0.0.2/js/src/00_header.ts +224 -0
- xy-0.0.2/js/src/10_colormaps.ts +51 -0
- xy-0.0.2/js/src/20_theme.ts +213 -0
- xy-0.0.2/js/src/30_ticks.ts +232 -0
- xy-0.0.2/js/src/40_gl.ts +975 -0
- xy-0.0.2/js/src/45_lod.ts +1457 -0
- xy-0.0.2/js/src/46_worker.ts +103 -0
- xy-0.0.2/js/src/50_chartview.ts +5647 -0
- xy-0.0.2/js/src/51_annotations.ts +674 -0
- xy-0.0.2/js/src/52_tooltip.ts +329 -0
- xy-0.0.2/js/src/53_interaction.ts +2491 -0
- xy-0.0.2/js/src/54_kernel.ts +995 -0
- xy-0.0.2/js/src/55_marks.ts +222 -0
- xy-0.0.2/js/src/56_animation.ts +504 -0
- xy-0.0.2/js/src/57_viewstate.ts +583 -0
- xy-0.0.2/js/src/60_entries.ts +94 -0
- xy-0.0.2/js/tsconfig.json +23 -0
- xy-0.0.2/package-lock.json +1324 -0
- xy-0.0.2/package.json +14 -0
- xy-0.0.2/pyproject.toml +230 -0
- xy-0.0.2/python/xy/__init__.py +325 -0
- xy-0.0.2/python/xy/_annotations.py +598 -0
- xy-0.0.2/python/xy/_arrowgeom.py +210 -0
- xy-0.0.2/python/xy/_chromium.py +436 -0
- xy-0.0.2/python/xy/_figure.py +1889 -0
- xy-0.0.2/python/xy/_framing.py +306 -0
- xy-0.0.2/python/xy/_hosts.py +68 -0
- xy-0.0.2/python/xy/_jpeg.py +382 -0
- xy-0.0.2/python/xy/_native.py +3258 -0
- xy-0.0.2/python/xy/_ooc.py +150 -0
- xy-0.0.2/python/xy/_paint.py +85 -0
- xy-0.0.2/python/xy/_payload.py +1140 -0
- xy-0.0.2/python/xy/_pdf.py +1262 -0
- xy-0.0.2/python/xy/_png.py +86 -0
- xy-0.0.2/python/xy/_raster.py +2177 -0
- xy-0.0.2/python/xy/_scene.py +107 -0
- xy-0.0.2/python/xy/_spatial.py +118 -0
- xy-0.0.2/python/xy/_svg.py +3000 -0
- xy-0.0.2/python/xy/_trace.py +147 -0
- xy-0.0.2/python/xy/_typing.py +57 -0
- xy-0.0.2/python/xy/_validate.py +502 -0
- xy-0.0.2/python/xy/_webp.py +322 -0
- xy-0.0.2/python/xy/channel.py +411 -0
- xy-0.0.2/python/xy/channels.py +811 -0
- xy-0.0.2/python/xy/columns.py +610 -0
- xy-0.0.2/python/xy/components.py +5197 -0
- xy-0.0.2/python/xy/config.py +174 -0
- xy-0.0.2/python/xy/dom.py +40 -0
- xy-0.0.2/python/xy/export.py +1139 -0
- xy-0.0.2/python/xy/facets.py +485 -0
- xy-0.0.2/python/xy/interaction.py +1398 -0
- xy-0.0.2/python/xy/kernels.py +155 -0
- xy-0.0.2/python/xy/lod.py +991 -0
- xy-0.0.2/python/xy/marks.py +2485 -0
- xy-0.0.2/python/xy/py.typed +0 -0
- xy-0.0.2/python/xy/pyplot/__init__.py +3093 -0
- xy-0.0.2/python/xy/pyplot/_artists.py +1175 -0
- xy-0.0.2/python/xy/pyplot/_axes.py +5325 -0
- xy-0.0.2/python/xy/pyplot/_axisgrid.py +301 -0
- xy-0.0.2/python/xy/pyplot/_colors.py +397 -0
- xy-0.0.2/python/xy/pyplot/_fmt.py +52 -0
- xy-0.0.2/python/xy/pyplot/_grid.py +345 -0
- xy-0.0.2/python/xy/pyplot/_mathtext.py +150 -0
- xy-0.0.2/python/xy/pyplot/_mplfig.py +1273 -0
- xy-0.0.2/python/xy/pyplot/_plot_types.py +4709 -0
- xy-0.0.2/python/xy/pyplot/_rc.py +190 -0
- xy-0.0.2/python/xy/pyplot/_state.py +155 -0
- xy-0.0.2/python/xy/pyplot/_ticker.py +276 -0
- xy-0.0.2/python/xy/pyplot/_transforms.py +111 -0
- xy-0.0.2/python/xy/pyplot/_translate.py +141 -0
- xy-0.0.2/python/xy/pyplot/dates.py +188 -0
- xy-0.0.2/python/xy/static/index.js +853 -0
- xy-0.0.2/python/xy/static/standalone.js +853 -0
- xy-0.0.2/python/xy/styles.py +379 -0
- xy-0.0.2/python/xy/widget.py +192 -0
- xy-0.0.2/scripts/_app_smoke.py +279 -0
- xy-0.0.2/scripts/_protocol.py +18 -0
- xy-0.0.2/scripts/abi_smoke.py +1699 -0
- xy-0.0.2/scripts/animation_smoke.py +420 -0
- xy-0.0.2/scripts/append_stream_smoke.py +430 -0
- xy-0.0.2/scripts/bench.py +11 -0
- xy-0.0.2/scripts/bench_dashboard.py +11 -0
- xy-0.0.2/scripts/bench_interaction.py +11 -0
- xy-0.0.2/scripts/bench_native.py +11 -0
- xy-0.0.2/scripts/bench_pyplot_vs_matplotlib.py +11 -0
- xy-0.0.2/scripts/bench_scatter_native.py +11 -0
- xy-0.0.2/scripts/bench_vs.py +11 -0
- xy-0.0.2/scripts/browser_conformance.mjs +300 -0
- xy-0.0.2/scripts/check_claim_guardrails.py +216 -0
- xy-0.0.2/scripts/check_public_api.py +520 -0
- xy-0.0.2/scripts/check_python_floor.py +180 -0
- xy-0.0.2/scripts/check_regressions.py +268 -0
- xy-0.0.2/scripts/check_release_version.py +81 -0
- xy-0.0.2/scripts/gen_font.py +124 -0
- xy-0.0.2/scripts/interaction_stress_smoke.py +131 -0
- xy-0.0.2/scripts/merge_benchmark_reports.py +116 -0
- xy-0.0.2/scripts/pick_boundary_smoke.py +316 -0
- xy-0.0.2/scripts/png_export_smoke.py +181 -0
- xy-0.0.2/scripts/pyodide_load_smoke.py +107 -0
- xy-0.0.2/scripts/reflex_lifecycle_smoke.py +325 -0
- xy-0.0.2/scripts/reflex_ws_smoke.py +356 -0
- xy-0.0.2/scripts/rename_fc_to_xy.py +199 -0
- xy-0.0.2/scripts/render_smoke_nonumpy.py +1624 -0
- xy-0.0.2/scripts/smoke_render.py +130 -0
- xy-0.0.2/scripts/step_tier_smoke.py +143 -0
- xy-0.0.2/scripts/sync_matplotlib_compat.py +186 -0
- xy-0.0.2/scripts/verify_benchmark_report.py +2179 -0
- xy-0.0.2/scripts/verify_ci_workflow.py +880 -0
- xy-0.0.2/scripts/verify_docs_quickstart.py +184 -0
- xy-0.0.2/scripts/verify_local.py +658 -0
- xy-0.0.2/scripts/verify_sdist.py +435 -0
- xy-0.0.2/scripts/verify_wheel.py +424 -0
- xy-0.0.2/scripts/visual_regression_smoke.py +259 -0
- xy-0.0.2/spec/README.md +111 -0
- xy-0.0.2/spec/api/api-examples.md +590 -0
- xy-0.0.2/spec/api/chart-kind-contract.md +259 -0
- xy-0.0.2/spec/api/chart-roadmap.md +453 -0
- xy-0.0.2/spec/api/export.md +218 -0
- xy-0.0.2/spec/api/interaction.md +507 -0
- xy-0.0.2/spec/api/styling.md +626 -0
- xy-0.0.2/spec/assets/benchmark-snapshot.svg +93 -0
- xy-0.0.2/spec/assets/launch-benchmark-comparison.svg +90 -0
- xy-0.0.2/spec/assets/xy-sdf-binned-scatter.png +0 -0
- xy-0.0.2/spec/benchmarks/methodology.md +371 -0
- xy-0.0.2/spec/benchmarks/metrics.md +56 -0
- xy-0.0.2/spec/benchmarks/results.md +1015 -0
- xy-0.0.2/spec/design/animation.md +164 -0
- xy-0.0.2/spec/design/chart-grammar.md +351 -0
- xy-0.0.2/spec/design/lod-architecture.md +738 -0
- xy-0.0.2/spec/design/pan-and-zoom-configuration.md +902 -0
- xy-0.0.2/spec/design/reflex-integration.md +641 -0
- xy-0.0.2/spec/design/reflex-shaped-api.md +778 -0
- xy-0.0.2/spec/design/renderer-architecture.md +418 -0
- xy-0.0.2/spec/design/rust-engine.md +340 -0
- xy-0.0.2/spec/design/view-state.md +447 -0
- xy-0.0.2/spec/design/wire-protocol.md +448 -0
- xy-0.0.2/spec/design-dossier.md +1945 -0
- xy-0.0.2/spec/matplotlib/compat-changelog.md +270 -0
- xy-0.0.2/spec/matplotlib/compat-matrix.md +138 -0
- xy-0.0.2/spec/matplotlib/compat.md +141 -0
- xy-0.0.2/spec/matplotlib/shim-todo.md +680 -0
- xy-0.0.2/spec/process/contributing.md +278 -0
- xy-0.0.2/spec/process/perf-audit-2026-07-22.md +194 -0
- xy-0.0.2/spec/process/production-readiness.md +471 -0
- xy-0.0.2/spec/process/rendering-verification.md +257 -0
- xy-0.0.2/spec/process/security-audit-2026-07-06.md +228 -0
- xy-0.0.2/src/css.rs +788 -0
- xy-0.0.2/src/font.rs +1039 -0
- xy-0.0.2/src/kernels.rs +7214 -0
- xy-0.0.2/src/lib.rs +3352 -0
- xy-0.0.2/src/raster.rs +3451 -0
- xy-0.0.2/src/simd.rs +448 -0
- xy-0.0.2/src/svg.rs +58 -0
- xy-0.0.2/src/tiles.rs +1160 -0
- xy-0.0.2/tests/conftest.py +167 -0
- xy-0.0.2/tests/pyplot/__init__.py +0 -0
- xy-0.0.2/tests/pyplot/compatibility.json +27 -0
- xy-0.0.2/tests/pyplot/conftest.py +17 -0
- xy-0.0.2/tests/pyplot/corpus/01_basic_line.py +7 -0
- xy-0.0.2/tests/pyplot/corpus/02_plot_fmt_red_dashed.py +8 -0
- xy-0.0.2/tests/pyplot/corpus/03_plot_fmt_green_circles.py +3 -0
- xy-0.0.2/tests/pyplot/corpus/04_plot_fmt_cycle_dashdot_square.py +7 -0
- xy-0.0.2/tests/pyplot/corpus/05_multi_series_one_call.py +6 -0
- xy-0.0.2/tests/pyplot/corpus/06_implicit_x.py +4 -0
- xy-0.0.2/tests/pyplot/corpus/07_labels_title_legend_grid.py +13 -0
- xy-0.0.2/tests/pyplot/corpus/08_xlim_ylim.py +8 -0
- xy-0.0.2/tests/pyplot/corpus/09_log_scale.py +9 -0
- xy-0.0.2/tests/pyplot/corpus/10_scatter_basic.py +6 -0
- xy-0.0.2/tests/pyplot/corpus/11_scatter_color_array_cmap.py +9 -0
- xy-0.0.2/tests/pyplot/corpus/12_scatter_size_array.py +9 -0
- xy-0.0.2/tests/pyplot/corpus/13_scatter_edgecolors.py +15 -0
- xy-0.0.2/tests/pyplot/corpus/14_bar_categories.py +7 -0
- xy-0.0.2/tests/pyplot/corpus/15_bar_stacked_bottom.py +9 -0
- xy-0.0.2/tests/pyplot/corpus/16_barh.py +5 -0
- xy-0.0.2/tests/pyplot/corpus/17_hist_bins.py +7 -0
- xy-0.0.2/tests/pyplot/corpus/18_hist_density.py +6 -0
- xy-0.0.2/tests/pyplot/corpus/19_hist_cumulative.py +7 -0
- xy-0.0.2/tests/pyplot/corpus/20_fill_between_band.py +11 -0
- xy-0.0.2/tests/pyplot/corpus/21_imshow_cmap.py +8 -0
- xy-0.0.2/tests/pyplot/corpus/22_step.py +7 -0
- xy-0.0.2/tests/pyplot/corpus/23_axhline_axvline.py +8 -0
- xy-0.0.2/tests/pyplot/corpus/24_axvspan_band.py +9 -0
- xy-0.0.2/tests/pyplot/corpus/25_annotate_text.py +10 -0
- xy-0.0.2/tests/pyplot/corpus/26_twinx_dual_axis.py +11 -0
- xy-0.0.2/tests/pyplot/corpus/27_subplots_2x2_mixed.py +17 -0
- xy-0.0.2/tests/pyplot/corpus/28_subplots_figsize.py +8 -0
- xy-0.0.2/tests/pyplot/corpus/29_implicit_state_savefig.py +13 -0
- xy-0.0.2/tests/pyplot/corpus/30_savefig_html.py +10 -0
- xy-0.0.2/tests/pyplot/corpus/31_rcparams_figsize.py +9 -0
- xy-0.0.2/tests/pyplot/corpus/32_xticks_rotation.py +7 -0
- xy-0.0.2/tests/pyplot/corpus/33_set_data_mutation.py +16 -0
- xy-0.0.2/tests/pyplot/corpus/34_gray_string_color.py +8 -0
- xy-0.0.2/tests/pyplot/corpus/35_tab_colors.py +5 -0
- xy-0.0.2/tests/pyplot/corpus/36_color_cycle_c0_c9.py +8 -0
- xy-0.0.2/tests/pyplot/corpus/37_markers_only_fmt.py +8 -0
- xy-0.0.2/tests/pyplot/corpus/38_close_all_hygiene.py +5 -0
- xy-0.0.2/tests/pyplot/corpus/39_multiple_figures.py +8 -0
- xy-0.0.2/tests/pyplot/corpus/40_subplots_row_sharex.py +9 -0
- xy-0.0.2/tests/pyplot/corpus/41_line_kwargs.py +17 -0
- xy-0.0.2/tests/pyplot/corpus/42_tick_params_rotation.py +5 -0
- xy-0.0.2/tests/pyplot/corpus/43_grid_html_suptitle.py +15 -0
- xy-0.0.2/tests/pyplot/corpus/44_subplot_classic.py +11 -0
- xy-0.0.2/tests/pyplot/corpus/45_xticks_positions_labels.py +7 -0
- xy-0.0.2/tests/pyplot/corpus/46_stackplot.py +7 -0
- xy-0.0.2/tests/pyplot/corpus/47_statistical_families.py +12 -0
- xy-0.0.2/tests/pyplot/corpus/48_vector_fields.py +11 -0
- xy-0.0.2/tests/pyplot/corpus/49_unstructured_mesh.py +14 -0
- xy-0.0.2/tests/pyplot/corpus/50_pie_donut.py +10 -0
- xy-0.0.2/tests/pyplot/corpus/51_basic_2d_aliases.py +11 -0
- xy-0.0.2/tests/pyplot/corpus/52_spectral.py +16 -0
- xy-0.0.2/tests/pyplot/corpus/53_matplotlib_311_plotting.py +47 -0
- xy-0.0.2/tests/pyplot/corpus/54_plotting_method_coverage.py +32 -0
- xy-0.0.2/tests/pyplot/matplotlib_311_plotting.json +29 -0
- xy-0.0.2/tests/pyplot/test_advanced_compatibility.py +82 -0
- xy-0.0.2/tests/pyplot/test_adversarial_parity_fixes.py +237 -0
- xy-0.0.2/tests/pyplot/test_annotation_label_clearance.py +174 -0
- xy-0.0.2/tests/pyplot/test_artist_mutations.py +94 -0
- xy-0.0.2/tests/pyplot/test_artist_transform_contracts.py +102 -0
- xy-0.0.2/tests/pyplot/test_axes_charts.py +588 -0
- xy-0.0.2/tests/pyplot/test_axes_helpers.py +116 -0
- xy-0.0.2/tests/pyplot/test_axes_layout.py +209 -0
- xy-0.0.2/tests/pyplot/test_boundaries.py +131 -0
- xy-0.0.2/tests/pyplot/test_color_pipeline_fixes.py +257 -0
- xy-0.0.2/tests/pyplot/test_compatibility_metadata.py +90 -0
- xy-0.0.2/tests/pyplot/test_corpus.py +85 -0
- xy-0.0.2/tests/pyplot/test_facetgrid.py +190 -0
- xy-0.0.2/tests/pyplot/test_figure_state.py +130 -0
- xy-0.0.2/tests/pyplot/test_fmt.py +39 -0
- xy-0.0.2/tests/pyplot/test_grid_legend_contracts.py +163 -0
- xy-0.0.2/tests/pyplot/test_launch_compat.py +375 -0
- xy-0.0.2/tests/pyplot/test_layout_noops.py +77 -0
- xy-0.0.2/tests/pyplot/test_layout_text_parity_fixes.py +298 -0
- xy-0.0.2/tests/pyplot/test_marker_fidelity.py +42 -0
- xy-0.0.2/tests/pyplot/test_p3_option_contracts.py +430 -0
- xy-0.0.2/tests/pyplot/test_pdsh_gap_features.py +704 -0
- xy-0.0.2/tests/pyplot/test_perf_guardrail.py +78 -0
- xy-0.0.2/tests/pyplot/test_public_annotations.py +45 -0
- xy-0.0.2/tests/pyplot/test_pyplot_state_management.py +97 -0
- xy-0.0.2/tests/pyplot/test_rc_chrome_contracts.py +98 -0
- xy-0.0.2/tests/pyplot/test_rc_color_export_contracts.py +114 -0
- xy-0.0.2/tests/pyplot/test_reference_corpus.py +136 -0
- xy-0.0.2/tests/pyplot/test_reference_semantics.py +268 -0
- xy-0.0.2/tests/pyplot/test_silent_drop_regressions.py +413 -0
- xy-0.0.2/tests/pyplot/test_state_and_figs.py +183 -0
- xy-0.0.2/tests/pyplot/test_toolbar_controls.py +68 -0
- xy-0.0.2/tests/pyplot/test_vectorized_styles.py +246 -0
- xy-0.0.2/tests/pyplot/test_visible_style_contracts.py +88 -0
- xy-0.0.2/tests/pyplot/test_visual_parity_fixes.py +250 -0
- xy-0.0.2/tests/test_accessibility_contract.py +128 -0
- xy-0.0.2/tests/test_animation.py +396 -0
- xy-0.0.2/tests/test_api_parity.py +179 -0
- xy-0.0.2/tests/test_arrow_ingest.py +122 -0
- xy-0.0.2/tests/test_arrowgeom.py +63 -0
- xy-0.0.2/tests/test_batch_export.py +109 -0
- xy-0.0.2/tests/test_bench_pyplot_vs_matplotlib.py +83 -0
- xy-0.0.2/tests/test_bench_transport.py +92 -0
- xy-0.0.2/tests/test_bench_vs.py +102 -0
- xy-0.0.2/tests/test_benchmark_environment.py +353 -0
- xy-0.0.2/tests/test_benchmark_requirements.py +22 -0
- xy-0.0.2/tests/test_channel.py +384 -0
- xy-0.0.2/tests/test_check_regressions.py +245 -0
- xy-0.0.2/tests/test_check_release_version.py +88 -0
- xy-0.0.2/tests/test_claim_guardrails.py +154 -0
- xy-0.0.2/tests/test_components.py +2214 -0
- xy-0.0.2/tests/test_context_loss_preserves_view.py +134 -0
- xy-0.0.2/tests/test_css_mark_styles.py +309 -0
- xy-0.0.2/tests/test_css_validation.py +172 -0
- xy-0.0.2/tests/test_declarative_colorbar.py +342 -0
- xy-0.0.2/tests/test_density_mean_color.py +523 -0
- xy-0.0.2/tests/test_density_pan_no_rebin.py +148 -0
- xy-0.0.2/tests/test_density_physical_alpha.py +151 -0
- xy-0.0.2/tests/test_density_request_economy.py +258 -0
- xy-0.0.2/tests/test_density_sample_resolvability_gate.py +218 -0
- xy-0.0.2/tests/test_density_sample_zoomout_fades.py +204 -0
- xy-0.0.2/tests/test_density_texture_eviction.py +121 -0
- xy-0.0.2/tests/test_density_wire_economy.py +105 -0
- xy-0.0.2/tests/test_dependencies.py +50 -0
- xy-0.0.2/tests/test_drill_point_window_cache.py +216 -0
- xy-0.0.2/tests/test_drill_transition_continuity.py +221 -0
- xy-0.0.2/tests/test_drill_window_padding.py +158 -0
- xy-0.0.2/tests/test_drill_zoomin_elides_request.py +214 -0
- xy-0.0.2/tests/test_drill_zoomout_clears.py +164 -0
- xy-0.0.2/tests/test_example_apps.py +268 -0
- xy-0.0.2/tests/test_facets.py +382 -0
- xy-0.0.2/tests/test_figure.py +2233 -0
- xy-0.0.2/tests/test_framing.py +307 -0
- xy-0.0.2/tests/test_framing_property.py +42 -0
- xy-0.0.2/tests/test_hatch_build.py +91 -0
- xy-0.0.2/tests/test_html_transport.py +50 -0
- xy-0.0.2/tests/test_image_export.py +494 -0
- xy-0.0.2/tests/test_import.py +384 -0
- xy-0.0.2/tests/test_interaction_probe_retries.py +84 -0
- xy-0.0.2/tests/test_jpeg.py +172 -0
- xy-0.0.2/tests/test_kernels.py +1215 -0
- xy-0.0.2/tests/test_legend_highlight.py +381 -0
- xy-0.0.2/tests/test_legend_resize_regression.py +690 -0
- xy-0.0.2/tests/test_legend_toggle.py +566 -0
- xy-0.0.2/tests/test_lod.py +629 -0
- xy-0.0.2/tests/test_merge_benchmark_reports.py +70 -0
- xy-0.0.2/tests/test_modebar_select_drill.py +105 -0
- xy-0.0.2/tests/test_ooc.py +170 -0
- xy-0.0.2/tests/test_pan_lock_containment.py +215 -0
- xy-0.0.2/tests/test_payload_update_rehome.py +130 -0
- xy-0.0.2/tests/test_pdf_export.py +220 -0
- xy-0.0.2/tests/test_pdsh_dual_engine_notebooks.py +50 -0
- xy-0.0.2/tests/test_plot_families.py +356 -0
- xy-0.0.2/tests/test_png_export.py +917 -0
- xy-0.0.2/tests/test_property_figure.py +223 -0
- xy-0.0.2/tests/test_public_api.py +407 -0
- xy-0.0.2/tests/test_pyodide_load_smoke.py +46 -0
- xy-0.0.2/tests/test_python_floor.py +117 -0
- xy-0.0.2/tests/test_scatter.py +1227 -0
- xy-0.0.2/tests/test_scatter_matrix.py +480 -0
- xy-0.0.2/tests/test_select_polygon.py +146 -0
- xy-0.0.2/tests/test_selection_rows.py +119 -0
- xy-0.0.2/tests/test_spatial.py +120 -0
- xy-0.0.2/tests/test_static_client_security.py +946 -0
- xy-0.0.2/tests/test_streaming.py +422 -0
- xy-0.0.2/tests/test_svg_export.py +733 -0
- xy-0.0.2/tests/test_symlog_axis.py +238 -0
- xy-0.0.2/tests/test_type_surface.py +453 -0
- xy-0.0.2/tests/test_ui_issue_regressions.py +325 -0
- xy-0.0.2/tests/test_verify_benchmark_report.py +1544 -0
- xy-0.0.2/tests/test_verify_ci_workflow.py +765 -0
- xy-0.0.2/tests/test_verify_local.py +675 -0
- xy-0.0.2/tests/test_verify_sdist.py +498 -0
- xy-0.0.2/tests/test_verify_wheel.py +471 -0
- xy-0.0.2/tests/test_view_state.py +344 -0
- xy-0.0.2/tests/test_view_state_client.py +1254 -0
- xy-0.0.2/tests/test_viewport_bounds.py +58 -0
- xy-0.0.2/tests/test_webp.py +142 -0
- xy-0.0.2/tests/test_wheel_gesture_end.py +178 -0
- xy-0.0.2/tests/test_widget.py +153 -0
- xy-0.0.2/tests/test_zoom_precision.py +104 -0
|
@@ -0,0 +1,796 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main", "claude/**"]
|
|
6
|
+
pull_request:
|
|
7
|
+
paths-ignore:
|
|
8
|
+
- "docs/**"
|
|
9
|
+
- "spec/**"
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
# Multiple sessions push to two branches in bursts; without this the runner
|
|
13
|
+
# pool drowns in runs for superseded commits and fresh tips queue for ~30 min.
|
|
14
|
+
# Only the newest run per ref carries signal — cancel the rest.
|
|
15
|
+
concurrency:
|
|
16
|
+
group: ci-${{ github.ref }}
|
|
17
|
+
cancel-in-progress: true
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
matplotlib_reference:
|
|
21
|
+
name: Matplotlib 3.11 reference compatibility
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
25
|
+
with:
|
|
26
|
+
# Full history and tags: the distribution version is derived
|
|
27
|
+
# from the latest `v*` tag, and a shallow clone has none.
|
|
28
|
+
fetch-depth: 0
|
|
29
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
30
|
+
with:
|
|
31
|
+
enable-cache: false
|
|
32
|
+
# The editable install below builds the render client from source via the
|
|
33
|
+
# hatch hook (generated, not committed; §33) — the pyplot corpus exports
|
|
34
|
+
# HTML, which inlines the client bundle. Pin Node so the build uses a
|
|
35
|
+
# vite-compatible version rather than the runner's system Node.
|
|
36
|
+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
37
|
+
with:
|
|
38
|
+
node-version: "22"
|
|
39
|
+
- name: Install xy and released reference wheel
|
|
40
|
+
run: |
|
|
41
|
+
uv venv .venv
|
|
42
|
+
uv pip install -p .venv/bin/python -e ".[dev]"
|
|
43
|
+
uv pip install -p .venv/bin/python "matplotlib==3.11.0"
|
|
44
|
+
- name: Verify released reference and reviewed snapshot
|
|
45
|
+
run: |
|
|
46
|
+
.venv/bin/python -c "import matplotlib; assert matplotlib.__version__ == '3.11.0'"
|
|
47
|
+
.venv/bin/python scripts/sync_matplotlib_compat.py --check
|
|
48
|
+
- name: Run optional-interoperability and dual-engine corpus tests
|
|
49
|
+
env:
|
|
50
|
+
MPLBACKEND: Agg
|
|
51
|
+
run: |
|
|
52
|
+
.venv/bin/pytest -q tests/pyplot/test_launch_compat.py
|
|
53
|
+
.venv/bin/pytest -q tests/pyplot/test_reference_corpus.py
|
|
54
|
+
.venv/bin/pytest -q tests/pyplot/test_reference_semantics.py
|
|
55
|
+
|
|
56
|
+
test:
|
|
57
|
+
name: Test (Rust + Python + JS)
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
61
|
+
with:
|
|
62
|
+
# Full history and tags: the distribution version is derived
|
|
63
|
+
# from the latest `v*` tag, and a shallow clone has none.
|
|
64
|
+
fetch-depth: 0
|
|
65
|
+
- uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
|
|
66
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
67
|
+
with:
|
|
68
|
+
enable-cache: false
|
|
69
|
+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
70
|
+
with:
|
|
71
|
+
node-version: "22"
|
|
72
|
+
|
|
73
|
+
- name: Install JS build toolchain
|
|
74
|
+
run: npm ci
|
|
75
|
+
|
|
76
|
+
- name: Rust unit tests
|
|
77
|
+
run: cargo test
|
|
78
|
+
|
|
79
|
+
- name: Rust lint
|
|
80
|
+
run: cargo clippy --all-targets -- -D warnings
|
|
81
|
+
|
|
82
|
+
- name: Build native core
|
|
83
|
+
run: cargo build --release
|
|
84
|
+
|
|
85
|
+
- name: Build JS client
|
|
86
|
+
run: node js/build.mjs
|
|
87
|
+
|
|
88
|
+
- name: JS parses (ESM + IIFE)
|
|
89
|
+
run: |
|
|
90
|
+
node -e "import('./python/xy/static/index.js').then(m => { if (!m.default.render) throw new Error('no render export'); })"
|
|
91
|
+
node --check python/xy/static/standalone.js
|
|
92
|
+
|
|
93
|
+
- name: Python 3.11 floor syntax check
|
|
94
|
+
run: python3 scripts/check_python_floor.py
|
|
95
|
+
|
|
96
|
+
- name: Workflow gate wiring
|
|
97
|
+
run: python3 scripts/verify_ci_workflow.py
|
|
98
|
+
|
|
99
|
+
# Dependency-free gates — verify the two seams cargo can't (the C ABI and
|
|
100
|
+
# the WebGL2 render path) and run even if PyPI is unreachable.
|
|
101
|
+
- name: Native ABI smoke (stdlib only)
|
|
102
|
+
run: python3 scripts/abi_smoke.py
|
|
103
|
+
|
|
104
|
+
# Playwright's Chromium, not the apt/snap one: Ubuntu's chromium-browser is
|
|
105
|
+
# a confined snap that can't read the temp HTML under /tmp and whose GPU
|
|
106
|
+
# process crashes headless — it fails the render probe for environment
|
|
107
|
+
# reasons, not code. Playwright ships an unconfined build.
|
|
108
|
+
- name: Install Chromium (Playwright)
|
|
109
|
+
run: |
|
|
110
|
+
npm ci
|
|
111
|
+
npx playwright install --with-deps chromium
|
|
112
|
+
- name: Headless render smoke (stdlib + Chromium)
|
|
113
|
+
run: |
|
|
114
|
+
CHROME=$(node -e "console.log(require('playwright').chromium.executablePath())")
|
|
115
|
+
echo "using $CHROME"
|
|
116
|
+
python3 scripts/render_smoke_nonumpy.py "$CHROME"
|
|
117
|
+
|
|
118
|
+
- name: Streaming append smoke (stdlib + Chromium)
|
|
119
|
+
run: |
|
|
120
|
+
CHROME=$(node -e "console.log(require('playwright').chromium.executablePath())")
|
|
121
|
+
python3 scripts/append_stream_smoke.py "$CHROME"
|
|
122
|
+
|
|
123
|
+
- name: Static PNG export smoke (stdlib + Chromium)
|
|
124
|
+
run: XY_CHROMIUM="$(node -e "console.log(require('playwright').chromium.executablePath())")" python3 scripts/png_export_smoke.py
|
|
125
|
+
|
|
126
|
+
- name: Native benchmark (stdlib only — executed numbers, no PyPI needed)
|
|
127
|
+
run: python3 scripts/bench_native.py --sizes 1e5,1e6
|
|
128
|
+
|
|
129
|
+
- name: Install package + dev deps
|
|
130
|
+
run: |
|
|
131
|
+
uv venv .venv
|
|
132
|
+
uv pip install -p .venv/bin/python -e ".[dev]"
|
|
133
|
+
|
|
134
|
+
- name: Regression gate (deterministic payloads = hard fail; timing = advisory)
|
|
135
|
+
run: |
|
|
136
|
+
.venv/bin/python benchmarks/bench_scatter_native.py \
|
|
137
|
+
--sizes 1e5,1e6,1e7 --production --json scatter.json
|
|
138
|
+
python3 scripts/bench_native.py --sizes 1e6,1e7 --json kernel.json
|
|
139
|
+
CHROME=$(node -e "console.log(require('playwright').chromium.executablePath())")
|
|
140
|
+
.venv/bin/python benchmarks/bench_transport.py --n 1e6 --reps 15 \
|
|
141
|
+
--browser-reps 12 --chromium "$CHROME" --require-browser --json transport.json
|
|
142
|
+
python3 scripts/verify_benchmark_report.py scatter.json --kind scatter-native
|
|
143
|
+
python3 scripts/verify_benchmark_report.py kernel.json --kind kernel-native
|
|
144
|
+
python3 scripts/verify_benchmark_report.py transport.json --kind transport-loopback
|
|
145
|
+
python3 scripts/check_regressions.py --scatter scatter.json --kernel kernel.json \
|
|
146
|
+
--transport transport.json --emit-md spec/benchmarks/metrics.md
|
|
147
|
+
|
|
148
|
+
- name: Upload regression benchmark report
|
|
149
|
+
if: always()
|
|
150
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
151
|
+
with:
|
|
152
|
+
name: regression-benchmark-report
|
|
153
|
+
if-no-files-found: warn
|
|
154
|
+
path: |
|
|
155
|
+
spec/benchmarks/metrics.md
|
|
156
|
+
scatter.json
|
|
157
|
+
kernel.json
|
|
158
|
+
transport.json
|
|
159
|
+
|
|
160
|
+
- name: Public API coherence
|
|
161
|
+
run: .venv/bin/python scripts/check_public_api.py
|
|
162
|
+
|
|
163
|
+
- name: Claim guardrails
|
|
164
|
+
run: .venv/bin/python scripts/check_claim_guardrails.py
|
|
165
|
+
|
|
166
|
+
- name: Lint (ruff)
|
|
167
|
+
run: |
|
|
168
|
+
.venv/bin/ruff check .
|
|
169
|
+
.venv/bin/ruff format --check .
|
|
170
|
+
|
|
171
|
+
# ty is pre-1.0 (0.0.x) and advisory here: it type-checks the shippable
|
|
172
|
+
# library and reports findings, but doesn't gate the build — pytest, ruff,
|
|
173
|
+
# and cargo are the hard gates. Test/benchmark code intentionally
|
|
174
|
+
# subscripts known-non-None Optionals, which ty can't narrow.
|
|
175
|
+
- name: Typecheck (ty, advisory)
|
|
176
|
+
run: .venv/bin/ty check python || echo "::warning::ty reported type findings (advisory)"
|
|
177
|
+
|
|
178
|
+
- name: Python tests (native core)
|
|
179
|
+
# Point PNG-export tests at the Playwright Chromium (not on PATH) so the
|
|
180
|
+
# full Figure.to_png path runs here rather than skipping.
|
|
181
|
+
run: |
|
|
182
|
+
export XY_CHROMIUM="$(node -e "console.log(require('playwright').chromium.executablePath())")"
|
|
183
|
+
# Chromium is installed here, so a probe that cannot launch it is a
|
|
184
|
+
# defect: fail rather than skip, so the browser layer cannot pass by
|
|
185
|
+
# absence. (A crash or non-zero exit already fails unconditionally.)
|
|
186
|
+
export XY_REQUIRE_BROWSER=1
|
|
187
|
+
.venv/bin/pytest -q
|
|
188
|
+
|
|
189
|
+
# End-to-end: a real Figure (line decimation + scatter) through to_html's
|
|
190
|
+
# exact payload path, rendered in Chromium, pixels read back. Complements
|
|
191
|
+
# the stdlib smoke, which hand-builds payloads. (Was dead code — audit
|
|
192
|
+
# finding: the only real-Figure→browser gate, never executed.)
|
|
193
|
+
- name: Real-Figure render smoke (numpy + Chromium)
|
|
194
|
+
run: |
|
|
195
|
+
CHROME=$(node -e "console.log(require('playwright').chromium.executablePath())")
|
|
196
|
+
.venv/bin/python scripts/smoke_render.py "$CHROME"
|
|
197
|
+
|
|
198
|
+
- name: Browser lifecycle smoke (Chromium)
|
|
199
|
+
run: |
|
|
200
|
+
CHROME=$(node -e "console.log(require('playwright').chromium.executablePath())")
|
|
201
|
+
.venv/bin/python scripts/reflex_lifecycle_smoke.py "$CHROME"
|
|
202
|
+
|
|
203
|
+
- name: Browser visual regression smoke (Chromium)
|
|
204
|
+
run: |
|
|
205
|
+
CHROME=$(node -e "console.log(require('playwright').chromium.executablePath())")
|
|
206
|
+
.venv/bin/python scripts/visual_regression_smoke.py "$CHROME"
|
|
207
|
+
|
|
208
|
+
- name: Step tier-update smoke (Chromium)
|
|
209
|
+
run: |
|
|
210
|
+
CHROME=$(node -e "console.log(require('playwright').chromium.executablePath())")
|
|
211
|
+
.venv/bin/python scripts/step_tier_smoke.py "$CHROME"
|
|
212
|
+
|
|
213
|
+
- name: Browser interaction stress smoke (Chromium)
|
|
214
|
+
run: |
|
|
215
|
+
CHROME=$(node -e "console.log(require('playwright').chromium.executablePath())")
|
|
216
|
+
.venv/bin/python scripts/interaction_stress_smoke.py "$CHROME"
|
|
217
|
+
|
|
218
|
+
- name: Browser dashboard reliability smoke (Chromium)
|
|
219
|
+
run: |
|
|
220
|
+
CHROME=$(node -e "console.log(require('playwright').chromium.executablePath())")
|
|
221
|
+
.venv/bin/python benchmarks/bench_dashboard.py \
|
|
222
|
+
--chart-counts 10,20,50 --chromium "$CHROME" --json dashboard-smoke.json
|
|
223
|
+
.venv/bin/python scripts/verify_benchmark_report.py \
|
|
224
|
+
dashboard-smoke.json --kind dashboard-browser
|
|
225
|
+
|
|
226
|
+
- name: Benchmark smoke (1e5/1e6 — §12 harness runs every phase)
|
|
227
|
+
run: .venv/bin/python scripts/bench.py --sizes 1e5,1e6
|
|
228
|
+
|
|
229
|
+
browser_conformance:
|
|
230
|
+
name: Accessibility + cross-browser conformance
|
|
231
|
+
runs-on: ubuntu-latest
|
|
232
|
+
steps:
|
|
233
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
234
|
+
with:
|
|
235
|
+
# Full history and tags: the distribution version is derived
|
|
236
|
+
# from the latest `v*` tag, and a shallow clone has none.
|
|
237
|
+
fetch-depth: 0
|
|
238
|
+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
239
|
+
with:
|
|
240
|
+
node-version: "22"
|
|
241
|
+
- name: Cache Playwright browsers
|
|
242
|
+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
243
|
+
with:
|
|
244
|
+
path: ~/.cache/ms-playwright
|
|
245
|
+
key: playwright-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('package-lock.json') }}
|
|
246
|
+
- name: Install Playwright browser engines
|
|
247
|
+
run: |
|
|
248
|
+
npm ci
|
|
249
|
+
npx playwright install --with-deps chromium firefox webkit
|
|
250
|
+
- name: Build render client
|
|
251
|
+
run: node js/build.mjs
|
|
252
|
+
- name: Run accessibility and cross-browser conformance
|
|
253
|
+
# Headless Firefox on GitHub's Linux runners exposes no WebGL2. Use the
|
|
254
|
+
# virtual display installed by Playwright so every engine still runs
|
|
255
|
+
# the real WebGL rendering and perceptual assertions.
|
|
256
|
+
env:
|
|
257
|
+
MOZ_WEBRENDER: "1"
|
|
258
|
+
XY_CONFORMANCE_HEADFUL: "1"
|
|
259
|
+
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x720x24" node scripts/browser_conformance.mjs
|
|
260
|
+
|
|
261
|
+
python_floor:
|
|
262
|
+
name: Python 3.11 floor
|
|
263
|
+
runs-on: ubuntu-latest
|
|
264
|
+
steps:
|
|
265
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
266
|
+
with:
|
|
267
|
+
# Full history and tags: the distribution version is derived
|
|
268
|
+
# from the latest `v*` tag, and a shallow clone has none.
|
|
269
|
+
fetch-depth: 0
|
|
270
|
+
- uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
|
|
271
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
272
|
+
with:
|
|
273
|
+
python-version: "3.11"
|
|
274
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
275
|
+
with:
|
|
276
|
+
enable-cache: false
|
|
277
|
+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
278
|
+
with:
|
|
279
|
+
node-version: "22"
|
|
280
|
+
|
|
281
|
+
- name: Verify syntax/annotation floor
|
|
282
|
+
run: python scripts/check_python_floor.py
|
|
283
|
+
|
|
284
|
+
- name: Build native core
|
|
285
|
+
run: cargo build --release
|
|
286
|
+
|
|
287
|
+
# The render client is a generated artifact (§33): build it so the full
|
|
288
|
+
# test suite (which reads python/xy/static/*.js) has it on disk.
|
|
289
|
+
- name: Build render client
|
|
290
|
+
run: |
|
|
291
|
+
npm ci
|
|
292
|
+
node js/build.mjs
|
|
293
|
+
|
|
294
|
+
- name: Install package for Python 3.11
|
|
295
|
+
run: |
|
|
296
|
+
python -VV
|
|
297
|
+
# Absolute path: newer uv resolves a bare name as a directory, not
|
|
298
|
+
# the interpreter setup-python put on PATH.
|
|
299
|
+
uv venv .venv --python "$(command -v python)"
|
|
300
|
+
uv pip install -p .venv/bin/python -e . "pytest>=8,<9"
|
|
301
|
+
|
|
302
|
+
- name: Public API coherence
|
|
303
|
+
run: .venv/bin/python scripts/check_public_api.py
|
|
304
|
+
|
|
305
|
+
- name: Python 3.11 tests (native core)
|
|
306
|
+
run: .venv/bin/python -m pytest -q
|
|
307
|
+
|
|
308
|
+
# Cross-library scatter benchmark incl. time-to-first-render (spec/benchmarks/results.md).
|
|
309
|
+
# Each isolated group uses the same deterministic input sizes and runs in
|
|
310
|
+
# parallel. The report job below merges the partial results.
|
|
311
|
+
benchmark_vs:
|
|
312
|
+
name: Cross-library benchmark (${{ matrix.name }})
|
|
313
|
+
runs-on: ubuntu-latest
|
|
314
|
+
timeout-minutes: 10
|
|
315
|
+
continue-on-error: true
|
|
316
|
+
strategy:
|
|
317
|
+
fail-fast: false
|
|
318
|
+
matrix:
|
|
319
|
+
include:
|
|
320
|
+
- name: native-and-webgl
|
|
321
|
+
libraries: xy,plotly_gl,bokeh_webgl,datashader
|
|
322
|
+
packages: plotly kaleido bokeh datashader psutil
|
|
323
|
+
max_n: 10000000
|
|
324
|
+
xy: true
|
|
325
|
+
browser: true
|
|
326
|
+
build_js: true
|
|
327
|
+
# These adapters each run a timed render plus a separate tracemalloc
|
|
328
|
+
# render. Keep them isolated so their expensive ceiling probes run in
|
|
329
|
+
# parallel instead of serializing the slowest benchmark job.
|
|
330
|
+
- name: matplotlib
|
|
331
|
+
libraries: matplotlib
|
|
332
|
+
packages: numpy matplotlib psutil
|
|
333
|
+
max_n: 10000000
|
|
334
|
+
xy: false
|
|
335
|
+
browser: false
|
|
336
|
+
build_js: false
|
|
337
|
+
- name: seaborn
|
|
338
|
+
libraries: seaborn
|
|
339
|
+
packages: numpy seaborn psutil
|
|
340
|
+
max_n: 10000000
|
|
341
|
+
xy: false
|
|
342
|
+
browser: false
|
|
343
|
+
build_js: false
|
|
344
|
+
- name: plotly-svg
|
|
345
|
+
libraries: plotly_svg
|
|
346
|
+
packages: numpy plotly kaleido psutil
|
|
347
|
+
max_n: 10000000
|
|
348
|
+
xy: false
|
|
349
|
+
browser: true
|
|
350
|
+
build_js: false
|
|
351
|
+
- name: bokeh-canvas
|
|
352
|
+
libraries: bokeh_canvas
|
|
353
|
+
packages: numpy bokeh psutil
|
|
354
|
+
max_n: 10000000
|
|
355
|
+
xy: false
|
|
356
|
+
browser: true
|
|
357
|
+
build_js: false
|
|
358
|
+
- name: html-adapters
|
|
359
|
+
libraries: altair,hvplot_bokeh
|
|
360
|
+
packages: numpy altair hvplot psutil
|
|
361
|
+
# HTML serialization becomes pathological at multi-million-point
|
|
362
|
+
# sizes. Keep this group aligned with the existing TTFR cap; the
|
|
363
|
+
# skipped rows remain explicit in the merged report.
|
|
364
|
+
max_n: 100000
|
|
365
|
+
xy: false
|
|
366
|
+
browser: true
|
|
367
|
+
build_js: false
|
|
368
|
+
steps:
|
|
369
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
370
|
+
with:
|
|
371
|
+
# Full history and tags: the distribution version is derived
|
|
372
|
+
# from the latest `v*` tag, and a shallow clone has none.
|
|
373
|
+
fetch-depth: 0
|
|
374
|
+
- uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
|
|
375
|
+
if: matrix.xy
|
|
376
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
377
|
+
with:
|
|
378
|
+
enable-cache: false
|
|
379
|
+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
380
|
+
if: matrix.browser || matrix.build_js
|
|
381
|
+
with:
|
|
382
|
+
node-version: "22"
|
|
383
|
+
- name: Install JS build toolchain
|
|
384
|
+
if: matrix.build_js
|
|
385
|
+
run: npm ci
|
|
386
|
+
- name: Build native core
|
|
387
|
+
if: matrix.xy
|
|
388
|
+
run: cargo build --release
|
|
389
|
+
- name: Build JS client
|
|
390
|
+
if: matrix.build_js
|
|
391
|
+
run: node js/build.mjs
|
|
392
|
+
- name: Create benchmark environment
|
|
393
|
+
run: uv venv .venv
|
|
394
|
+
- name: Install xy
|
|
395
|
+
if: matrix.xy
|
|
396
|
+
env:
|
|
397
|
+
XY_REQUIRE_CARGO: "1"
|
|
398
|
+
run: |
|
|
399
|
+
uv pip install -p .venv/bin/python \
|
|
400
|
+
--constraint benchmarks/requirements-ci.lock -e .
|
|
401
|
+
- name: Install selected competitors
|
|
402
|
+
run: |
|
|
403
|
+
uv pip install -p .venv/bin/python \
|
|
404
|
+
--constraint benchmarks/requirements-ci.lock ${{ matrix.packages }}
|
|
405
|
+
- name: Verify native benchmark backend
|
|
406
|
+
if: matrix.xy
|
|
407
|
+
run: |
|
|
408
|
+
.venv/bin/python - <<'PY'
|
|
409
|
+
import xy.kernels as k
|
|
410
|
+
assert k.BACKEND == "native", f"benchmark job requires native backend, got {k.BACKEND!r}"
|
|
411
|
+
PY
|
|
412
|
+
- name: Install Chromium (Playwright)
|
|
413
|
+
if: matrix.browser
|
|
414
|
+
run: |
|
|
415
|
+
npm ci
|
|
416
|
+
npx playwright install --with-deps chromium
|
|
417
|
+
- name: Run cross-library benchmark group
|
|
418
|
+
run: |
|
|
419
|
+
CHROMIUM_ARGS=()
|
|
420
|
+
if [[ "${{ matrix.browser }}" == "true" ]]; then
|
|
421
|
+
CHROME=$(node -e "console.log(require('playwright').chromium.executablePath())")
|
|
422
|
+
CHROMIUM_ARGS=(--chromium "$CHROME")
|
|
423
|
+
fi
|
|
424
|
+
.venv/bin/python scripts/bench_vs.py \
|
|
425
|
+
--libraries "${{ matrix.libraries }}" \
|
|
426
|
+
--max-n "${{ matrix.max_n }}" \
|
|
427
|
+
--sizes 1e3,1e4,1e5,1e6,3e6,1e7 --budget 45 \
|
|
428
|
+
--ttfr --ttfr-max-n 1e5 "${CHROMIUM_ARGS[@]}" \
|
|
429
|
+
--out scatter-${{ matrix.name }}.md --json scatter-${{ matrix.name }}.json
|
|
430
|
+
- name: Upload cross-library benchmark part
|
|
431
|
+
if: always()
|
|
432
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
433
|
+
with:
|
|
434
|
+
name: benchmark-vs-${{ matrix.name }}
|
|
435
|
+
if-no-files-found: warn
|
|
436
|
+
path: |
|
|
437
|
+
scatter-${{ matrix.name }}.md
|
|
438
|
+
scatter-${{ matrix.name }}.json
|
|
439
|
+
|
|
440
|
+
# Other benchmark suites run concurrently with the cross-library matrix.
|
|
441
|
+
benchmark_methodology:
|
|
442
|
+
name: Benchmark methodology suites
|
|
443
|
+
runs-on: ubuntu-latest
|
|
444
|
+
continue-on-error: true
|
|
445
|
+
steps:
|
|
446
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
447
|
+
with:
|
|
448
|
+
# Full history and tags: the distribution version is derived
|
|
449
|
+
# from the latest `v*` tag, and a shallow clone has none.
|
|
450
|
+
fetch-depth: 0
|
|
451
|
+
- uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
|
|
452
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
453
|
+
with:
|
|
454
|
+
enable-cache: false
|
|
455
|
+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
456
|
+
with:
|
|
457
|
+
node-version: "22"
|
|
458
|
+
- name: Install JS build toolchain
|
|
459
|
+
run: npm ci
|
|
460
|
+
- name: Build core + JS client
|
|
461
|
+
run: |
|
|
462
|
+
node js/build.mjs
|
|
463
|
+
cargo build --release
|
|
464
|
+
- name: Install xy + competitors
|
|
465
|
+
env:
|
|
466
|
+
XY_REQUIRE_CARGO: "1"
|
|
467
|
+
run: |
|
|
468
|
+
uv venv .venv
|
|
469
|
+
uv pip install -p .venv/bin/python \
|
|
470
|
+
--constraint benchmarks/requirements-ci.lock -e .
|
|
471
|
+
uv pip install -p .venv/bin/python \
|
|
472
|
+
--constraint benchmarks/requirements-ci.lock \
|
|
473
|
+
matplotlib seaborn plotly kaleido bokeh altair datashader hvplot \
|
|
474
|
+
plotly-resampler psutil
|
|
475
|
+
- name: Verify native benchmark backend
|
|
476
|
+
run: |
|
|
477
|
+
.venv/bin/python - <<'PY'
|
|
478
|
+
import xy.kernels as k
|
|
479
|
+
assert k.BACKEND == "native", f"benchmark job requires native backend, got {k.BACKEND!r}"
|
|
480
|
+
PY
|
|
481
|
+
# Playwright Chromium for the time-to-first-render (browser paint) pass.
|
|
482
|
+
- name: Install Chromium (Playwright)
|
|
483
|
+
run: |
|
|
484
|
+
npm ci
|
|
485
|
+
npx playwright install --with-deps chromium
|
|
486
|
+
- name: Line decimation vs plotly-resampler (methodology line_10M)
|
|
487
|
+
run: |
|
|
488
|
+
CHROME=$(node -e "console.log(require('playwright').chromium.executablePath())")
|
|
489
|
+
.venv/bin/python benchmarks/bench_line.py --sizes 1e5,1e6,1e7 \
|
|
490
|
+
--ttfr --ttfr-max-n 1e5 --chromium "$CHROME" --json line.json \
|
|
491
|
+
| tee -a docs/benchmark_ci.md
|
|
492
|
+
- name: Install footprint & cold import (methodology §1)
|
|
493
|
+
run: .venv/bin/python benchmarks/bench_install.py --json install.json | tee -a docs/benchmark_ci.md
|
|
494
|
+
- name: Fresh-environment footprint (xy vs Plotly)
|
|
495
|
+
run: |
|
|
496
|
+
.venv/bin/python benchmarks/bench_install.py --packages xy,plotly \
|
|
497
|
+
--repeat 3 --fresh-venv --json install-fresh.json | tee -a docs/benchmark_ci.md
|
|
498
|
+
- name: Ingestion, streaming, and export workflows
|
|
499
|
+
run: |
|
|
500
|
+
CHROME=$(node -e "console.log(require('playwright').chromium.executablePath())")
|
|
501
|
+
.venv/bin/python benchmarks/bench_workflows.py --profile standard --reps 5 \
|
|
502
|
+
--chromium "$CHROME" --json workflows.json | tee -a docs/benchmark_ci.md
|
|
503
|
+
- name: Browser interaction latency (methodology interaction_smoothness)
|
|
504
|
+
run: |
|
|
505
|
+
CHROME=$(node -e "console.log(require('playwright').chromium.executablePath())")
|
|
506
|
+
.venv/bin/python benchmarks/bench_interaction.py \
|
|
507
|
+
--sizes 1e4,2.5e5 --reps 24 --chromium "$CHROME" --retries 2 \
|
|
508
|
+
--json interaction.json | tee -a docs/benchmark_ci.md
|
|
509
|
+
- name: Many-chart dashboard startup (methodology dashboard_20)
|
|
510
|
+
run: |
|
|
511
|
+
CHROME=$(node -e "console.log(require('playwright').chromium.executablePath())")
|
|
512
|
+
.venv/bin/python benchmarks/bench_dashboard.py \
|
|
513
|
+
--chart-counts 10,20,50 --chromium "$CHROME" \
|
|
514
|
+
--json dashboard.json | tee -a docs/benchmark_ci.md
|
|
515
|
+
- name: Verify benchmark JSON artifacts
|
|
516
|
+
run: |
|
|
517
|
+
.venv/bin/python scripts/verify_benchmark_report.py line.json --kind line-decimation
|
|
518
|
+
.venv/bin/python scripts/verify_benchmark_report.py install.json --kind install-footprint
|
|
519
|
+
.venv/bin/python scripts/verify_benchmark_report.py install-fresh.json --kind install-footprint
|
|
520
|
+
.venv/bin/python scripts/verify_benchmark_report.py workflows.json --kind workflow-native
|
|
521
|
+
.venv/bin/python scripts/verify_benchmark_report.py interaction.json --kind interaction-browser
|
|
522
|
+
.venv/bin/python scripts/verify_benchmark_report.py dashboard.json --kind dashboard-browser
|
|
523
|
+
- name: Show report
|
|
524
|
+
run: cat docs/benchmark_ci.md
|
|
525
|
+
- name: Upload benchmark methodology
|
|
526
|
+
if: always()
|
|
527
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
528
|
+
with:
|
|
529
|
+
name: benchmark-methodology
|
|
530
|
+
if-no-files-found: warn
|
|
531
|
+
path: |
|
|
532
|
+
docs/benchmark_ci.md
|
|
533
|
+
line.json
|
|
534
|
+
install.json
|
|
535
|
+
install-fresh.json
|
|
536
|
+
workflows.json
|
|
537
|
+
interaction.json
|
|
538
|
+
dashboard.json
|
|
539
|
+
|
|
540
|
+
benchmark:
|
|
541
|
+
name: Merge and verify benchmark reports
|
|
542
|
+
runs-on: ubuntu-latest
|
|
543
|
+
needs: [benchmark_vs, benchmark_methodology]
|
|
544
|
+
if: always()
|
|
545
|
+
continue-on-error: true
|
|
546
|
+
steps:
|
|
547
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
548
|
+
with:
|
|
549
|
+
# Full history and tags: the distribution version is derived
|
|
550
|
+
# from the latest `v*` tag, and a shallow clone has none.
|
|
551
|
+
fetch-depth: 0
|
|
552
|
+
- name: Download cross-library benchmark parts
|
|
553
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
554
|
+
with:
|
|
555
|
+
pattern: benchmark-vs-*
|
|
556
|
+
path: benchmark-vs-parts
|
|
557
|
+
- name: Download methodology benchmark artifacts
|
|
558
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
559
|
+
with:
|
|
560
|
+
name: benchmark-methodology
|
|
561
|
+
path: benchmark-methodology
|
|
562
|
+
- name: Merge cross-library benchmark parts
|
|
563
|
+
run: |
|
|
564
|
+
python scripts/merge_benchmark_reports.py \
|
|
565
|
+
--parts benchmark-vs-parts/*/*.json \
|
|
566
|
+
--expected-libraries xy,matplotlib,seaborn,plotly_gl,plotly_svg,bokeh_canvas,bokeh_webgl,altair,datashader,hvplot_bokeh \
|
|
567
|
+
--out benchmark.json --out-md scatter-vs.md
|
|
568
|
+
- name: Assemble benchmark report
|
|
569
|
+
run: |
|
|
570
|
+
cat scatter-vs.md benchmark-methodology/docs/benchmark_ci.md > docs/benchmark_ci.md
|
|
571
|
+
- name: Verify benchmark JSON artifacts
|
|
572
|
+
run: |
|
|
573
|
+
python scripts/verify_benchmark_report.py benchmark.json --kind scatter-vs
|
|
574
|
+
python scripts/verify_benchmark_report.py benchmark-methodology/line.json --kind line-decimation
|
|
575
|
+
python scripts/verify_benchmark_report.py benchmark-methodology/install.json --kind install-footprint
|
|
576
|
+
python scripts/verify_benchmark_report.py benchmark-methodology/install-fresh.json --kind install-footprint
|
|
577
|
+
python scripts/verify_benchmark_report.py benchmark-methodology/workflows.json --kind workflow-native
|
|
578
|
+
python scripts/verify_benchmark_report.py benchmark-methodology/interaction.json --kind interaction-browser
|
|
579
|
+
python scripts/verify_benchmark_report.py benchmark-methodology/dashboard.json --kind dashboard-browser
|
|
580
|
+
- name: Show report
|
|
581
|
+
run: cat docs/benchmark_ci.md
|
|
582
|
+
- name: Upload benchmark report
|
|
583
|
+
if: always()
|
|
584
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
585
|
+
with:
|
|
586
|
+
name: benchmark-report
|
|
587
|
+
if-no-files-found: warn
|
|
588
|
+
path: |
|
|
589
|
+
docs/benchmark_ci.md
|
|
590
|
+
benchmark.json
|
|
591
|
+
benchmark-methodology/line.json
|
|
592
|
+
benchmark-methodology/install.json
|
|
593
|
+
benchmark-methodology/install-fresh.json
|
|
594
|
+
benchmark-methodology/workflows.json
|
|
595
|
+
benchmark-methodology/interaction.json
|
|
596
|
+
benchmark-methodology/dashboard.json
|
|
597
|
+
|
|
598
|
+
sdist:
|
|
599
|
+
name: Source distribution
|
|
600
|
+
runs-on: ubuntu-latest
|
|
601
|
+
steps:
|
|
602
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
603
|
+
with:
|
|
604
|
+
# Full history and tags: the distribution version is derived
|
|
605
|
+
# from the latest `v*` tag, and a shallow clone has none.
|
|
606
|
+
fetch-depth: 0
|
|
607
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
608
|
+
with:
|
|
609
|
+
enable-cache: false
|
|
610
|
+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
611
|
+
with:
|
|
612
|
+
node-version: "22"
|
|
613
|
+
|
|
614
|
+
- name: Install JS build toolchain
|
|
615
|
+
run: npm ci
|
|
616
|
+
|
|
617
|
+
- name: Build sdist
|
|
618
|
+
# The hatch build hook builds the render client (node) and the artifacts
|
|
619
|
+
# config carries it into the sdist. The client is required by default, so
|
|
620
|
+
# a client-less sdist fails the build here rather than shipping broken.
|
|
621
|
+
run: uv build --sdist
|
|
622
|
+
|
|
623
|
+
- name: Verify sdist contents
|
|
624
|
+
shell: bash
|
|
625
|
+
run: |
|
|
626
|
+
sdist=$(ls dist/*.tar.gz)
|
|
627
|
+
python scripts/verify_sdist.py "$sdist"
|
|
628
|
+
|
|
629
|
+
- name: Verify sdist without Rust errors clearly on import
|
|
630
|
+
shell: bash
|
|
631
|
+
env:
|
|
632
|
+
XY_SKIP_CARGO: "1"
|
|
633
|
+
run: |
|
|
634
|
+
uv venv smoke
|
|
635
|
+
uv pip install -p smoke dist/*.tar.gz numpy anywidget
|
|
636
|
+
./smoke/bin/python - <<'PY'
|
|
637
|
+
# No native core and no fallback: importing the compute layer must
|
|
638
|
+
# raise a clear, actionable error (§33: no-wheel behavior is defined
|
|
639
|
+
# as a loud failure, not a silent degrade).
|
|
640
|
+
import xy
|
|
641
|
+
|
|
642
|
+
assert xy.__version__
|
|
643
|
+
try:
|
|
644
|
+
import xy.kernels # noqa: F401
|
|
645
|
+
except ImportError as err:
|
|
646
|
+
msg = str(err)
|
|
647
|
+
assert "native Rust core" in msg, msg
|
|
648
|
+
assert "rustup.rs" in msg, msg
|
|
649
|
+
print("sdist without Rust errors clearly on import")
|
|
650
|
+
else:
|
|
651
|
+
raise SystemExit("expected ImportError without the native core")
|
|
652
|
+
PY
|
|
653
|
+
|
|
654
|
+
# Linux and Windows wheel smoke matrix (§33). macOS wheels are built only by
|
|
655
|
+
# release.yml so pull requests do not consume macOS runners; release coverage
|
|
656
|
+
# still includes both Intel and Apple Silicon macOS wheels.
|
|
657
|
+
wheels:
|
|
658
|
+
name: Wheel ${{ matrix.os }}
|
|
659
|
+
needs: test
|
|
660
|
+
runs-on: ${{ matrix.os }}
|
|
661
|
+
strategy:
|
|
662
|
+
fail-fast: false
|
|
663
|
+
matrix:
|
|
664
|
+
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest]
|
|
665
|
+
steps:
|
|
666
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
667
|
+
with:
|
|
668
|
+
# Full history and tags: the distribution version is derived
|
|
669
|
+
# from the latest `v*` tag, and a shallow clone has none.
|
|
670
|
+
fetch-depth: 0
|
|
671
|
+
- uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
|
|
672
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
673
|
+
with:
|
|
674
|
+
enable-cache: false
|
|
675
|
+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
676
|
+
with:
|
|
677
|
+
node-version: "22"
|
|
678
|
+
|
|
679
|
+
- name: Install JS build toolchain
|
|
680
|
+
run: npm ci
|
|
681
|
+
|
|
682
|
+
- name: Build wheel
|
|
683
|
+
# The hatch build hook builds both artifacts. The render client is
|
|
684
|
+
# required by default; REQUIRE_CARGO additionally forbids the native core
|
|
685
|
+
# from silently falling back to a pure-Python wheel here.
|
|
686
|
+
env:
|
|
687
|
+
XY_REQUIRE_CARGO: "1"
|
|
688
|
+
run: uv build --wheel
|
|
689
|
+
|
|
690
|
+
# A raw linux_x86_64 tag is not installable off this machine and PyPI
|
|
691
|
+
# rejects it; auditwheel relabels to manylinux (our cdylib needs only
|
|
692
|
+
# libc/libm, so there's nothing external to bundle). The glibc floor is
|
|
693
|
+
# the runner's; a manylinux-container build (cibuildwheel) would lower it.
|
|
694
|
+
- name: Repair Linux wheel to manylinux
|
|
695
|
+
if: runner.os == 'Linux'
|
|
696
|
+
shell: bash
|
|
697
|
+
run: |
|
|
698
|
+
sudo apt-get update && sudo apt-get install -y patchelf
|
|
699
|
+
uv tool install auditwheel
|
|
700
|
+
uv tool run auditwheel repair dist/*.whl -w wheelhouse
|
|
701
|
+
rm dist/*.whl && mv wheelhouse/*.whl dist/
|
|
702
|
+
ls -la dist/
|
|
703
|
+
|
|
704
|
+
- name: Verify wheel contents
|
|
705
|
+
shell: bash
|
|
706
|
+
run: |
|
|
707
|
+
whl=$(ls dist/*.whl)
|
|
708
|
+
python scripts/verify_wheel.py "$whl" --expect-native
|
|
709
|
+
|
|
710
|
+
- name: Install-size budget (≤ 15 MB, §33)
|
|
711
|
+
shell: bash
|
|
712
|
+
run: |
|
|
713
|
+
# wc -c is portable (macOS/BSD du has no -b); trim BSD's leading spaces.
|
|
714
|
+
whl=$(ls dist/*.whl)
|
|
715
|
+
size=$(wc -c < "$whl" | tr -d ' ')
|
|
716
|
+
echo "wheel size: $size bytes ($whl)"
|
|
717
|
+
test "$size" -le 15728640
|
|
718
|
+
|
|
719
|
+
- name: Smoke-install and import
|
|
720
|
+
shell: bash
|
|
721
|
+
run: |
|
|
722
|
+
uv venv smoke
|
|
723
|
+
uv pip install -p smoke dist/*.whl numpy anywidget
|
|
724
|
+
./smoke/bin/python -c "import xy, xy.kernels as k; assert k.BACKEND == 'native', k.BACKEND; print('native core loaded')" \
|
|
725
|
+
|| ./smoke/Scripts/python.exe -c "import xy, xy.kernels as k; assert k.BACKEND == 'native', k.BACKEND; print('native core loaded')"
|
|
726
|
+
|
|
727
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
728
|
+
with:
|
|
729
|
+
name: wheel-${{ matrix.os }}
|
|
730
|
+
path: dist/*.whl
|
|
731
|
+
|
|
732
|
+
# Proves the no-wheel contract (§33): a source build with NO Rust toolchain
|
|
733
|
+
# produces a pure-Python wheel that imports the package fine but raises a
|
|
734
|
+
# clear, actionable error the moment compute is needed — never a silent
|
|
735
|
+
# fallback and never an opaque crash.
|
|
736
|
+
install_without_rust:
|
|
737
|
+
name: Install without Rust (clear error)
|
|
738
|
+
runs-on: ubuntu-latest
|
|
739
|
+
steps:
|
|
740
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
741
|
+
with:
|
|
742
|
+
# Full history and tags: the distribution version is derived
|
|
743
|
+
# from the latest `v*` tag, and a shallow clone has none.
|
|
744
|
+
fetch-depth: 0
|
|
745
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
746
|
+
with:
|
|
747
|
+
enable-cache: false
|
|
748
|
+
# The render client is independent of the native core (§33): this job
|
|
749
|
+
# drops Rust but keeps Node so the coreless wheel still carries the client.
|
|
750
|
+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
751
|
+
with:
|
|
752
|
+
node-version: "22"
|
|
753
|
+
- name: Install JS build toolchain
|
|
754
|
+
run: npm ci
|
|
755
|
+
# GitHub runners preinstall Rust (which would defeat this job's purpose
|
|
756
|
+
# by happily compiling the core!) — strip every cargo from PATH so the
|
|
757
|
+
# build genuinely exercises the no-toolchain path.
|
|
758
|
+
- name: Remove preinstalled Rust
|
|
759
|
+
run: |
|
|
760
|
+
while p=$(command -v cargo 2>/dev/null); do
|
|
761
|
+
echo "removing $p"; sudo rm -f "$p"
|
|
762
|
+
done
|
|
763
|
+
if command -v cargo; then echo "cargo still present"; exit 1; fi
|
|
764
|
+
echo "cargo is gone"
|
|
765
|
+
- name: Build wheel with no toolchain
|
|
766
|
+
# No Rust (pure-Python wheel), but the render client is required by
|
|
767
|
+
# default and must still ship — this job exercises exactly that split
|
|
768
|
+
# (verify_wheel.py --expect-pure below asserts the client is present).
|
|
769
|
+
run: uv build --wheel
|
|
770
|
+
- name: Wheel is pure-Python (py3-none-any)
|
|
771
|
+
shell: bash
|
|
772
|
+
run: ls dist/ && ls dist/*-py3-none-any.whl
|
|
773
|
+
- name: Verify pure (no-native) wheel contents
|
|
774
|
+
shell: bash
|
|
775
|
+
run: |
|
|
776
|
+
whl=$(ls dist/*.whl)
|
|
777
|
+
python scripts/verify_wheel.py "$whl" --expect-pure
|
|
778
|
+
- name: Install and verify it errors clearly on compute
|
|
779
|
+
shell: bash
|
|
780
|
+
run: |
|
|
781
|
+
uv venv smoke
|
|
782
|
+
uv pip install -p smoke dist/*.whl numpy anywidget
|
|
783
|
+
./smoke/bin/python - <<'PY'
|
|
784
|
+
import xy # lazy top-level import still succeeds
|
|
785
|
+
|
|
786
|
+
assert xy.__version__
|
|
787
|
+
try:
|
|
788
|
+
import xy.kernels # noqa: F401
|
|
789
|
+
except ImportError as err:
|
|
790
|
+
msg = str(err)
|
|
791
|
+
assert "native Rust core" in msg, msg
|
|
792
|
+
assert "rustup.rs" in msg, msg
|
|
793
|
+
print("no-Rust install errors clearly on import; OK")
|
|
794
|
+
else:
|
|
795
|
+
raise SystemExit("expected ImportError without the native core")
|
|
796
|
+
PY
|