Documentation/Nightly/Developers/Tutorials/MigrationGuide/SlicerExtension

From Slicer Wiki
Revision as of 23:17, 5 November 2019 by Jamesobutler (talk | contribs) (Added dicom to pydicom migration note)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
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)