diff --git a/Modules/Core/Common/wrapping/itkOptimizerParameters.wrap b/Modules/Core/Common/wrapping/itkOptimizerParameters.wrap index 30c9c13e06c..007f5183157 100644 --- a/Modules/Core/Common/wrapping/itkOptimizerParameters.wrap +++ b/Modules/Core/Common/wrapping/itkOptimizerParameters.wrap @@ -7,6 +7,10 @@ itk_end_wrap_class() itk_wrap_class("itk::OptimizerParameters") foreach(t ${types}) + # SetHelper stores the argument in a unique_ptr member. + string(APPEND ITK_WRAP_PYTHON_SWIG_EXT + "%apply SWIGTYPE *DISOWN { itkOptimizerParametersHelper${ITKM_${t}} * helper };\n") + itk_wrap_template("${ITKM_${t}}" "${ITKT_${t}}") endforeach() itk_end_wrap_class() diff --git a/Modules/Core/Common/wrapping/test/CMakeLists.txt b/Modules/Core/Common/wrapping/test/CMakeLists.txt index 4190c3e3694..a8ac538f04e 100644 --- a/Modules/Core/Common/wrapping/test/CMakeLists.txt +++ b/Modules/Core/Common/wrapping/test/CMakeLists.txt @@ -62,4 +62,9 @@ if(ITK_WRAP_PYTHON) itkImageLifetimePythonTest COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/itkImageLifetimeTest.py) + itk_python_add_test( + NAME + itkOptimizerParametersOwnershipPythonTest + COMMAND + ${CMAKE_CURRENT_SOURCE_DIR}/itkOptimizerParametersOwnershipTest.py) endif() diff --git a/Modules/Core/Common/wrapping/test/itkOptimizerParametersOwnershipTest.py b/Modules/Core/Common/wrapping/test/itkOptimizerParametersOwnershipTest.py new file mode 100644 index 00000000000..740c991563a --- /dev/null +++ b/Modules/Core/Common/wrapping/test/itkOptimizerParametersOwnershipTest.py @@ -0,0 +1,36 @@ +# ========================================================================== +# +# Copyright NumFOCUS +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ========================================================================== +"""OptimizerParameters.SetHelper takes ownership of its argument. + +Without a DISOWN typemap, Python and the m_Helper unique_ptr both free the helper, +and the interpreter aborts at shutdown. A non-zero exit code from this script is +itself part of the regression check. +""" + +import itk + +parameters = itk.OptimizerParameters[itk.D](3) +helper = itk.OptimizerParametersHelper[itk.D]() + +assert helper.thisown, "Python should own a freshly constructed helper" + +parameters.SetHelper(helper) + +assert not helper.thisown, "SetHelper must transfer ownership away from Python" + +print("Test finished.") diff --git a/Modules/Filtering/ImageGradient/wrapping/itkGradientImageFilter.wrap b/Modules/Filtering/ImageGradient/wrapping/itkGradientImageFilter.wrap index be96554cf79..91fb261c5b9 100644 --- a/Modules/Filtering/ImageGradient/wrapping/itkGradientImageFilter.wrap +++ b/Modules/Filtering/ImageGradient/wrapping/itkGradientImageFilter.wrap @@ -4,6 +4,10 @@ foreach(d ${ITK_WRAP_IMAGE_DIMS}) set(vector_dim ${d}) # Wrap only vector dimensions which are the same as image dimensions foreach(t ${WRAP_ITK_SCALAR}) + # OverrideBoundaryCondition stores the argument in a unique_ptr member. + string(APPEND ITK_WRAP_PYTHON_SWIG_EXT + "%apply SWIGTYPE *DISOWN { itkImageBoundaryCondition${ITKM_I${t}${vector_dim}} * boundaryCondition };\n") + if(ITK_WRAP_covariant_vector_float) itk_wrap_template("${ITKM_I${t}${vector_dim}}${ITKM_F}${ITKM_F}" "${ITKT_I${t}${vector_dim}},${ITKT_F},${ITKT_F}") endif() diff --git a/Modules/Filtering/ImageGradient/wrapping/test/CMakeLists.txt b/Modules/Filtering/ImageGradient/wrapping/test/CMakeLists.txt index ff6f12f4c98..611028479c7 100644 --- a/Modules/Filtering/ImageGradient/wrapping/test/CMakeLists.txt +++ b/Modules/Filtering/ImageGradient/wrapping/test/CMakeLists.txt @@ -20,4 +20,10 @@ if(ITK_WRAP_PYTHON DATA{${test_input_dir}/BrainProtonDensitySlice.png} ${ITK_TEST_OUTPUT_DIR}/GradientMagnitudeRecursiveGaussianImageFilterTest.png 5) + + itk_python_add_test( + NAME + itkGradientImageFilterOwnershipPythonTest + COMMAND + ${CMAKE_CURRENT_SOURCE_DIR}/itkGradientImageFilterOwnershipTest.py) endif() diff --git a/Modules/Filtering/ImageGradient/wrapping/test/itkGradientImageFilterOwnershipTest.py b/Modules/Filtering/ImageGradient/wrapping/test/itkGradientImageFilterOwnershipTest.py new file mode 100644 index 00000000000..323e5e485d9 --- /dev/null +++ b/Modules/Filtering/ImageGradient/wrapping/test/itkGradientImageFilterOwnershipTest.py @@ -0,0 +1,57 @@ +# ========================================================================== +# +# Copyright NumFOCUS +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ========================================================================== +"""GradientImageFilter.OverrideBoundaryCondition takes ownership of its argument. + +Without a DISOWN typemap, Python and the filter's unique_ptr member both free the +boundary condition, and the interpreter aborts at shutdown. A non-zero exit code +from this script is itself part of the regression check. +""" + +import itk + +itk.auto_progress(2) + +ImageType = itk.Image[itk.F, 2] + +filt = itk.GradientImageFilter[ImageType, itk.F, itk.F].New() +boundaryCondition = itk.PeriodicBoundaryCondition[ImageType]() + +assert ( + boundaryCondition.thisown +), "Python should own a freshly constructed boundary condition" + +filt.OverrideBoundaryCondition(boundaryCondition) + +assert ( + not boundaryCondition.thisown +), "OverrideBoundaryCondition must transfer ownership away from Python" + +# The filter must remain usable with the adopted boundary condition. +image = ImageType.New() +region = itk.ImageRegion[2]() +region.SetSize([8, 8]) +image.SetRegions(region) +image.Allocate() +image.FillBuffer(1.0) + +filt.SetInput(image) +filt.Update() + +assert filt.GetOutput().GetLargestPossibleRegion().GetSize()[0] == 8 + +print("Test finished.")