%global pypi_name monai_physio # ============================================================================ # THIS PACKAGE IS DELIBERATELY INCOMPLETE. Five of upstream's 27 hard # dependencies are not in Fedora or this COPR and are stripped in %%prep; two # more (usd-core, pyvista[all]) are satisfied but invisible to rpm. The goal is # the COMPLETE package, so each stripped item is listed here with what it takes # to put back. Un-stripping one means: delete its sed line AND its guard in # %%prep, add its Requires/BuildRequires, and delete its entry from the # %%pyproject_check_import exclusion list. # # STRIPPED -- feature lost until restored # --------------------------------------- # antspyx -> monai_physio.register_images_ants (the ANTs registration # backend; RegisterImagesANTS raises a clear ImportError when # used). Patch0 is what lets the rest of the package import # without it -- upstream imports it eagerly in __init__. # PyPI ships WHEELS ONLY of prebuilt binaries, and # upstream's own pyproject sets no-build-package = ["antspyx"] # because building ANTs+ITK from source "fails". Restoring it # means either an unbundled build against our ANTs/ITK # (a project in itself) or a decision to repackage the wheel. # picsl-greedy -> monai_physio.register_images_greedy (the Greedy backend; # the import is lazy, so the module itself imports fine). # Our `greedy` package is the C++ program, 1.3.0~alpha; the # Python wrapper picsl_greedy is a separate PyPI project that # ships wheels only. Likely path: a python3-picsl-greedy # subpackage of greedy.spec, if upstream's tree builds it. # nvidia-physicsnemo -> the four physicsnemo modules and the # monai-physio-{train,infer}-physicsnemo commands. Upstream # caps it at Python <3.15, so it can NEVER build on f45 or # rawhide until upstream lifts the cap; on f44 it needs 26 # core dependencies of which warp-lang is the hard one. # torch-scatter -> process_physicsnemo only. Needed ONLY together with # physicsnemo. Upstream git master supports torch 2.12 # (PR #510) but PyPI's 2.1.2 is from 2023, so a git snapshot. # # PERMANENTLY NOT IMPORTABLE -- not a dependency we can ever package # ------------------------------------------------------------------ # simpleware -> monai_physio.simpleware_medical.SimplewareScript_heart_ # segmentation does `import simpleware.scripting`. That module # is the scripting interface of Synopsys Simpleware, a # commercial application, and the script is written to run # INSIDE its embedded interpreter, so it is not an importable # library at all. The file ships as data for that workflow # and is excluded from the import check. Nothing to restore. # # SATISFIED BUT INVISIBLE TO RPM -- replaced by a hand-written Requires # --------------------------------------------------------------------- # (itk used to be here; see below) # itk -> RESOLVED. python3-itk 5.4.7-23 ships itk-5.4.7.dist-info and # provides python3dist(itk), so upstream's # "itk>=5.3.0,<6.0.0" is a generated requirement again and is # no longer stripped. (Before -23 it provided none, and this # package stripped the line and wrote Requires: python3-itk.) # usd-core -> python3-usd (Fedora's OpenUSD, which provides the `pxr` # module but not python3dist(usd-core)). # pyvista[all] -> python3-pyvista. The [all] extra has no provide here. # ============================================================================ Name: python-monai-physio Version: 2026.9.1 Release: 2%{?dist} Summary: Personalized physiological digital twins from 3D/4D medical images # LICENSE in the sdist is the full Apache License 2.0 text and PyPI agrees. License: Apache-2.0 URL: https://github.com/Project-MONAI/monai-physio Source0: %{pypi_source %{pypi_name}} # Makes the bare `import ants` in register_images_ants.py optional. Upstream's # __init__.py imports every module eagerly, so without this the WHOLE package fails # to import when antspyx is absent, not just RegisterImagesANTS. Every other # optional backend (picsl_greedy, physicsnemo) is already lazy. Report upstream; # DELETE WHEN antspyx IS PACKAGED (roadmap above). Patch0: python-monai-physio-optional-antspyx.patch BuildArch: noarch BuildRequires: python3-devel BuildRequires: pyproject-rpm-macros # Needed by %%check, and absent from the generated set because they are stripped # from the metadata below (see the Requires block). BuildRequires: python3-usd BuildRequires: python3-pyvista %global _description %{expand: MONAI Physio is a collection of methods, workflows, tutorials and command-line tools for creating personalized physiological digital twins. It typically starts from a 3D medical image of a subject, extracts anatomic models from it, and uses statistical shape models and AI surrogates to estimate the subject's physiology. MONAI Physio is a research and visualization toolkit. Upstream states that it is NOT validated for clinical use, is not a medical device, and must not be used for diagnosis, treatment planning or clinical decision-making. This package is built WITHOUT the ANTs and Greedy registration backends and without the PhysicsNeMo AI-surrogate workflows, whose dependencies are not packaged. The rest of the toolkit is complete.} %description %{_description} %package -n python3-monai-physio Summary: %{summary} # See the roadmap at the top of this file for why these two are written by hand. # (python3-itk is NOT here: itk is generated from the metadata since ITK -23.) Requires: python3-usd Requires: python3-pyvista %description -n python3-monai-physio %{_description} %prep %autosetup -n %{pypi_name}-%{version} -p1 # Remove the hard dependencies that cannot be satisfied as written. Each line # matches exactly one line of the sdist's pyproject.toml (counted before this was # written), and each removal is checked afterwards, so an upstream reformat fails # the build here instead of silently leaving an unsatisfiable requirement behind. # NOTE grep -P throughout: see python-icon-registration.spec for why plain grep # lies about \s. for dep in 'nvidia-physicsnemo>=2.0.0' \ 'torch-scatter>=2.1.0' \ 'antspyx>=0.6.1' \ 'picsl-greedy>=0.0.12' \ 'usd-core>=23.11' \ 'pyvista\[all\]>=0.47.0'; do # Present exactly once before the edit... # grep -c exits 1 on a zero count, and %%prep runs under set -e: without || true # the build would die here silently, before the message below can print. n=$(grep -cP "^\s+\"${dep//./\\.}\",\s*$" pyproject.toml || true) [ "$n" = 1 ] || { echo "ERROR: expected exactly one pyproject line for $dep, found $n" >&2; exit 1; } sed -i -E "/^\s+\"${dep//./\\.}\",\s*$/d" pyproject.toml # ...and gone afterwards. grep -qP "^\s+\"${dep//./\\.}\",\s*$" pyproject.toml && { echo "ERROR: $dep still in pyproject.toml" >&2; exit 1; } done # pyvista was stripped only for its [all] extra; the plain requirement is wanted. sed -i -E '0,/^(\s+)"torch-geometric>=2\.5\.0",\s*$/s//&\n\1"pyvista>=0.47.0",/' pyproject.toml grep -qP '^\s+"pyvista>=0\.47\.0",\s*$' pyproject.toml || { echo "ERROR: plain pyvista requirement was not restored" >&2; exit 1; } # itk is no longer stripped; it must still be there for the generator to see it. grep -qP '^\s+"itk>=5\.3\.0,<6\.0\.0",\s*$' pyproject.toml || { echo "ERROR: itk requirement is gone from pyproject.toml" >&2; exit 1; } # And confirm the ones we DO keep are still there, so the loop cannot quietly have # eaten the whole block. grep -qP '^\s+"icon-registration>=1\.0\.0",\s*$' pyproject.toml || { echo "ERROR: dependency block looks wrong after edit" >&2; exit 1; } grep -qP '^\s+"unigradicon>=1\.0\.0",\s*$' pyproject.toml || { echo "ERROR: dependency block looks wrong after edit" >&2; exit 1; } # The sdist ships a stale egg-info/requires.txt listing the stripped names. # setuptools regenerates it, but remove it so it cannot leak into the metadata. rm -rf src/%{pypi_name}.egg-info %generate_buildrequires %pyproject_buildrequires %build %pyproject_wheel %install %pyproject_install %pyproject_save_files %{pypi_name} %check # Import only. Upstream's tests need datasets and, for several, a GPU. # # ONE module is excluded, identified from a failing check rather than guessed: # the Simpleware script, which only runs inside a commercial application's own # interpreter (roadmap above). Everything else imports -- 78 of 79 modules -- # INCLUDING the four physicsnemo modules and register_images_ants/greedy, because # their optional imports are lazy or, for ants, made so by Patch0. # All ONE physical line: rpm parses a macro's arguments from a single line. %pyproject_check_import -e monai_physio.simpleware_medical.SimplewareScript_heart_segmentation %files -n python3-monai-physio -f %{pyproject_files} %license LICENSE %doc README.md %{_bindir}/monai-physio-* %changelog * Mon Oct 05 2026 Morgan Hough - 2026.9.1-2 - Stop stripping itk. python3-itk 5.4.7-23 ships itk-5.4.7.dist-info and provides python3dist(itk) = 5.4.7, so "itk>=5.3.0,<6.0.0" is generated and satisfiable. Removed the itk entry from the %%prep strip loop, the hand-written Requires: python3-itk and the matching BuildRequires, and added a guard that asserts the itk line is still PRESENT. The other stripped dependencies (antspyx, picsl-greedy, nvidia-physicsnemo, torch-scatter) and the hand-written python3-usd and python3-pyvista are unchanged. - DO NOT BUILD against python3-itk older than -23: the generated requirement would be unsatisfiable. * Mon Oct 05 2026 Morgan Hough - 2026.9.1-1 - Initial package: monai-physio 2026.9.1 (Project-MONAI), the original target of the dependency work that produced python-torch-geometric, -transformers, -footsteps, -icon-registration, -unigradicon and -totalsegmentator. - %%check excludes exactly one module, monai_physio.simpleware_medical. SimplewareScript_heart_segmentation, which imports the scripting interface of the commercial Synopsys Simpleware application and is meant to run inside that application. 78 of 79 modules import, including the four physicsnemo modules. - Patch0 makes antspyx optional. A first local build showed 46 modules failing with "No module named 'ants'": upstream's __init__.py imports every module eagerly and register_images_ants.py opens with a bare `import ants`, so without antspyx the whole package, not just the ANTs backend, fails to import. It was the ONLY hard top-level import of a missing package; picsl_greedy and physicsnemo are already imported lazily inside functions. - INCOMPLETE ON PURPOSE, with the roadmap to the complete package at the top of the spec. Stripped from the metadata: antspyx, picsl-greedy, nvidia-physicsnemo, torch-scatter. Replaced by hand-written Requires because they are present but invisible to rpm: itk, usd-core (python3-usd), pyvista[all] (python3-pyvista).