Skip to content

Commit 3fccb7e

Browse files
ENH: Add ParameterSerializer Support
With the paramater serializer support, one can now serialize and deserialize CLIs with the command line. The python test CLISerializationTest is added to make sure it works as expected.
1 parent 353fb83 commit 3fccb7e

File tree

5 files changed

+291
-1
lines changed

5 files changed

+291
-1
lines changed
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
#!/usr/bin/env python
2+
3+
#
4+
# Program: 3D Slicer
5+
#
6+
# Copyright (c) Kitware Inc.
7+
#
8+
# See COPYRIGHT.txt
9+
# or http://www.slicer.org/copyright/copyright.txt for details.
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
# This file was originally developed by Johan Andruejol, Kitware Inc.
18+
#
19+
20+
import argparse
21+
import json
22+
import os
23+
import sys
24+
25+
from SlicerAppTesting import *
26+
27+
"""
28+
Usage:
29+
CLISerializationTest.py
30+
/path/to/Slicer /path/to/CLIExecutables /path/to/data_dir /path/to/temp_dir
31+
"""
32+
class CLISerializationTest(object):
33+
def __init__(self):
34+
self.SlicerExecutable = None
35+
self.CLIDir = None
36+
self.DataDir = None
37+
self.TempDir = None
38+
self.CLIName = None
39+
self.CLIRequiredInputs = []
40+
self.CLIOptionnalInputs = []
41+
self.ExpectedJSON = None
42+
self.Success = False
43+
44+
def __del__(self):
45+
if self.Success:
46+
try:
47+
os.remove(self.JSONFilePath)
48+
except AttributeError, OSError:
49+
pass
50+
51+
def runTest(self):
52+
# Run the test of serializing/deserializing a CLI
53+
#slicer_base_dir = os.path.dirname(self.SlicerExecutable)
54+
55+
# Run and serialize the CLI
56+
self.JSONFilePath = '%s/%s.json' %(self.TempDir, self.CLIName)
57+
serialize_cli_args = self.CLIRequiredInputs
58+
serialize_cli_args.extend(self.CLIOptionnalInputs)
59+
60+
args = ['--launch',
61+
self.CLIName,
62+
'--serialize', self.JSONFilePath,
63+
]
64+
args.extend(serialize_cli_args)
65+
(returncode, serializeErr, serializeOut) = run(slicer_executable, args)
66+
if returncode != EXIT_SUCCESS:
67+
print("Problem while serializing the CLI: %s" %serializeErr)
68+
return EXIT_FAILURE
69+
70+
# Make sure the Json is generated correctly
71+
with open(self.JSONFilePath) as json_file:
72+
data = json.load(json_file)
73+
if data != self.ExpectedJSON:
74+
print('Json comparison failed !')
75+
return EXIT_FAILURE
76+
77+
# Now try to deserialize the CLI.
78+
deserialize_cli_args = self.CLIRequiredInputs
79+
args = ['--launch',
80+
self.CLIName,
81+
'--deserialize', self.JSONFilePath,
82+
]
83+
84+
(returncode, deserializeErr, deserializeOut) = run(slicer_executable, args)
85+
if returncode != EXIT_SUCCESS:
86+
print("Problem while deserializing the CLI: %s" %deserializeErr)
87+
return EXIT_FAILURE
88+
89+
# Compare the stdout to make sure the CLI ran the same.
90+
if deserializeOut != serializeOut:
91+
print("Serialize and deserialize stdout are different: %s != %s" %(serializeOut, deserializeOut))
92+
return EXIT_FAILURE
93+
if deserializeErr != serializeErr:
94+
print("Serialize and deserialize stderr are different: %s != %s" %(serializeErr, deserializeErr))
95+
return EXIT_FAILURE
96+
97+
self.Success = True
98+
return EXIT_SUCCESS
99+
100+
if __name__ == '__main__':
101+
102+
parser = argparse.ArgumentParser(description='Test command line CLIs serialization/deserialization.')
103+
# Common options
104+
parser.add_argument("/path/to/Slicer")
105+
parser.add_argument("/path/to/data_dir")
106+
parser.add_argument("/path/to/temp_dir")
107+
args = parser.parse_args()
108+
109+
# Get testing parameters
110+
slicer_executable = os.path.expanduser(getattr(args, "/path/to/Slicer"))
111+
data_dir = os.path.expanduser(getattr(args, "/path/to/data_dir"))
112+
temp_dir = os.path.expanduser(getattr(args, "/path/to/temp_dir"))
113+
114+
# -- Test ExecutionModelTour --
115+
EMTSerializer = CLISerializationTest()
116+
EMTSerializer.SlicerExecutable = slicer_executable
117+
EMTSerializer.DataDir = data_dir
118+
EMTSerializer.TempDir = temp_dir
119+
EMTSerializer.CLIName = 'ExecutionModelTour'
120+
EMTSerializer.CLIRequiredInputs = [
121+
'--transform1', '%s/ExecutionModelTourTest.mrml#vtkMRMLLinearTransformNode1'%(data_dir),
122+
'--transform2', '%s/ExecutionModelTourTest.mrml#vtkMRMLLinearTransformNode2'%(data_dir),
123+
'%s/MRHeadResampled.nhdr'%(data_dir),
124+
'%s/CTHeadAxial.nhdr'%(data_dir),
125+
]
126+
EMTSerializer.CLIOptionnalInputs = [
127+
'--integer', '30',
128+
'--double', '30',
129+
'-f', '1.3,2,-14',
130+
'--files', '1.does,2.not,3.matter',
131+
'--string_vector', 'foo,bar,foobar',
132+
'--enumeration', 'Bill',
133+
'--boolean1',
134+
]
135+
EMTSerializer.ExpectedJSON = {
136+
"Parameters" :
137+
{
138+
"Boolean Parameters" :
139+
{
140+
"boolean1" : True,
141+
"boolean2" : False,
142+
"boolean3" : False
143+
},
144+
"Enumeration Parameters" :
145+
{
146+
"stringChoice" : "Bill"
147+
},
148+
"File, Directory and Image Parameters" :
149+
{
150+
"directory1" : "",
151+
"file1" : "",
152+
"files" : [ "1.does", "2.not", "3.matter" ],
153+
"image1" : "",
154+
"image2" : "",
155+
"outputFile1" : "",
156+
"seed" : [],
157+
"seedsFile" : "",
158+
"seedsOutFile" : "",
159+
"transform1" : "%s/ExecutionModelTourTest.mrml#vtkMRMLLinearTransformNode1"%(data_dir),
160+
"transform2" : "%s/ExecutionModelTourTest.mrml#vtkMRMLLinearTransformNode2"%(data_dir)
161+
},
162+
"Generic Tables" :
163+
{
164+
"inputDT" : "",
165+
"outputDT" : ""
166+
},
167+
"Index Parameters" :
168+
{
169+
"arg0" : "%s/MRHeadResampled.nhdr"%(data_dir),
170+
"arg1" : "%s/CTHeadAxial.nhdr"%(data_dir)
171+
},
172+
"Measurements" :
173+
{
174+
"inputFA" : "",
175+
"outputFA" : ""
176+
},
177+
"Regions of interest" :
178+
{
179+
"regions" : []
180+
},
181+
"Scalar Parameters" :
182+
{
183+
"doubleVariable" : 30,
184+
"integerVariable" : 30
185+
},
186+
"Simple return types" :
187+
{
188+
"abooleanreturn" : False,
189+
"adoublereturn" : 14,
190+
"afloatreturn" : 7,
191+
"anintegerreturn" : 5,
192+
"anintegervectorreturn" : [],
193+
"astringchoicereturn" : "Bill",
194+
"astringreturn" : "Hello"
195+
},
196+
"Vector Parameters" :
197+
{
198+
"floatVector" : [ 1.2999999523162842, 2, -14 ],
199+
"stringVector" : [ "foo", "bar", "foobar" ]
200+
}
201+
}
202+
}
203+
204+
error = EMTSerializer.runTest()
205+
if error != EXIT_SUCCESS:
206+
print("Error with the execution model tour")
207+
exit(EXIT_FAILURE)
208+
209+
print("\n=> ok")
210+
exit(EXIT_SUCCESS)

Applications/SlicerApp/Testing/Python/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,17 @@ if(UNIX)
163163
)
164164
endif()
165165

166+
if(Slicer_BUILD_PARAMETERSERIALIZER_SUPPORT)
167+
add_test(
168+
NAME py_nomainwindow_CLISerializationTest
169+
COMMAND ${PYTHON_EXECUTABLE}
170+
${CMAKE_CURRENT_SOURCE_DIR}/CLISerializationTest.py
171+
${Slicer_LAUNCHER_EXECUTABLE}
172+
${Slicer_SOURCE_DIR}/Testing/Data/Input
173+
${Slicer_BINARY_DIR}/Testing/Temporary
174+
)
175+
endif()
176+
166177
#
167178
# Check if scripted module import works as expected
168179
#

CMake/vtkSlicerConfigure.h.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#cmakedefine Slicer_BUILD_DICOM_SUPPORT
6565
#cmakedefine Slicer_BUILD_DIFFUSION_SUPPORT
6666
#cmakedefine Slicer_BUILD_I18N_SUPPORT
67+
#cmakedefine Slicer_BUILD_PARAMETERSERIALIZER_SUPPORT
6768

6869
#cmakedefine Slicer_BUILD_CLI_SUPPORT
6970
#cmakedefine Slicer_BUILD_CLI
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
2+
set( proj ParameterSerializer )
3+
4+
# Set dependency list
5+
set(${proj}_DEPENDENCIES JsonCpp ITKv4)
6+
7+
# Include dependent projects if any
8+
ExternalProject_Include_Dependencies(${proj} PROJECT_VAR proj DEPENDS_VAR ${proj}_DEPENDENCIES)
9+
10+
# Sanity checks
11+
if(${CMAKE_PROJECT_NAME}_USE_SYSTEM_${proj})
12+
message(FATAL_ERROR "Enabling ${CMAKE_PROJECT_NAME}_USE_SYSTEM_${proj} is not supported !")
13+
endif()
14+
if(DEFINED ${proj}_DIR AND NOT EXISTS ${${proj}_DIR})
15+
message(FATAL_ERROR "${proj}_DIR variable is defined but corresponds to nonexistent directory")
16+
endif()
17+
18+
if(NOT DEFINED ${proj}_DIR AND NOT ${CMAKE_PROJECT_NAME}_USE_SYSTEM_${proj})
19+
20+
set(EXTERNAL_PROJECT_OPTIONAL_ARGS)
21+
22+
if(NOT DEFINED git_protocol)
23+
set(git_protocol "git")
24+
endif()
25+
26+
ExternalProject_Add(${proj}
27+
${${proj}_EP_ARGS}
28+
GIT_REPOSITORY "${git_protocol}://github.com/Slicer/ParameterSerializer.git"
29+
GIT_TAG "fd2836262413a2a5d2876d141eeaa7a17fe63f5c"
30+
SOURCE_DIR ${CMAKE_BINARY_DIR}/${proj}
31+
BINARY_DIR ${proj}-build
32+
CMAKE_CACHE_ARGS
33+
-DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER}
34+
-DCMAKE_CXX_FLAGS:STRING=${ep_common_cxx_flags}
35+
-DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER}
36+
-DCMAKE_C_FLAGS:STRING=${ep_common_c_flags} # Unused
37+
-DBUILD_TESTING:BOOL=OFF
38+
#-DBUILD_SHARED:BOOL=ON
39+
-DJsonCpp_DIR:PATH=${JsonCpp_DIR}
40+
-DITK_DIR:PATH=${ITK_DIR}
41+
${EXTERNAL_PROJECT_OPTIONAL_ARGS}
42+
INSTALL_COMMAND ""
43+
DEPENDS
44+
${${proj}_DEPENDENCIES}
45+
)
46+
set(${proj}_DIR ${CMAKE_BINARY_DIR}/${proj}-build)
47+
set(${proj}_SOURCE_DIR ${CMAKE_BINARY_DIR}/${proj})
48+
49+
#-----------------------------------------------------------------------------
50+
# Launcher setting specific to build tree
51+
52+
set(${proj}_LIBRARY_PATHS_LAUNCHER_BUILD ${${proj}_DIR}/lib/<CMAKE_CFG_INTDIR>)
53+
mark_as_superbuild(
54+
VARS ${proj}_LIBRARY_PATHS_LAUNCHER_BUILD
55+
LABELS "LIBRARY_PATHS_LAUNCHER_BUILD"
56+
)
57+
58+
else()
59+
ExternalProject_Add_Empty(${proj} DEPENDS ${${proj}_DEPENDENCIES})
60+
endif()
61+
62+
mark_as_superbuild(
63+
VARS ParameterSerializer_DIR:PATH
64+
LABELS "FIND_PACKAGE"
65+
)

SuperBuild/External_SlicerExecutionModel.cmake

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ set(proj SlicerExecutionModel)
55
set(${proj}_DEPENDENCIES ${ITK_EXTERNAL_NAME})
66

77
if(Slicer_BUILD_PARAMETERSERIALIZER_SUPPORT)
8-
set(${proj}_DEPENDENCIES ${${proj}_DEPENDENCIES} JsonCpp)
8+
set(${proj}_DEPENDENCIES ${${proj}_DEPENDENCIES} JsonCpp ParameterSerializer)
99
endif()
1010

1111
# Include dependent projects if any
@@ -57,6 +57,7 @@ if(NOT DEFINED SlicerExecutionModel_DIR AND NOT ${CMAKE_PROJECT_NAME}_USE_SYSTEM
5757

5858
if(Slicer_BUILD_PARAMETERSERIALIZER_SUPPORT)
5959
_set(JsonCpp_DIR PATH ${JsonCpp_DIR})
60+
_set(ParameterSerializer_DIR PATH ${ParameterSerializer_DIR})
6061
endif()
6162

6263
ExternalProject_Add(${proj}
@@ -72,6 +73,8 @@ if(NOT DEFINED SlicerExecutionModel_DIR AND NOT ${CMAKE_PROJECT_NAME}_USE_SYSTEM
7273
-DCMAKE_C_FLAGS:STRING=${ep_common_c_flags} # Unused
7374
-DBUILD_TESTING:BOOL=OFF
7475
-DITK_DIR:PATH=${ITK_DIR}
76+
-DGenerateCLP_USE_SERIALIZER:BOOL=${Slicer_BUILD_PARAMETERSERIALIZER_SUPPORT}
77+
-DModuleDescriptionParser_USE_SERIALIZER:BOOL=${Slicer_BUILD_PARAMETERSERIALIZER_SUPPORT}
7578
-DSlicerExecutionModel_USE_JSONCPP:BOOL=${Slicer_BUILD_PARAMETERSERIALIZER_SUPPORT}
7679
-DSlicerExecutionModel_LIBRARY_PROPERTIES:STRING=${Slicer_LIBRARY_PROPERTIES}
7780
-DSlicerExecutionModel_INSTALL_BIN_DIR:PATH=${Slicer_INSTALL_LIB_DIR}

0 commit comments

Comments
 (0)