Parcourir la source

Fix memory leaks associated with ctkSimplePythonShell tests

* Calling "sys.exit()" within a python script ends up calling the
 function "std::exit()" [1] which terminate the application without calling
the destructor of variables with "automatic storage durations".

Considering the description of such variables [2], calling either
"std::exit()", "exit()" or "sys.exit(0)" from within python will prevent
the destruction of the QApplication singleton hence the proper destruction
of the object where the vtkObject have been instantiated (i.e. ctkTestWrappedVTKSlot and
ctkTestWrappedVTKQInvokable)


[1] http://en.cppreference.com/w/cpp/utility/program/exit
[2] http://en.cppreference.com/w/cpp/language/storage_duration


* To properly terminate the application, the solution consisted in
both running the test script after the event loop has been initiated and
calling "qt.QApplication.exit()" instead of "sys.exit()'. That said,
note that calling "sys.exit()"  in a pure python script is valid since
the implementation of "sys.exit()" takes care of doing the appropriate
cleaning before calling "exit()". [See Py_Finalize(), Py_Exit(), handle_system_exit]
in "pythonrun.c"

* An alternate solution could consist in using the "atexit" python module
and register a cleanup function. [3]

[3] http://docs.python.org/library/atexit.html
Jean-Christophe Fillion-Robin il y a 13 ans
Parent
commit
1da8d5498f

+ 7 - 7
Applications/ctkSimplePythonShell/Testing/Python/vtkPythonSmoke.py

@@ -3,19 +3,19 @@
 # Copied from VTK/Common/Testing/Python/PythonSmoke.py
 #
 
-import sys
+import qt
 
 try:
   import vtk
 
 except:
   print "Cannot import vtk"
-  sys.exit(1)
+  qt.QApplication.exit(1)
 try:
   print dir(vtk)
 except:
   print "Cannot print dir(vtk)"
-  sys.exit(1)
+  qt.QApplication.exit(1)
 
 try:
   try:
@@ -30,7 +30,7 @@ try:
     print "Using Common"
 except:
   print "Cannot create vtkObject"
-  sys.exit(1)
+  qt.QApplication.exit(1)
 
 try:
   print o
@@ -38,7 +38,7 @@ try:
   print "Class name: %s" % o.GetClassName()
 except:
   print "Cannot print object"
-  sys.exit(1)
+  qt.QApplication.exit(1)
 
 try:
   b = vtk.vtkObject()
@@ -46,7 +46,7 @@ try:
   print b, d
 except:
   print "Cannot downcast"
-  sys.exit(1)
+  qt.QApplication.exit(1)
 
-sys.exit(0)
+qt.QApplication.exit(0)
 

+ 6 - 6
Applications/ctkSimplePythonShell/Testing/Python/wrappedQInvokableTest.py

@@ -1,10 +1,10 @@
-import sys
+import qt
 
 if (_testWrappedQInvokableInstance.value() != 0):
-  sys.exit(1)
-  
+  qt.QApplication.exit(1)
+
 _testWrappedQInvokableInstance.setValue(74)
 if (_testWrappedQInvokableInstance.value() != 74):
-  sys.exit(1)
-  
-sys.exit(0)
+  qt.QApplication.exit(1)
+
+qt.QApplication.exit(0)

+ 6 - 6
Applications/ctkSimplePythonShell/Testing/Python/wrappedQPropertyTest.py

@@ -1,11 +1,11 @@
 
-import sys
+import qt
 
 if (_testWrappedQPropertyInstance.value != 0):
-  sys.exit(1)
-  
+  qt.QApplication.exit(1)
+
 _testWrappedQPropertyInstance.value = 74
 if (_testWrappedQPropertyInstance.value != 74):
-  sys.exit(1)
-  
-sys.exit(0)
+  qt.QApplication.exit(1)
+
+qt.QApplication.exit(0)

+ 6 - 6
Applications/ctkSimplePythonShell/Testing/Python/wrappedSlotTest.py

@@ -1,10 +1,10 @@
-import sys
+import qt
 
 if (_testWrappedSlotInstance.value() != 0):
-  sys.exit(1)
-  
+  qt.QApplication.exit(1)
+
 _testWrappedSlotInstance.setValue(74)
 if (_testWrappedSlotInstance.value() != 74):
-  sys.exit(1)
-  
-sys.exit(0)
+  qt.QApplication.exit(1)
+
+qt.QApplication.exit(0)

+ 5 - 5
Applications/ctkSimplePythonShell/Testing/Python/wrappedVTKQInvokableTest.py

@@ -1,7 +1,7 @@
 
-import sys
+import qt
 
-# Importing vtk initializes vtkPythonMap owned by vtkPythonUtil and prevent 
+# Importing vtk initializes vtkPythonMap owned by vtkPythonUtil and prevent
 # call to vtkPythonUtil::GetObjectFromPointer() from segfaulting.
 # PythonQt internally uses vtkPythonUtil to properly wrap/unwrap VTK objects
 from vtk import *
@@ -12,6 +12,6 @@ print t.GetClassName()
 t2 = vtkTable()
 _testWrappedVTKQInvokableInstance.setTable(t2)
 if _testWrappedVTKQInvokableInstance.getTable() != t2:
-  sys.exit(1)
-  
-sys.exit(0)
+  qt.QApplication.exit(1)
+
+qt.QApplication.exit(0)

+ 5 - 5
Applications/ctkSimplePythonShell/Testing/Python/wrappedVTKSlotTest.py

@@ -1,7 +1,7 @@
 
-import sys
+import qt
 
-# Importing vtk initializes vtkPythonMap owned by vtkPythonUtil and prevent 
+# Importing vtk initializes vtkPythonMap owned by vtkPythonUtil and prevent
 # call to vtkPythonUtil::GetObjectFromPointer() from segfaulting.
 # PythonQt internally uses vtkPythonUtil to properly wrap/unwrap VTK objects
 from vtk import *
@@ -12,6 +12,6 @@ print t.GetClassName()
 t2 = vtkTable()
 _testWrappedVTKSlotInstance.setTable(t2)
 if _testWrappedVTKSlotInstance.getTable() != t2:
-  sys.exit(1)
-  
-sys.exit(0)
+  qt.QApplication.exit(1)
+
+qt.QApplication.exit(0)

+ 30 - 7
Applications/ctkSimplePythonShell/ctkSimplePythonShellMain.cpp

@@ -21,8 +21,10 @@
 // Qt includes
 #include <QApplication>
 #include <QTextStream>
+#include <QTimer>
 
 // CTK includes
+#include <ctkCallback.h>
 #include <ctkPythonConsole.h>
 #include <ctkCommandLineParser.h>
 
@@ -39,6 +41,26 @@
 # include <vtkDebugLeaks.h>
 #endif
 
+namespace
+{
+//-----------------------------------------------------------------------------
+void executeScripts(void * data)
+{
+  ctkSimplePythonManager * pythonManager = reinterpret_cast<ctkSimplePythonManager*>(data);
+  QStringList scripts = pythonManager->property("scripts").toStringList();
+  foreach(const QString& script, scripts)
+    {
+    pythonManager->executeFile(script);
+    if (pythonManager->pythonErrorOccured())
+      {
+      QApplication::exit(EXIT_FAILURE);
+      }
+    }
+}
+
+} // end of anonymous namespace
+
+//-----------------------------------------------------------------------------
 int main(int argc, char** argv)
 {
 #ifdef CTK_WRAP_PYTHONQT_USE_VTK
@@ -75,9 +97,9 @@ int main(int argc, char** argv)
           << parser.helpText();
     return EXIT_SUCCESS;
   }
-  
+
   ctkSimplePythonManager pythonManager;
-  
+
   ctkPythonConsole console;
   console.initialize(&pythonManager);
   console.setAttribute(Qt::WA_QuitOnClose, true);
@@ -112,10 +134,11 @@ int main(int argc, char** argv)
   pythonManager.addObjectToPythonMain("_testWrappedQListOfVTKObjectInstance", &testWrappedQListOfVTKObject);
 #endif
 
-  foreach(const QString& script, parser.unparsedArguments())
-    {
-    pythonManager.executeFile(script);
-    }
-  
+  ctkCallback callback;
+  callback.setCallbackData(&pythonManager);
+  pythonManager.setProperty("scripts", parser.unparsedArguments());
+  callback.setCallback(executeScripts);
+  QTimer::singleShot(0, &callback, SLOT(invoke()));
+
   return app.exec();
 }