Skip to content

Commit 47bbce5

Browse files
authored
Merge pull request #741 from CosmoStat/ngmix_v2.0
Ngmix v2.0 (CI mirror of #740)
2 parents fc656da + 662fad4 commit 47bbce5

145 files changed

Lines changed: 8148 additions & 6846 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/deploy-image.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ jobs:
123123
- name: Test dev — pytest suite
124124
run: |
125125
IMAGE=$(echo "${{ steps.meta-dev.outputs.tags }}" | head -n1)
126-
docker run --rm -e HYPOTHESIS_PROFILE=ci "$IMAGE" pytest -rX
126+
docker run --rm -e HYPOTHESIS_PROFILE=ci -e SHAPEPIPE_ON_CANDIDE=0 "$IMAGE" pytest -rX
127127
128128
# ----------------------------------------------------------------
129129
# Publish (push events only — never on pull_request, incl. forks).

CLAUDE.md

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,30 @@ way to get all of that is the container.
3636
sandbox with a host clone of the repo bind-mounted in and `pip install -e`
3737
pointed at it, so edits on the host are live inside the container.
3838

39+
**Shadow a library build with `PYTHONPATH` (no image rebuild).** When you need
40+
the container to run a *different* build of a pure-Python library than the one
41+
baked into its venv — a feature worktree, an unreleased branch, this repo's own
42+
`src/` against a stale image — prepend the host checkout to `PYTHONPATH`. Python
43+
resolves the prepended path first, so the on-disk version shadows
44+
`/app/.venv/...` without touching the image. This is the local-testing
45+
counterpart of a git-ref dependency (e.g. `cs_util @ develop` in
46+
`pyproject.toml`): the dep change makes CI build the right version; the shadow
47+
lets you test that version *now*, before any rebuild. The recipe:
48+
49+
```bash
50+
apptainer exec --bind /n17data,/automnt <image.sif> bash -c \
51+
"cd <repo-worktree> && \
52+
PYTHONPATH=/path/to/libfoo-checkout:<repo-worktree>/src \
53+
python -m pytest <targets> -o addopts='' -q"
54+
```
55+
56+
Notes: the checkout path is the **parent** of the importable package dir (the
57+
dir containing `foo/`, not `foo/` itself); list several `:`-separated to stack
58+
shadows; `-o addopts=''` clears `pyproject.toml`'s pytest defaults when a plugin
59+
they reference (e.g. `pytest-cov`) isn't in the image. Use this for a quick
60+
verify; land the real fix as the `pyproject.toml` / `uv.lock` dep change so CI
61+
and the next image agree.
62+
3963
**Testing container changes: build remotely, pull locally.** Don't
4064
`apptainer build` images on a cluster — quotas are tight and the build is slow.
4165
The loop for any change to `Dockerfile` / `pyproject.toml` / `uv.lock` is: edit
@@ -56,7 +80,10 @@ Full detail: `docs/source/installation.md` and `docs/source/container.md`.
5680
I/O; `utilities/`; `canfar/` is CANFAR/cluster job orchestration. Console entry
5781
points (`shapepipe_run`, `summary_run`, `canfar_*`) are defined under
5882
`[project.scripts]`.
59-
- `src/shapepipe/tests/` — unit tests; `tests/unit/` holds newer test scaffolding.
83+
- `tests/` — the whole test suite, one discovery root: `module/` (per-module
84+
unit/property/integration tests), `unit/` (structural), `science/` (fast
85+
guardrails), `cluster/` (candide-only), `helpers/` (shared library code).
86+
See `tests/README.md`.
6087
- `example/` — a runnable example pipeline (`example/config.ini`) on a single
6188
CFIS tile; doubles as the CI smoke test.
6289
- `scripts/` — shell / Python / notebook helpers (`sh/`, `python/`, `jupyter/`),
@@ -75,8 +102,6 @@ Full detail: `docs/source/installation.md` and `docs/source/container.md`.
75102
from `master` (`cd.yml`).
76103
- **Style**: PEP 8; numpydoc docstrings on public modules, classes, and methods.
77104
Match the surrounding code.
78-
- **ngmix** is pinned to a fork branch (`aguinot/ngmix@stable_version`) until the
79-
fixes land upstream — don't bump that dependency line.
80105

81106
## Project knowledge — felt
82107

Dockerfile.jupyter

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Dockerfile for ShapePipe Jupyter Lab
22
# Uses the shared base image
33

4-
FROM ghcr.io/cosmostat/shapepipe:v2.0
4+
FROM ghcr.io/cosmostat/shapepipe:develop
55

66
# Default command starts Jupyter Lab
77
ENTRYPOINT ["jupyter","lab","--ip=0.0.0.0","--port=8888","--no-browser","--allow-root"]

bin/canfar_monitor.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env python
2+
from shapepipe.canfar_run import run_log
3+
run_log()

bin/canfar_monitor_log.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env python
2+
from shapepipe.canfar_run import run_monitor_log
3+
run_monitor_log()

bin/canfar_submit_job.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env python
2+
from shapepipe.canfar_run import run_job
3+
run_job()

bin/shapepipe_run.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env python
2+
from shapepipe.shapepipe_run import main
3+
main()

bin/summary_run.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env python
2+
from shapepipe.summary_run import main
3+
main()

conftest.py

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,106 @@
1-
"""Shared pytest configuration for the ShapePipe test suite."""
1+
"""Shared pytest configuration for the ShapePipe test suite.
2+
3+
Markers, environment detection, and the candide skip policy live here so
4+
every test module — wherever it sits in the tree — sees the same rules.
5+
6+
Markers (also declared in ``pyproject.toml`` so ``--strict-markers`` is on):
7+
8+
* ``slow`` — heavy compute (minutes), not part of the fast inner loop.
9+
* ``candide`` — needs the candide cluster and/or its real on-disk data;
10+
meaningless (and auto-skipped) anywhere else.
11+
12+
A ``candide``-marked test only runs on a candide node. Everywhere else it
13+
is skipped with a clear reason, so the same suite is green on a laptop, in
14+
CI, and on the cluster — the cluster-only tests simply do not fire off it.
15+
"""
216

317
import os
18+
import re
19+
import socket
20+
21+
import pytest
422

523
from hypothesis import settings
624

725

826
settings.register_profile("ci", derandomize=True, max_examples=50)
927
settings.register_profile("dev", max_examples=200)
1028
settings.load_profile(os.environ.get("HYPOTHESIS_PROFILE", "ci"))
29+
30+
31+
# --------------------------------------------------------------------------- #
32+
# Candide detection
33+
# --------------------------------------------------------------------------- #
34+
35+
# Candide compute / login nodes are named c01, c03, n22..n36, etc. The login
36+
# host this suite is most often driven from is ``c03``. We match the candide
37+
# node-name families rather than a fixed list so new nodes are covered, and
38+
# allow an explicit override for CI or odd hostnames.
39+
_CANDIDE_HOST_RE = re.compile(r"^(c\d|n\d{2})", re.IGNORECASE)
40+
41+
42+
def on_candide():
43+
"""Return True when running on a candide node.
44+
45+
The check is, in order: an explicit ``SHAPEPIPE_ON_CANDIDE`` override
46+
(``1``/``0``), then the hostname against the candide node-name families
47+
(``c0x`` login, ``nXX`` compute). Cheap, import-safe, no cluster calls.
48+
"""
49+
override = os.environ.get("SHAPEPIPE_ON_CANDIDE")
50+
if override is not None:
51+
return override == "1"
52+
return bool(_CANDIDE_HOST_RE.match(socket.gethostname()))
53+
54+
55+
# --------------------------------------------------------------------------- #
56+
# Marker registration + skip policy
57+
# --------------------------------------------------------------------------- #
58+
59+
60+
def pytest_configure(config):
61+
config.addinivalue_line(
62+
"markers",
63+
"slow: heavy compute (minutes); excluded from the fast inner loop.",
64+
)
65+
config.addinivalue_line(
66+
"markers",
67+
"candide: needs the candide cluster and/or its real data; "
68+
"auto-skipped elsewhere.",
69+
)
70+
71+
72+
def pytest_collection_modifyitems(config, items):
73+
"""Skip ``candide`` tests off-cluster.
74+
75+
Collection still happens everywhere — so ``pytest --collect-only`` shows
76+
the cluster tests exist — they are just marked skipped at run time when
77+
not on candide.
78+
"""
79+
if on_candide():
80+
return
81+
skip_candide = pytest.mark.skip(
82+
reason="needs candide (set SHAPEPIPE_ON_CANDIDE=1 to force)"
83+
)
84+
for item in items:
85+
if "candide" in item.keywords:
86+
item.add_marker(skip_candide)
87+
88+
89+
# --------------------------------------------------------------------------- #
90+
# Shared fixtures
91+
# --------------------------------------------------------------------------- #
92+
93+
94+
@pytest.fixture(scope="session")
95+
def artifacts_dir():
96+
"""Directory where guardrail tests drop plots + status summaries.
97+
98+
The artifacts SEAM: a later GitHub Pages / status step publishes from
99+
here. Created on demand so a clean checkout has nothing to commit until
100+
a test actually emits.
101+
"""
102+
from pathlib import Path
103+
104+
path = Path(__file__).parent / "tests" / "_artifacts"
105+
path.mkdir(parents=True, exist_ok=True)
106+
return path

docs/source/configuration.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,10 @@ The following options can be added to the `[FILE]` section of the config file
8484
(*e.g.* `.`, `-`, `:`, *etc.*). *optional*ly a regular expression can also be
8585
passed if it is preceded by `RE:` (*e.g.* `RE:-\d{9}`).
8686
- `NUMBER_LIST` : (`str` or `list`, *optional*) A list of number strings
87-
matching the numbering scheme or a file name.
87+
matching the numbering scheme or a file name. Restricts the run to these
88+
numbers; every entry must match an input file found on disk, otherwise the
89+
run fails at start-up. This is also how a single image is processed per
90+
job (formerly the `-e`/`--exclusive` command-line flag).
8891
- `CORRECT_FILE_PATTERN` : (`bool`, *optional*) Option to allow substring file
8992
patterns. Default value is `True`.
9093

0 commit comments

Comments
 (0)