From 6e7b41cfa907ff5786cb8b29454962293045ec52 Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Tue, 27 Jan 2026 14:55:37 -0500 Subject: [PATCH 1/7] first draft of devcontainer setup --- .devcontainer/README.rst | 145 ++++++++++++++++++++++++ .devcontainer/example.devcontainer.json | 42 +++++++ .gitignore | 1 + 3 files changed, 188 insertions(+) create mode 100644 .devcontainer/README.rst create mode 100644 .devcontainer/example.devcontainer.json diff --git a/.devcontainer/README.rst b/.devcontainer/README.rst new file mode 100644 index 00000000..bcfe75eb --- /dev/null +++ b/.devcontainer/README.rst @@ -0,0 +1,145 @@ +Development Container Setup +============================ + +This directory contains configuration files for running PETPrep in a Docker-based development container. +The devcontainer provides a pre-configured environment with all PETPrep dependencies, allowing you to +develop and test PETPrep without manually setting up the environment. For more information about devcontainers, see the `VS Code Dev Containers documentation +`__. or `Containers.dev `__. + +Prerequisites +------------- + +* VS Code or a VS Code-based IDE (such as Cursor, GitHub Codespaces, etc.) +* Docker Desktop installed and running +* The `Dev Containers` extension installed in VS Code +* A FreeSurfer license file (obtain one for free at https://surfer.nmr.mgh.harvard.edu/registration.html) + +Quick Start +----------- + +1. **Copy the example configuration:** + + .. code-block:: bash + + cp .devcontainer/example.devcontainer.json .devcontainer/devcontainer.json + +2. **Edit the mount points in ``.devcontainer/devcontainer.json``:** + + Open the file and update the following lines in the ``mounts`` array: + + * **Data directory mount** (line 8): Replace ``/FullPath/to/the/data/directory/contaning/your/bids/data`` + with the absolute path to directory containing bids data on your host machine. + + * **FreeSurfer license mount** (line 9): Replace ``/FullPath/to/your/freesurfer/license.txt`` + with the absolute path to your FreeSurfer license file on your host machine. + + Example for macOS: + + .. code-block:: json + + "mounts": [ + "source=/Users/yourusername/Data/my_bids_dataset,target=/data,type=bind,consistency=cached", + "source=/Applications/freesurfer/7.4.1/license.txt,target=/opt/freesurfer/license.txt,type=bind,consistency=cached,readonly=true" + ] + + Example for Linux: + + .. code-block:: json + + "mounts": [ + "source=/home/yourusername/data/my_bids_dataset,target=/data,type=bind,consistency=cached", + "source=/opt/freesurfer/license.txt,target=/opt/freesurfer/license.txt,type=bind,consistency=cached,readonly=true" + ] + +3. **Open the project in VS Code:** + + * Open VS Code + * Open the PETPrep repository folder + * VS Code should detect the ``.devcontainer/devcontainer.json`` file and prompt you to reopen in container + * Click "Reopen in Container" when prompted + + Alternatively, you can manually trigger the container: + + * Press ``Ctrl+Shift+P`` (or ``Cmd+Shift+P`` on macOS) to open the command palette + * Type "Dev Containers: Reopen in Container" and select it + +4. **Wait for the container to build and initialize:** + + * The first time will take longer as Docker downloads the PETPrep image + * The ``postCreateCommand`` will install PETPrep in editable mode with development dependencies + * You'll see progress in the VS Code terminal + +What's Included +--------------- + +The devcontainer provides: + +* **Pre-configured Python environment** with all PETPrep dependencies +* **Development tools**: ruff (linter), black (formatter), pytest (testing) +* **VS Code extensions**: Python, Pylance, Ruff, Black Formatter +* **Mounted data directory**: Your BIDS dataset accessible at ``/data`` +* **FreeSurfer license**: Automatically mounted for FreeSurfer workflows +* **TemplateFlow cache**: Pre-cached templates at ``/home/petprep/.cache/templateflow`` + +Configuration Details +--------------------- + +The devcontainer configuration includes: + +* **Image**: Uses the official PETPrep Docker image (``ghcr.io/nipreps/petprep:main``) +* **User**: Runs as ``petprep`` user (non-root for security) +* **Workspace**: Your code is mounted at ``/workspace`` +* **Python path**: ``/opt/conda/envs/petprep/bin/python`` +* **Environment variables**: + * ``TEMPLATEFLOW_HOME``: Set to ``/home/petprep/.cache/templateflow`` + * ``PYTHONPATH``: Includes ``/workspace`` for editable installs + +Troubleshooting +--------------- + +**Container won't start:** + * Ensure Docker Desktop is running + * Check that the paths in your ``devcontainer.json`` are correct and accessible + * Try rebuilding: Command Palette → "Dev Containers: Rebuild Container" + +**Permission errors:** + * The container runs as the ``petprep`` user + * If you need to install packages, use ``--user`` flag: ``pip install --user package_name`` + +**TemplateFlow path errors:** + * If you see errors about templateflow paths from your host machine, delete the ``work/`` directory: + ``rm -rf work/`` + * The container has templates pre-cached, so you don't need to mount your host templateflow cache + +**FreeSurfer license not found:** + * Verify the license file path in ``devcontainer.json`` is correct + * Ensure the file exists and is readable + * Check that the mount uses ``readonly=true`` for security + +**Package installation conflicts:** + * The container already has all runtime dependencies installed + * Use ``--no-deps`` when installing packages to avoid conflicts + * Development dependencies are installed via ``postCreateCommand`` + +Rebuilding the Container +------------------------ + +If you need to rebuild the container (e.g., after changing ``devcontainer.json``): + +1. Command Palette → "Dev Containers: Rebuild Container" +2. Or for a clean rebuild: "Dev Containers: Rebuild Container Without Cache" + +The container will be rebuilt and the ``postCreateCommand`` will run again. + +Additional Notes +---------------- + +* The ``devcontainer.json`` file is gitignored to prevent committing personal paths +* The ``example.devcontainer.json`` serves as a template for new users +* All changes made inside the container persist until you rebuild it +* The container uses the same image as the production PETPrep Docker image for consistency +* If you need to change the PETPrep version, you can change the image in the ``devcontainer.json`` file or +rebuild and tag the container with the same name and tag as the official PETPrep Docker image (``ghcr.io/nipreps/petprep:main``). + +For more information about devcontainers, see the `VS Code Dev Containers documentation +`__. diff --git a/.devcontainer/example.devcontainer.json b/.devcontainer/example.devcontainer.json new file mode 100644 index 00000000..5c2604ca --- /dev/null +++ b/.devcontainer/example.devcontainer.json @@ -0,0 +1,42 @@ +{ + "name": "PETPrep Development", + "image": "ghcr.io/nipreps/petprep:main", + "runArgs": [ + "--privileged" + ], + "mounts": [ + "source=/FullPath/to/the/data/directory/contaning/your/bids/data,target=/data,type=bind,consistency=cached", + "source=/FullPath/to/your/freesurfer/license.txt,target=/opt/freesurfer/license.txt,type=bind,consistency=cached" + ], + "overrideCommand": true, + "workspaceFolder": "/workspace", + "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind", + "remoteUser": "petprep", + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-python.debugpy", + "ms-python.vscode-pylance", + "charliermarsh.ruff", + "ms-python.black-formatter" + ], + "settings": { + "python.defaultInterpreterPath": "/opt/conda/envs/petprep/bin/python", + "python.terminal.activateEnvironment": false, + "python.linting.enabled": true, + "python.linting.ruffEnabled": true, + "python.formatting.provider": "black", + "editor.formatOnSave": true, + "files.eol": "\n" + } + } + }, + "postCreateCommand": "bash -c 'cd /workspace && pip install --user --ignore-installed --no-cache-dir -e .[dev,test]'", + "postStartCommand": "", + "remoteEnv": { + "PATH": "/home/petprep/.local/bin:/opt/conda/envs/petprep/bin:${containerEnv:PATH}", + "PYTHONPATH": "/workspace:${containerEnv:PYTHONPATH}", + "TEMPLATEFLOW_HOME": "/home/petprep/.cache/templateflow" + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index a06e6a49..74ec7823 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ dist/ .idea/ .env .envrc +.devcontainer/devcontainer.json # Additional .ipynb_checkpoints/ From 083ee22ef1f900bad5e0256c3a3a3a4e6309394f Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:20:42 +0000 Subject: [PATCH 2/7] order is fixed with updated nipreps.json --- .devcontainer/Dockerfile | 12 ++++++++++++ petprep/data/nipreps.json | 20 +++++++++++++------- petprep/interfaces/__init__.py | 8 ++++++++ 3 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 .devcontainer/Dockerfile diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..7818d877 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,12 @@ +FROM ghcr.io/nipreps/petprep:main + +# Install sudo, git, vim, and htop (add any other dev or quality of life tools you'd like here) +RUN apt-get install -y sudo git vim htop + + +# Add petprep user to sudoers group and configure passwordless sudo for a more useful dev environment +RUN if id -u petprep > /dev/null 2>&1; then \ + usermod -aG sudo petprep 2>/dev/null || usermod -aG wheel petprep 2>/dev/null || true; \ + echo "petprep ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/petprep; \ + chmod 0440 /etc/sudoers.d/petprep; \ + fi diff --git a/petprep/data/nipreps.json b/petprep/data/nipreps.json index b3684ec8..78575463 100644 --- a/petprep/data/nipreps.json +++ b/petprep/data/nipreps.json @@ -153,15 +153,21 @@ "name": "cohort", "pattern": "[_/\\\\]+cohort-0*(\\d+)", "dtype": "int" + }, + { + "name": "seg", + "pattern": "[_/\\\\]+seg-([a-zA-Z0-9+]+)" } ], "default_path_patterns": [ - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", + "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_seg-{seg}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_hemi-{hemi}]_from-{from}_to-{to}_mode-{mode|image}_{suffix|xfm}{extension<.txt|.h5>}", "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}]_hemi-{hemi}[_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.surf.gii|.shape.gii>}", "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.dscalar.nii|.json>}", - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}]_desc-{desc}_{suffix|mask}{extension<.nii|.nii.gz|.json>|.nii.gz}", + "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_label-{label}]_desc-{desc}_{suffix|mask}{extension<.nii|.nii.gz|.json>|.nii.gz}", "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}]_label-{label}[_desc-{desc}]_{suffix|probseg}{extension<.nii|.nii.gz|.json>|.nii.gz}", + "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_seg-{seg}][_desc-{desc}]_{suffix}{extension<.tsv|.json>|.tsv}", + "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_seg-{seg}][_label-{label}][_desc-{desc}]_{suffix}{extension<.tsv|.json>|.tsv}", "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_hemi-{hemi}]_from-{from}_to-{to}_mode-{mode|image}[_desc-{desc}]_{suffix|xfm}{extension<.txt|.h5>}", @@ -187,11 +193,11 @@ "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.dscalar.nii|.json>}", "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}]_desc-{desc}_{suffix|mask}{extension<.nii|.nii.gz|.json>|.nii.gz}", "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}]_label-{label}[_desc-{desc}]_{suffix|probseg}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_part-{part}][_space-{space}][_atlas-{atlas}][_cohort-{cohort}][_desc-{desc}]_{suffix|timeseries}{extension<.json|.tsv>|.tsv}", - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", + "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_part-{part}][_space-{space}][_atlas-{atlas}][_cohort-{cohort}][_seg-{seg}][_label-{label}][_desc-{desc}]_{suffix|timeseries}{extension<.json|.tsv>|.tsv}", + "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_label-{label}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_fmapid-{fmapid}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg|.png>|.html}" + "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_label-{label}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", + "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_label-{label}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", + "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_label-{label}][_desc-{desc}]_{suffix}{extension<.html|.svg|.png>|.html}" ] } diff --git a/petprep/interfaces/__init__.py b/petprep/interfaces/__init__.py index 56c9b9ea..10f80a24 100644 --- a/petprep/interfaces/__init__.py +++ b/petprep/interfaces/__init__.py @@ -1,7 +1,11 @@ # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- # vi: set ft=python sts=4 ts=4 sw=4 et: +from json import loads + from niworkflows.interfaces.bids import DerivativesDataSink as _DDSink +from petprep.utils.bids import load_data + from .cifti import GeneratePetCifti from .motion import MotionPlot from .tacs import ExtractRefTAC, ExtractTACs @@ -9,6 +13,10 @@ class DerivativesDataSink(_DDSink): out_path_base = '' + _petprep_spec = loads(load_data.readable('nipreps.json').read_text()) + _config_entities = frozenset({e['name'] for e in _petprep_spec['entities']}) + _config_entities_dict = _petprep_spec['entities'] + _file_patterns = tuple(_petprep_spec['default_path_patterns']) __all__ = ( From c153c7d8060edabf773ba75098c8b8950379b5e5 Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:21:50 +0000 Subject: [PATCH 3/7] update test --- petprep/workflows/pet/tests/test_fit.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/petprep/workflows/pet/tests/test_fit.py b/petprep/workflows/pet/tests/test_fit.py index 81d2c417..c6b41b2d 100644 --- a/petprep/workflows/pet/tests/test_fit.py +++ b/petprep/workflows/pet/tests/test_fit.py @@ -359,8 +359,9 @@ def test_refmask_report_connections(bids_root: Path, tmp_path: Path, pvc_method) ds_tacs = wf.get_node('ds_ref_tacs') assert ds_tacs.inputs.label == 'cerebellum' assert 'label' in ds_tacs.interface._allowed_entities - assert 'seg' not in ds_tacs.interface._allowed_entities - assert not hasattr(ds_tacs.inputs, 'seg') + assert 'seg' in ds_tacs.interface._config_entities + assert hasattr(ds_tacs.inputs, 'seg') + assert ds_tacs.inputs.seg is Undefined assert ds_tacs.inputs.desc == 'preproc' edge_tacs = wf._graph.get_edge_data(wf.get_node('pet_ref_tacs_wf'), ds_tacs) assert ('outputnode.timeseries', 'in_file') in edge_tacs['connect'] From 401c901b4f8661580901cba2c9b5585ce40e470f Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:43:01 +0000 Subject: [PATCH 4/7] clean up unused args for allowed_entities for label and seg --- petprep/cli/run.py | 8 +++++++- petprep/workflows/pet/outputs.py | 4 ---- petprep/workflows/pet/reference_mask.py | 1 - petprep/workflows/pet/segmentation.py | 3 --- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/petprep/cli/run.py b/petprep/cli/run.py index c49e844a..f13e9ba3 100644 --- a/petprep/cli/run.py +++ b/petprep/cli/run.py @@ -22,6 +22,7 @@ # """PET preprocessing workflow.""" + from .. import config @@ -74,7 +75,12 @@ def main(): # CRITICAL Call build_workflow(config_file, retval) in a subprocess. # Because Python on Linux does not ever free virtual memory (VM), running the # workflow construction jailed within a process preempts excessive VM buildup. - if 'pdb' not in config.execution.debug: + # Skip multiprocessing if debugging (pdb) or if running under a debugger + + is_debugging = ( + 'pdb' in config.execution.debug or hasattr(sys, 'gettrace') and sys.gettrace() is not None + ) + if not is_debugging: with Manager() as mgr: retval = mgr.dict() p = Process(target=build_workflow, args=(str(config_file), retval)) diff --git a/petprep/workflows/pet/outputs.py b/petprep/workflows/pet/outputs.py index 389660e3..2c03e173 100644 --- a/petprep/workflows/pet/outputs.py +++ b/petprep/workflows/pet/outputs.py @@ -195,7 +195,6 @@ def init_func_fit_reports_wf( desc='ref', label=ref_name, datatype='figures', - allowed_entities=('label',), ), name='ds_report_refmask', run_without_submitting=True, @@ -334,7 +333,6 @@ def init_func_fit_reports_wf( label=ref_name, suffix='pet', datatype='figures', - allowed_entities=('label',), ), name='ds_pet_t1_refmask_report', ) @@ -517,7 +515,6 @@ def init_ds_refmask_wf( suffix='mask', desc='ref', label=ref_name, - allowed_entities=('label',), compress=True, ), name='ds_refmask', @@ -1004,7 +1001,6 @@ def init_refmask_report_wf( desc='ref', label=ref_name, datatype='figures', - allowed_entities=('label',), suffix='pet', ), name='ds_report_refmask', diff --git a/petprep/workflows/pet/reference_mask.py b/petprep/workflows/pet/reference_mask.py index 5e68b27a..071bc8bc 100644 --- a/petprep/workflows/pet/reference_mask.py +++ b/petprep/workflows/pet/reference_mask.py @@ -74,7 +74,6 @@ def init_pet_refmask_wf( base_directory=config.execution.petprep_dir, label=ref_mask_name, desc='ref', - allowed_entities=('label',), suffix='morph', extension='.tsv', datatype='anat', diff --git a/petprep/workflows/pet/segmentation.py b/petprep/workflows/pet/segmentation.py index 8608cfdd..3f37238e 100644 --- a/petprep/workflows/pet/segmentation.py +++ b/petprep/workflows/pet/segmentation.py @@ -169,7 +169,6 @@ def _build_nodes( DerivativesDataSink( base_directory=config.execution.petprep_dir, seg=seg, - allowed_entities=('seg',), suffix='dseg', extension='.nii.gz', compress=True, @@ -220,7 +219,6 @@ def _build_nodes( DerivativesDataSink( base_directory=config.execution.petprep_dir, seg=seg, - allowed_entities=('seg',), suffix='dseg', extension='.tsv', datatype='anat', @@ -234,7 +232,6 @@ def _build_nodes( DerivativesDataSink( base_directory=config.execution.petprep_dir, seg=seg, - allowed_entities=('seg',), suffix='morph', extension='.tsv', datatype='anat', From 3678beddeacbe39334c5060f19374b1a890967f4 Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Wed, 28 Jan 2026 22:11:19 +0000 Subject: [PATCH 5/7] reformat run.py --- petprep/cli/run.py | 1 - 1 file changed, 1 deletion(-) diff --git a/petprep/cli/run.py b/petprep/cli/run.py index f13e9ba3..ae23436a 100644 --- a/petprep/cli/run.py +++ b/petprep/cli/run.py @@ -22,7 +22,6 @@ # """PET preprocessing workflow.""" - from .. import config From 69cf2ea1bc36817f737464fbfaeb0d56f1df0021 Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Mon, 23 Mar 2026 10:40:46 -0400 Subject: [PATCH 6/7] remove non-dev-container changes --- petprep/data/nipreps.json | 20 +++++++------------- petprep/interfaces/__init__.py | 8 -------- petprep/workflows/pet/outputs.py | 4 ++++ petprep/workflows/pet/reference_mask.py | 1 + petprep/workflows/pet/segmentation.py | 3 +++ petprep/workflows/pet/tests/test_fit.py | 5 ++--- 6 files changed, 17 insertions(+), 24 deletions(-) diff --git a/petprep/data/nipreps.json b/petprep/data/nipreps.json index 78575463..b3684ec8 100644 --- a/petprep/data/nipreps.json +++ b/petprep/data/nipreps.json @@ -153,21 +153,15 @@ "name": "cohort", "pattern": "[_/\\\\]+cohort-0*(\\d+)", "dtype": "int" - }, - { - "name": "seg", - "pattern": "[_/\\\\]+seg-([a-zA-Z0-9+]+)" } ], "default_path_patterns": [ - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_seg-{seg}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", + "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_hemi-{hemi}]_from-{from}_to-{to}_mode-{mode|image}_{suffix|xfm}{extension<.txt|.h5>}", "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}]_hemi-{hemi}[_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.surf.gii|.shape.gii>}", "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.dscalar.nii|.json>}", - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_label-{label}]_desc-{desc}_{suffix|mask}{extension<.nii|.nii.gz|.json>|.nii.gz}", + "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}]_desc-{desc}_{suffix|mask}{extension<.nii|.nii.gz|.json>|.nii.gz}", "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}]_label-{label}[_desc-{desc}]_{suffix|probseg}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_seg-{seg}][_desc-{desc}]_{suffix}{extension<.tsv|.json>|.tsv}", - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_seg-{seg}][_label-{label}][_desc-{desc}]_{suffix}{extension<.tsv|.json>|.tsv}", "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_hemi-{hemi}]_from-{from}_to-{to}_mode-{mode|image}[_desc-{desc}]_{suffix|xfm}{extension<.txt|.h5>}", @@ -193,11 +187,11 @@ "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.dscalar.nii|.json>}", "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}]_desc-{desc}_{suffix|mask}{extension<.nii|.nii.gz|.json>|.nii.gz}", "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}]_label-{label}[_desc-{desc}]_{suffix|probseg}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_part-{part}][_space-{space}][_atlas-{atlas}][_cohort-{cohort}][_seg-{seg}][_label-{label}][_desc-{desc}]_{suffix|timeseries}{extension<.json|.tsv>|.tsv}", - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_label-{label}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", + "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_part-{part}][_space-{space}][_atlas-{atlas}][_cohort-{cohort}][_desc-{desc}]_{suffix|timeseries}{extension<.json|.tsv>|.tsv}", + "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_fmapid-{fmapid}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_label-{label}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_label-{label}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_label-{label}][_desc-{desc}]_{suffix}{extension<.html|.svg|.png>|.html}" + "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", + "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", + "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg|.png>|.html}" ] } diff --git a/petprep/interfaces/__init__.py b/petprep/interfaces/__init__.py index 10f80a24..56c9b9ea 100644 --- a/petprep/interfaces/__init__.py +++ b/petprep/interfaces/__init__.py @@ -1,11 +1,7 @@ # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- # vi: set ft=python sts=4 ts=4 sw=4 et: -from json import loads - from niworkflows.interfaces.bids import DerivativesDataSink as _DDSink -from petprep.utils.bids import load_data - from .cifti import GeneratePetCifti from .motion import MotionPlot from .tacs import ExtractRefTAC, ExtractTACs @@ -13,10 +9,6 @@ class DerivativesDataSink(_DDSink): out_path_base = '' - _petprep_spec = loads(load_data.readable('nipreps.json').read_text()) - _config_entities = frozenset({e['name'] for e in _petprep_spec['entities']}) - _config_entities_dict = _petprep_spec['entities'] - _file_patterns = tuple(_petprep_spec['default_path_patterns']) __all__ = ( diff --git a/petprep/workflows/pet/outputs.py b/petprep/workflows/pet/outputs.py index 2c03e173..389660e3 100644 --- a/petprep/workflows/pet/outputs.py +++ b/petprep/workflows/pet/outputs.py @@ -195,6 +195,7 @@ def init_func_fit_reports_wf( desc='ref', label=ref_name, datatype='figures', + allowed_entities=('label',), ), name='ds_report_refmask', run_without_submitting=True, @@ -333,6 +334,7 @@ def init_func_fit_reports_wf( label=ref_name, suffix='pet', datatype='figures', + allowed_entities=('label',), ), name='ds_pet_t1_refmask_report', ) @@ -515,6 +517,7 @@ def init_ds_refmask_wf( suffix='mask', desc='ref', label=ref_name, + allowed_entities=('label',), compress=True, ), name='ds_refmask', @@ -1001,6 +1004,7 @@ def init_refmask_report_wf( desc='ref', label=ref_name, datatype='figures', + allowed_entities=('label',), suffix='pet', ), name='ds_report_refmask', diff --git a/petprep/workflows/pet/reference_mask.py b/petprep/workflows/pet/reference_mask.py index 071bc8bc..5e68b27a 100644 --- a/petprep/workflows/pet/reference_mask.py +++ b/petprep/workflows/pet/reference_mask.py @@ -74,6 +74,7 @@ def init_pet_refmask_wf( base_directory=config.execution.petprep_dir, label=ref_mask_name, desc='ref', + allowed_entities=('label',), suffix='morph', extension='.tsv', datatype='anat', diff --git a/petprep/workflows/pet/segmentation.py b/petprep/workflows/pet/segmentation.py index 3f37238e..8608cfdd 100644 --- a/petprep/workflows/pet/segmentation.py +++ b/petprep/workflows/pet/segmentation.py @@ -169,6 +169,7 @@ def _build_nodes( DerivativesDataSink( base_directory=config.execution.petprep_dir, seg=seg, + allowed_entities=('seg',), suffix='dseg', extension='.nii.gz', compress=True, @@ -219,6 +220,7 @@ def _build_nodes( DerivativesDataSink( base_directory=config.execution.petprep_dir, seg=seg, + allowed_entities=('seg',), suffix='dseg', extension='.tsv', datatype='anat', @@ -232,6 +234,7 @@ def _build_nodes( DerivativesDataSink( base_directory=config.execution.petprep_dir, seg=seg, + allowed_entities=('seg',), suffix='morph', extension='.tsv', datatype='anat', diff --git a/petprep/workflows/pet/tests/test_fit.py b/petprep/workflows/pet/tests/test_fit.py index c6b41b2d..81d2c417 100644 --- a/petprep/workflows/pet/tests/test_fit.py +++ b/petprep/workflows/pet/tests/test_fit.py @@ -359,9 +359,8 @@ def test_refmask_report_connections(bids_root: Path, tmp_path: Path, pvc_method) ds_tacs = wf.get_node('ds_ref_tacs') assert ds_tacs.inputs.label == 'cerebellum' assert 'label' in ds_tacs.interface._allowed_entities - assert 'seg' in ds_tacs.interface._config_entities - assert hasattr(ds_tacs.inputs, 'seg') - assert ds_tacs.inputs.seg is Undefined + assert 'seg' not in ds_tacs.interface._allowed_entities + assert not hasattr(ds_tacs.inputs, 'seg') assert ds_tacs.inputs.desc == 'preproc' edge_tacs = wf._graph.get_edge_data(wf.get_node('pet_ref_tacs_wf'), ds_tacs) assert ('outputnode.timeseries', 'in_file') in edge_tacs['connect'] From 9b2b8c23b4f91ddfcae4a8e5e0333e3bddf5058e Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Mon, 23 Mar 2026 16:12:30 -0400 Subject: [PATCH 7/7] Apply suggestion from @bendhouseart revert run.py --- petprep/cli/run.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/petprep/cli/run.py b/petprep/cli/run.py index ae23436a..c49e844a 100644 --- a/petprep/cli/run.py +++ b/petprep/cli/run.py @@ -74,12 +74,7 @@ def main(): # CRITICAL Call build_workflow(config_file, retval) in a subprocess. # Because Python on Linux does not ever free virtual memory (VM), running the # workflow construction jailed within a process preempts excessive VM buildup. - # Skip multiprocessing if debugging (pdb) or if running under a debugger - - is_debugging = ( - 'pdb' in config.execution.debug or hasattr(sys, 'gettrace') and sys.gettrace() is not None - ) - if not is_debugging: + if 'pdb' not in config.execution.debug: with Manager() as mgr: retval = mgr.dict() p = Process(target=build_workflow, args=(str(config_file), retval))