-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathpyproject.toml
More file actions
392 lines (349 loc) · 18.7 KB
/
Copy pathpyproject.toml
File metadata and controls
392 lines (349 loc) · 18.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
[build-system]
# pixi-build-python invokes hatchling via PEP 517 to produce the wheel.
# The version is declared statically in [project] below and is the
# single source of truth -- pixi-build-python extracts it (and other
# package metadata) from [project] when building the conda package, so
# there is no need to repeat it under [tool.pixi.package].
#
# We tried dynamic versioning (hatch-vcs) but pixi-build-python's preview
# doesn't trigger PEP 517 metadata resolution for [project] -- it reads
# the static value directly and errors out with "There was no version
# defined for the recipe" when ``version`` is dynamic. Revisit once
# pixi-build is no longer in preview.
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "parallelproj"
# IMPORTANT: bumping a release requires editing this one line.
version = "2.0.3.dev0"
description = "Python tools for PET projection and reconstruction workflows."
readme = "README.md"
requires-python = ">=3.12"
license = {text = "Apache-2.0"}
authors = [
{name = "Georg Schramm", email = "georg.schramm@kuleuven.be"},
]
# Runtime dependencies are intentionally NOT enumerated here.
#
# ``parallelproj`` is distributed only via conda-forge. The channels
# that actually install the package have their own dependency declarations:
#
# * the conda-forge feedstock (recipe/meta.yaml) -> ``conda install``
# * [tool.pixi.package.run-dependencies] below -> ``pixi install``
#
# Listing them here would duplicate them and force a "conda-or-PyPI?"
# decision every time they change. ``parallelproj-core`` (the binary
# backend) is conda-forge-only and listing it here would also break PyPI
# resolution outright.
dependencies = []
[project.urls]
Homepage = "https://parallelproj.readthedocs.io/"
Repository = "https://github.com/KUL-recon-lab/parallelproj"
Documentation = "https://parallelproj.readthedocs.io/"
# Tell hatchling that the importable package lives at ``src/parallelproj``.
# Hatchling's auto-discovery handles flat layouts out of the box but
# wants this explicit for the ``src/`` layout we use.
[tool.hatch.build.targets.wheel]
packages = ["src/parallelproj"]
[tool.pytest.ini_options]
testpaths = ["tests"]
# Several tests call ``fig.show()`` for visual/manual runs. Under a
# non-interactive backend (e.g. Agg, auto-selected on headless CI) this is a
# harmless no-op but emits a UserWarning; silence just that one message so the
# test output stays clean.
filterwarnings = [
"ignore:FigureCanvasAgg is non-interactive:UserWarning",
]
[tool.coverage.run]
# ``parallelproj._examples_utils`` is a private, examples-only helper module
# (interactive matplotlib viewer, analytic phantoms, demo priors) that backs
# the documentation gallery. It is smoke-tested for importability but is
# intentionally kept out of the coverage target, so it does not drag the
# project coverage number (see the matching ``ignore`` in codecov.yml).
omit = ["*/parallelproj/_examples_utils.py"]
# ---------------------------------------------------------------------------
# Pixi configuration -- everything below this point is consumed by Pixi,
# not by hatchling / PEP 517.
#
# Build chain:
# pixi install -> pixi-build-python -> hatchling (PEP 517) -> wheel
# -> conda package -> installed into the pixi env
#
# Quick start:
# pixi install # default env (runtime-only + IPython, Python 3.12)
# pixi run ipython-examples # IPython for interactive example exploration
# pixi run -e cuda12 ipython-examples
# # same, but with the CUDA 12 stack
# pixi run test # run the test suite (Python 3.12)
# pixi run -e test-py-latest test
# # run the test suite (newest Python)
# pixi run -e docs docs # build the documentation locally
# pixi run -e docs docs-fast # build the docs without running any example
# pixi run -e docs-gpu docs-gpu # build the docs on a GPU box, running ALL examples
# # (run `pixi install` first if the env is new)
# # (gallery examples run on the CUDA backend)
# pixi run -e cuda12 test # run tests against a CUDA 12 stack
# pixi run -e cuda13 test # run tests against a CUDA 13 stack
#
# See:
# * https://pixi.prefix.dev/latest/reference/pixi_manifest/
# * https://pixi.prefix.dev/latest/build/backends/pixi-build-python/
# * https://pixi.prefix.dev/latest/python/pyproject_toml/
# ---------------------------------------------------------------------------
[tool.pixi.workspace]
# Workspace ``name`` / ``description`` / ``authors`` / ``license`` / readme
# / URLs are auto-derived from [project] above when the manifest is in
# pyproject.toml form, so only the pixi-specific bits need to live here.
channels = ["conda-forge"]
# Rich platforms (pixi >= 0.71): besides the plain OS/arch platforms used by
# the CPU environments, we declare CUDA-tagged ``linux-64`` and ``win-64``
# platforms for the GPU environments. These are *distinct platform identities*
# from the plain ``linux-64`` / ``win-64`` (they carry a ``__cuda`` virtual
# package), so the CPU environments stay on the plain platforms and remain
# installable on GPU-less CI / Read the Docs runners. The CUDA-tagged
# platforms are referenced by name from the ``cuda12`` / ``cuda13`` features;
# the plain platforms are kept off the GPU envs (and the CUDA ones off the CPU
# envs) via the ``cpu`` feature below. This replaces the now-deprecated
# feature-level ``system-requirements = { cuda = ... }``.
platforms = [
"linux-64",
"osx-arm64",
"osx-64",
"win-64",
{ name = "linux-64-cuda12", platform = "linux-64", cuda = "12.0" },
{ name = "linux-64-cuda13", platform = "linux-64", cuda = "13.0" },
{ name = "win-64-cuda12", platform = "win-64", cuda = "12.0" },
{ name = "win-64-cuda13", platform = "win-64", cuda = "13.0" },
]
preview = ["pixi-build"]
[tool.pixi.package]
# Empty: pixi-build-python's default (``ignore-pyproject-manifest = false``)
# extracts ``name``, ``version``, ``description``, ``license`` and URLs
# from [project] above, so we don't repeat them here.
[tool.pixi.package.build]
# Pin to a minor for stability across pixi-build-python's own preview
# iteration. Bump when you've validated a new backend release.
# ``channels`` is omitted so pixi uses its built-in defaults for resolving
# the build backend and build-time conda deps.
backend = { name = "pixi-build-python", version = "0.6.*" }
[tool.pixi.package.host-dependencies]
# Build-time deps: what hatchling needs to produce the wheel.
python = "*"
hatchling = "*"
[tool.pixi.package.run-dependencies]
# Runtime deps: what every consumer of the built parallelproj conda
# package needs. Mirrors what the conda-forge feedstock declares.
parallelproj-core = ">=2.0.7"
numpy = ">=1.23"
scipy = ">=1.15"
array-api-compat = ">=1.7"
matplotlib = "*"
# ---------------------------------------------------------------------------
# Workspace-level dependencies (i.e. what shows up in every environment).
#
# The only "dependency" the workspace itself declares is the local package,
# referenced as a path-dep so pixi-build-python builds and installs it.
# Its run-dependencies (declared above) are pulled in transitively.
# ---------------------------------------------------------------------------
[tool.pixi.dependencies]
parallelproj = { path = "." }
# ---- Workspace-level tasks ----
#
# Tasks that aren't tied to a specific feature (test/docs) live here.
#
# setup-hooks - Opts this clone into the repo-tracked git hooks in
# ``.githooks/`` (the pre-push tag/version check). Run
# once per fresh clone; the test / docs / release tasks
# ``depends-on`` it so a contributor who runs any of
# them gets hooks activated implicitly.
# release - Tag HEAD with ``v<[project].version>`` and push.
# Logic lives in scripts/release.py; this task just
# invokes it. The script validates that
# [project].version looks like a PEP 440
# release/pre-release string, refuses if the
# v<version> tag already exists, then tags and
# pushes. The pre-push hook in .githooks/ still
# validates the result as the final safety net.
# release-dry-run - Same as release but with ``--dry-run`` -- runs all
# the checks and prints what *would* happen, without
# creating any tag or pushing. Use this to verify
# the release will work before committing to it.
# ``pixi run release --dry-run`` also works (pixi
# forwards extra args).
[tool.pixi.tasks]
setup-hooks = { cmd = "git config core.hooksPath .githooks", description = "Activate the repo-tracked git hooks in .githooks/" }
release = { cmd = "python scripts/release.py", description = "Validate [project].version, refuse if v<version> tag exists, then tag HEAD and push.", depends-on = ["setup-hooks"] }
release-dry-run = { cmd = "python scripts/release.py --dry-run", description = "Same as release, but only run the checks and print what would happen.", depends-on = ["setup-hooks"] }
# ---- Python-version features ----
#
# Each environment below picks exactly one of these so its Python version
# is unambiguous. Add a new ``py3xx`` feature when a new Python release
# is worth pinning to.
[tool.pixi.feature.py312.dependencies]
python = "3.12.*"
[tool.pixi.feature.py-latest.dependencies]
python = ">=3.13"
# ---- test feature ----
[tool.pixi.feature.test.dependencies]
pytest = ">=8"
pytest-cov = "*"
array-api-strict = "*"
# NOTE: pytorch is intentionally NOT declared here. It is pulled in per
# environment instead: the CPU environments add the ``torch-cpu`` feature
# (CPU build), while the CUDA environments get it from the ``cuda*``
# features (CUDA build). Declaring an unconstrained ``pytorch = "*"`` here
# would let the solver pick the higher-numbered CUDA build on linux-64
# whenever the locking machine exposes ``__cuda`` -- which then needs
# ``__cuda`` at install time and breaks CPU CI / Read the Docs.
[tool.pixi.feature.test.tasks]
# ``depends-on = ["setup-hooks"]`` is the contributor-friendly workaround
# for git's refusal to auto-activate hooks on clone: the first time
# anyone runs ``pixi run test`` on a fresh clone, ``setup-hooks`` runs
# first and points ``core.hooksPath`` at ``.githooks/``. Subsequent
# runs are no-ops -- ``git config`` is idempotent. In CI this is also
# harmless (it just touches the runner's disposable local git config).
test = { cmd = "pytest tests/ -v", depends-on = ["setup-hooks"] }
test-cov = { cmd = "pytest tests/ --cov=parallelproj --cov-report=term-missing", depends-on = ["setup-hooks"] }
# Same as test-cov, but additionally writes coverage.xml (with branch
# coverage) for upload to Codecov -- used by the GitHub test workflow.
test-cov-xml = { cmd = "pytest tests/ --cov=parallelproj --cov-branch --cov-report=term-missing --cov-report=xml", depends-on = ["setup-hooks"] }
# ---- docs feature ----
[tool.pixi.feature.docs.dependencies]
sphinx = "*"
sphinx-gallery = "*"
sphinx-design = "*"
sphinxext-opengraph = "*"
furo = "*"
sphinxcontrib-bibtex = "*"
joblib = "*" # used by sphinx-gallery for parallel example execution
# pytorch (used by docs/examples/05_torch/01_run_projection_layer.py) comes
# from the ``torch-cpu`` feature on the ``docs`` env and from the ``cuda13``
# feature on the ``docs-gpu`` env -- see the note in the ``test`` feature.
[tool.pixi.feature.docs.tasks]
# See note on ``depends-on = ["setup-hooks"]`` under the test feature.
docs = { cmd = "sphinx-build -b html docs docs/_build/html", depends-on = ["setup-hooks"] }
# fast local build: render the gallery but execute NO examples
docs-fast = { cmd = "sphinx-build -W --keep-going -b html docs docs/_build/html", env = { BUILD_NO_EXAMPLES = "1" }, depends-on = ["setup-hooks"], description = "Build the docs without executing any example (fast prose/structure check). Uses -W --keep-going to mirror Read the Docs strictness." }
docs-clean = "rm -rf docs/_build docs/auto_examples"
# ---- docs-gpu feature ----
#
# Task-only feature: combined with the ``docs`` and ``cuda13`` features in
# the ``docs-gpu`` environment below, it allows building the docs locally
# on a system with a GPU (``pixi run docs-gpu``). The gallery examples
# then auto-select the CUDA backend via suggest_array_backend_and_device().
# ``BUILD_ALL_EXAMPLES=1`` makes sphinx-gallery execute *every* example (not
# only the ``_run_`` ones that Read the Docs runs within its time limit).
# Defining the task in its own feature (instead of in ``docs``) makes
# ``pixi run docs-gpu`` resolve uniquely to the GPU environment.
[tool.pixi.feature.docs-gpu.tasks]
docs-gpu = { cmd = "sphinx-build -b html docs docs/_build/html", env = { BUILD_ALL_EXAMPLES = "1" }, depends-on = ["setup-hooks"], description = "Build the docs on a GPU system, executing ALL examples on the CUDA backend." }
# ---- examples feature ----
#
# Adds IPython and a convenience task for running the docs/examples
# interactively. No PYTHONPATH or download is needed: the shared example
# helpers ship inside the package as ``parallelproj._examples_utils``, so the
# examples import them straight from ``parallelproj``. This feature is included
# in every environment so that ``pixi run ipython-examples`` and
# ``pixi run -e cuda12 ipython-examples`` both work out of the box.
[tool.pixi.feature.examples.dependencies]
ipython = "*"
h5py = ">=3.0"
[tool.pixi.feature.examples.tasks]
ipython-examples = { cmd = "ipython", description = "Start IPython for interactive example exploration (run examples with %run; helpers come from parallelproj._examples_utils)." }
# ---- CPU pytorch ----
#
# Pins the CPU build of pytorch for the non-GPU environments. The build
# string match keeps the lock reproducible regardless of whether the
# machine that regenerates it exposes ``__cuda``: without it, pixi picks
# the higher-numbered CUDA build on linux-64, which then requires
# ``__cuda`` at install time and breaks CPU CI and Read the Docs. This
# feature is deliberately kept separate from ``test``/``docs`` (which are
# also composed into the CUDA environments) so the CPU pin never leaks
# into a GPU environment, where ``cpu*`` and ``cuda*`` builds cannot be
# intersected.
[tool.pixi.feature.torch-cpu.dependencies]
pytorch = { version = "*", build = "cpu*" }
# ---- CPU platform restriction ----
#
# Restricts the CPU environments to the plain OS/arch platforms, i.e. keeps
# the CUDA-tagged ``linux-64-cuda*`` platforms (declared in
# [tool.pixi.workspace]) OUT of the CPU environments. Without this, an
# environment that does not otherwise restrict its platforms would inherit
# *all* workspace platforms -- including the CUDA-tagged ones, which require
# ``__cuda`` and would again break installs on GPU-less runners. Add this
# feature to every non-GPU environment; leave it off the GPU environments
# (whose ``cuda*`` feature already restricts them to a CUDA-tagged platform).
[tool.pixi.feature.cpu]
platforms = ["linux-64", "osx-arm64", "osx-64", "win-64"]
# ---- CUDA 12 stack ----
#
# GPU environments are supported on linux-64 and win-64 (conda-forge ships
# CUDA-enabled builds of parallelproj-core, pytorch and cupy for both).
#
# We use pixi's "rich platform" support (pixi >= 0.71) to declare CUDA-tagged
# ``linux-64`` / ``win-64`` platforms here. This is the replacement for the
# now-deprecated ``system-requirements = { cuda = "12" }``. Crucially, it
# keeps these CUDA platforms *distinct identities* from the plain ``linux-64``
# / ``win-64`` declared in [tool.pixi.workspace]: the CPU environments
# (test/docs/default) stay on the plain platforms (no ``__cuda``) so they
# install on GPU-less CI / Read the Docs runners, while only the GPU
# environments carry the ``__cuda`` requirement. The ``cuda-version``
# constraint then steers pytorch/cupy to their cuda-12 builds.
[tool.pixi.feature.cuda12]
platforms = ["linux-64-cuda12", "win-64-cuda12"]
[tool.pixi.feature.cuda12.dependencies]
cuda-version = "12.*"
cupy = "*"
# CUDA build of pytorch (selected via the cuda-tagged platform above).
pytorch = "*"
# On Windows a system-wide CUDA install may set CUDA_PATH; override it so
# cupy's NVRTC uses the conda-managed CUDA headers instead of the system ones.
[tool.pixi.feature.cuda12.target.win-64.activation.env]
CUDA_PATH = "$CONDA_PREFIX/Library"
# ---- CUDA 13 stack ----
[tool.pixi.feature.cuda13]
platforms = ["linux-64-cuda13", "win-64-cuda13"]
[tool.pixi.feature.cuda13.dependencies]
cuda-version = "13.*"
cupy = "*"
# CUDA build of pytorch (selected via the cuda-tagged platform above).
pytorch = "*"
[tool.pixi.feature.cuda13.target.win-64.activation.env]
CUDA_PATH = "$CONDA_PREFIX/Library"
# ---- environments ----
#
# Each entry composes one or more features into a named environment that
# pixi can solve, lock, and install independently. Every env picks
# exactly one Python-version feature (``py312`` or ``py-latest``) so its
# Python version is unambiguous.
#
# The ``examples`` feature is included in every environment so that
# ``pixi run ipython-examples`` works regardless of which env is active,
# including CUDA ones (``pixi run -e cuda12 ipython-examples``).
#
# default - minimal "runtime only" env -- just parallelproj + its
# run-dependencies + IPython on Python 3.12. The go-to
# env for interactive example exploration.
# test - default + pytest / array-api-strict / pytorch on
# Python 3.12 (CPU).
# test-py-latest - same as ``test`` but on the newest available Python.
# docs - default + sphinx + sphinx-gallery + friends on
# Python 3.12.
# cuda12 - test + CUDA 12 (pytorch & cupy resolve to cuda-12 builds).
# cuda13 - test + CUDA 13 (pytorch & cupy resolve to cuda-13 builds).
# docs-gpu - docs + CUDA 13: build the documentation on a system
# with a GPU (``pixi run docs-gpu``); the gallery
# examples auto-select the CUDA backend.
#
# Order matters when tasks are shared across envs: ``pixi run <task>``
# picks the first env that has the task. Putting ``test`` before
# ``test-py-latest`` makes ``pixi run test`` default to the Python 3.12
# env, which is what we want for local development.
[tool.pixi.environments]
default = { features = ["cpu", "py312", "examples"] }
test = { features = ["cpu", "test", "torch-cpu", "py312", "examples"] }
test-py-latest = { features = ["cpu", "test", "torch-cpu", "py-latest", "examples"] }
docs = { features = ["cpu", "docs", "torch-cpu", "py312", "examples"] }
cuda12 = { features = ["test", "cuda12", "py312", "examples"] }
cuda13 = { features = ["test", "cuda13", "py312", "examples"] }
docs-gpu = { features = ["docs", "docs-gpu", "cuda13", "py312", "examples"] }