From 8e39d3728991ad194f21a70b1df038a5e1e828a4 Mon Sep 17 00:00:00 2001 From: Axel Garcia Date: Thu, 29 Jan 2026 17:01:18 +0100 Subject: [PATCH 1/3] ENH: Run Python tests by the CI --- .github/workflows/build-test-package.yml | 40 +++++++++++++++++++++ test/pctOutputArgumentWrapping.py | 19 +++++----- test/pctPythonWrappingInstantiationTest.py | 42 +++++++++++----------- 3 files changed, 73 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build-test-package.yml b/.github/workflows/build-test-package.yml index e0c28c8..9178d99 100644 --- a/.github/workflows/build-test-package.yml +++ b/.github/workflows/build-test-package.yml @@ -22,3 +22,43 @@ jobs: itk-module-deps: RTKConsortium/RTK@main secrets: pypi_password: ${{ secrets.pypi_password }} + + test-python-wheel: + needs: + - python-build-workflow + runs-on: self-hosted-linux + + steps: + - uses: actions/checkout@v4 + + - name: Download all wheel artifacts + uses: actions/download-artifact@v4 + with: + path: wheels + + - name: Set up Python 3.11 + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Test python wheel + run: | + # Find wheel for Python 3.11 dynamically + wheel=$(find wheels -name "*cp311*manylinux_2_28_x86_64.whl" -type f | head -1) + + pip uninstall -y $(pip list | grep -E '^itk-') || true + echo "Installing wheel: $wheel" + pip install $wheel + + pip install pytest matplotlib + + # Run tests with warning detection. + # SWIG < 4.4 triggers the Py_LIMITED_API "builtin type swig…" warning when + # Python runs with -Walways; pytest enables that behavior, so we ignore it. + stderr_log=$(mktemp) + pytest $GITHUB_WORKSPACE/test/*.py -vv -s -W "ignore:builtin type swig" 2> "$stderr_log" + if grep -q "Warning" "$stderr_log"; then + echo "Warnings found in stderr, failing the build" + cat "$stderr_log" + exit 1 + fi diff --git a/test/pctOutputArgumentWrapping.py b/test/pctOutputArgumentWrapping.py index 752c061..eb8d9e2 100644 --- a/test/pctOutputArgumentWrapping.py +++ b/test/pctOutputArgumentWrapping.py @@ -2,13 +2,16 @@ from itk import PCT as pct -pos_in = [25.0, -25.0, -110.0] -pos_out = [-25.0, 25.0, 110.0] -dir_in = [0.0, 0.0, 1.0] -dir_out = [0.0, 0.0, 1.0] -mlp = pct.PolynomialMLPFunction.New() -mlp.SetPolynomialDegree(2) -mlp.Init(pos_in, pos_out, dir_in, dir_out) +def test_output_argument_wrapping(): + pos_in = [25.0, -25.0, -110.0] + pos_out = [-25.0, 25.0, 110.0] + dir_in = [0.0, 0.0, 1.0] + dir_out = [0.0, 0.0, 1.0] -print(mlp.Evaluate([-50.0, 0.0, 50.0])) + mlp = pct.PolynomialMLPFunction.New() + mlp.SetPolynomialDegree(2) + mlp.Init(pos_in, pos_out, dir_in, dir_out) + + result = mlp.Evaluate([-50.0, 0.0, 50.0]) + print(result) diff --git a/test/pctPythonWrappingInstantiationTest.py b/test/pctPythonWrappingInstantiationTest.py index 2d43fd6..f82ee59 100644 --- a/test/pctPythonWrappingInstantiationTest.py +++ b/test/pctPythonWrappingInstantiationTest.py @@ -1,27 +1,29 @@ import itk from itk import PCT as pct -imageType = itk.Image[itk.F, 3] -pct.FDKDDWeightProjectionFilter[imageType].New() -pct.FDKDDConeBeamReconstructionFilter[imageType].New() -pct.FDKDDBackProjectionImageFilter[imageType, imageType].New() -pct.ProtonPairsToDistanceDrivenProjection[imageType, imageType].New() -pct.SmallHoleFiller[imageType]() -imageType = itk.Image[itk.F, 4] -pct.DDParkerShortScanImageFilter[imageType, imageType].New() -pct.ProtonPairsToBackProjection[imageType, imageType].New() -pct.ZengWeightedBackProjectionImageFilter[imageType].New() -pct.SmallHoleFiller[imageType]() +def test_python_wrapping_instantiation(): + imageType = itk.Image[itk.F, 3] + pct.FDKDDWeightProjectionFilter[imageType].New() + pct.FDKDDConeBeamReconstructionFilter[imageType].New() + pct.FDKDDBackProjectionImageFilter[imageType, imageType].New() + pct.ProtonPairsToDistanceDrivenProjection[imageType, imageType].New() + pct.SmallHoleFiller[imageType]() -pct.ThirdOrderPolynomialMLPFunction[itk.D].New() -pct.ThirdOrderPolynomialMLPFunction[itk.F].New() + imageType = itk.Image[itk.F, 4] + pct.DDParkerShortScanImageFilter[imageType, imageType].New() + pct.ProtonPairsToBackProjection[imageType, imageType].New() + pct.ZengWeightedBackProjectionImageFilter[imageType].New() + pct.SmallHoleFiller[imageType]() -pct.SchulteMLPFunction.New() -pct.PolynomialMLPFunction.New() + pct.ThirdOrderPolynomialMLPFunction[itk.D].New() + pct.ThirdOrderPolynomialMLPFunction[itk.F].New() -for t1 in [itk.F, itk.D]: - for t2 in [itk.F, itk.D]: - pct.EnergyStragglingFunctor[t1, t2]() - pct.BetheBlochProtonStoppingPower[t1, t2]() - pct.IntegratedBetheBlochProtonStoppingPowerInverse[t1, t2](1, 1) + pct.SchulteMLPFunction.New() + pct.PolynomialMLPFunction.New() + + for t1 in [itk.F, itk.D]: + for t2 in [itk.F, itk.D]: + pct.EnergyStragglingFunctor[t1, t2]() + pct.BetheBlochProtonStoppingPower[t1, t2]() + pct.IntegratedBetheBlochProtonStoppingPowerInverse[t1, t2](1, 1) From 33ced1f20d187cbb1f94886b31f15f0076435af9 Mon Sep 17 00:00:00 2001 From: Simon Rit Date: Sun, 1 Feb 2026 23:16:06 +0000 Subject: [PATCH 2/3] BUG: Use the two templates in DDParkerShortScanImageFilter --- applications/pctfdk/pctfdk.py | 4 +++- wrapping/pctDDParkerShortScanImageFilter.wrap | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/applications/pctfdk/pctfdk.py b/applications/pctfdk/pctfdk.py index 181b73d..23a3075 100644 --- a/applications/pctfdk/pctfdk.py +++ b/applications/pctfdk/pctfdk.py @@ -113,7 +113,9 @@ def process(args_info: argparse.Namespace): geometryReader.GenerateOutputInformation() # Short scan image filter - pssf = pct.DDParkerShortScanImageFilter[ProjectionImageType].New() + pssf = pct.DDParkerShortScanImageFilter[ + ProjectionImageType, ProjectionImageType + ].New() pssf.SetInput(reader.GetOutput()) pssf.SetGeometry(geometryReader.GetOutputObject()) pssf.InPlaceOff() diff --git a/wrapping/pctDDParkerShortScanImageFilter.wrap b/wrapping/pctDDParkerShortScanImageFilter.wrap index febee28..f9e25b3 100644 --- a/wrapping/pctDDParkerShortScanImageFilter.wrap +++ b/wrapping/pctDDParkerShortScanImageFilter.wrap @@ -1,5 +1,5 @@ itk_wrap_class("pct::DDParkerShortScanImageFilter" POINTER) foreach(t ${WRAP_ITK_REAL}) - itk_wrap_template("I${ITKM_${t}}4" "itk::Image<${ITKT_${t}}, 4>") + itk_wrap_template("I${ITKM_${t}}4I${ITKM_${t}}4" "itk::Image<${ITKT_${t}}, 4>, itk::Image<${ITKT_${t}}, 4>") endforeach() itk_end_wrap_class() From 1ccfc76b5377f208fc6b383047691fa3aa012f6b Mon Sep 17 00:00:00 2001 From: Simon Rit Date: Mon, 2 Feb 2026 12:26:41 +0000 Subject: [PATCH 3/3] BUG: Remove dysfunctional pctpairprotons test --- test/CMakeLists.txt | 1 - test/pctPairProtonsTest.py | 26 -------------------------- 2 files changed, 27 deletions(-) delete mode 100644 test/pctPairProtonsTest.py diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f9ffca5..d009dab 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -16,7 +16,6 @@ itk_add_test(NAME pctProtonPairsToDistanceDrivenProjectionTest if(ITK_WRAP_PYTHON) itk_python_add_test(NAME pctPythonWrappingInstantiationTest COMMAND pctPythonWrappingInstantiationTest.py) itk_python_add_test(NAME pctOutputArgumentWrappingTest COMMAND pctOutputArgumentWrapping.py) - itk_python_add_test(NAME pctPairProtonsTest COMMAND pctPairProtonsTest.py) endif() #----------------------------------------------------------------------------- diff --git a/test/pctPairProtonsTest.py b/test/pctPairProtonsTest.py deleted file mode 100644 index 4dada01..0000000 --- a/test/pctPairProtonsTest.py +++ /dev/null @@ -1,26 +0,0 @@ -# This test should be placed in some 'test' folder, but currently lies in the root folder in order to easily import gate.protonct and pctpairprotons. -# It should be moved once the testing mechanics are finalized. - -import os -import itk -import numpy as np - -from protonct import protonct -from pctpairprotons import pctpairprotons - -test_folder = "/tmp/pctPairProtons" - -protonct(output=test_folder, projections=1, protons_per_projection=100, seed=123) - -pctpairprotons( - input_in=os.path.join(test_folder, "PhaseSpaceIn.root"), - input_out=os.path.join(test_folder, "PhaseSpaceOut.root"), - output=os.path.join(test_folder, "pairs.mhd"), - plane_in=-110, - plane_out=110, - psin="PhaseSpaceIn", - psout="PhaseSpaceOut", -) - -im = itk.imread(os.path.join(test_folder, "pairs0000.mhd")) -assert np.asarray(im).shape == (96, 5, 3)