Difference between revisions of "Documentation/Nightly/Developers/Tutorials/MigrationGuide/SlicerExtension"

From Slicer Wiki
Jump to: navigation, search
(Added dicom to pydicom migration note)
(17 intermediate revisions by 2 users not shown)
Line 2: Line 2:
 
==Slicer Extension updates==
 
==Slicer Extension updates==
  
===Slicer 5.0: Supporting both ITKv4 and ITKv5===
+
===Slicer 5.0: ITKv4 to ITKv5===
  
If completely removing support for [[Documentation/Nightly/Developers/Tutorials/MigrationGuide#Transition_from_ITK4_to_ITK5|ITKv4 and only supporting ITKv5]] is not possible.  It is possible to conditionally include code.
+
To remove support for ITKv4 and only support ITKv5. See [[Documentation/Nightly/Developers/Tutorials/MigrationGuide#Transition_from_ITK4_to_ITK5]], it includes
 +
the following sections:
 +
* [[Documentation/Nightly/Developers/Tutorials/MigrationGuide#Upgrading_to_ITKv5_or_keep_using_ITKv4_GenerateThreadedData|Upgrading to ITKv5 or keep using ITKv4 GenerateThreadedData]]
 +
* [[Documentation/Nightly/Developers/Tutorials/MigrationGuide#itkMultiThreader_refactor|itkMultiThreader refactor]]
 +
* [[Documentation/Nightly/Developers/Tutorials/MigrationGuide#SimpleFastMutexLock.2C_FastMutexLock_and_MutexLock_are_deprecated|SimpleFastMutexLock, FastMutexLock and MutexLock are deprecated]]
  
Here are examples originally associated with [https://github.com/Slicer/Slicer/pull/1069 PR#1069 ITK v5 updates]
+
If completely removing support for ITKv4 and only supporting ITKv5 is not possible.  It is possible to conditionally include the ITKv4 code.
  
<pre>
+
For example:
#if ITK_VERSION_MAJOR >= 5
 
#include "itkMultiThreaderBase.h"
 
#else
 
#include "itkMultiThreader.h"
 
#endif
 
</pre>
 
  
 
<pre>
 
<pre>
Line 24: Line 22:
 
</pre>
 
</pre>
  
<pre>
+
Example of commits:
#if ITK_VERSION_MAJOR >= 5
+
* ResampleDTIlogEuclidean
  itk::ITK_THREAD_RETURN_DEFAULT_VALUE
+
** [https://github.com/NIRALUser/ResampleDTIlogEuclidean/commit/f779bf775aad3c4e1493ed8fa722b32623ac9561 ResampleDTIlogEuclidean@f779bf7]  COMP: Use std::mutex instead of deprecated ITK implementation
#else
+
** [https://github.com/NIRALUser/ResampleDTIlogEuclidean/commit/ed5093b15d0ded34db08978c6c5933c36e40b803 ResampleDTIlogEuclidean@ed5093b] BUG: ITKv5: Fix tests updating ITK filters to use ITKv5 dynamic multi-threading
  ITK_THREAD_RETURN_VALUE
+
* PETTumorSegmentation
#endif
+
** [https://github.com/QIICR/PETTumorSegmentation/pull/18/commits/4aead376ed1d909310d43496dd349e8f3b8c8210 PETTumorSegmentation PR#18] BUG: Add support for ITKv5 dynamic multithreader
</pre>
+
** [https://github.com/QIICR/PETTumorSegmentation/pull/18/commits/814a689501ab06d70d3ee3d561c14674e724a0d2 PETTumorSegmentation PR#18] COMP: Support ITKv5 refactored threading models
 
 
<pre>
 
#if ITK_VERSION_MAJOR >= 5
 
  this->ProcessingThreader = itk::PlatformMultiThreader::New();
 
#else
 
  this->ProcessingThreader = itk::MultiThreader::New();
 
#endif
 
</pre>
 
 
 
<pre>
 
#if ITK_VERSION_MAJOR >= 5
 
    (((itk::PlatformMultiThreader::WorkUnitInfo *)(arg))->UserData);
 
#else
 
    (((itk::MultiThreader::ThreadInfoStruct *)(arg))->UserData);
 
#endif
 
</pre>
 
 
 
<pre>
 
#if ITK_VERSION_MAJOR >= 5
 
  itk::PlatformMultiThreader::Pointer ProcessingThreader;
 
#else
 
  itk::MultiThreader::Pointer ProcessingThreader;
 
#endif
 
</pre>
 
 
 
<pre>
 
#if ITK_VERSION_MAJOR >= 5
 
  m_ThreadCount( MultiThreaderBase::GetGlobalDefaultNumberOfThreads() ),
 
#else
 
  m_ThreadCount( MultiThreader::GetGlobalDefaultNumberOfThreads() ),
 
#endif
 
</pre>
 
 
 
<pre>
 
#if ITK_VERSION_MAJOR >= 5
 
    m_And[threadId]->SetNumberOfWorkUnits( 1 );
 
#else
 
    m_And[threadId]->SetNumberOfThreads( 1 );
 
#endif
 
</pre>
 
  
 
===Slicer 5.0: Python2 to Python3===
 
===Slicer 5.0: Python2 to Python3===
Line 84: Line 42:
 
* [https://github.com/fedorov/MultiVolumeImporter/commit/f9917b237c3bc3255e3c7677dc9af351dc2325e1 MultiVolumeImporter@f9917b2] STYLE: Apply lib2to3.fixes.fix_idioms to support python3
 
* [https://github.com/fedorov/MultiVolumeImporter/commit/f9917b237c3bc3255e3c7677dc9af351dc2325e1 MultiVolumeImporter@f9917b2] STYLE: Apply lib2to3.fixes.fix_idioms to support python3
 
* [https://github.com/fedorov/MultiVolumeImporter/commit/3edd1bc593f178f11aa92d9baa62685bc96ac540 MultiVolumeImporter@3edd1bc] ENH: Support for Python3  
 
* [https://github.com/fedorov/MultiVolumeImporter/commit/3edd1bc593f178f11aa92d9baa62685bc96ac540 MultiVolumeImporter@3edd1bc] ENH: Support for Python3  
 +
* [https://github.com/slicersalt/ShapeVariationAnalyzer/commit/42343774c44dc78b8560fc0a9e33e3a6018cb6e1 ShapeVariationAnalyzer@4234377] STYLE: Update python scripts for python 3.x
 +
* [https://github.com/QIICR/PETTumorSegmentation/pull/18/commits/b298ab8e672e1ab52ae6b189a34d9930ccac19ba PETTumorSegmentation PR#18] BUG: Add support for Python 3
  
  
Line 96: Line 56:
 
${Slicer_DIR}/../python-install/bin/PythonSlicer -m pip install future
 
${Slicer_DIR}/../python-install/bin/PythonSlicer -m pip install future
 
</pre>
 
</pre>
<small>{{note}} Alternatively the <tt>future</tt> package could be installed in a regular python environment and used from there.</small>
 
 
</li>
 
</li>
 
<li>applying all fixes
 
<li>applying all fixes
Line 105: Line 64:
 
   done
 
   done
 
</pre>
 
</pre>
 +
 +
<small>{{note}} Alternatively the <tt>future</tt> package could be installed in a regular python environment and used from there.
 +
 +
<pre>
 +
  for f in `find ./ -name "*.py"`; do \
 +
    futurize --nobackups --write $f; \
 +
  done
 +
</pre>
 +
</small>
 +
 
</li>
 
</li>
 
</ol>
 
</ol>
Line 222: Line 191:
 
   done
 
   done
 
</pre>
 
</pre>
 +
 +
===Slicer 5.0: Python2 to Python3 (EditorEffect imports)===
 +
 +
'''Error:'''
 +
 +
<pre>
 +
    from EditorLib import EditorLib
 +
ImportError: cannot import name 'EditorLib'
 +
</pre>
 +
 +
Import of Editor classes should be updated. For example, see [http://viewvc.slicer.org/viewvc.cgi/Slicer4/trunk/Utilities/Templates/Modules/ScriptedEditorEffect/TemplateKeyEffect.py?r1=28122&r2=28121&pathrev=28122 r28122].
 +
 +
 +
'''Before:'''
 +
 +
<pre>
 +
import EditorLib
 +
from EditorLib.EditOptions import HelpButton
 +
from EditorLib.EditOptions import EditOptions
 +
from EditorLib import EditUtil
 +
</pre>
 +
 +
<pre>
 +
class TemplateKeyEffectOptions(EditorLib.LabelEffectOptions):
 +
  [...]
 +
</pre>
 +
 +
 +
'''After:'''
 +
 +
<pre>
 +
import EditorLib
 +
from EditorLib import EditOptions, HelpButton
 +
from EditorLib import EditUtil
 +
from EditorLib import LabelEffectOptions, LabelEffectTool, LabelEffectLogic, LabelEffect
 +
</pre>
 +
 +
<pre>
 +
class TemplateKeyEffectOptions(LabelEffectOptions):
 +
  [...]
 +
</pre>
 +
 +
===Slicer 5.0: Python packaging dicom to pydicom===
 +
 +
The [https://pypi.org/project/dicom/ dicom] python package has transitioned to become the [https://pypi.org/project/pydicom/ pydicom] python package. The dicom package is no longer included and all previous usages should be transitioned to use the pydicom package.  See the [https://pydicom.github.io/pydicom/stable/transition_to_pydicom1.html Transition to pydicom 1.x] migration guide.
 +
 +
===Slicer 4.9: Explicit include of ExternalProject module not needed anymore===
 +
 +
Following [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=26984 r26984], calling <tt>find_package(Slicer REQUIRED)</tt> and <tt>include(${Slicer_USE_FILE})</tt> ensure the <tt>ExternalProject</tt> CMake module is included.
 +
 +
Note that <tt>ExternalProjectDependency</tt> CMake module is also included.
 +
 +
===Slicer 4.9: Explicit passing of CMAKE_OSX_* variables not needed anymore===
 +
 +
Following [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=26983 r26983], calling <tt>find_package(Slicer REQUIRED)</tt> and <tt>include(${Slicer_USE_FILE})</tt> initializes <tt>CMAKE_OSX_*</tt> variables and ensures the variables <tt>CMAKE_OSX_ARCHITECTURES</tt>, <tt>CMAKE_OSX_SYSROOT</tt> and <tt>CMAKE_OSX_DEPLOYMENT_TARGET</tt> are passed to all external projects when configuring SuperBuild based extension.
  
 
===Slicer 4.9: Explicit initialization of CMAKE_BUILD_TYPE not needed anymore===
 
===Slicer 4.9: Explicit initialization of CMAKE_BUILD_TYPE not needed anymore===
Line 227: Line 251:
 
Following [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=26978 r26978], calling <tt>find_package(Slicer REQUIRED)</tt> and <tt>include(${Slicer_USE_FILE})</tt> initializes <tt>CMAKE_BUILD_TYPE</tt> and ensures the variables <tt>CMAKE_BUILD_TYPE</tt> and <tt>CMAKE_CONFIGURATION_TYPES</tt> are passed to all external projects when configuring SuperBuild based extension.
 
Following [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=26978 r26978], calling <tt>find_package(Slicer REQUIRED)</tt> and <tt>include(${Slicer_USE_FILE})</tt> initializes <tt>CMAKE_BUILD_TYPE</tt> and ensures the variables <tt>CMAKE_BUILD_TYPE</tt> and <tt>CMAKE_CONFIGURATION_TYPES</tt> are passed to all external projects when configuring SuperBuild based extension.
  
The module <tt>SlicerInitializeBuildType</tt> is automatically included in <tt>UserSlicer</tt>.
+
The module <tt>SlicerInitializeBuildType</tt> is automatically included in <tt>UseSlicer</tt> CMake module.
  
 
===Slicer 4.9: Subversion not required anymore===
 
===Slicer 4.9: Subversion not required anymore===

Revision as of 23:17, 5 November 2019

Home < Documentation < Nightly < Developers < Tutorials < MigrationGuide < SlicerExtension

Slicer Extension updates

Slicer 5.0: ITKv4 to ITKv5

To remove support for ITKv4 and only support ITKv5. See Documentation/Nightly/Developers/Tutorials/MigrationGuide#Transition_from_ITK4_to_ITK5, it includes the following sections:

If completely removing support for ITKv4 and only supporting ITKv5 is not possible. It is possible to conditionally include the ITKv4 code.

For example:

#if ITK_VERSION_MAJOR >= 5
  itk::ITK_THREAD_RETURN_TYPE
#else
  ITK_THREAD_RETURN_TYPE
#endif

Example of commits:

Slicer 5.0: Python2 to Python3

Depending on the complexity of the extension, two approaches shall be considered:

  • code base common to Slicer 4.10 and Slicer 5.0. This means backward compatibility with the latest release is maintained.
  • specific branch for each Slicer version (e.g master-4.10 and master)


Example of commits


Step 1

Understanding the scope of changes needed to support Python 3 can be done by:

  1. installing future package (see https://python-future.org/)
    Slicer_DIR=/path/to/Slicer-SuperBuild/Slicer-build
    ${Slicer_DIR}/../python-install/bin/PythonSlicer -m pip install future
    
  2. applying all fixes
      for f in `find ./ -name "*.py"`; do \
        ${Slicer_DIR}/../python-install/bin/PythonSlicer \
          --launch futurize --nobackups --write $f; \
      done
    

    Note Alternatively the future package could be installed in a regular python environment and used from there.

      for f in `find ./ -name "*.py"`; do \
        futurize --nobackups --write $f; \
      done
    

This first step will apply the following transformations:

lib2to3.fixes.fix_apply
lib2to3.fixes.fix_dict
lib2to3.fixes.fix_except
lib2to3.fixes.fix_exec
lib2to3.fixes.fix_exitfunc
lib2to3.fixes.fix_filter
lib2to3.fixes.fix_funcattrs
lib2to3.fixes.fix_getcwdu
lib2to3.fixes.fix_has_key
lib2to3.fixes.fix_input
lib2to3.fixes.fix_intern
lib2to3.fixes.fix_isinstance
lib2to3.fixes.fix_itertools
lib2to3.fixes.fix_itertools_imports
lib2to3.fixes.fix_long
lib2to3.fixes.fix_map
lib2to3.fixes.fix_methodattrs
lib2to3.fixes.fix_ne
lib2to3.fixes.fix_next
lib2to3.fixes.fix_nonzero
lib2to3.fixes.fix_numliterals
lib2to3.fixes.fix_operator
lib2to3.fixes.fix_paren
lib2to3.fixes.fix_raw_input
lib2to3.fixes.fix_reduce
lib2to3.fixes.fix_renamesSlicer migration guide describes how to update the code. See https://www.slicer.org/wiki/Documentation/Nightly/Developers/Tutorials/MigrationGuide/SlicerExtension#Slicer_5.0:_Python2_to_Python3
lib2to3.fixes.fix_repr
lib2to3.fixes.fix_standarderror
lib2to3.fixes.fix_sys_exc
lib2to3.fixes.fix_throw
lib2to3.fixes.fix_tuple_params
lib2to3.fixes.fix_types
lib2to3.fixes.fix_xreadlines
lib2to3.fixes.fix_zip
libfuturize.fixes.fix_absolute_import
libfuturize.fixes.fix_basestring
libfuturize.fixes.fix_cmp
libfuturize.fixes.fix_division_safe
libfuturize.fixes.fix_execfile
libfuturize.fixes.fix_future_builtins
libfuturize.fixes.fix_future_standard_library
libfuturize.fixes.fix_future_standard_library_urllib
libfuturize.fixes.fix_metaclass
libfuturize.fixes.fix_next_call
libfuturize.fixes.fix_object
libfuturize.fixes.fix_print_with_import
libfuturize.fixes.fix_raise
libfuturize.fixes.fix_unicode_keep_u
libfuturize.fixes.fix_xrange_with_import
libpasteurize.fixes.fix_newstyle

expect these two:

lib2to3.fixes.fix_idioms
lib2to3.fixes.fix_ws_comma

Note If the number of changes is large, you should consider applying each fixes independently.


Step 2

Not all changes applied automatically should be integrated. Most of the changes introducing imports from the future python package can simply be removed or removed after making use of try/except or check of sys.version_info[0].

  • Systematic conversion of keys, values or items from dictionaries do not need to be systematically converted from dict_keys/dict_values/dict_items to list. For example, this Slicer r28077 reverted some of the automatic changes applied by the future CLI.
  • Unless python classes implement the object functions next and __unicode__ specific to Python 2, nothing specific should be done and from builtins import object can be removed. Automatic removal of imports can also be automated doing:
 for f in `find ./ -name "*.py"`; do \
   sed -i '/from builtins import object/ d' $f; \
 done
  • Unless there are performances issue associated with using range in Python 2, the from builtins import range can be removed:
 for f in `find ./ -name "*.py"`; do \
   sed -i '/from builtins import range/ d' $f; \
 done
  • If there are performance issue with using range in Python 2, the following could also be done:
 import sys
 if sys.version_info[0] == 2:
   range = xrange
  • Use of aliases can also be avoided by removing
 from future import standard_library
 standard_library.install_aliases()

and instead doing something like this:

 try:
   import queue
 except ImportError:
   import Queue as queue

For a complete list aliases, see https://python-future.org/reference.html#module-future.standard_library

  • Use of old_div() function can generally be avoided by using int()

Step 3

The lib2to3.fixes.fix_idioms transformation should explicitly be applied:

  for f in `find ./ -name "*.py"`; do \
    ${Slicer_DIR}/../python-install/bin/PythonSlicer \
      --launch futurize -f lib2to3.fixes.fix_idioms --nobackups --write $f; \
  done

Slicer 5.0: Python2 to Python3 (EditorEffect imports)

Error:

    from EditorLib import EditorLib
ImportError: cannot import name 'EditorLib'

Import of Editor classes should be updated. For example, see r28122.


Before:

import EditorLib
from EditorLib.EditOptions import HelpButton
from EditorLib.EditOptions import EditOptions
from EditorLib import EditUtil
class TemplateKeyEffectOptions(EditorLib.LabelEffectOptions):
  [...]


After:

import EditorLib
from EditorLib import EditOptions, HelpButton
from EditorLib import EditUtil
from EditorLib import LabelEffectOptions, LabelEffectTool, LabelEffectLogic, LabelEffect
class TemplateKeyEffectOptions(LabelEffectOptions):
  [...]

Slicer 5.0: Python packaging dicom to pydicom

The dicom python package has transitioned to become the pydicom python package. The dicom package is no longer included and all previous usages should be transitioned to use the pydicom package. See the Transition to pydicom 1.x migration guide.

Slicer 4.9: Explicit include of ExternalProject module not needed anymore

Following r26984, calling find_package(Slicer REQUIRED) and include(${Slicer_USE_FILE}) ensure the ExternalProject CMake module is included.

Note that ExternalProjectDependency CMake module is also included.

Slicer 4.9: Explicit passing of CMAKE_OSX_* variables not needed anymore

Following r26983, calling find_package(Slicer REQUIRED) and include(${Slicer_USE_FILE}) initializes CMAKE_OSX_* variables and ensures the variables CMAKE_OSX_ARCHITECTURES, CMAKE_OSX_SYSROOT and CMAKE_OSX_DEPLOYMENT_TARGET are passed to all external projects when configuring SuperBuild based extension.

Slicer 4.9: Explicit initialization of CMAKE_BUILD_TYPE not needed anymore

Following r26978, calling find_package(Slicer REQUIRED) and include(${Slicer_USE_FILE}) initializes CMAKE_BUILD_TYPE and ensures the variables CMAKE_BUILD_TYPE and CMAKE_CONFIGURATION_TYPES are passed to all external projects when configuring SuperBuild based extension.

The module SlicerInitializeBuildType is automatically included in UseSlicer CMake module.

Slicer 4.9: Subversion not required anymore

Following r27060, Subversion is not required anymore.

Slicer 4.9: Support EP_GIT_PROTOCOL and use of ExternalProject_SetIfNotDefined for setting GIT_REPOSITORY, GIT_TAG and alike

Following r26957, extension may use the following convention to define GIT_REPOSITORY and GIT_TAG, this allows developer to override the value before the first configuration by setting the corresponding environment variable, or by explicitly configuring the project with that variable.

The option EP_GIT_PROTOCOL is also already set in ExternalProjectDependency module included by Slicer and its value is updated based on the <SUPERBUILD_TOPLEVEL_PROJECT>_USE_GIT_PROTOCOL option.

  ExternalProject_SetIfNotDefined(
    ${CMAKE_PROJECT_NAME}_${proj}_GIT_REPOSITORY
    "${EP_GIT_PROTOCOL}://github.com/jcfr/shape4D.git"
    QUIET
    )

  ExternalProject_SetIfNotDefined(
    ${CMAKE_PROJECT_NAME}_${proj}_GIT_TAG
    "12fef84ca2a56feffc59d8159bdadd2ce4a4138e" # slicersalt-2018-01-22-c74c766a4c
    QUIET
    )

See:

Slicer 4.9: Use ExternalProject_AlwaysConfigure to force reconfigure of inner project

Following r26551, the function ExternalProject_AlwaysConfigure may be used to ensure the inner project is always reconfigured.

Using the `BUILD_ALWAYS` option supported by ExternalProject_Add will not have the intended effect.

See https://cmake-artichoke.readthedocs.io/en/latest/ExternalProjectDependency.html#function:ExternalProject_AlwaysConfigure

Slicer 4.9: Specifying external projects to install in SuperBuild extension

Following r27267 and r27232, the following should be used to ensure the extension can also be bundled into a Slicer custom application. When bundled, the variable ${EXTENSION_NAME}_CPACK_INSTALL_CMAKE_PROJECTS is then used to update the application's list of project to install.

#-----------------------------------------------------------------------------
set(EXTENSION_CPACK_INSTALL_CMAKE_PROJECTS)
#list(APPEND EXTENSION_CPACK_INSTALL_CMAKE_PROJECTS "${Foo_DIR};Foo;RuntimeLibraries;/")
set(${EXTENSION_NAME}_CPACK_INSTALL_CMAKE_PROJECTS "${EXTENSION_CPACK_INSTALL_CMAKE_PROJECTS}" CACHE STRING "List of external projects to install" FORCE)

#-----------------------------------------------------------------------------
list(APPEND CPACK_INSTALL_CMAKE_PROJECTS "${CMAKE_BINARY_DIR};${EXTENSION_NAME};ALL;/")
list(APPEND CPACK_INSTALL_CMAKE_PROJECTS "${${EXTENSION_NAME}_CPACK_INSTALL_CMAKE_PROJECTS}")
include(${Slicer_EXTENSION_GENERATE_CONFIG})
include(${Slicer_EXTENSION_CPACK})

Slicer 4.9: Generating (Extension)Config.cmake

Initially introduced in r25944, and later improved in r25991, including ${Slicer_EXTENSION_GENERATE_CONFIG} ensure a config is generated and allow an extension to import targets from another extension by using find_package(ExtensionName REQUIRED).

[...]

include(${Slicer_EXTENSION_GENERATE_CONFIG})
include(${Slicer_EXTENSION_CPACK})

Slicer 4.9: Initializing <projectName>_BUILD_SLICER_EXTENSION option: Standalone vs Slicer extension build

The following snippet allows to automatically initialize <projectName>_BUILD_SLICER_EXTENSION to ON if Slicer_DIR is defined.

#-----------------------------------------------------------------------------
# Standalone vs Slicer extension option
#-----------------------------------------------------------------------------

# This option should be named after the project name, it corresponds to the
# option set to ON when the project is build by the Slicer Extension build
# system.

set(_default OFF)
set(_reason "${PROJECT_NAME}_BUILD_SLICER_EXTENSION is ON")
if(NOT DEFINED ${PROJECT_NAME}_BUILD_SLICER_EXTENSION AND DEFINED Slicer_DIR)
  set(_default ON)
  set(_reason "Slicer_DIR is SET")
endif()

option(${PROJECT_NAME}_BUILD_SLICER_EXTENSION "Build as a Slicer Extension" ${_default})

set(_msg "Checking if building as a Slicer extension")
message(STATUS ${_msg})
if(${PROJECT_NAME}_BUILD_SLICER_EXTENSION)
  message(STATUS "${_msg} - yes (${_reason})")
else()
  message(STATUS "${_msg} - no (${PROJECT_NAME}_BUILD_SLICER_EXTENSION is OFF)")
endif()
mark_as_superbuild(${PROJECT_NAME}_BUILD_SLICER_EXTENSION:BOOL)