diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000..a14e4c3 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,59 @@ +# `.github/workflows/` + +CI for `autolens_profiling`. Two workflows, deliberately split. + +## `lint.yml` — PR + push-to-main gate + +Runs on every PR and every push to `main`. CPU-only, target wall time under 5 minutes. + +What it checks: + +| Step | Purpose | +|------|---------| +| `ruff check .` | Pyflakes + pycodestyle + isort + pyupgrade + flake8-bugbear (see `ruff.toml`) | +| `ruff format --check .` | Formatting parity with sister PyAutoLabs repos (black-compatible defaults) | +| `python scripts/build_readme.py --check` | Dashboard idempotence — the auto-generated tables in every section README must match what `build_readme.py` would generate from the current `results/` artifacts. Catches the "forgot to rerun the dashboard generator after dropping a new result" class of bug | +| `lychee` | Markdown link-rot across every `README.md` | +| Smoke — one script per section | Runs `likelihood/imaging/mge.py`, `simulators/imaging.py`, and `searches/nautilus/simple.py` with `AUTOLENS_PROFILING_SMOKE=1`. Every profile script reads that env var at module top and exits 0 after the import + setup section. Catches import-graph breakage (broken `sys.path` injection, missing dependency, renamed module) without running the full profile | + +The smoke step does **not** produce real result artifacts — every script short-circuits before the JIT compile / sampling / FITS writes. If you need full smoke output, run `profile.yml` manually instead. + +## `profile.yml` — manual + on-release profile re-run + +Triggered by: + +- `workflow_dispatch` (manual via the GitHub UI). Optional `sections` input lets you scope a run to one of `likelihood`, `simulators`, `searches`, or any comma-separated combination. Leave blank to run everything. +- `release: published` — when a new GitHub release is published. + +What it does: + +1. Runs every script under `likelihood/`, `simulators/`, and `searches/nautilus/`, producing JSON+PNG artifacts under `results/`. `continue-on-error: true` per section so a single regression doesn't block the dashboard refresh for the remaining 16+ scripts; failures emit a `::warning::` annotation and the matching dashboard cell will show `ERR`. +2. Skips `simulators/point_source.py` in the simulator loop because its default `dataset_name="simple"` overwrites the Phase 1 likelihood input JSONs (see `simulators/README.md`). Run that one manually with a non-conflicting `dataset_name` when needed. +3. Runs `python scripts/build_readme.py` to refresh every auto-generated table from the latest artifacts. +4. Commits the diff back to `main` as `github-actions[bot]` with `[skip ci]` in the subject (prevents the lint workflow from re-triggering on the auto-generated commit). + +Hardware: GitHub-hosted `ubuntu-latest` (CPU). Expect Nautilus's `n_live=200` runs to take 30–60 minutes each on CPU; total job time can approach the 4-hour `timeout-minutes` budget on a full run. Self-hosted GPU runners can be added later as a separate job that appends `*_gpu*.json` artifacts to `results/` without restructuring this workflow — the dashboard's hardware-tier column extension (top-level README "Future enhancements") is the matching reader-side change. + +## How to trigger `profile.yml` manually + +From the GitHub UI: + +1. Repo → **Actions** → **profile** workflow. +2. Click **Run workflow**. +3. (Optional) Enter a sections filter, e.g. `likelihood,simulators`, or leave blank for all. +4. Click **Run workflow**. + +Or via `gh`: + +```bash +gh workflow run profile --repo PyAutoLabs/autolens_profiling -F sections=likelihood +gh workflow run profile --repo PyAutoLabs/autolens_profiling # all sections +``` + +## Design decisions (captured for future maintainers) + +- **CPU-only runners** to start. GitHub-hosted is free and the dashboard's CPU column is the most-requested baseline. Future GPU laptop / A100 columns require either self-hosted runners or external upload of `*__.json` files; both are additive on this workflow's shape. +- **No matrix across Python versions / OSes**. Lens-modelling profiling is Linux-only in practice, and a single Python version (3.12) keeps the matrix lean. +- **No coverage reporting**. This repo has no unit tests by design — it's a scripts collection, and the smoke step + dashboard idempotence check are the practical equivalents. +- **Why `[skip ci]` rather than path filters**: the lint workflow's smoke step does run scripts, and a commit that touches only `results/` and `README.md` files could in principle re-trigger the lint workflow (which is cheap, but pointless). Subject-line `[skip ci]` is the simplest fix and is honoured by GitHub Actions natively. +- **`continue-on-error: true`** on each profile section, rather than fail-fast: a regression in one script shouldn't block the dashboard refresh for the other 16. The `::warning::` annotation surfaces the failure in the run UI and the dashboard cell will show `ERR` until the next successful refresh. diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..2ea28e1 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,114 @@ +# lint.yml — PR / push-to-main gate. +# +# Cheap, fast (<5 min), CPU-only. Runs on every PR and on push to main. +# Does NOT produce real result artifacts — the smoke step short-circuits +# every profile script via AUTOLENS_PROFILING_SMOKE=1 (lands as part of +# Phase 5; see scripts under likelihood/, simulators/, searches/nautilus/). +# +# Profile re-runs that actually produce results live in `profile.yml`. + +name: lint + +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + +jobs: + lint: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout autolens_profiling + uses: actions/checkout@v4 + with: + path: autolens_profiling + + - name: Checkout PyAutoConf + uses: actions/checkout@v4 + with: + repository: PyAutoLabs/PyAutoConf + path: PyAutoConf + - name: Checkout PyAutoFit + uses: actions/checkout@v4 + with: + repository: PyAutoLabs/PyAutoFit + path: PyAutoFit + - name: Checkout PyAutoArray + uses: actions/checkout@v4 + with: + repository: PyAutoLabs/PyAutoArray + path: PyAutoArray + - name: Checkout PyAutoGalaxy + uses: actions/checkout@v4 + with: + repository: PyAutoLabs/PyAutoGalaxy + path: PyAutoGalaxy + - name: Checkout PyAutoLens + uses: actions/checkout@v4 + with: + repository: PyAutoLabs/PyAutoLens + path: PyAutoLens + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install runtime dependencies + run: | + python -m pip install --upgrade pip + pip install numpy scipy matplotlib jax jaxlib + pip install nautilus-sampler==1.0.5 + pip install numba + # autoconf / autofit / autoarray / autogalaxy / autolens are imported + # from the sibling checkouts via PYTHONPATH below — no install needed. + + - name: Install lint tools + run: | + pip install ruff + # lychee is a Rust binary; fetch the prebuilt release. + curl -sSfL https://github.com/lycheeverse/lychee/releases/latest/download/lychee-x86_64-unknown-linux-gnu.tar.gz \ + | tar -xz -C /usr/local/bin lychee + + - name: ruff check + working-directory: autolens_profiling + run: ruff check . + + - name: ruff format --check + working-directory: autolens_profiling + run: ruff format --check . + + - name: build_readme.py --check (dashboard idempotence) + working-directory: autolens_profiling + run: python scripts/build_readme.py --check + + - name: lychee — markdown link-rot + working-directory: autolens_profiling + run: | + # Conservative scope: only README.md files in this repo, exclude + # GitHub UI links that lychee tends to false-positive on. + lychee \ + --exclude '^https://github\.com/.*/(issues|pull|commit|releases)/' \ + --no-progress \ + --accept '200..=299,429' \ + --max-redirects 5 \ + $(find . -name 'README.md' -not -path './results/*' -not -path './dataset/*') + + - name: Smoke — one script per section + working-directory: autolens_profiling + env: + AUTOLENS_PROFILING_SMOKE: "1" + NUMBA_CACHE_DIR: /tmp/numba_cache + MPLCONFIGDIR: /tmp/matplotlib + PYTHONPATH: ${{ github.workspace }}/PyAutoConf:${{ github.workspace }}/PyAutoFit:${{ github.workspace }}/PyAutoArray:${{ github.workspace }}/PyAutoGalaxy:${{ github.workspace }}/PyAutoLens + run: | + # Each script reads AUTOLENS_PROFILING_SMOKE at module top and + # exits 0 immediately after the import + setup section. Catches + # import-graph breakage without running the full profile. + python likelihood/imaging/mge.py + python simulators/imaging.py + python searches/nautilus/simple.py diff --git a/.github/workflows/profile.yml b/.github/workflows/profile.yml new file mode 100644 index 0000000..45b7006 --- /dev/null +++ b/.github/workflows/profile.yml @@ -0,0 +1,172 @@ +# profile.yml — manual + on-release profile re-run + dashboard refresh. +# +# Runs the real profile scripts (NumPy + JAX paths on CPU runners), +# regenerates the JSON+PNG artifacts under results/, runs +# `scripts/build_readme.py` to refresh the dashboard tables in every +# README, and commits the diff back to main as `github-actions[bot]` +# with `[skip ci]` in the subject to avoid loops. +# +# Design decisions (deliberate, captured here for future maintainers): +# +# - **CPU-only on GitHub-hosted ubuntu-latest**. Self-hosted GPU runners +# can be added later as a separate job that appends *_gpu*.json / *_a100*.json +# artifacts without restructuring this workflow. The dashboard's +# hardware-tier column extension (see top-level README "Future +# enhancements") is the matching reader-side change. +# +# - **Manual + on release tag**, not a weekly cron. Profile scripts on +# CPU take O(minutes) each; running them on every PR or every commit +# burns CI minutes for noise. Releases are the natural cadence for +# cross-version comparison anyway. +# +# - **`github-actions[bot]` with `[skip ci]`**: needs `contents: write`. +# The `[skip ci]` tag prevents the lint workflow from re-running on the +# auto-generated commit, which would loop. +# +# - **`continue-on-error` per script**: a single regression shouldn't +# block the dashboard refresh for the other 16 scripts. The build_readme +# step still runs over whatever artifacts landed, so partial data is +# better than no data. + +name: profile + +on: + workflow_dispatch: + inputs: + sections: + description: "Comma-separated section list to run (likelihood,simulators,searches) — leave blank for all" + required: false + default: "" + release: + types: [published] + +permissions: + contents: write + +jobs: + profile: + runs-on: ubuntu-latest + timeout-minutes: 240 # 4 hours — generous upper bound for CPU runs + steps: + - name: Checkout autolens_profiling + uses: actions/checkout@v4 + with: + path: autolens_profiling + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Checkout PyAutoConf + uses: actions/checkout@v4 + with: + repository: PyAutoLabs/PyAutoConf + path: PyAutoConf + - name: Checkout PyAutoFit + uses: actions/checkout@v4 + with: + repository: PyAutoLabs/PyAutoFit + path: PyAutoFit + - name: Checkout PyAutoArray + uses: actions/checkout@v4 + with: + repository: PyAutoLabs/PyAutoArray + path: PyAutoArray + - name: Checkout PyAutoGalaxy + uses: actions/checkout@v4 + with: + repository: PyAutoLabs/PyAutoGalaxy + path: PyAutoGalaxy + - name: Checkout PyAutoLens + uses: actions/checkout@v4 + with: + repository: PyAutoLabs/PyAutoLens + path: PyAutoLens + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install numpy scipy matplotlib jax jaxlib + pip install nautilus-sampler==1.0.5 + pip install numba + + - name: Run likelihood/ scripts + if: ${{ inputs.sections == '' || contains(inputs.sections, 'likelihood') }} + working-directory: autolens_profiling + continue-on-error: true + env: + NUMBA_CACHE_DIR: /tmp/numba_cache + MPLCONFIGDIR: /tmp/matplotlib + PYTHONPATH: ${{ github.workspace }}/PyAutoConf:${{ github.workspace }}/PyAutoFit:${{ github.workspace }}/PyAutoArray:${{ github.workspace }}/PyAutoGalaxy:${{ github.workspace }}/PyAutoLens + run: | + set +e # don't abort the whole step on one script's failure + for script in likelihood/imaging/*.py likelihood/interferometer/*.py \ + likelihood/point_source/*.py likelihood/datacube/delaunay.py; do + echo "::group::$script" + python "$script" || echo "::warning::$script failed — see log; dashboard cell will show ERR" + echo "::endgroup::" + done + + - name: Run simulators/ scripts + if: ${{ inputs.sections == '' || contains(inputs.sections, 'simulators') }} + working-directory: autolens_profiling + continue-on-error: true + env: + NUMBA_CACHE_DIR: /tmp/numba_cache + MPLCONFIGDIR: /tmp/matplotlib + PYTHONPATH: ${{ github.workspace }}/PyAutoConf:${{ github.workspace }}/PyAutoFit:${{ github.workspace }}/PyAutoArray:${{ github.workspace }}/PyAutoGalaxy:${{ github.workspace }}/PyAutoLens + run: | + set +e + for script in simulators/imaging.py simulators/interferometer.py \ + simulators/cluster.py simulators/group.py simulators/multi.py; do + # point_source.py default dataset_name="simple" overwrites tracked + # likelihood inputs; skip in CI (see simulators/README.md). Manual + # runs with a non-default dataset_name remain supported. + echo "::group::$script" + python "$script" || echo "::warning::$script failed — see log; dashboard cell will show ERR" + echo "::endgroup::" + done + + - name: Run searches/nautilus/ scripts + if: ${{ inputs.sections == '' || contains(inputs.sections, 'searches') }} + working-directory: autolens_profiling + continue-on-error: true + env: + NUMBA_CACHE_DIR: /tmp/numba_cache + MPLCONFIGDIR: /tmp/matplotlib + PYTHONPATH: ${{ github.workspace }}/PyAutoConf:${{ github.workspace }}/PyAutoFit:${{ github.workspace }}/PyAutoArray:${{ github.workspace }}/PyAutoGalaxy:${{ github.workspace }}/PyAutoLens + run: | + set +e + # Nautilus at n_live=200 on CPU takes O(minutes) to O(hours) per + # script. The full pair runs sequentially; expect 30-60 min total + # on the GitHub-hosted runner. + python searches/nautilus/simple.py || echo "::warning::nautilus/simple failed" + python searches/nautilus/jax.py || echo "::warning::nautilus/jax failed" + + - name: Refresh dashboard tables + working-directory: autolens_profiling + run: python scripts/build_readme.py + + - name: Commit and push refreshed artifacts + READMEs + working-directory: autolens_profiling + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + if [ -z "$(git status --porcelain)" ]; then + echo "No changes — exiting clean." + exit 0 + fi + # Force-add tracked-but-gitignored result artifacts. Phase 4 + # currently keeps results/{likelihood,simulators,searches}/ + # gitignored; future hardware-tier work may flip a subset to + # tracked, in which case this `add -f` becomes redundant but + # harmless. + git add -f results/ likelihood/README.md likelihood/*/README.md \ + simulators/README.md searches/README.md \ + searches/nautilus/README.md README.md + git commit -m "chore: refresh profile artifacts + dashboard tables [skip ci] + + Triggered by ${{ github.event_name }} (${{ github.event.release.tag_name || github.run_id }})." + git push origin HEAD:main diff --git a/likelihood/datacube/delaunay.py b/likelihood/datacube/delaunay.py index 7051d1c..6e84f13 100644 --- a/likelihood/datacube/delaunay.py +++ b/likelihood/datacube/delaunay.py @@ -106,6 +106,16 @@ # Instrument configuration # --------------------------------------------------------------------------- + +# AUTOLENS_PROFILING_SMOKE=1 short-circuit (Phase 5 / CI lint smoke). +# Verifies the import graph + module-level setup succeeded without running +# the full profiling pipeline. Skipped entirely when the env var is unset. +import os as _smoke_os +import sys as _smoke_sys +if _smoke_os.environ.get("AUTOLENS_PROFILING_SMOKE") == "1": + print(f"[smoke] {__file__}: imports + module setup OK; exiting.") + _smoke_sys.exit(0) + INSTRUMENTS = { "sma": {"pixel_scale": 0.1, "real_space_shape": (256, 256), "mask_radius": 3.0}, "alma": {"pixel_scale": 0.05, "real_space_shape": (256, 256), "mask_radius": 3.0}, diff --git a/likelihood/imaging/delaunay.py b/likelihood/imaging/delaunay.py index 64e47a0..b49ccdf 100644 --- a/likelihood/imaging/delaunay.py +++ b/likelihood/imaging/delaunay.py @@ -58,6 +58,16 @@ # Instrument configuration # --------------------------------------------------------------------------- + +# AUTOLENS_PROFILING_SMOKE=1 short-circuit (Phase 5 / CI lint smoke). +# Verifies the import graph + module-level setup succeeded without running +# the full profiling pipeline. Skipped entirely when the env var is unset. +import os as _smoke_os +import sys as _smoke_sys +if _smoke_os.environ.get("AUTOLENS_PROFILING_SMOKE") == "1": + print(f"[smoke] {__file__}: imports + module setup OK; exiting.") + _smoke_sys.exit(0) + INSTRUMENTS = { "euclid": {"pixel_scale": 0.1}, "hst": {"pixel_scale": 0.05}, diff --git a/likelihood/imaging/mge.py b/likelihood/imaging/mge.py index ed4a50b..f919c96 100644 --- a/likelihood/imaging/mge.py +++ b/likelihood/imaging/mge.py @@ -67,6 +67,16 @@ # Instrument configuration # --------------------------------------------------------------------------- + +# AUTOLENS_PROFILING_SMOKE=1 short-circuit (Phase 5 / CI lint smoke). +# Verifies the import graph + module-level setup succeeded without running +# the full profiling pipeline. Skipped entirely when the env var is unset. +import os as _smoke_os +import sys as _smoke_sys +if _smoke_os.environ.get("AUTOLENS_PROFILING_SMOKE") == "1": + print(f"[smoke] {__file__}: imports + module setup OK; exiting.") + _smoke_sys.exit(0) + INSTRUMENTS = { "euclid": {"pixel_scale": 0.1}, "hst": {"pixel_scale": 0.05}, diff --git a/likelihood/imaging/pixelization.py b/likelihood/imaging/pixelization.py index bce0580..5e89938 100644 --- a/likelihood/imaging/pixelization.py +++ b/likelihood/imaging/pixelization.py @@ -49,6 +49,16 @@ # Instrument configuration # --------------------------------------------------------------------------- + +# AUTOLENS_PROFILING_SMOKE=1 short-circuit (Phase 5 / CI lint smoke). +# Verifies the import graph + module-level setup succeeded without running +# the full profiling pipeline. Skipped entirely when the env var is unset. +import os as _smoke_os +import sys as _smoke_sys +if _smoke_os.environ.get("AUTOLENS_PROFILING_SMOKE") == "1": + print(f"[smoke] {__file__}: imports + module setup OK; exiting.") + _smoke_sys.exit(0) + INSTRUMENTS = { "euclid": {"pixel_scale": 0.1}, "hst": {"pixel_scale": 0.05}, diff --git a/likelihood/interferometer/delaunay.py b/likelihood/interferometer/delaunay.py index 33abf51..01870fb 100644 --- a/likelihood/interferometer/delaunay.py +++ b/likelihood/interferometer/delaunay.py @@ -97,6 +97,16 @@ # Instrument configuration # --------------------------------------------------------------------------- + +# AUTOLENS_PROFILING_SMOKE=1 short-circuit (Phase 5 / CI lint smoke). +# Verifies the import graph + module-level setup succeeded without running +# the full profiling pipeline. Skipped entirely when the env var is unset. +import os as _smoke_os +import sys as _smoke_sys +if _smoke_os.environ.get("AUTOLENS_PROFILING_SMOKE") == "1": + print(f"[smoke] {__file__}: imports + module setup OK; exiting.") + _smoke_sys.exit(0) + INSTRUMENTS = { "sma": {"pixel_scale": 0.1, "real_space_shape": (256, 256), "mask_radius": 3.0}, "alma": {"pixel_scale": 0.05, "real_space_shape": (256, 256), "mask_radius": 3.0}, diff --git a/likelihood/interferometer/mge.py b/likelihood/interferometer/mge.py index 7d5ab25..8985d87 100644 --- a/likelihood/interferometer/mge.py +++ b/likelihood/interferometer/mge.py @@ -56,6 +56,16 @@ # Instrument configuration # --------------------------------------------------------------------------- + +# AUTOLENS_PROFILING_SMOKE=1 short-circuit (Phase 5 / CI lint smoke). +# Verifies the import graph + module-level setup succeeded without running +# the full profiling pipeline. Skipped entirely when the env var is unset. +import os as _smoke_os +import sys as _smoke_sys +if _smoke_os.environ.get("AUTOLENS_PROFILING_SMOKE") == "1": + print(f"[smoke] {__file__}: imports + module setup OK; exiting.") + _smoke_sys.exit(0) + INSTRUMENTS = { "sma": {"pixel_scale": 0.1, "real_space_shape": (256, 256)}, "alma": {"pixel_scale": 0.05, "real_space_shape": (256, 256)}, diff --git a/likelihood/interferometer/pixelization.py b/likelihood/interferometer/pixelization.py index 4b67b59..619d33a 100644 --- a/likelihood/interferometer/pixelization.py +++ b/likelihood/interferometer/pixelization.py @@ -60,6 +60,16 @@ # Instrument configuration # --------------------------------------------------------------------------- + +# AUTOLENS_PROFILING_SMOKE=1 short-circuit (Phase 5 / CI lint smoke). +# Verifies the import graph + module-level setup succeeded without running +# the full profiling pipeline. Skipped entirely when the env var is unset. +import os as _smoke_os +import sys as _smoke_sys +if _smoke_os.environ.get("AUTOLENS_PROFILING_SMOKE") == "1": + print(f"[smoke] {__file__}: imports + module setup OK; exiting.") + _smoke_sys.exit(0) + INSTRUMENTS = { "sma": {"pixel_scale": 0.1, "real_space_shape": (256, 256)}, "alma": {"pixel_scale": 0.05, "real_space_shape": (256, 256)}, diff --git a/likelihood/point_source/image_plane.py b/likelihood/point_source/image_plane.py index cd51051..9c8c9b0 100644 --- a/likelihood/point_source/image_plane.py +++ b/likelihood/point_source/image_plane.py @@ -49,6 +49,16 @@ import jax.numpy as jnp import matplotlib + +# AUTOLENS_PROFILING_SMOKE=1 short-circuit (Phase 5 / CI lint smoke). +# Verifies the import graph + module-level setup succeeded without running +# the full profiling pipeline. Skipped entirely when the env var is unset. +import os as _smoke_os +import sys as _smoke_sys +if _smoke_os.environ.get("AUTOLENS_PROFILING_SMOKE") == "1": + print(f"[smoke] {__file__}: imports + module setup OK; exiting.") + _smoke_sys.exit(0) + matplotlib.use("Agg") import matplotlib.pyplot as plt diff --git a/likelihood/point_source/source_plane.py b/likelihood/point_source/source_plane.py index 660e010..71e8a26 100644 --- a/likelihood/point_source/source_plane.py +++ b/likelihood/point_source/source_plane.py @@ -31,6 +31,16 @@ import jax.numpy as jnp import matplotlib + +# AUTOLENS_PROFILING_SMOKE=1 short-circuit (Phase 5 / CI lint smoke). +# Verifies the import graph + module-level setup succeeded without running +# the full profiling pipeline. Skipped entirely when the env var is unset. +import os as _smoke_os +import sys as _smoke_sys +if _smoke_os.environ.get("AUTOLENS_PROFILING_SMOKE") == "1": + print(f"[smoke] {__file__}: imports + module setup OK; exiting.") + _smoke_sys.exit(0) + matplotlib.use("Agg") import matplotlib.pyplot as plt diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..6f152d5 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,51 @@ +# ruff configuration for autolens_profiling. +# +# This repo is a collection of standalone profiling scripts, not a Python +# package — there is no pyproject.toml. A standalone ruff.toml at the +# repository root is the supported configuration location for that case +# (see https://docs.astral.sh/ruff/configuration/). +# +# Sister PyAuto* repos do not yet have a ruff config; this file is the +# greenfield baseline for the autolens_profiling repo. If the broader +# PyAutoLabs ecosystem later standardises on a shared ruff config, this +# file should be updated to match. + +target-version = "py311" +line-length = 100 + +# Exclude generated / external folders. +extend-exclude = [ + "results", + "dataset", + ".venv", + "venv", +] + +[lint] +# Conservative selection — catch real bugs without imposing a strict style. +# Profiling scripts use scientific patterns that can clash with stricter rule +# sets (long lines for table headers, `if __name__ == "__main__":` is rare +# because scripts run top-level, etc.). +select = [ + "E", # pycodestyle errors + "F", # pyflakes (undefined names, unused imports) + "W", # pycodestyle warnings + "I", # isort (import order) + "UP", # pyupgrade (Python version idioms) + "B", # flake8-bugbear (likely bugs) +] +ignore = [ + "E501", # long lines — common in scientific code; ruff format handles wrapping + "E402", # module-level imports not at top of file — profiling scripts often + # set env vars (NUMBA_CACHE_DIR etc.) before importing autolens + "F401", # unused imports — some scripts deliberately import-for-side-effect + # (e.g. `from autoconf import jax_wrapper # noqa: F401`) + "B008", # function call in default argument — common autofit pattern +] + +[lint.per-file-ignores] +# build_readme.py uses dynamic re.sub callbacks that confuse some checkers. +"scripts/build_readme.py" = ["B023"] # function-uses-loop-variable + +[format] +# Defaults: black-compatible. Nothing to override. diff --git a/searches/nautilus/jax.py b/searches/nautilus/jax.py index e90a334..05435ba 100644 --- a/searches/nautilus/jax.py +++ b/searches/nautilus/jax.py @@ -33,6 +33,16 @@ import matplotlib + +# AUTOLENS_PROFILING_SMOKE=1 short-circuit (Phase 5 / CI lint smoke). +# Verifies the import graph + module-level setup succeeded without running +# the full profiling pipeline. Skipped entirely when the env var is unset. +import os as _smoke_os +import sys as _smoke_sys +if _smoke_os.environ.get("AUTOLENS_PROFILING_SMOKE") == "1": + print(f"[smoke] {__file__}: imports + module setup OK; exiting.") + _smoke_sys.exit(0) + matplotlib.use("Agg") import matplotlib.pyplot as plt diff --git a/searches/nautilus/simple.py b/searches/nautilus/simple.py index d870737..0cce08c 100644 --- a/searches/nautilus/simple.py +++ b/searches/nautilus/simple.py @@ -26,6 +26,16 @@ import matplotlib + +# AUTOLENS_PROFILING_SMOKE=1 short-circuit (Phase 5 / CI lint smoke). +# Verifies the import graph + module-level setup succeeded without running +# the full profiling pipeline. Skipped entirely when the env var is unset. +import os as _smoke_os +import sys as _smoke_sys +if _smoke_os.environ.get("AUTOLENS_PROFILING_SMOKE") == "1": + print(f"[smoke] {__file__}: imports + module setup OK; exiting.") + _smoke_sys.exit(0) + matplotlib.use("Agg") import matplotlib.pyplot as plt diff --git a/simulators/cluster.py b/simulators/cluster.py index 3b7835d..cb16768 100644 --- a/simulators/cluster.py +++ b/simulators/cluster.py @@ -33,6 +33,16 @@ import jax.numpy as jnp import matplotlib + +# AUTOLENS_PROFILING_SMOKE=1 short-circuit (Phase 5 / CI lint smoke). +# Verifies the import graph + module-level setup succeeded without running +# the full profiling pipeline. Skipped entirely when the env var is unset. +import os as _smoke_os +import sys as _smoke_sys +if _smoke_os.environ.get("AUTOLENS_PROFILING_SMOKE") == "1": + print(f"[smoke] {__file__}: imports + module setup OK; exiting.") + _smoke_sys.exit(0) + matplotlib.use("Agg") import matplotlib.pyplot as plt diff --git a/simulators/group.py b/simulators/group.py index 8d236cd..bf387f9 100644 --- a/simulators/group.py +++ b/simulators/group.py @@ -29,6 +29,16 @@ import jax.numpy as jnp import matplotlib + +# AUTOLENS_PROFILING_SMOKE=1 short-circuit (Phase 5 / CI lint smoke). +# Verifies the import graph + module-level setup succeeded without running +# the full profiling pipeline. Skipped entirely when the env var is unset. +import os as _smoke_os +import sys as _smoke_sys +if _smoke_os.environ.get("AUTOLENS_PROFILING_SMOKE") == "1": + print(f"[smoke] {__file__}: imports + module setup OK; exiting.") + _smoke_sys.exit(0) + matplotlib.use("Agg") import matplotlib.pyplot as plt diff --git a/simulators/imaging.py b/simulators/imaging.py index 704854a..c22d455 100644 --- a/simulators/imaging.py +++ b/simulators/imaging.py @@ -28,6 +28,16 @@ import jax.numpy as jnp import matplotlib + +# AUTOLENS_PROFILING_SMOKE=1 short-circuit (Phase 5 / CI lint smoke). +# Verifies the import graph + module-level setup succeeded without running +# the full profiling pipeline. Skipped entirely when the env var is unset. +import os as _smoke_os +import sys as _smoke_sys +if _smoke_os.environ.get("AUTOLENS_PROFILING_SMOKE") == "1": + print(f"[smoke] {__file__}: imports + module setup OK; exiting.") + _smoke_sys.exit(0) + matplotlib.use("Agg") import matplotlib.pyplot as plt diff --git a/simulators/interferometer.py b/simulators/interferometer.py index 31e5ca5..9621874 100644 --- a/simulators/interferometer.py +++ b/simulators/interferometer.py @@ -30,6 +30,16 @@ import jax.numpy as jnp import matplotlib + +# AUTOLENS_PROFILING_SMOKE=1 short-circuit (Phase 5 / CI lint smoke). +# Verifies the import graph + module-level setup succeeded without running +# the full profiling pipeline. Skipped entirely when the env var is unset. +import os as _smoke_os +import sys as _smoke_sys +if _smoke_os.environ.get("AUTOLENS_PROFILING_SMOKE") == "1": + print(f"[smoke] {__file__}: imports + module setup OK; exiting.") + _smoke_sys.exit(0) + matplotlib.use("Agg") import matplotlib.pyplot as plt diff --git a/simulators/multi.py b/simulators/multi.py index 7e65ae2..494b093 100644 --- a/simulators/multi.py +++ b/simulators/multi.py @@ -28,6 +28,16 @@ import jax.numpy as jnp import matplotlib + +# AUTOLENS_PROFILING_SMOKE=1 short-circuit (Phase 5 / CI lint smoke). +# Verifies the import graph + module-level setup succeeded without running +# the full profiling pipeline. Skipped entirely when the env var is unset. +import os as _smoke_os +import sys as _smoke_sys +if _smoke_os.environ.get("AUTOLENS_PROFILING_SMOKE") == "1": + print(f"[smoke] {__file__}: imports + module setup OK; exiting.") + _smoke_sys.exit(0) + matplotlib.use("Agg") import matplotlib.pyplot as plt diff --git a/simulators/point_source.py b/simulators/point_source.py index 302b1c8..2bd4712 100644 --- a/simulators/point_source.py +++ b/simulators/point_source.py @@ -29,6 +29,16 @@ import jax.numpy as jnp import matplotlib + +# AUTOLENS_PROFILING_SMOKE=1 short-circuit (Phase 5 / CI lint smoke). +# Verifies the import graph + module-level setup succeeded without running +# the full profiling pipeline. Skipped entirely when the env var is unset. +import os as _smoke_os +import sys as _smoke_sys +if _smoke_os.environ.get("AUTOLENS_PROFILING_SMOKE") == "1": + print(f"[smoke] {__file__}: imports + module setup OK; exiting.") + _smoke_sys.exit(0) + matplotlib.use("Agg") import matplotlib.pyplot as plt