Skip to main content

pfdo_run

https://github.com/FNNDSC/pfdo_run/actions/workflows/ci.yml/badge.svg https://img.shields.io/pypi/v/pfdo_run.svg https://img.shields.io/badge/python-3.11%2B-blue.svg

Run a command, written as a pftag template, in every directory of a tree.

Overview

pfdo_run walks an input directory with pftree, selects files in each directory with the pfdo filters and --analyzeFileIndex, and runs the --exec command once per selected file. The command is a pftag template: %{inputWorkingDir}, %{outputWorkingDir} and %{inputWorkingFile} name the directory being processed, its counterpart in the output tree, and the file; functions derive names, %{inputWorkingFile|rmext}.png. The output tree mirrors the input tree, and each job leaves its command, stdout, stderr and return code beside its results. Directories run in parallel under --threads.

The template is split like a shell command line and each argument has its tags resolved on its own, so a path with a space stays one argument and nothing a file name contains can change the command. The command runs without a shell.

Installation

pip install pfdo_run

Python 3.11 or newer; depends on pfdo and pftag.

Command line

pfdo_run --inputDir /data --outputDir /png --fileFilter 'glob:*.jpg' --threads 4 \
    --exec "convert %{inputWorkingDir}/%{inputWorkingFile} %{outputWorkingDir}/%{inputWorkingFile|rmext}.png"

pfdo_run --inputDir /dicom --outputDir /nifti --fileFilter 'glob:*.dcm' --analyzeFileIndex f \
    --exec "mri_convert %{inputWorkingDir}/%{inputWorkingFile} %{outputWorkingDir}/%{inputWorkingDir|basename}.nii.gz"

pfdo_run --man

All of pftree’s and pfdo’s options apply. pfdo_run adds:

--exec <commandTemplate>

The command to run per selected file. Tags: %{inputWorkingDir}, %{outputWorkingDir}, %{inputWorkingFile}, every option by name (%{outputDir}), and pftag’s core tags. Functions: rmext, ext, basename, dirname, replace, re, upper, lower, strip, pad, slice, md5, sha256, strftime, mask. A tag that does not resolve fails that job before it runs.

--noJobLogging

Do not write <file>-cmd, <file>-stdout, <file>-stderr and <file>-returncode beside each job’s results. The stream files are written live and rendered as a terminal would show them when the job ends.

A job whose command exits non-zero fails its directory; the run continues and the directory’s record says why. --json prints the whole result, each directory’s jobs included.

Library

from pathlib import Path
from pfdo_run import PfdoRun, PfdoRunOptions

options = PfdoRunOptions(
    inputDir=Path("/data"), outputDir=Path("/png"), fileFilter="glob:*.jpg", threads=4,
    exec="convert %{inputWorkingDir}/%{inputWorkingFile} %{outputWorkingDir}/%{inputWorkingFile|rmext}.png",
)
result = PfdoRun(options).run()
for key, stage in result.process.analyze.items():
    for job in stage.payload["jobs"]:
        print(key, job["file"], job["run"]["returncode"] if job["run"] else job["errors"])

pfdo_run.__main__ exposes its parser and manual in parts (parser_build(standalone=False), synopsis(command=...), CLI_DS, ARGS_SYNOPSIS_DS) so that a ChRIS plugin such as pl-shexec composes its own command around them.

Changes in 4.0

  • Built on pfdo 6 and pftag 3; pfmisc, pudb and Faker are no longer dependencies.

  • The template grammar is pftag’s: %{name} and %{name|fn(arg)}. The 3.x forms %name and %_fn_name are gone; a bare %name of a known tag is reported as a 1.x template and fails the job.

  • The command is split before substitution and run without a shell; 3.x substituted into the string and split on whitespace, so a path with a space broke it.

  • The command runs through hostlib: no shell, both streams read concurrently as they arrive, so nothing is dropped and nothing can deadlock; 3.x read stdout line by line, lost the last line, and could deadlock on stderr. At --verbosity 2 and above the output also streams to the console while the job runs.

  • Per-job records are written by default and --noJobLogging exists; in 3.x the option was documented but undefined, so logs were never written. The stream files are rendered as a terminal would have shown them, so a redrawn progress bar is one line; the record in --json keeps every frame.

  • Each job’s record is FileJob: the file, the command as built, status, errors, and run, hostlib’s envelope of the run (both streams, exit status, host facts, timing) when the command ran.

  • A non-zero exit status fails the job and its directory; the run’s status is truthful (3.x reported False always).

  • main(argv) honours argv; --version, --man and --synopsis exit 0; packaging is pyproject.toml only, published from CI on a v* tag.

Metadata

Release files for pfdo-run 4.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pfdo-run 4.0.0
File Size Uploaded
pfdo_run-4.0.0.tar.gz 19.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pfdo-run 4.0.0
File Interpreter ABI Platform
pfdo_run-4.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 35.9 kB

Release files / pfdo_run-4.0.0.tar.gz

Download URL pfdo_run-4.0.0.tar.gz
Size 19.1 kB
Tags Source
SHA-256 checksum
How to use checksums
853011e4b746f26540a18ba12ba81b42bc4863eb0e5a60506eb1c35e77e03cbf
BLAKE2b-256 checksum
How to use checksums
9f4c61770817db6a9f1bbf9d889a41337bea0897917fa38556f7afe33cba1c9b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release files / pfdo_run-4.0.0-py3-none-any.whl

Download URL pfdo_run-4.0.0-py3-none-any.whl
Size 16.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
55ac6f6000935502c94d179036fdface58f572ea2f503416e74eb086c9ebdd6e
BLAKE2b-256 checksum
How to use checksums
1319d3c3483cce520e96cbb97297d0de488f821b0e0af3bc8b6ffeb86343f0da
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release history Release notifications | RSS feed

4.3.0

2 release files

4.2.0

2 release files

4.1.0

2 release files

4.0.1

2 release files

This release

4.0.0 This release

2 release files

3.2.6

1 release file

3.2.5

1 release file

3.2.4

1 release file

3.2.2

1 release file

3.2.0

1 release file

2.2.8

1 release file

2.2.4

1 release file

2.2.2

1 release file

2.2.0

1 release file

2.0.2

1 release file

2.0.0

1 release file

1.0.10

1 release file

1.0.8

1 release file

1.0.6

1 release file

1.0.4

1 release file

1.0.2

1 release file

1.0.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page