dcccpy 0.2.0__tar.gz → 0.3.0a1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dcccpy
3
- Version: 0.2.0
3
+ Version: 0.3.0a1
4
4
  Summary: Python wrapper for the DCCCcore PET biomarker command-line tool
5
5
  Author: DCCCSlicer contributors
6
6
  License-Expression: CC-BY-NC-ND-4.0
@@ -138,6 +138,23 @@ dcccpy centiloid --input amyloid_pet.nii --output result.nii
138
138
  dcccpy adni-pet-core --input fdg.nii --output fdg_adni.nii --tracer fdg
139
139
  ```
140
140
 
141
+ The Python helper accepts the same staged exports. Pass one integer or a
142
+ sequence; only the requested stages are saved:
143
+
144
+ ```python
145
+ dcccpy.adni_pet_core(
146
+ "dynamic_pet.nii.gz",
147
+ "standardized.nii.gz",
148
+ tracer="fdg",
149
+ level=(1, 3),
150
+ )
151
+ ```
152
+
153
+ Level 1 is the motion-corrected dynamic PET, Level 2 is its 3D average, and
154
+ Level 3 is the standardized ADNI-style image. The output argument names the
155
+ highest selected level, while selected lower levels receive `_Coreg` or
156
+ `_Coreg_Avg` suffixes.
157
+
141
158
  `adni_pet_core` deliberately has no default tracer. Use `abeta`, `tau`, or
142
159
  `fdg`; FDG selects ADNI's iterative masked global-mean normalization, while
143
160
  Aβ and tau retain cerebellar gray normalization.
@@ -189,7 +206,7 @@ the calculation again.
189
206
  Release wheels should vendor the matching `DCCCcore` runtime tree before build:
190
207
 
191
208
  ```bash
192
- python scripts/vendor_dccccore.py --version 4.4.0 --release-platform ubuntu-latest-x64
209
+ python scripts/vendor_dccccore.py --version 4.5.0-alpha --release-platform ubuntu-latest-x64
193
210
  python -m build --wheel
194
211
  ```
195
212
 
@@ -197,7 +214,7 @@ The Linux ARM64 runtime package uses the corresponding release asset:
197
214
 
198
215
  ```bash
199
216
  cd python/dcccpy-linux-arm64-runtime
200
- python scripts/vendor_dccccore.py --version 4.4.0 --release-platform ubuntu-latest-arm64
217
+ python scripts/vendor_dccccore.py --version 4.5.0-alpha --release-platform ubuntu-latest-arm64
201
218
  python -m build --wheel
202
219
  ```
203
220
 
@@ -215,11 +232,11 @@ The preferred distribution layout is:
215
232
 
216
233
  ## Packaging note
217
234
 
218
- The runtime wheels use a PyPI-size profile for DCCCcore version 4.4.0. They omit the
235
+ The current optional runtime wheels use a PyPI-size profile for DCCCcore version 4.4.0. They omit the
219
236
  `fast_and_acc` registration model/config and the ADAD decoupler ONNX ensemble,
220
237
  while keeping the default spatial normalization model and assets needed by
221
238
  common Centiloid/CenTauR/CenTauRz workflows.
222
239
 
223
- The plain `dcccpy` package remains slim and downloads the full matching
224
- `DCCCcore` release package from GitHub on first use when no installed runtime is
225
- available.
240
+ The `dcccpy` 0.3 alpha package targets DCCCcore 4.5.0-alpha. Because the optional
241
+ runtime wheels still contain DCCCcore 4.4.0, this alpha wrapper ignores those
242
+ older runtimes and downloads the full matching GitHub release on first use.
@@ -107,6 +107,23 @@ dcccpy centiloid --input amyloid_pet.nii --output result.nii
107
107
  dcccpy adni-pet-core --input fdg.nii --output fdg_adni.nii --tracer fdg
108
108
  ```
109
109
 
110
+ The Python helper accepts the same staged exports. Pass one integer or a
111
+ sequence; only the requested stages are saved:
112
+
113
+ ```python
114
+ dcccpy.adni_pet_core(
115
+ "dynamic_pet.nii.gz",
116
+ "standardized.nii.gz",
117
+ tracer="fdg",
118
+ level=(1, 3),
119
+ )
120
+ ```
121
+
122
+ Level 1 is the motion-corrected dynamic PET, Level 2 is its 3D average, and
123
+ Level 3 is the standardized ADNI-style image. The output argument names the
124
+ highest selected level, while selected lower levels receive `_Coreg` or
125
+ `_Coreg_Avg` suffixes.
126
+
110
127
  `adni_pet_core` deliberately has no default tracer. Use `abeta`, `tau`, or
111
128
  `fdg`; FDG selects ADNI's iterative masked global-mean normalization, while
112
129
  Aβ and tau retain cerebellar gray normalization.
@@ -158,7 +175,7 @@ the calculation again.
158
175
  Release wheels should vendor the matching `DCCCcore` runtime tree before build:
159
176
 
160
177
  ```bash
161
- python scripts/vendor_dccccore.py --version 4.4.0 --release-platform ubuntu-latest-x64
178
+ python scripts/vendor_dccccore.py --version 4.5.0-alpha --release-platform ubuntu-latest-x64
162
179
  python -m build --wheel
163
180
  ```
164
181
 
@@ -166,7 +183,7 @@ The Linux ARM64 runtime package uses the corresponding release asset:
166
183
 
167
184
  ```bash
168
185
  cd python/dcccpy-linux-arm64-runtime
169
- python scripts/vendor_dccccore.py --version 4.4.0 --release-platform ubuntu-latest-arm64
186
+ python scripts/vendor_dccccore.py --version 4.5.0-alpha --release-platform ubuntu-latest-arm64
170
187
  python -m build --wheel
171
188
  ```
172
189
 
@@ -184,11 +201,11 @@ The preferred distribution layout is:
184
201
 
185
202
  ## Packaging note
186
203
 
187
- The runtime wheels use a PyPI-size profile for DCCCcore version 4.4.0. They omit the
204
+ The current optional runtime wheels use a PyPI-size profile for DCCCcore version 4.4.0. They omit the
188
205
  `fast_and_acc` registration model/config and the ADAD decoupler ONNX ensemble,
189
206
  while keeping the default spatial normalization model and assets needed by
190
207
  common Centiloid/CenTauR/CenTauRz workflows.
191
208
 
192
- The plain `dcccpy` package remains slim and downloads the full matching
193
- `DCCCcore` release package from GitHub on first use when no installed runtime is
194
- available.
209
+ The `dcccpy` 0.3 alpha package targets DCCCcore 4.5.0-alpha. Because the optional
210
+ runtime wheels still contain DCCCcore 4.4.0, this alpha wrapper ignores those
211
+ older runtimes and downloads the full matching GitHub release on first use.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dcccpy"
7
- version = "0.2.0"
7
+ version = "0.3.0a1"
8
8
  description = "Python wrapper for the DCCCcore PET biomarker command-line tool"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -411,12 +411,23 @@ def adni_pet_core(
411
411
  output: str | os.PathLike[str] | None = None,
412
412
  *,
413
413
  tracer: str,
414
+ level: int | Sequence[int] = 3,
414
415
  **kwargs: object,
415
416
  ) -> DCCCResult:
416
- """Run tracer-specific ADNI PET Core preprocessing."""
417
+ """Run tracer-specific ADNI PET Core preprocessing.
418
+
419
+ ``level`` accepts one level or a sequence: 1 exports Coreg, 2 exports
420
+ Coreg/Avg, and 3 exports the standardized image. Multiple levels produce
421
+ multiple files while ``output`` identifies the deepest requested level.
422
+ """
417
423
 
418
424
  extra_args = list(kwargs.pop("extra_args", ()))
419
425
  extra_args.extend(["--tracer", tracer])
426
+ levels = (level,) if isinstance(level, int) else tuple(level)
427
+ if not levels or any(value not in (1, 2, 3) for value in levels):
428
+ raise ValueError("level must contain one or more of: 1, 2, 3")
429
+ if levels != (3,):
430
+ extra_args.extend(["--level", *(str(value) for value in levels)])
420
431
  return _spatial_command("adni-pet-core", input, output, extra_args=extra_args, **kwargs)
421
432
 
422
433
 
@@ -12,7 +12,10 @@ from pathlib import Path
12
12
  from typing import Iterable
13
13
 
14
14
 
15
- DCCCCORE_VERSION = "4.4.0"
15
+ DCCCCORE_VERSION = "4.5.0-alpha"
16
+ # The optional runtime wheels currently contain the last stable native core.
17
+ # Do not let an installed older runtime shadow the release selected above.
18
+ RUNTIME_PACKAGE_DCCCCORE_VERSION = "4.4.0"
16
19
  RELEASE_REPO = "tctco/DCCCSlicer"
17
20
 
18
21
 
@@ -99,7 +102,10 @@ def _main_package_vendor_dirs() -> list[Path]:
99
102
  return [vendor_root / key, vendor_root]
100
103
 
101
104
 
102
- def _runtime_package_dirs() -> list[Path]:
105
+ def _runtime_package_dirs(version: str = DCCCCORE_VERSION) -> list[Path]:
106
+ if version != RUNTIME_PACKAGE_DCCCCORE_VERSION:
107
+ return []
108
+
103
109
  roots: list[Path] = []
104
110
  try:
105
111
  import dcccpy_linux_runtime
@@ -141,7 +147,7 @@ def find_existing_dccccore(version: str = DCCCCORE_VERSION) -> Path | None:
141
147
  if candidate.exists():
142
148
  return candidate
143
149
 
144
- for candidate in candidate_executables(_runtime_package_dirs()):
150
+ for candidate in candidate_executables(_runtime_package_dirs(version)):
145
151
  if candidate.exists():
146
152
  return candidate
147
153
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dcccpy
3
- Version: 0.2.0
3
+ Version: 0.3.0a1
4
4
  Summary: Python wrapper for the DCCCcore PET biomarker command-line tool
5
5
  Author: DCCCSlicer contributors
6
6
  License-Expression: CC-BY-NC-ND-4.0
@@ -138,6 +138,23 @@ dcccpy centiloid --input amyloid_pet.nii --output result.nii
138
138
  dcccpy adni-pet-core --input fdg.nii --output fdg_adni.nii --tracer fdg
139
139
  ```
140
140
 
141
+ The Python helper accepts the same staged exports. Pass one integer or a
142
+ sequence; only the requested stages are saved:
143
+
144
+ ```python
145
+ dcccpy.adni_pet_core(
146
+ "dynamic_pet.nii.gz",
147
+ "standardized.nii.gz",
148
+ tracer="fdg",
149
+ level=(1, 3),
150
+ )
151
+ ```
152
+
153
+ Level 1 is the motion-corrected dynamic PET, Level 2 is its 3D average, and
154
+ Level 3 is the standardized ADNI-style image. The output argument names the
155
+ highest selected level, while selected lower levels receive `_Coreg` or
156
+ `_Coreg_Avg` suffixes.
157
+
141
158
  `adni_pet_core` deliberately has no default tracer. Use `abeta`, `tau`, or
142
159
  `fdg`; FDG selects ADNI's iterative masked global-mean normalization, while
143
160
  Aβ and tau retain cerebellar gray normalization.
@@ -189,7 +206,7 @@ the calculation again.
189
206
  Release wheels should vendor the matching `DCCCcore` runtime tree before build:
190
207
 
191
208
  ```bash
192
- python scripts/vendor_dccccore.py --version 4.4.0 --release-platform ubuntu-latest-x64
209
+ python scripts/vendor_dccccore.py --version 4.5.0-alpha --release-platform ubuntu-latest-x64
193
210
  python -m build --wheel
194
211
  ```
195
212
 
@@ -197,7 +214,7 @@ The Linux ARM64 runtime package uses the corresponding release asset:
197
214
 
198
215
  ```bash
199
216
  cd python/dcccpy-linux-arm64-runtime
200
- python scripts/vendor_dccccore.py --version 4.4.0 --release-platform ubuntu-latest-arm64
217
+ python scripts/vendor_dccccore.py --version 4.5.0-alpha --release-platform ubuntu-latest-arm64
201
218
  python -m build --wheel
202
219
  ```
203
220
 
@@ -215,11 +232,11 @@ The preferred distribution layout is:
215
232
 
216
233
  ## Packaging note
217
234
 
218
- The runtime wheels use a PyPI-size profile for DCCCcore version 4.4.0. They omit the
235
+ The current optional runtime wheels use a PyPI-size profile for DCCCcore version 4.4.0. They omit the
219
236
  `fast_and_acc` registration model/config and the ADAD decoupler ONNX ensemble,
220
237
  while keeping the default spatial normalization model and assets needed by
221
238
  common Centiloid/CenTauR/CenTauRz workflows.
222
239
 
223
- The plain `dcccpy` package remains slim and downloads the full matching
224
- `DCCCcore` release package from GitHub on first use when no installed runtime is
225
- available.
240
+ The `dcccpy` 0.3 alpha package targets DCCCcore 4.5.0-alpha. Because the optional
241
+ runtime wheels still contain DCCCcore 4.4.0, this alpha wrapper ignores those
242
+ older runtimes and downloads the full matching GitHub release on first use.
@@ -142,7 +142,7 @@ def test_dccccore_path_can_disable_auto_download(tmp_path: Path, monkeypatch: py
142
142
 
143
143
 
144
144
  def test_dccccore_path_auto_downloads_to_cache(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
145
- asset_root = tmp_path / "asset" / "DCCCcore-4.4.0-ubuntu-latest-x64"
145
+ asset_root = tmp_path / "asset" / "DCCCcore-4.5.0-alpha-ubuntu-latest-x64"
146
146
  asset_root.mkdir(parents=True)
147
147
  exe = asset_root / "DCCCcore"
148
148
  exe.write_text("#!/usr/bin/env sh\nprintf 'fake dccccore\\n'\n")
@@ -150,7 +150,7 @@ def test_dccccore_path_auto_downloads_to_cache(tmp_path: Path, monkeypatch: pyte
150
150
 
151
151
  archive = tmp_path / "DCCCcore.zip"
152
152
  with zipfile.ZipFile(archive, "w") as zf:
153
- zf.write(exe, "DCCCcore-4.4.0-ubuntu-latest-x64/DCCCcore")
153
+ zf.write(exe, "DCCCcore-4.5.0-alpha-ubuntu-latest-x64/DCCCcore")
154
154
 
155
155
  monkeypatch.delenv("DCCCPY_DCCCCORE", raising=False)
156
156
  monkeypatch.setenv("DCCCPY_AUTO_DOWNLOAD", "1")
@@ -179,20 +179,37 @@ def test_find_existing_dccccore_checks_macos_runtime_package(
179
179
  monkeypatch.setitem(sys.modules, "dcccpy_macos_runtime", module)
180
180
  monkeypatch.setenv("PATH", str(tmp_path / "empty-path"))
181
181
 
182
- assert find_existing_dccccore() == exe
182
+ assert find_existing_dccccore("4.4.0") == exe
183
183
 
184
184
 
185
185
  def test_linux_arm64_release_platform() -> None:
186
186
  assert release_platform("linux-arm64") == "ubuntu-latest-arm64"
187
187
 
188
188
 
189
- def test_default_release_targets_dccccore_440() -> None:
190
- assert DCCCCORE_VERSION == "4.4.0"
189
+ def test_default_release_targets_dccccore_450_alpha() -> None:
190
+ assert DCCCCORE_VERSION == "4.5.0-alpha"
191
191
  assert release_url(platform_name="ubuntu-latest-x64").endswith(
192
- "/releases/download/v4.4.0/DCCCcore-4.4.0-ubuntu-latest-x64.zip"
192
+ "/releases/download/v4.5.0-alpha/DCCCcore-4.5.0-alpha-ubuntu-latest-x64.zip"
193
193
  )
194
194
 
195
195
 
196
+ def test_alpha_ignores_incompatible_stable_runtime_package(
197
+ tmp_path: Path,
198
+ monkeypatch: pytest.MonkeyPatch,
199
+ ) -> None:
200
+ root = tmp_path / "stable-runtime"
201
+ root.mkdir()
202
+ exe = root / "DCCCcore"
203
+ exe.write_text("#!/usr/bin/env sh\n")
204
+ exe.chmod(0o755)
205
+
206
+ module = types.SimpleNamespace(dccccore_root=lambda: root)
207
+ monkeypatch.setitem(sys.modules, "dcccpy_linux_runtime", module)
208
+ monkeypatch.setenv("PATH", str(tmp_path / "empty-path"))
209
+
210
+ assert find_existing_dccccore() is None
211
+
212
+
196
213
  def test_find_existing_dccccore_checks_linux_arm64_runtime_package(
197
214
  tmp_path: Path,
198
215
  monkeypatch: pytest.MonkeyPatch,
@@ -207,7 +224,7 @@ def test_find_existing_dccccore_checks_linux_arm64_runtime_package(
207
224
  monkeypatch.setitem(sys.modules, "dcccpy_linux_arm64_runtime", module)
208
225
  monkeypatch.setenv("PATH", str(tmp_path / "empty-path"))
209
226
 
210
- assert find_existing_dccccore() == exe
227
+ assert find_existing_dccccore("4.4.0") == exe
211
228
 
212
229
 
213
230
  def test_cli_forwards_raw_args(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]) -> None:
@@ -293,6 +310,43 @@ def test_adni_pet_core_python_api_has_no_default_tracer() -> None:
293
310
  dcccpy.adni_pet_core("pet.nii")
294
311
 
295
312
 
313
+ def test_adni_pet_core_forwards_combined_export_levels(
314
+ tmp_path: Path,
315
+ monkeypatch: pytest.MonkeyPatch,
316
+ ) -> None:
317
+ fake_args = tmp_path / "args.txt"
318
+ fake_exe = make_fake_dccccore(tmp_path)
319
+ input_path = tmp_path / "pet.nii"
320
+ output_path = tmp_path / "adni.nii"
321
+ input_path.write_text("fake PET")
322
+ monkeypatch.setenv("DCCCPY_DCCCCORE", str(fake_exe))
323
+ monkeypatch.setenv("DCCCPY_FAKE_ARGS", str(fake_args))
324
+
325
+ result = dcccpy.adni_pet_core(
326
+ input_path, output_path, tracer="fdg", level=(1, 2)
327
+ )
328
+
329
+ assert result.returncode == 0
330
+ assert fake_args.read_text().splitlines() == [
331
+ "adni-pet-core",
332
+ "--input",
333
+ str(input_path),
334
+ "--output",
335
+ str(output_path),
336
+ "--tracer",
337
+ "fdg",
338
+ "--level",
339
+ "1",
340
+ "2",
341
+ ]
342
+
343
+
344
+ @pytest.mark.parametrize("level", [(), 0, 4, (1, 4)])
345
+ def test_adni_pet_core_rejects_invalid_export_levels(level: object) -> None:
346
+ with pytest.raises(ValueError, match="level must contain"):
347
+ dcccpy.adni_pet_core("pet.nii", tracer="fdg", level=level) # type: ignore[arg-type]
348
+
349
+
296
350
  def test_run_adds_macos_security_hint_for_blocked_runtime(
297
351
  tmp_path: Path,
298
352
  monkeypatch: pytest.MonkeyPatch,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes