Commit be75a19a authored by Utkarsh Ayachit's avatar Utkarsh Ayachit
Browse files

Added a new module, vtkPythonInterpreter.

VTK now includes a Utilities module, vtkPythonInterpreter that provides a Python
interpreter. While its use it totally optional, it helps in applications with
embedded Python interpreter, such as ParaView.

vtkMatplotlibMathTextUtilities now uses vtkPythonInterpreter to initialize
Python. This enables applications to setup Python environment as needed witout
directly having to hack environment variables to setup matplotlib environment.

Since vtkPythonInterpreter allows applications to initialize/finalize Python
interpreter mutliple times, fixed vtkPythonCommand to cleanup
PyObject/PyThreadState pointers when Python interpreter is exitted.

Change-Id: I80ed56fc3f64dda2766a6aa2e6e92c3aa7290993
parent a7d31f98
......@@ -4,7 +4,7 @@ vtk_module(vtkRenderingMatplotlib
DEPENDS
vtkImagingCore
vtkRenderingCore
vtkPython
vtkPythonInterpreter
TEST_DEPENDS
vtkCommonColor
vtkInteractionImage
......
......@@ -12,21 +12,10 @@
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkPython.h" // must be the first thing that's included.
#include "vtkMatplotlibMathTextUtilities.h"
// Prevent redefined symbol warnings in vtkPython.h:
#ifdef _POSIX_C_SOURCE
# undef _POSIX_C_SOURCE
#endif // _POSIX_C_SOURCE
#ifdef _XOPEN_SOURCE
# undef _XOPEN_SOURCE
#endif // _XOPEN_SOURCE
#ifdef _LARGEFILE_SOURCE
# undef _LARGEFILE_SOURCE
#endif // _LARGEFILE_SOURCE
#include "vtkPython.h"
#include "vtkCommand.h"
#include "vtkImageData.h"
#include "vtkImageReslice.h"
#include "vtkMath.h"
......@@ -34,6 +23,7 @@
#include "vtkObjectFactory.h"
#include "vtkPath.h"
#include "vtkPoints.h"
#include "vtkPythonInterpreter.h"
#include "vtkStdString.h"
#include "vtkTextProperty.h"
#include "vtkTransform.h"
......@@ -46,9 +36,9 @@
#if PYTHON_API_VERSION < 1013
// Taken from pyport.h
# ifdef HAVE_SSIZE_T
typedef ssize_t Py_ssize_t;
typedef ssize_t Py_ssize_t;
# elif SIZEOF_VOID_P == SIZEOF_SIZE_T
typedef Py_intptr_t Py_ssize_t;
typedef Py_intptr_t Py_ssize_t;
# else
# error "Python needs a typedef for Py_ssize_t."
# endif
......@@ -84,250 +74,152 @@ public:
//----------------------------------------------------------------------------
vtkMatplotlibMathTextUtilities::Availablity
vtkMatplotlibMathTextUtilities::MPLMathTextAvailable =
vtkMatplotlibMathTextUtilities::NOT_TESTED;
bool vtkMatplotlibMathTextUtilities::InitializedPython = false;
vtkMatplotlibMathTextUtilities::NOT_TESTED;
// A macro that is used in New() to print warnings if VTK_MATPLOTLIB_DEBUG
// is defined in the environment. Use warnings to allow this to work in
// release mode builds. This is adapted from vtkWarningWithObjectMacro
#define vtkMplStartUpDebugMacro(x) \
if (debug) \
{ \
if (vtkObject::GetGlobalWarningDisplay()) \
{ \
vtkOStreamWrapper::EndlType endl; \
vtkOStreamWrapper::UseEndl(endl); \
vtkOStrStreamWrapper vtkmsg; \
vtkmsg << "Warning: In " __FILE__ ", line " << __LINE__ \
<< "\nvtkMatplotlibMathTextUtilities::New(): " \
<< x << "\n\n"; \
vtkOutputWindowDisplayWarningText(vtkmsg.str()); \
vtkmsg.rdbuf()->freeze(0); \
} \
}
// is defined in the environment. Use vtkGenericWarningMacro to allow this to
// work in release mode builds.
#define vtkMplStartUpDebugMacro(x) if(debug){vtkGenericWarningMacro(x);}
namespace {
//----------------------------------------------------------------------------
// Used to replace "\ " with " " in paths.
void UnEscapeSpaces(std::string &str)
{
size_t pos = str.rfind("\\ ");
while (pos != std::string::npos)
//----------------------------------------------------------------------------
// Used to replace "\ " with " " in paths.
void UnEscapeSpaces(std::string &str)
{
str.erase(pos, 1);
pos = str.rfind("\\ ", pos);
size_t pos = str.rfind("\\ ");
while (pos != std::string::npos)
{
str.erase(pos, 1);
pos = str.rfind("\\ ", pos);
}
}
}
} // end anon namespace
//----------------------------------------------------------------------------
vtkMatplotlibMathTextUtilities* vtkMatplotlibMathTextUtilities::New()
void vtkMatplotlibMathTextUtilities::CheckMPLAvailability()
{
if (vtkMatplotlibMathTextUtilities::MPLMathTextAvailable != NOT_TESTED)
{
// Already tested. Nothing to do now.
return;
}
// Enable startup debugging output. This will be set to true when
// VTK_MATPLOTLIB_DEBUG is defined in the process environment.
bool debug = false;
bool debug = (vtksys::SystemTools::GetEnv("VTK_MATPLOTLIB_DEBUG") != NULL);
// Initialize the python interpretor if needed
vtkMplStartUpDebugMacro("Initializing Python, if not already.");
vtkPythonInterpreter::Initialize();
vtkMplStartUpDebugMacro("Attempting to import matplotlib.");
if (PyErr_Occurred() || !PyImport_ImportModule("matplotlib") || PyErr_Occurred())
{
// FIXME: Check if we need this. Wouldn't pipe-ing the stdout/stderr make
// this unnecessary?
// Fetch the exception info. Note that value and traceback may still be
// NULL after the call to PyErr_Fetch().
PyObject *type = NULL;
PyObject *value = NULL;
PyObject *traceback = NULL;
PyErr_Fetch(&type, &value, &traceback);
SmartPyObject typeStr(PyObject_Str(type));
SmartPyObject valueStr(PyObject_Str(value));
SmartPyObject tracebackStr(PyObject_Str(traceback));
vtkMplStartUpDebugMacro(
"Error during matplotlib import:\n"
<< "\nStack:\n"
<< (tracebackStr.GetPointer() == NULL
? "(none)"
: const_cast<char*>(
PyString_AsString(tracebackStr.GetPointer())))
<< "\nValue:\n"
<< (valueStr.GetPointer() == NULL
? "(none)"
: const_cast<char*>(
PyString_AsString(valueStr.GetPointer())))
<< "\nType:\n"
<< (typeStr.GetPointer() == NULL
? "(none)"
: const_cast<char*>(
PyString_AsString(typeStr.GetPointer()))));
PyErr_Clear();
vtkMatplotlibMathTextUtilities::MPLMathTextAvailable = UNAVAILABLE;
}
else
{
vtkMplStartUpDebugMacro("Successfully imported matplotlib.");
vtkMatplotlibMathTextUtilities::MPLMathTextAvailable = AVAILABLE;
}
}
//----------------------------------------------------------------------------
vtkMatplotlibMathTextUtilities* vtkMatplotlibMathTextUtilities::New()
{
vtkMatplotlibMathTextUtilities::CheckMPLAvailability();
// Attempt to import matplotlib to check for availability
switch (vtkMatplotlibMathTextUtilities::MPLMathTextAvailable)
{
default:
case vtkMatplotlibMathTextUtilities::UNAVAILABLE:
return NULL;
case vtkMatplotlibMathTextUtilities::AVAILABLE:
break;
case vtkMatplotlibMathTextUtilities::NOT_TESTED:
case vtkMatplotlibMathTextUtilities::AVAILABLE:
break;
// Print setup details if this env flag is set. The debug variable is used
// by the vtkMplStartUpDebugMacro.
debug = (vtksys::SystemTools::GetEnv("VTK_MATPLOTLIB_DEBUG") != NULL);
// Initialize the python interpretor if needed
vtkMplStartUpDebugMacro("Testing if matplotlib is already init'd.");
if (!Py_IsInitialized())
{
// Check for a specified interpreter in the system environment.
vtkMplStartUpDebugMacro("Not initialized. Checking "
"VTK_MATPLOTLIB_PYTHONHOME.");
vtkStdString mplPyHome;
if (vtksys::SystemTools::GetEnv("VTK_MATPLOTLIB_PYTHONHOME",
mplPyHome) &&
mplPyHome.size() != 0)
{
UnEscapeSpaces(mplPyHome);
vtkMplStartUpDebugMacro("VTK_MATPLOTLIB_PYTHONHOME="<<mplPyHome);
Py_SetPythonHome(const_cast<char*>(mplPyHome.c_str()));
}
else
{
vtkMplStartUpDebugMacro("VTK_MATPLOTLIB_PYTHONHOME undefined.");
}
vtkMplStartUpDebugMacro("Checking VTK_MATPLOTLIB_PYTHONINTERP.");
vtkStdString mplPyInterp;
if (vtksys::SystemTools::GetEnv("VTK_MATPLOTLIB_PYTHONINTERP",
mplPyInterp) &&
mplPyInterp.size() != 0)
{
UnEscapeSpaces(mplPyInterp);
vtkMplStartUpDebugMacro("VTK_MATPLOTLIB_PYTHONINTERP="<<mplPyInterp);
Py_SetProgramName(const_cast<char*>(mplPyInterp.c_str()));
}
else
{
vtkMplStartUpDebugMacro("VTK_MATPLOTLIB_PYTHONINTERP undefined.");
}
vtkMplStartUpDebugMacro("Initializing python. (if there is a segfault "
"and error 'ImportError: No module named site',"
" VTK_MATPLOTLIB_PYTHONHOME is incorrect).");
Py_InitializeEx(0);
vtkMatplotlibMathTextUtilities::InitializedPython = true;
}
if (!Py_IsInitialized())
{
vtkMplStartUpDebugMacro("Python environment failed to initialize. "
"Matplotlib will be unavailable.");
vtkMatplotlibMathTextUtilities::MPLMathTextAvailable = UNAVAILABLE;
return NULL;
}
// The call to Py_InitializeEx(0) should disable signal handlers, but
// for some reason SIGINT is still handled (and ignored) by the threading
// module. This works around that issue.
vtkMplStartUpDebugMacro("Disabling interrupt signal handlers.");
PyRun_SimpleString("import signal;"
"signal.signal(signal.SIGINT, signal.SIG_DFL);");
vtkMplStartUpDebugMacro("Python environment initialized. Checking "
"VTK_MATPLOTLIB_PYTHONPATH.");
PyObject *pypath = PySys_GetObject(const_cast<char*>("path"));
vtkStdString envPaths;
if (vtksys::SystemTools::GetEnv("VTK_MATPLOTLIB_PYTHONPATH",
envPaths) && envPaths.size() != 0)
{
vtkMplStartUpDebugMacro("VTK_MATPLOTLIB_PYTHONPATH=" << envPaths);
// Split paths and prepend them to the python path object.
#ifdef _WIN32
char delim = ';';
#else // _WIN32
char delim = ':';
#endif // _WIN32
size_t pathEnd = vtkStdString::npos;
size_t pathStart = envPaths.rfind(delim, pathEnd);
while (pathStart != vtkStdString::npos)
{
vtkStdString envPath(envPaths, pathStart + 1,
pathEnd == vtkStdString::npos
? vtkStdString::npos : pathEnd - pathStart);
UnEscapeSpaces(envPath);
PyList_Insert(pypath, 0, PyString_FromString(envPath.c_str()));
pathEnd = pathStart - 1;
pathStart = envPaths.rfind(delim, pathEnd);
}
vtkStdString envPath(envPaths, 0,
pathEnd == vtkStdString::npos
? vtkStdString::npos : pathEnd + 1);
UnEscapeSpaces(envPath);
PyList_Insert(pypath, 0, PyString_FromString(envPath.c_str()));
}
else
{
vtkMplStartUpDebugMacro("VTK_MATPLOTLIB_PYTHONPATH undefined.");
}
SmartPyObject pypathStr = PyObject_Str(pypath);
vtkMplStartUpDebugMacro("sys.path =\n"
<< PyString_AsString(pypathStr.GetPointer()));
vtkMplStartUpDebugMacro("Attempting to import matplotlib.");
if (PyErr_Occurred() ||
!PyImport_ImportModule("matplotlib") ||
PyErr_Occurred())
{
// Fetch the exception info. Note that value and traceback may still be
// NULL after the call to PyErr_Fetch().
PyObject *type = NULL;
PyObject *value = NULL;
PyObject *traceback = NULL;
PyErr_Fetch(&type, &value, &traceback);
SmartPyObject typeStr(PyObject_Str(type));
SmartPyObject valueStr(PyObject_Str(value));
SmartPyObject tracebackStr(PyObject_Str(traceback));
vtkMplStartUpDebugMacro(
"Error during matplotlib import:\n"
<< "\nStack:\n"
<< (tracebackStr.GetPointer() == NULL
? "(none)"
: const_cast<char*>(
PyString_AsString(tracebackStr.GetPointer())))
<< "\nValue:\n"
<< (valueStr.GetPointer() == NULL
? "(none)"
: const_cast<char*>(
PyString_AsString(valueStr.GetPointer())))
<< "\nType:\n"
<< (typeStr.GetPointer() == NULL
? "(none)"
: const_cast<char*>(
PyString_AsString(typeStr.GetPointer()))));
PyErr_Clear();
vtkMatplotlibMathTextUtilities::MPLMathTextAvailable = UNAVAILABLE;
if (vtkMatplotlibMathTextUtilities::InitializedPython)
{
Py_Finalize();
}
return NULL;
}
else
{
vtkMplStartUpDebugMacro("Successfully imported matplotlib.");
vtkMatplotlibMathTextUtilities::MPLMathTextAvailable = AVAILABLE;
}
break;
case vtkMatplotlibMathTextUtilities::NOT_TESTED:
case vtkMatplotlibMathTextUtilities::UNAVAILABLE:
default:
return NULL;
}
// Adapted from VTK_OBJECT_FACTORY_NEW_BODY to enable debugging output when
// requested.
vtkObject* ret =
vtkObjectFactory::CreateInstance("vtkMatplotlibMathTextUtilities");
if(ret)
vtkObjectFactory::CreateInstance("vtkMatplotlibMathTextUtilities");
if (ret)
{
ret->SetDebug(debug);
return static_cast<vtkMatplotlibMathTextUtilities*>(ret);
}
return new vtkMatplotlibMathTextUtilities;
}
vtkInstantiatorNewMacro(vtkMatplotlibMathTextUtilities)
vtkInstantiatorNewMacro(vtkMatplotlibMathTextUtilities)
//----------------------------------------------------------------------------
vtkMatplotlibMathTextUtilities::vtkMatplotlibMathTextUtilities()
: Superclass(), MaskParser(NULL), PathParser(NULL), FontPropertiesClass(NULL),
ScaleToPowerOfTwo(true)
{
this->Interpreter = vtkPythonInterpreter::New();
this->Interpreter->AddObserver(vtkCommand::ExitEvent,
this, &vtkMatplotlibMathTextUtilities::CleanupPythonObjects);
}
//----------------------------------------------------------------------------
vtkMatplotlibMathTextUtilities::~vtkMatplotlibMathTextUtilities()
{
this->CleanupPythonObjects();
this->Interpreter->Delete();
}
//----------------------------------------------------------------------------
void vtkMatplotlibMathTextUtilities::CleanupPythonObjects()
{
Py_XDECREF(this->MaskParser);
Py_XDECREF(this->PathParser);
Py_XDECREF(this->FontPropertiesClass);
if(vtkMatplotlibMathTextUtilities::InitializedPython)
{
Py_Finalize();
}
this->MaskParser = NULL;
this->PathParser = NULL;
this->FontPropertiesClass = NULL;
}
//----------------------------------------------------------------------------
bool vtkMatplotlibMathTextUtilities::InitializeMaskParser()
{
// ensure that Python is initialized.
vtkPythonInterpreter::Initialize();
SmartPyObject mplMathTextLib(PyImport_ImportModule("matplotlib.mathtext"));
if (this->CheckForError(mplMathTextLib.GetPointer()))
{
......@@ -356,6 +248,9 @@ bool vtkMatplotlibMathTextUtilities::InitializeMaskParser()
//----------------------------------------------------------------------------
bool vtkMatplotlibMathTextUtilities::InitializePathParser()
{
// ensure that Python is initialized.
vtkPythonInterpreter::Initialize();
SmartPyObject mplTextPathLib(PyImport_ImportModule("matplotlib.textpath"));
if (this->CheckForError(mplTextPathLib.GetPointer()))
{
......@@ -382,6 +277,9 @@ bool vtkMatplotlibMathTextUtilities::InitializePathParser()
//----------------------------------------------------------------------------
bool vtkMatplotlibMathTextUtilities::InitializeFontPropertiesClass()
{
// ensure that Python is initialized.
vtkPythonInterpreter::Initialize();
SmartPyObject mplFontManagerLib(
PyImport_ImportModule("matplotlib.font_manager"));
if (this->CheckForError(mplFontManagerLib.GetPointer()))
......
......@@ -17,18 +17,10 @@
// vtkMatplotlibMathTextUtilities provides access to the MatPlotLib MathText
// implementation.
//
// This class is aware of a number of enviroment variables that can be used to
// This class is aware of a number of environment variables that can be used to
// configure and debug python initialization (all are optional):
// - VTK_MATPLOTLIB_DEBUG: Enable verbose debugging output during initialization
// of the python environment.
// - VTK_MATPLOTLIB_PYTHONINTERP: Path to the python interpreter. This will be
// passed to Py_SetProgramName prior to calling Py_Initialize.
// - VTK_MATPLOTLIB_PYTHONHOME: See the Python documentation on the PYTHONHOME
// environment variable. This will be passed to Py_SetPythonHome prior to
// calling Py_Initialize.
// - VTK_MATPLOTLIB_PYTHONPATH: A list of additional python module paths to be
// prepended to the sys.path object after initialization. Use ';' on windows and
// ':' on apple/linux to separate multiple paths.
#ifndef __vtkMatplotlibMathTextUtilities_h
#define __vtkMatplotlibMathTextUtilities_h
......@@ -40,6 +32,7 @@ struct _object;
typedef struct _object PyObject;
class vtkImageData;
class vtkPath;
class vtkPythonInterpreter;
class vtkTextProperty;
class VTKRENDERINGMATPLOTLIB_EXPORT vtkMatplotlibMathTextUtilities :
......@@ -94,6 +87,13 @@ protected:
// the vtkTextProperty tprop.
PyObject * GetFontProperties(vtkTextProperty *tprop);
// Description:
// Cleanup and destroy any python objects. This is called during destructor as
// well as when the Python interpreter is finalized. Thus this class must
// handle the case where the internal python objects disappear between calls.
void CleanupPythonObjects();
vtkPythonInterpreter* Interpreter;
PyObject *MaskParser;
PyObject *PathParser;
PyObject *FontPropertiesClass;
......@@ -110,19 +110,20 @@ protected:
AVAILABLE,
UNAVAILABLE
};
static Availablity MPLMathTextAvailable;
bool ScaleToPowerOfTwo;
bool PrepareImageData(vtkImageData *data, int bbox[4]);
// Description:
// Set to true if this class initialized the python interpreter. This
// is used to determine if Py_Finalize() should be called when destructed.
static bool InitializedPython;
// Function used to check MPL availability and update MPLMathTextAvailable.
// This will do tests only the first time this method is called.
static void CheckMPLAvailability();
private:
vtkMatplotlibMathTextUtilities(const vtkMatplotlibMathTextUtilities&); // Not implemented.
void operator=(const vtkMatplotlibMathTextUtilities&); // Not implemented.
static Availablity MPLMathTextAvailable;
};
#endif
set (Module_SRCS
vtkPythonInterpreter.cxx
vtkPythonInteractiveInterpreter.cxx
)
set (${vtk-module}_HDRS
vtkPythonStdStreamCaptureHelper.h)
vtk_module_library(vtkPythonInterpreter
${Module_SRCS})
vtk_module(vtkPythonInterpreter
DEPENDS
vtkCommonCore
vtkPython
vtksys
)
/*=========================================================================
Program: Visualization Toolkit
Module: vtkPythonInteractiveInterpreter.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkPython.h"
#include "vtkPythonInteractiveInterpreter.h"
#include "vtkObjectFactory.h"
#include "vtkSmartPointer.h"
#include "vtkCommand.h"
#include "vtkPythonInterpreter.h"
#include <string>
class vtkPythonInteractiveInterpreter::vtkInternals
{
PyObject* InteractiveConsole;
PyObject* InteractiveConsoleLocals;
std::string PS1;
std::string PS2;
public:
vtkSmartPointer<vtkPythonInterpreter> Interpreter;
vtkInternals() : InteractiveConsole(0), InteractiveConsoleLocals(0) {}
~vtkInternals()
{
this->CleanupPythonObjects();
}
PyObject* GetInteractiveConsolePyObject()
{ return this->InteractiveConsole; }
PyObject* GetInteractiveConsoleLocalsPyObject()
{ return this->InteractiveConsoleLocals; }
void CleanupPythonObjects()
{
Py_XDECREF(this->InteractiveConsoleLocals);
Py_XDECREF(this->InteractiveConsole);
this->InteractiveConsole = NULL;
this->InteractiveConsoleLocals = NULL;
if (vtkPythonInterpreter::IsInitialized())
{
const char* code = "import gc; gc.collect()\n";
vtkPythonInterpreter::RunSimpleString(code);
}
}
PyObject* GetInteractiveConsole()
{
if (this->InteractiveConsole)
{
return this->InteractiveConsole;
}
vtkPythonInterpreter::Initialize();
// set up the code.InteractiveConsole instance that we'll use.
const char* code = "import code\n"
"__vtkConsoleLocals={'__name__':'__vtkconsole__','__doc__':None}\n"
"__vtkConsole=code.InteractiveConsole(__vtkConsoleLocals)\n";
// The cast is necessary because PyRun_SimpleString() hasn't always been
// const-correct
PyRun_SimpleString(const_cast<char*>(code));
// Now get the reference to __vtkConsole and save the pointer.
PyObject* main_module = PyImport_AddModule((char*)"__main__");
PyObject* global_dict = PyModule_GetDict(main_module);
this->InteractiveConsole = PyDict_GetItemString(global_dict, "__vtkConsole");
this->InteractiveConsoleLocals = PyDict_GetItemString(global_dict, "__vtkConsoleLocals");
if (!this->InteractiveConsole || !this->InteractiveConsoleLocals)
{
vtkGenericWarningMacro(
"Failed to locate the InteractiveConsole/InteractiveConsoleLocals object.");
return NULL;
}
Py_INCREF(this->InteractiveConsole);
Py_INCREF(this->InteractiveConsoleLocals);
PyRun_SimpleString(
const_cast<char*>("del __vtkConsole; del __vtkConsoleLocals"));
// Maybe we need an API to enable developers to set the prompts.
PyObject* ps1 = PySys_GetObject(const_cast<char*>("ps1"));