pantr 0.6.0__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.
- pantr-0.6.0/.claude/settings.json +14 -0
- pantr-0.6.0/.claude/skills/new-feature/SKILL.md +195 -0
- pantr-0.6.0/.claude/skills/pre-pr-checks/SKILL.md +86 -0
- pantr-0.6.0/.github/workflows/ci.yaml +318 -0
- pantr-0.6.0/.github/workflows/release.yaml +185 -0
- pantr-0.6.0/.gitignore +80 -0
- pantr-0.6.0/.pre-commit-config.yaml +48 -0
- pantr-0.6.0/.pre-commit-hooks/conditional_check.py +57 -0
- pantr-0.6.0/.readthedocs.yaml +28 -0
- pantr-0.6.0/CITATION.cff +22 -0
- pantr-0.6.0/CLAUDE.md +189 -0
- pantr-0.6.0/CONTRIBUTING.md +88 -0
- pantr-0.6.0/LICENSE +22 -0
- pantr-0.6.0/Makefile +59 -0
- pantr-0.6.0/PKG-INFO +178 -0
- pantr-0.6.0/README.md +106 -0
- pantr-0.6.0/RELEASING.md +104 -0
- pantr-0.6.0/coverage.toml +12 -0
- pantr-0.6.0/docs/Makefile +30 -0
- pantr-0.6.0/docs/_static/.gitkeep +2 -0
- pantr-0.6.0/docs/_templates/.gitkeep +2 -0
- pantr-0.6.0/docs/api/reference.md +94 -0
- pantr-0.6.0/docs/changelog.md +142 -0
- pantr-0.6.0/docs/conf.py +268 -0
- pantr-0.6.0/docs/getting-started.md +69 -0
- pantr-0.6.0/docs/guide/cad.md +197 -0
- pantr-0.6.0/docs/guide/concepts.md +152 -0
- pantr-0.6.0/docs/guide/distributed.md +242 -0
- pantr-0.6.0/docs/guide/parallelism.md +152 -0
- pantr-0.6.0/docs/guide/spaces-knots.md +198 -0
- pantr-0.6.0/docs/guide/visualization.md +211 -0
- pantr-0.6.0/docs/index.md +107 -0
- pantr-0.6.0/docs/references.bib +216 -0
- pantr-0.6.0/docs/references.md +35 -0
- pantr-0.6.0/mypy.ini +47 -0
- pantr-0.6.0/pyproject.toml +134 -0
- pantr-0.6.0/pytest.ini +13 -0
- pantr-0.6.0/ruff.toml +45 -0
- pantr-0.6.0/src/pantr/__init__.py +130 -0
- pantr-0.6.0/src/pantr/_array_utils.py +86 -0
- pantr-0.6.0/src/pantr/_control_points_utils.py +84 -0
- pantr-0.6.0/src/pantr/_interpolation_utils.py +71 -0
- pantr-0.6.0/src/pantr/_numba_compat.py +57 -0
- pantr-0.6.0/src/pantr/_parallel.py +155 -0
- pantr-0.6.0/src/pantr/_transform_control_points.py +77 -0
- pantr-0.6.0/src/pantr/basis/__init__.py +35 -0
- pantr-0.6.0/src/pantr/basis/_basis_1D.py +280 -0
- pantr-0.6.0/src/pantr/basis/_basis_core.py +469 -0
- pantr-0.6.0/src/pantr/basis/_basis_lagrange.py +130 -0
- pantr-0.6.0/src/pantr/basis/_basis_multidim.py +258 -0
- pantr-0.6.0/src/pantr/basis/_basis_tabulate.py +354 -0
- pantr-0.6.0/src/pantr/basis/_basis_utils.py +170 -0
- pantr-0.6.0/src/pantr/bezier/__init__.py +32 -0
- pantr-0.6.0/src/pantr/bezier/_batch_core.py +190 -0
- pantr-0.6.0/src/pantr/bezier/_bezier.py +917 -0
- pantr-0.6.0/src/pantr/bezier/_bezier_collapse.py +94 -0
- pantr-0.6.0/src/pantr/bezier/_bezier_compose.py +256 -0
- pantr-0.6.0/src/pantr/bezier/_bezier_core.py +657 -0
- pantr-0.6.0/src/pantr/bezier/_bezier_degree.py +279 -0
- pantr-0.6.0/src/pantr/bezier/_bezier_derivative.py +274 -0
- pantr-0.6.0/src/pantr/bezier/_bezier_eval.py +714 -0
- pantr-0.6.0/src/pantr/bezier/_bezier_interpolate.py +756 -0
- pantr-0.6.0/src/pantr/bezier/_bezier_product.py +265 -0
- pantr-0.6.0/src/pantr/bezier/_bezier_restrict.py +73 -0
- pantr-0.6.0/src/pantr/bezier/_bezier_slice.py +76 -0
- pantr-0.6.0/src/pantr/bezier/_bezier_split.py +66 -0
- pantr-0.6.0/src/pantr/bezier/_bezier_utils.py +44 -0
- pantr-0.6.0/src/pantr/bezier/_clipping_core.py +416 -0
- pantr-0.6.0/src/pantr/bezier/_find_roots.py +341 -0
- pantr-0.6.0/src/pantr/bezier/_root_finding.py +167 -0
- pantr-0.6.0/src/pantr/bezier/_root_finding_core.py +408 -0
- pantr-0.6.0/src/pantr/bezier/_yuksel_core.py +397 -0
- pantr-0.6.0/src/pantr/bspline/__init__.py +107 -0
- pantr-0.6.0/src/pantr/bspline/_bspline.py +1191 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_basis_core.py +851 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_basis_multidim.py +344 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_blossom.py +72 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_blossom_core.py +111 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_degree.py +255 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_degree_core.py +470 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_derivative.py +471 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_eval.py +1287 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_extraction.py +377 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_interpolate.py +1198 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_knot_insertion.py +701 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_knot_insertion_core.py +148 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_knot_removal.py +155 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_knot_removal_core.py +196 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_knots.py +663 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_product.py +729 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_product_nd.py +538 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_quasi_interpolation.py +297 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_restrict.py +258 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_slice.py +154 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_space_1d.py +797 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_space_factory.py +499 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_space_nd.py +292 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_split.py +146 -0
- pantr-0.6.0/src/pantr/bspline/_bspline_to_beziers.py +199 -0
- pantr-0.6.0/src/pantr/bspline/_coupling_graph.py +253 -0
- pantr-0.6.0/src/pantr/bspline/_extraction_helpers.py +672 -0
- pantr-0.6.0/src/pantr/bspline/_extraction_kernels.py +1934 -0
- pantr-0.6.0/src/pantr/bspline/_local_space.py +433 -0
- pantr-0.6.0/src/pantr/bspline/_partition_graph.py +312 -0
- pantr-0.6.0/src/pantr/bspline/_thb_eval_core.py +71 -0
- pantr-0.6.0/src/pantr/bspline/_thb_quasi_interpolation.py +162 -0
- pantr-0.6.0/src/pantr/bspline/_thb_spline.py +374 -0
- pantr-0.6.0/src/pantr/bspline/_thb_spline_space.py +2045 -0
- pantr-0.6.0/src/pantr/bspline/multilevel_extraction.py +398 -0
- pantr-0.6.0/src/pantr/bspline/spanwise_element_extraction.py +1147 -0
- pantr-0.6.0/src/pantr/cad/__init__.py +40 -0
- pantr-0.6.0/src/pantr/cad/_compat.py +262 -0
- pantr-0.6.0/src/pantr/cad/_coons.py +372 -0
- pantr-0.6.0/src/pantr/cad/_derived.py +130 -0
- pantr-0.6.0/src/pantr/cad/_join.py +206 -0
- pantr-0.6.0/src/pantr/cad/_operations.py +358 -0
- pantr-0.6.0/src/pantr/cad/_primitives.py +366 -0
- pantr-0.6.0/src/pantr/cad/_validation.py +62 -0
- pantr-0.6.0/src/pantr/change_basis.py +400 -0
- pantr-0.6.0/src/pantr/geometry.py +513 -0
- pantr-0.6.0/src/pantr/grid/__init__.py +73 -0
- pantr-0.6.0/src/pantr/grid/_bvh.py +335 -0
- pantr-0.6.0/src/pantr/grid/_bvh_core.py +329 -0
- pantr-0.6.0/src/pantr/grid/_cell_index.py +96 -0
- pantr-0.6.0/src/pantr/grid/_cell_quadrature.py +110 -0
- pantr-0.6.0/src/pantr/grid/_grid.py +620 -0
- pantr-0.6.0/src/pantr/grid/_grid_utils.py +35 -0
- pantr-0.6.0/src/pantr/grid/_hier_core.py +367 -0
- pantr-0.6.0/src/pantr/grid/_hierarchical_grid.py +1433 -0
- pantr-0.6.0/src/pantr/grid/_locate_core.py +111 -0
- pantr-0.6.0/src/pantr/grid/_overlay.py +129 -0
- pantr-0.6.0/src/pantr/grid/_partition.py +113 -0
- pantr-0.6.0/src/pantr/grid/_partition_grid.py +403 -0
- pantr-0.6.0/src/pantr/grid/_tags.py +495 -0
- pantr-0.6.0/src/pantr/grid/_tensor_product_grid.py +497 -0
- pantr-0.6.0/src/pantr/mpi/__init__.py +124 -0
- pantr-0.6.0/src/pantr/mpi/_collocation.py +462 -0
- pantr-0.6.0/src/pantr/mpi/_create.py +112 -0
- pantr-0.6.0/src/pantr/mpi/_distributed_function.py +204 -0
- pantr-0.6.0/src/pantr/mpi/_distributed_space.py +256 -0
- pantr-0.6.0/src/pantr/mpi/_from_dolfinx.py +178 -0
- pantr-0.6.0/src/pantr/mpi/_l2.py +297 -0
- pantr-0.6.0/src/pantr/mpi/_qi.py +151 -0
- pantr-0.6.0/src/pantr/mpi/_thb_qi.py +165 -0
- pantr-0.6.0/src/pantr/mpi/_thread_policy.py +138 -0
- pantr-0.6.0/src/pantr/py.typed +0 -0
- pantr-0.6.0/src/pantr/quad.py +746 -0
- pantr-0.6.0/src/pantr/tolerance.py +239 -0
- pantr-0.6.0/src/pantr/transform.py +511 -0
- pantr-0.6.0/src/pantr/viz/__init__.py +46 -0
- pantr-0.6.0/src/pantr/viz/_common.py +59 -0
- pantr-0.6.0/src/pantr/viz/_control_points.py +263 -0
- pantr-0.6.0/src/pantr/viz/_grid.py +99 -0
- pantr-0.6.0/src/pantr/viz/_knot_lines.py +233 -0
- pantr-0.6.0/src/pantr/viz/_lazy_import.py +23 -0
- pantr-0.6.0/src/pantr/viz/_scene.py +351 -0
- pantr-0.6.0/src/pantr/viz/_vtk_cells.py +582 -0
- pantr-0.6.0/src/pantr/viz/_vtk_ordering.py +320 -0
- pantr-0.6.0/tests/__init__.py +4 -0
- pantr-0.6.0/tests/_thb_assembly.py +152 -0
- pantr-0.6.0/tests/conftest.py +50 -0
- pantr-0.6.0/tests/data/tanh_sinh_golden.npz +0 -0
- pantr-0.6.0/tests/mpi/test_distributed_mpi.py +422 -0
- pantr-0.6.0/tests/mpi/test_thread_policy_mpi.py +56 -0
- pantr-0.6.0/tests/test_aabb.py +359 -0
- pantr-0.6.0/tests/test_basis.py +919 -0
- pantr-0.6.0/tests/test_bezier.py +647 -0
- pantr-0.6.0/tests/test_bezier_collapse.py +311 -0
- pantr-0.6.0/tests/test_bezier_compose.py +358 -0
- pantr-0.6.0/tests/test_bezier_interpolate.py +698 -0
- pantr-0.6.0/tests/test_bezier_reverse_permute.py +188 -0
- pantr-0.6.0/tests/test_bezier_slice.py +295 -0
- pantr-0.6.0/tests/test_bezier_split.py +364 -0
- pantr-0.6.0/tests/test_bspline.py +436 -0
- pantr-0.6.0/tests/test_bspline_basis_derivatives_1D.py +543 -0
- pantr-0.6.0/tests/test_bspline_blossom.py +205 -0
- pantr-0.6.0/tests/test_bspline_conversion.py +973 -0
- pantr-0.6.0/tests/test_bspline_derivative.py +604 -0
- pantr-0.6.0/tests/test_bspline_evaluate_derivatives_multi_dim.py +579 -0
- pantr-0.6.0/tests/test_bspline_evaluate_multi_dim.py +449 -0
- pantr-0.6.0/tests/test_bspline_evaluation.py +428 -0
- pantr-0.6.0/tests/test_bspline_interpolate.py +523 -0
- pantr-0.6.0/tests/test_bspline_product.py +588 -0
- pantr-0.6.0/tests/test_bspline_product_nd.py +683 -0
- pantr-0.6.0/tests/test_bspline_reverse_permute.py +243 -0
- pantr-0.6.0/tests/test_bspline_slice.py +365 -0
- pantr-0.6.0/tests/test_bspline_space.py +1103 -0
- pantr-0.6.0/tests/test_bspline_space_1D.py +2374 -0
- pantr-0.6.0/tests/test_bspline_split.py +238 -0
- pantr-0.6.0/tests/test_cad_compat.py +193 -0
- pantr-0.6.0/tests/test_cad_coons.py +172 -0
- pantr-0.6.0/tests/test_cad_derived.py +151 -0
- pantr-0.6.0/tests/test_cad_join.py +107 -0
- pantr-0.6.0/tests/test_cad_operations.py +171 -0
- pantr-0.6.0/tests/test_cad_primitives.py +362 -0
- pantr-0.6.0/tests/test_cad_revolve_sweep.py +171 -0
- pantr-0.6.0/tests/test_cardinal_bspline_1D.py +106 -0
- pantr-0.6.0/tests/test_change_basis_1D.py +622 -0
- pantr-0.6.0/tests/test_coupling_graph.py +272 -0
- pantr-0.6.0/tests/test_degree_elevation.py +241 -0
- pantr-0.6.0/tests/test_degree_reduction.py +395 -0
- pantr-0.6.0/tests/test_distributed_space.py +461 -0
- pantr-0.6.0/tests/test_extraction_kernels.py +359 -0
- pantr-0.6.0/tests/test_from_dolfinx.py +213 -0
- pantr-0.6.0/tests/test_grid_abc.py +166 -0
- pantr-0.6.0/tests/test_grid_bvh.py +294 -0
- pantr-0.6.0/tests/test_grid_cell_quadrature.py +213 -0
- pantr-0.6.0/tests/test_grid_hierarchical.py +1009 -0
- pantr-0.6.0/tests/test_grid_overlay.py +119 -0
- pantr-0.6.0/tests/test_grid_partition.py +89 -0
- pantr-0.6.0/tests/test_grid_tags.py +241 -0
- pantr-0.6.0/tests/test_grid_tensor_product.py +588 -0
- pantr-0.6.0/tests/test_knot_insertion.py +680 -0
- pantr-0.6.0/tests/test_knot_removal.py +307 -0
- pantr-0.6.0/tests/test_knots_1D.py +478 -0
- pantr-0.6.0/tests/test_lagrange_1D.py +162 -0
- pantr-0.6.0/tests/test_legendre_1D.py +123 -0
- pantr-0.6.0/tests/test_local_space.py +531 -0
- pantr-0.6.0/tests/test_metadata.py +70 -0
- pantr-0.6.0/tests/test_mpi.py +132 -0
- pantr-0.6.0/tests/test_mpi_collocation.py +466 -0
- pantr-0.6.0/tests/test_mpi_l2.py +498 -0
- pantr-0.6.0/tests/test_mpi_qi.py +417 -0
- pantr-0.6.0/tests/test_mpi_thb_qi.py +403 -0
- pantr-0.6.0/tests/test_mpi_thread_policy.py +197 -0
- pantr-0.6.0/tests/test_multilevel_extraction.py +400 -0
- pantr-0.6.0/tests/test_parallel.py +117 -0
- pantr-0.6.0/tests/test_partition_graph.py +376 -0
- pantr-0.6.0/tests/test_partition_grid.py +431 -0
- pantr-0.6.0/tests/test_quad.py +620 -0
- pantr-0.6.0/tests/test_quad_tanh_sinh.py +239 -0
- pantr-0.6.0/tests/test_quasi_interpolation.py +446 -0
- pantr-0.6.0/tests/test_restrict.py +330 -0
- pantr-0.6.0/tests/test_review_regressions.py +729 -0
- pantr-0.6.0/tests/test_root_finding.py +643 -0
- pantr-0.6.0/tests/test_root_finding_stress.py +314 -0
- pantr-0.6.0/tests/test_spanwise_element_extraction.py +1473 -0
- pantr-0.6.0/tests/test_thb_spline_space.py +1926 -0
- pantr-0.6.0/tests/test_thb_validation_basis.py +202 -0
- pantr-0.6.0/tests/test_thb_validation_identities.py +243 -0
- pantr-0.6.0/tests/test_thb_validation_l2.py +142 -0
- pantr-0.6.0/tests/test_thb_validation_qi.py +165 -0
- pantr-0.6.0/tests/test_to_beziers_multiplicity.py +312 -0
- pantr-0.6.0/tests/test_tolerance.py +239 -0
- pantr-0.6.0/tests/test_transform.py +661 -0
- pantr-0.6.0/tests/test_tutorials.py +462 -0
- pantr-0.6.0/tests/test_viz_cells.py +338 -0
- pantr-0.6.0/tests/test_viz_grid.py +68 -0
- pantr-0.6.0/tests/test_viz_knot_lines.py +205 -0
- pantr-0.6.0/tests/test_viz_ordering.py +199 -0
- pantr-0.6.0/tests/test_viz_scene.py +147 -0
- pantr-0.6.0/tests/test_viz_thb.py +321 -0
- pantr-0.6.0/tutorials/01_first_bspline.py +87 -0
- pantr-0.6.0/tutorials/02_visualization.py +80 -0
- pantr-0.6.0/tutorials/03_knot_operations.py +67 -0
- pantr-0.6.0/tutorials/04_cad_modeling.py +68 -0
- pantr-0.6.0/tutorials/05_approximation.py +91 -0
- pantr-0.6.0/tutorials/06_polynomial_bases.py +78 -0
- pantr-0.6.0/tutorials/07_bezier_and_roots.py +69 -0
- pantr-0.6.0/tutorials/08_thb_adaptive_refinement.py +104 -0
- pantr-0.6.0/tutorials/09_grids_and_quadrature.py +66 -0
- pantr-0.6.0/tutorials/10_transforms.py +56 -0
- pantr-0.6.0/tutorials/GALLERY_HEADER.rst +52 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: new-feature
|
|
3
|
+
description: End-to-end workflow for implementing a new feature, bug fix, or refactor — from creating a worktree and branch through implementation, testing, validation, PR creation, and user notification. Use this skill whenever the user asks you to implement, add, build, create, or fix something that will result in a PR. Trigger on requests like "add support for X", "implement Y", "fix bug Z", "refactor W", or any task that involves writing code that should be merged. Even if the user doesn't mention PRs or branches, if the task involves code changes that need review, use this skill.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# New Feature Workflow
|
|
7
|
+
|
|
8
|
+
This skill orchestrates the full lifecycle of a code change — from branch creation to PR. Follow these phases in order.
|
|
9
|
+
|
|
10
|
+
## Phase 1: Worktree and branch
|
|
11
|
+
|
|
12
|
+
Create an isolated worktree to work in. Use the `EnterWorktree` tool — it will create a new worktree under `.claude/worktrees/` with a fresh branch based on HEAD.
|
|
13
|
+
|
|
14
|
+
After entering the worktree, rename the branch to follow the convention:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
git branch -m <type>/<short-description>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Where `<type>` matches the conventional commit type (`feat`, `fix`, `refactor`, etc.) and `<short-description>` is a kebab-case summary (e.g., `feat/periodic-bspline-product`, `fix/bernstein-derivative-at-boundary`).
|
|
21
|
+
|
|
22
|
+
## Phase 2: Plan
|
|
23
|
+
|
|
24
|
+
Before writing code, understand the task and form a plan:
|
|
25
|
+
|
|
26
|
+
1. Read the relevant existing code to understand the architecture and conventions
|
|
27
|
+
2. Identify which layer(s) the change touches (Layer 1 / 2 / 3 — see CLAUDE.md)
|
|
28
|
+
3. Outline the approach — what files to create or modify, what the public API looks like
|
|
29
|
+
4. Share the plan with the user and get confirmation before proceeding
|
|
30
|
+
|
|
31
|
+
For non-trivial features, use `EnterPlanMode` to align on the approach.
|
|
32
|
+
|
|
33
|
+
## Phase 3: Implement
|
|
34
|
+
|
|
35
|
+
Write the code following the project's architecture and conventions:
|
|
36
|
+
|
|
37
|
+
- **Layer 3 (kernels)**: Pure Numba computation, no validation, `@nb_jit(nopython=True, cache=True)`
|
|
38
|
+
- **Layer 2 (helpers)**: Input validation, array allocation, calls Layer 3
|
|
39
|
+
- **Layer 1 (public API)**: Lightweight validation, delegates to Layer 2
|
|
40
|
+
- Follow strict mypy typing, Google-style docstrings, and all conventions in CLAUDE.md
|
|
41
|
+
|
|
42
|
+
### Commit conventions
|
|
43
|
+
|
|
44
|
+
Commit regularly as you complete logical units of work. Use **conventional commits**:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
<type>(<scope>): <imperative summary>
|
|
48
|
+
|
|
49
|
+
Optional body explaining why, not what.
|
|
50
|
+
|
|
51
|
+
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Types**: `feat`, `fix`, `refactor`, `test`, `docs`, `style`, `perf`, `chore`
|
|
55
|
+
**Scope**: the module or area affected (e.g., `bspline`, `basis`, `quad`, `docs`)
|
|
56
|
+
|
|
57
|
+
Examples:
|
|
58
|
+
- `feat(bspline): add exact 1D B-spline product via Bezier multiplication`
|
|
59
|
+
- `fix(basis): correct off-by-one in Cox-de Boor recursion`
|
|
60
|
+
- `refactor(bspline): move to_open_bspline to Layer 2`
|
|
61
|
+
- `test(bspline): add boundary case tests for derivative evaluation`
|
|
62
|
+
|
|
63
|
+
Keep commits focused — one logical change per commit. Do not bundle unrelated changes.
|
|
64
|
+
|
|
65
|
+
## Phase 4: Test
|
|
66
|
+
|
|
67
|
+
Write tests for the new code:
|
|
68
|
+
|
|
69
|
+
- Place tests in the appropriate file under `tests/` (follow existing naming: `test_<module>.py`)
|
|
70
|
+
- Cover normal cases, edge cases, and error cases
|
|
71
|
+
- Test public API (Layer 1) primarily; test Layer 2 directly only if it has complex validation logic
|
|
72
|
+
- Use `pytest --no-cov -v` to run tests during development
|
|
73
|
+
- Commit tests in a dedicated commit (e.g., `test(bspline): add tests for exact product`)
|
|
74
|
+
|
|
75
|
+
## Phase 5: Pre-PR validation
|
|
76
|
+
|
|
77
|
+
Before creating the PR, run the full check suite. Invoke the `pre-pr-checks` skill (or run the checks manually):
|
|
78
|
+
|
|
79
|
+
1. `ruff check .` — lint
|
|
80
|
+
2. `ruff format --check .` — formatting
|
|
81
|
+
3. `mypy --config-file mypy.ini src tests` — type checking
|
|
82
|
+
4. `pytest --no-cov -v` — tests
|
|
83
|
+
5. `NUMBA_DISABLE_JIT=1 sphinx-build -M html docs/ docs/_build -W --keep-going -j auto` — docs
|
|
84
|
+
|
|
85
|
+
Fix any issues and commit the fixes before proceeding. All checks must pass.
|
|
86
|
+
|
|
87
|
+
**Always run every check on the whole repo, not single files.** Running
|
|
88
|
+
`ruff check <one_file>` while skipping `ruff format --check .` (or vice versa) has
|
|
89
|
+
let real failures through. Run all five commands, every push — no shortcuts.
|
|
90
|
+
|
|
91
|
+
**Local green ≠ CI green.** The local environment can differ from CI in ways that
|
|
92
|
+
hide failures; the local suite passing is necessary but not sufficient. Known traps:
|
|
93
|
+
|
|
94
|
+
- **mypy runs on a Python matrix (3.11–3.14).** `mypy.ini` pins `python_version = 3.11`,
|
|
95
|
+
but the *numpy stub version* is whatever is installed locally. Be defensive with numpy
|
|
96
|
+
typing: no bare `np.ndarray`; `np.einsum` still returns `Any` in current stubs, so wrap
|
|
97
|
+
its result with `np.asarray(..., dtype=np.float64)` where the dtype matters.
|
|
98
|
+
- **Headless GL.** The CI test job runs without a display. pyvista/VTK tests must not
|
|
99
|
+
call `.show()` or `pantr.viz.plot()` (they force a render and segfault headless) —
|
|
100
|
+
build the scene with `to_plotter()` instead. The full suite runs under coverage with
|
|
101
|
+
`NUMBA_DISABLE_JIT=1`; a segfault there crashes the whole run.
|
|
102
|
+
- If a dependency the suite needs (e.g. `pyvista`, `mpi4py`) is missing locally, the
|
|
103
|
+
relevant tests silently skip — install it so they actually run before you rely on a
|
|
104
|
+
green local result.
|
|
105
|
+
|
|
106
|
+
## Phase 5.5: Automated review (fresh Sonnet reviewers)
|
|
107
|
+
|
|
108
|
+
After checks pass and before pushing, run a fresh-context review of the branch diff.
|
|
109
|
+
|
|
110
|
+
**Carve-out — skip this phase entirely** if the diff is trivial: docs-only,
|
|
111
|
+
formatting-only, or comment-only changes (`git diff main...HEAD` touches no
|
|
112
|
+
executable logic). For these, proceed straight to Phase 6.
|
|
113
|
+
|
|
114
|
+
Otherwise, launch the pr-review-toolkit agents **in parallel** via the Task tool,
|
|
115
|
+
each with **`model: sonnet`** (the orchestrator stays on Opus; reviewers get clean,
|
|
116
|
+
uncontaminated context):
|
|
117
|
+
|
|
118
|
+
- `pr-review-toolkit:code-reviewer` — always
|
|
119
|
+
- `pr-review-toolkit:pr-test-analyzer` — if test files changed
|
|
120
|
+
- `pr-review-toolkit:comment-analyzer` — if comments/docstrings changed
|
|
121
|
+
- `pr-review-toolkit:silent-failure-hunter` — if error handling changed
|
|
122
|
+
- `pr-review-toolkit:type-design-analyzer` — if new types added
|
|
123
|
+
|
|
124
|
+
Tell each agent to review the branch diff against `main` (`git diff main...HEAD`).
|
|
125
|
+
|
|
126
|
+
Aggregate findings into **Critical / Important / Suggestion** buckets, then:
|
|
127
|
+
|
|
128
|
+
1. Fix every **Critical** and **Important** finding. Commit the fixes
|
|
129
|
+
(`fix(<scope>): address review findings`).
|
|
130
|
+
2. Re-run Phase 5 checks — they must pass.
|
|
131
|
+
3. Re-run the review on the new diff. Repeat until no Critical/Important findings
|
|
132
|
+
remain, or two consecutive rounds surface nothing new (**hard cap: 3 rounds**).
|
|
133
|
+
4. List any unaddressed **Suggestions** in the PR body under a "Review notes"
|
|
134
|
+
heading — do not silently drop them.
|
|
135
|
+
|
|
136
|
+
This phase is **unattended** — do not pause for approval. Proceed to Phase 6 when clean.
|
|
137
|
+
|
|
138
|
+
> Note: findings come from fresh Sonnet reviewers, but fixes are applied by this
|
|
139
|
+
> (Opus) orchestrator, which carries implementation context. The re-review in
|
|
140
|
+
> step 3 is what guards against the implementer rationalizing away a real finding.
|
|
141
|
+
|
|
142
|
+
## Phase 6: PR and notify
|
|
143
|
+
|
|
144
|
+
Once all checks pass:
|
|
145
|
+
|
|
146
|
+
1. Push the branch:
|
|
147
|
+
```bash
|
|
148
|
+
git push -u origin HEAD
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
2. Create the PR using `gh pr create`:
|
|
152
|
+
- Title: concise, under 70 characters
|
|
153
|
+
- Body: summary of changes, test plan, and the generated-by footer
|
|
154
|
+
- Target the `main` branch
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
gh pr create --title "<type>(<scope>): <summary>" --body "$(cat <<'EOF'
|
|
158
|
+
## Summary
|
|
159
|
+
<bullet points describing the changes>
|
|
160
|
+
|
|
161
|
+
## Test plan
|
|
162
|
+
- [ ] All existing tests pass
|
|
163
|
+
- [ ] New tests added for <feature>
|
|
164
|
+
- [ ] Pre-PR checks pass (ruff, mypy, pytest, docs)
|
|
165
|
+
|
|
166
|
+
Generated with [Claude Code](https://claude.com/claude-code)
|
|
167
|
+
EOF
|
|
168
|
+
)"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
3. **Verify CI is green before declaring done.** Pushing is not the finish line —
|
|
172
|
+
local checks can pass while CI fails (see the local-vs-CI traps in Phase 5). After
|
|
173
|
+
creating the PR, watch the checks to completion:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
gh pr checks <pr-number> --watch # or poll `gh pr checks <pr-number>`
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
If any job fails, read its log (`gh run view --job <id> --log`), fix the cause,
|
|
180
|
+
commit, and push again — then re-watch. Do not report the PR as ready until every
|
|
181
|
+
required check passes. Avoid pushing twice in quick succession: a second push can
|
|
182
|
+
cancel the first run's in-progress jobs (showing a spurious "failure"); wait for the
|
|
183
|
+
run to settle, or confirm a job was *canceled* (not a real failure) before reacting.
|
|
184
|
+
|
|
185
|
+
4. **Notify the user**: Tell them the PR is ready *and CI is green*, provide the URL,
|
|
186
|
+
and give a brief summary of what was done.
|
|
187
|
+
|
|
188
|
+
## Important notes
|
|
189
|
+
|
|
190
|
+
- If the user asks you to **plan only** (not implement), stop after Phase 2 and present the plan.
|
|
191
|
+
- If at any point you're unsure about a design decision, ask the user rather than guessing.
|
|
192
|
+
- Do not push or create a PR without running all checks first (every command, whole repo).
|
|
193
|
+
- Do not push or create a PR without running Phase 5.5 review first (unless the diff hit the trivial carve-out).
|
|
194
|
+
- The work is not done until **CI is green**, not merely when local checks pass — always confirm with `gh pr checks` before declaring the PR ready.
|
|
195
|
+
- Keep the user informed at natural milestones (plan ready, implementation done, tests passing, PR created, CI green).
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pre-pr-checks
|
|
3
|
+
description: Run the full pre-PR validation suite (ruff lint, ruff format check, mypy type checking, pytest tests, and Sphinx documentation build). Use this skill whenever you need to verify code quality before pushing, creating a PR, or when the user asks to "run checks", "validate", "run CI locally", or "make sure everything passes". Also trigger when about to push code or create a pull request — always validate first.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Pre-PR Checks
|
|
7
|
+
|
|
8
|
+
Run all quality checks that CI would run, and report results. The goal is to catch issues before they reach the remote, saving CI time and avoiding back-and-forth.
|
|
9
|
+
|
|
10
|
+
## Environment setup
|
|
11
|
+
|
|
12
|
+
Activate the conda environment first:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
conda activate pantr
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Checks to run
|
|
19
|
+
|
|
20
|
+
Run these checks **sequentially** in this order — later checks are pointless if earlier ones fail on syntax or type errors.
|
|
21
|
+
|
|
22
|
+
### 1. Ruff lint
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
ruff check .
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
If there are auto-fixable issues, fix them with `ruff check --fix .` and re-run to confirm. Stage the fixes.
|
|
29
|
+
|
|
30
|
+
### 2. Ruff format check
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
ruff format --check .
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
If formatting issues are found, run `ruff format .` to fix them and stage the changes.
|
|
37
|
+
|
|
38
|
+
### 3. mypy type checking
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
mypy --config-file mypy.ini src tests
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
If there are type errors, fix them before proceeding. Type errors often indicate real bugs.
|
|
45
|
+
|
|
46
|
+
### 4. Pytest (without coverage)
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pytest --no-cov -v
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Use `--no-cov` to keep things fast. All tests must pass.
|
|
53
|
+
|
|
54
|
+
### 5. Documentation build
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
NUMBA_DISABLE_JIT=1 sphinx-build -M html docs/ docs/_build -W --keep-going -j auto
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The `-W` flag treats warnings as errors (matching CI). `NUMBA_DISABLE_JIT=1` avoids Numba compilation during doc build.
|
|
61
|
+
|
|
62
|
+
## Reporting
|
|
63
|
+
|
|
64
|
+
After running all checks, present a clear summary:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
Pre-PR Checks Summary
|
|
68
|
+
---------------------
|
|
69
|
+
Ruff lint: PASS / FAIL (N issues)
|
|
70
|
+
Ruff format: PASS / FAIL (N files)
|
|
71
|
+
mypy: PASS / FAIL (N errors)
|
|
72
|
+
pytest: PASS / FAIL (N passed, M failed)
|
|
73
|
+
docs build: PASS / FAIL (N warnings)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
If any check failed and you were unable to fix it, explain what went wrong and suggest next steps. If all checks pass, say so clearly — the code is ready to push.
|
|
77
|
+
|
|
78
|
+
## When auto-fixing
|
|
79
|
+
|
|
80
|
+
If you fix lint, format, or type issues during this process, commit those fixes in a dedicated commit:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
style: fix lint and formatting issues
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Keep these commits separate from feature work so the git history stays clean.
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
lint:
|
|
15
|
+
name: Lint (Ruff)
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
strategy:
|
|
18
|
+
matrix:
|
|
19
|
+
python-version: ["3.11", "3.13", "3.14"]
|
|
20
|
+
steps:
|
|
21
|
+
- name: Checkout
|
|
22
|
+
uses: actions/checkout@v7
|
|
23
|
+
|
|
24
|
+
- name: Set up Python
|
|
25
|
+
id: setup-python
|
|
26
|
+
uses: actions/setup-python@v6
|
|
27
|
+
with:
|
|
28
|
+
python-version: ${{ matrix.python-version }}
|
|
29
|
+
cache: "pip"
|
|
30
|
+
cache-dependency-path: |
|
|
31
|
+
pyproject.toml
|
|
32
|
+
|
|
33
|
+
- name: Cache Ruff cache
|
|
34
|
+
uses: actions/cache@v5
|
|
35
|
+
with:
|
|
36
|
+
path: ~/.cache/ruff
|
|
37
|
+
key: ${{ runner.os }}-ruff-${{ hashFiles('ruff.toml', '.ruff.toml') }}
|
|
38
|
+
restore-keys: |
|
|
39
|
+
${{ runner.os }}-ruff-
|
|
40
|
+
|
|
41
|
+
- name: Cache virtualenv
|
|
42
|
+
id: cache-venv
|
|
43
|
+
uses: actions/cache@v5
|
|
44
|
+
with:
|
|
45
|
+
path: .venv
|
|
46
|
+
key: ${{ runner.os }}-venv-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('pyproject.toml') }}
|
|
47
|
+
restore-keys: |
|
|
48
|
+
${{ runner.os }}-venv-${{ steps.setup-python.outputs.python-version }}-
|
|
49
|
+
|
|
50
|
+
- name: Create venv and install dev deps (on cache miss)
|
|
51
|
+
if: steps.cache-venv.outputs.cache-hit != 'true'
|
|
52
|
+
run: |
|
|
53
|
+
python -m venv .venv
|
|
54
|
+
. .venv/bin/activate
|
|
55
|
+
python -m pip install --upgrade pip
|
|
56
|
+
pip install -e ".[dev]"
|
|
57
|
+
|
|
58
|
+
- name: Ruff lint
|
|
59
|
+
run: |
|
|
60
|
+
. .venv/bin/activate
|
|
61
|
+
make ruff-lint
|
|
62
|
+
|
|
63
|
+
- name: Ruff formatting check
|
|
64
|
+
run: |
|
|
65
|
+
. .venv/bin/activate
|
|
66
|
+
make ruff-format-check
|
|
67
|
+
|
|
68
|
+
- name: Import boundary check (import-linter)
|
|
69
|
+
run: |
|
|
70
|
+
. .venv/bin/activate
|
|
71
|
+
make import-lint
|
|
72
|
+
|
|
73
|
+
type-check:
|
|
74
|
+
name: Type check (mypy)
|
|
75
|
+
runs-on: ubuntu-latest
|
|
76
|
+
strategy:
|
|
77
|
+
matrix:
|
|
78
|
+
python-version: ["3.11", "3.13", "3.14"]
|
|
79
|
+
steps:
|
|
80
|
+
- name: Checkout
|
|
81
|
+
uses: actions/checkout@v7
|
|
82
|
+
|
|
83
|
+
- name: Set up Python
|
|
84
|
+
id: setup-python
|
|
85
|
+
uses: actions/setup-python@v6
|
|
86
|
+
with:
|
|
87
|
+
python-version: ${{ matrix.python-version }}
|
|
88
|
+
cache: "pip"
|
|
89
|
+
cache-dependency-path: |
|
|
90
|
+
pyproject.toml
|
|
91
|
+
|
|
92
|
+
- name: Cache mypy cache
|
|
93
|
+
uses: actions/cache@v5
|
|
94
|
+
with:
|
|
95
|
+
path: .mypy_cache
|
|
96
|
+
key: ${{ runner.os }}-mypy-${{ hashFiles('pyproject.toml', 'mypy.ini') }}
|
|
97
|
+
restore-keys: |
|
|
98
|
+
${{ runner.os }}-mypy-
|
|
99
|
+
|
|
100
|
+
- name: Cache virtualenv
|
|
101
|
+
id: cache-venv
|
|
102
|
+
uses: actions/cache@v5
|
|
103
|
+
with:
|
|
104
|
+
path: .venv
|
|
105
|
+
key: ${{ runner.os }}-venv-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('pyproject.toml') }}
|
|
106
|
+
restore-keys: |
|
|
107
|
+
${{ runner.os }}-venv-${{ steps.setup-python.outputs.python-version }}-
|
|
108
|
+
|
|
109
|
+
- name: Create venv and install dev deps (on cache miss)
|
|
110
|
+
if: steps.cache-venv.outputs.cache-hit != 'true'
|
|
111
|
+
run: |
|
|
112
|
+
python -m venv .venv
|
|
113
|
+
. .venv/bin/activate
|
|
114
|
+
python -m pip install --upgrade pip
|
|
115
|
+
pip install -e ".[dev]"
|
|
116
|
+
|
|
117
|
+
- name: Type check
|
|
118
|
+
run: |
|
|
119
|
+
. .venv/bin/activate
|
|
120
|
+
make type-check
|
|
121
|
+
|
|
122
|
+
tests:
|
|
123
|
+
name: Tests with coverage
|
|
124
|
+
needs: [lint, type-check]
|
|
125
|
+
runs-on: ubuntu-latest
|
|
126
|
+
strategy:
|
|
127
|
+
matrix:
|
|
128
|
+
python-version: ["3.11", "3.13", "3.14"]
|
|
129
|
+
steps:
|
|
130
|
+
- name: Checkout
|
|
131
|
+
uses: actions/checkout@v7
|
|
132
|
+
|
|
133
|
+
- name: Set up Python
|
|
134
|
+
id: setup-python
|
|
135
|
+
uses: actions/setup-python@v6
|
|
136
|
+
with:
|
|
137
|
+
python-version: ${{ matrix.python-version }}
|
|
138
|
+
cache: "pip"
|
|
139
|
+
cache-dependency-path: |
|
|
140
|
+
pyproject.toml
|
|
141
|
+
|
|
142
|
+
- name: Cache virtualenv
|
|
143
|
+
id: cache-venv
|
|
144
|
+
uses: actions/cache@v5
|
|
145
|
+
with:
|
|
146
|
+
path: .venv
|
|
147
|
+
key: ${{ runner.os }}-venv-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('pyproject.toml') }}
|
|
148
|
+
restore-keys: |
|
|
149
|
+
${{ runner.os }}-venv-${{ steps.setup-python.outputs.python-version }}-
|
|
150
|
+
|
|
151
|
+
- name: Cache Numba compiled objects
|
|
152
|
+
uses: actions/cache@v5
|
|
153
|
+
with:
|
|
154
|
+
path: |
|
|
155
|
+
src/pantr/basis/__pycache__
|
|
156
|
+
src/pantr/bezier/__pycache__
|
|
157
|
+
src/pantr/bspline/__pycache__
|
|
158
|
+
key: ${{ runner.os }}-numba-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('src/**/*.py') }}
|
|
159
|
+
restore-keys: |
|
|
160
|
+
${{ runner.os }}-numba-${{ steps.setup-python.outputs.python-version }}-
|
|
161
|
+
|
|
162
|
+
- name: Install OpenMPI
|
|
163
|
+
run: |
|
|
164
|
+
sudo apt-get update -q
|
|
165
|
+
sudo apt-get install -y libopenmpi-dev openmpi-bin
|
|
166
|
+
|
|
167
|
+
- name: Create venv and install dev deps (on cache miss)
|
|
168
|
+
if: steps.cache-venv.outputs.cache-hit != 'true'
|
|
169
|
+
run: |
|
|
170
|
+
python -m venv .venv
|
|
171
|
+
. .venv/bin/activate
|
|
172
|
+
python -m pip install --upgrade pip
|
|
173
|
+
pip install -e ".[dev]"
|
|
174
|
+
|
|
175
|
+
- name: Run tests with JIT (populates Numba cache)
|
|
176
|
+
run: |
|
|
177
|
+
. .venv/bin/activate
|
|
178
|
+
make test
|
|
179
|
+
|
|
180
|
+
- name: Run pytest with coverage
|
|
181
|
+
run: |
|
|
182
|
+
. .venv/bin/activate
|
|
183
|
+
make coverage
|
|
184
|
+
|
|
185
|
+
- name: Upload coverage xml
|
|
186
|
+
uses: actions/upload-artifact@v7
|
|
187
|
+
with:
|
|
188
|
+
name: coverage-xml-py${{ matrix.python-version }}-run${{ github.run_attempt }}
|
|
189
|
+
path: coverage.xml
|
|
190
|
+
if-no-files-found: error
|
|
191
|
+
|
|
192
|
+
docs:
|
|
193
|
+
name: Build documentation
|
|
194
|
+
needs: [lint, type-check]
|
|
195
|
+
runs-on: ubuntu-latest
|
|
196
|
+
steps:
|
|
197
|
+
- name: Checkout
|
|
198
|
+
uses: actions/checkout@v7
|
|
199
|
+
|
|
200
|
+
- name: Cache Sphinx doctrees
|
|
201
|
+
uses: actions/cache@v5
|
|
202
|
+
with:
|
|
203
|
+
path: docs/_build/doctrees
|
|
204
|
+
key: ${{ runner.os }}-sphinx-${{ hashFiles('docs/**/*.md', 'docs/conf.py', 'pyproject.toml', 'src/**/*.py') }}
|
|
205
|
+
|
|
206
|
+
- name: Cache intersphinx inventories
|
|
207
|
+
id: cache-intersphinx
|
|
208
|
+
uses: actions/cache@v5
|
|
209
|
+
with:
|
|
210
|
+
path: docs/_intersphinx
|
|
211
|
+
key: ${{ runner.os }}-intersphinx-${{ hashFiles('docs/conf.py') }}
|
|
212
|
+
restore-keys: |
|
|
213
|
+
${{ runner.os }}-intersphinx-
|
|
214
|
+
|
|
215
|
+
- name: Set up Python
|
|
216
|
+
id: setup-python
|
|
217
|
+
uses: actions/setup-python@v6
|
|
218
|
+
with:
|
|
219
|
+
python-version: "3.11"
|
|
220
|
+
cache: "pip"
|
|
221
|
+
cache-dependency-path: |
|
|
222
|
+
pyproject.toml
|
|
223
|
+
|
|
224
|
+
- name: Cache virtualenv
|
|
225
|
+
id: cache-venv
|
|
226
|
+
uses: actions/cache@v5
|
|
227
|
+
with:
|
|
228
|
+
path: .venv
|
|
229
|
+
key: ${{ runner.os }}-venv-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('pyproject.toml', 'docs/**/*.md', 'docs/conf.py') }}
|
|
230
|
+
restore-keys: |
|
|
231
|
+
${{ runner.os }}-venv-${{ steps.setup-python.outputs.python-version }}-
|
|
232
|
+
|
|
233
|
+
- name: Create venv and install docs deps (on cache miss)
|
|
234
|
+
if: steps.cache-venv.outputs.cache-hit != 'true'
|
|
235
|
+
run: |
|
|
236
|
+
python -m venv .venv
|
|
237
|
+
. .venv/bin/activate
|
|
238
|
+
python -m pip install --upgrade pip
|
|
239
|
+
pip install -e ".[docs]"
|
|
240
|
+
|
|
241
|
+
- name: Download intersphinx inventories (on cache miss)
|
|
242
|
+
if: steps.cache-intersphinx.outputs.cache-hit != 'true'
|
|
243
|
+
run: |
|
|
244
|
+
mkdir -p docs/_intersphinx
|
|
245
|
+
curl -fsSL --retry 3 --retry-delay 2 https://docs.python.org/3/objects.inv \
|
|
246
|
+
-o docs/_intersphinx/python.inv || rm -f docs/_intersphinx/python.inv
|
|
247
|
+
curl -fsSL --retry 3 --retry-delay 2 https://numpy.org/doc/stable/objects.inv \
|
|
248
|
+
-o docs/_intersphinx/numpy.inv || rm -f docs/_intersphinx/numpy.inv
|
|
249
|
+
curl -fsSL --retry 3 --retry-delay 2 https://docs.scipy.org/doc/scipy/objects.inv \
|
|
250
|
+
-o docs/_intersphinx/scipy.inv || rm -f docs/_intersphinx/scipy.inv
|
|
251
|
+
curl -fsSL --retry 3 --retry-delay 2 https://matplotlib.org/stable/objects.inv \
|
|
252
|
+
-o docs/_intersphinx/matplotlib.inv || rm -f docs/_intersphinx/matplotlib.inv
|
|
253
|
+
|
|
254
|
+
- name: Build docs
|
|
255
|
+
run: |
|
|
256
|
+
. .venv/bin/activate
|
|
257
|
+
make docs SPHINXOPTS="-W --keep-going -j auto"
|
|
258
|
+
|
|
259
|
+
mpi-tests:
|
|
260
|
+
name: MPI tests (mpiexec)
|
|
261
|
+
needs: [lint, type-check]
|
|
262
|
+
runs-on: ubuntu-latest
|
|
263
|
+
# The only job that exercises the real distributed stack under multiple ranks. It
|
|
264
|
+
# builds mpi4py from source against the system OpenMPI -- the prebuilt wheels are not
|
|
265
|
+
# guaranteed to match the system launcher's wire protocol. PANTR_RUN_MPI enables the
|
|
266
|
+
# otherwise-skipped tests under tests/mpi/.
|
|
267
|
+
env:
|
|
268
|
+
PANTR_RUN_MPI: "1"
|
|
269
|
+
PIP_NO_BINARY: "mpi4py"
|
|
270
|
+
steps:
|
|
271
|
+
- name: Checkout
|
|
272
|
+
uses: actions/checkout@v7
|
|
273
|
+
|
|
274
|
+
- name: Set up Python
|
|
275
|
+
id: setup-python
|
|
276
|
+
uses: actions/setup-python@v6
|
|
277
|
+
with:
|
|
278
|
+
python-version: "3.12"
|
|
279
|
+
cache: "pip"
|
|
280
|
+
cache-dependency-path: |
|
|
281
|
+
pyproject.toml
|
|
282
|
+
|
|
283
|
+
- name: Install OpenMPI
|
|
284
|
+
run: |
|
|
285
|
+
sudo apt-get update -q
|
|
286
|
+
sudo apt-get install -y libopenmpi-dev openmpi-bin
|
|
287
|
+
|
|
288
|
+
- name: Cache virtualenv
|
|
289
|
+
id: cache-venv
|
|
290
|
+
uses: actions/cache@v5
|
|
291
|
+
with:
|
|
292
|
+
path: .venv
|
|
293
|
+
key: ${{ runner.os }}-venv-${{ steps.setup-python.outputs.python-version }}-mpi-${{ hashFiles('pyproject.toml') }}
|
|
294
|
+
restore-keys: |
|
|
295
|
+
${{ runner.os }}-venv-${{ steps.setup-python.outputs.python-version }}-mpi-
|
|
296
|
+
|
|
297
|
+
- name: Create venv and install (with mpi4py built from source, on cache miss)
|
|
298
|
+
if: steps.cache-venv.outputs.cache-hit != 'true'
|
|
299
|
+
run: |
|
|
300
|
+
python -m venv .venv
|
|
301
|
+
. .venv/bin/activate
|
|
302
|
+
python -m pip install --upgrade pip
|
|
303
|
+
pip install -e ".[dev]"
|
|
304
|
+
|
|
305
|
+
- name: Verify mpi4py matches the launcher
|
|
306
|
+
run: |
|
|
307
|
+
. .venv/bin/activate
|
|
308
|
+
mpiexec --oversubscribe -n 2 python -c "from mpi4py import MPI; assert MPI.COMM_WORLD.size == 2"
|
|
309
|
+
|
|
310
|
+
- name: Run MPI smoke tests (2 ranks)
|
|
311
|
+
run: |
|
|
312
|
+
. .venv/bin/activate
|
|
313
|
+
mpiexec --oversubscribe -n 2 python -m pytest tests/mpi/ --no-cov -p no:cacheprovider
|
|
314
|
+
|
|
315
|
+
- name: Run MPI smoke tests (3 ranks)
|
|
316
|
+
run: |
|
|
317
|
+
. .venv/bin/activate
|
|
318
|
+
mpiexec --oversubscribe -n 3 python -m pytest tests/mpi/ --no-cov -p no:cacheprovider
|