fazekasgy@37: /*
fazekasgy@37: 
fazekasgy@37:  * Vampy : This plugin is a wrapper around the Vamp plugin API.
fazekasgy@37:  * It allows for writing Vamp plugins in Python.
fazekasgy@37: 
fazekasgy@37:  * Centre for Digital Music, Queen Mary University of London.
fazekasgy@37:  * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources 
fazekasgy@37:  * for licence information.)
fazekasgy@37: 
fazekasgy@37: */
fazekasgy@37: 
fazekasgy@37: #include <Python.h>
fazekasgy@37: #include "PyPlugin.h"
fazekasgy@37: #include "PyTypeInterface.h"
fazekasgy@37: #include <stdlib.h>
fazekasgy@37: #include "PyExtensionModule.h"
Chris@67: #include "Debug.h"
fazekasgy@37: 
fazekasgy@37: #ifdef _WIN32
fazekasgy@37: #define PATHSEP ('\\')
fazekasgy@37: #else 
fazekasgy@37: #define PATHSEP ('/')
fazekasgy@37: #endif
fazekasgy@37: 
fazekasgy@37: using std::string;
fazekasgy@37: using std::vector;
fazekasgy@37: using std::cerr;
fazekasgy@37: using std::endl;
fazekasgy@37: using std::map;
fazekasgy@37: 
fazekasgy@37: Mutex PyPlugin::m_pythonInterpreterMutex;
fazekasgy@37: 
fazekasgy@51: PyPlugin::PyPlugin(std::string pluginKey, float inputSampleRate, PyObject *pyClass, int &instcount, bool &numpyInstalled) :
fazekasgy@37: 	Plugin(inputSampleRate),
fazekasgy@37: 	m_pyClass(pyClass),
fazekasgy@37: 	m_instcount(instcount),
fazekasgy@37: 	m_stepSize(0),
fazekasgy@37: 	m_blockSize(0),
fazekasgy@37: 	m_channels(0),
fazekasgy@37: 	m_plugin(pluginKey),
fazekasgy@37: 	m_class(pluginKey.substr(pluginKey.rfind(':')+1,pluginKey.size()-1)),
fazekasgy@37: 	m_path((pluginKey.substr(0,pluginKey.rfind(PATHSEP)))),
fazekasgy@37: 	m_processType(not_implemented),
fazekasgy@37: 	m_pyProcess(NULL),
fazekasgy@37: 	m_inputDomain(TimeDomain),
fazekasgy@37: 	m_quitOnErrorFlag(false),
fazekasgy@51: 	m_debugFlag(false),
fazekasgy@51: 	m_numpyInstalled(numpyInstalled),
fazekasgy@51: 	m_processFailure(false)
fazekasgy@37: {	
fazekasgy@37: 	m_ti.setInputSampleRate(inputSampleRate);
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
Chris@67: 	DSTREAM << "Creating instance " << m_instcount << " of " << pluginKey << endl;
fazekasgy@37: 		
fazekasgy@37: 	// Create an instance
fazekasgy@37: 	Py_INCREF(m_pyClass);
fazekasgy@37: 	PyObject *pyInputSampleRate = PyFloat_FromDouble(inputSampleRate);
fazekasgy@37: 	PyObject *args = PyTuple_Pack(1, pyInputSampleRate);
fazekasgy@37: 	m_pyInstance = PyObject_Call(m_pyClass, args, NULL);
fazekasgy@37: 	
fazekasgy@37: 	if (!m_pyInstance || PyErr_Occurred()) { 
fazekasgy@37: 		if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
fazekasgy@37: 		Py_DECREF(m_pyClass);
fazekasgy@37: 		Py_CLEAR(args);
fazekasgy@37: 		Py_CLEAR(pyInputSampleRate);
fazekasgy@37: 		cerr << "PyPlugin::PyPlugin: Failed to create Python plugin instance for key \"" 
Chris@89: 		     << pluginKey << "\" (is the 1-arg class constructor from sample rate correctly provided?)" << endl;
fazekasgy@37: 		throw std::string("Constructor failed");
fazekasgy@37: 	}
Chris@89: 
fazekasgy@37: 	Py_INCREF(m_pyInstance);
fazekasgy@37: 	Py_DECREF(args);
fazekasgy@37: 	Py_DECREF(pyInputSampleRate);
fazekasgy@37: 	
fazekasgy@37: 	m_instcount++;
fazekasgy@37: 	
fazekasgy@37: 	// query and decode vampy flags
fazekasgy@37: 	m_vampyFlags = getBinaryFlags("vampy_flags",vf_NULL);
fazekasgy@37: 
fazekasgy@37: 	m_debugFlag = (bool) (m_vampyFlags & vf_DEBUG);
fazekasgy@37: 	m_quitOnErrorFlag = (bool) (m_vampyFlags & vf_QUIT);
fazekasgy@37: 	bool st_flag = (bool) (m_vampyFlags & vf_STRICT);
fazekasgy@37: 	m_useRealTimeFlag = (bool) (m_vampyFlags & vf_REALTIME);
fazekasgy@37: 		
fazekasgy@37: 	if (m_debugFlag) cerr << "Debug messages ON for Vampy plugin: " << m_class << endl;
Chris@67: 	else DSTREAM << "Debug messages OFF for Vampy plugin: " << m_class << endl;
fazekasgy@37: 	
fazekasgy@37: 	if (m_debugFlag && m_quitOnErrorFlag) cerr << "Quit on type error ON for: " << m_class << endl;
fazekasgy@37:    
fazekasgy@37: 	if (m_debugFlag && st_flag) cerr << "Strict type conversion ON for: " << m_class << endl;
fazekasgy@37: 	m_ti.setStrictTypingFlag(st_flag);
fazekasgy@51: 	m_ti.setNumpyInstalled(m_numpyInstalled);
fazekasgy@37: 
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: PyPlugin::~PyPlugin()
fazekasgy@37: {
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 	m_instcount--;
fazekasgy@37: 	// cerr << "Deleting plugin instance. Count: " << m_instcount << endl;
fazekasgy@37: 	
fazekasgy@37: 	if (m_pyInstance) Py_DECREF(m_pyInstance);
fazekasgy@37: 	//we increase the class refcount before creating an instance 
fazekasgy@37: 	if (m_pyClass) Py_DECREF(m_pyClass); 
fazekasgy@37: 	if (m_pyProcess) Py_CLEAR(m_pyProcess);
fazekasgy@37: 
Chris@67: 	DSTREAM << "PyPlugin::PyPlugin:" << m_class << " instance " << m_instcount << " deleted." << endl;
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: string
fazekasgy@37: PyPlugin::getIdentifier() const
fazekasgy@37: {	
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 	string rString="vampy-xxx";
fazekasgy@37: 	if (!m_debugFlag) return genericMethodCall("getIdentifier",rString);
fazekasgy@37: 
fazekasgy@37: 	rString = genericMethodCall("getIdentifier",rString);
fazekasgy@37: 	if (rString == "vampy-xxx")
fazekasgy@37: 		cerr << "Warning: Plugin must return a unique identifier." << endl;
fazekasgy@37: 	return rString;
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: string
fazekasgy@37: PyPlugin::getName() const
fazekasgy@37: {
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 	string rString="VamPy Plugin (Noname)";
fazekasgy@37:     return genericMethodCall("getName",rString);
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: string
fazekasgy@37: PyPlugin::getDescription() const
fazekasgy@37: {
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 	string rString="Not given. (Hint: Implement getDescription method.)";
fazekasgy@37: 	return genericMethodCall("getDescription",rString);
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: 
fazekasgy@37: string
fazekasgy@37: PyPlugin::getMaker() const
fazekasgy@37: {
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 	string rString="VamPy Plugin.";
fazekasgy@37: 	return genericMethodCall("getMaker",rString);
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: int
fazekasgy@37: PyPlugin::getPluginVersion() const
fazekasgy@37: {
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 	size_t rValue=2;
fazekasgy@37: 	return genericMethodCall("getPluginVersion",rValue);
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: string
fazekasgy@37: PyPlugin::getCopyright() const
fazekasgy@37: {
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 	string rString="Licence information not available.";
fazekasgy@37: 	return genericMethodCall("getCopyright",rString);
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: 
fazekasgy@37: bool
fazekasgy@37: PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
fazekasgy@37: {
fazekasgy@37: 
fazekasgy@37: 	if (channels < getMinChannelCount() ||
fazekasgy@37: 	    channels > getMaxChannelCount()) return false;
fazekasgy@37: 
fazekasgy@37: 	m_inputDomain = getInputDomain();
fazekasgy@37: 
fazekasgy@37: 	//Note: placing Mutex before the calls above causes deadlock !!
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 
fazekasgy@37: 	m_stepSize = stepSize;
fazekasgy@37: 	m_blockSize = blockSize;
fazekasgy@37: 	m_channels = channels;
fazekasgy@37: 
fazekasgy@37: 	//query the process implementation type
fazekasgy@37: 	//two optional flags can be used: 'use_numpy_interface' or 'use_legacy_interface'
fazekasgy@37: 	//if they are not provided, we fall back to the original method
fazekasgy@37: 	setProcessType();
fazekasgy@37: 	
fazekasgy@37: 	return genericMethodCallArgs<bool>("initialise",channels,stepSize,blockSize);
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: void
fazekasgy@37: PyPlugin::reset()
fazekasgy@37: {
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@51: 	m_processFailure = false;
fazekasgy@37: 	genericMethodCall("reset");
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: PyPlugin::InputDomain 
fazekasgy@37: PyPlugin::getInputDomain() const  
fazekasgy@37: { 
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 	return genericMethodCall("getInputDomain",m_inputDomain);
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: size_t 
fazekasgy@37: PyPlugin::getPreferredBlockSize() const 
fazekasgy@37: { 
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 	size_t rValue = 0;
fazekasgy@37: 	return genericMethodCall("getPreferredBlockSize",rValue); 
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: size_t 
fazekasgy@37: PyPlugin::getPreferredStepSize() const 
fazekasgy@37: { 
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 	size_t rValue = 0;
fazekasgy@37:     return genericMethodCall("getPreferredStepSize",rValue); 
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: size_t 
fazekasgy@37: PyPlugin::getMinChannelCount() const 
fazekasgy@37: { 
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 	size_t rValue = 1;
fazekasgy@37:     return genericMethodCall("getMinChannelCount",rValue); 
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: size_t 
fazekasgy@37: PyPlugin::getMaxChannelCount() const 
fazekasgy@37: { 
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 	size_t rValue = 1;
fazekasgy@37:     return genericMethodCall("getMaxChannelCount",rValue); 
fazekasgy@37: }	
fazekasgy@37: 
fazekasgy@37: PyPlugin::OutputList
fazekasgy@37: PyPlugin::getOutputDescriptors() const
fazekasgy@37: {
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 	OutputList list;
fazekasgy@37: 	return genericMethodCall("getOutputDescriptors",list);
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: PyPlugin::ParameterList
fazekasgy@37: PyPlugin::getParameterDescriptors() const
fazekasgy@37: {
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 	ParameterList list;
fazekasgy@37: #ifdef _DEBUG	
fazekasgy@37: 	///Note: This function is often called first by the host.
fazekasgy@37: 	if (!m_pyInstance) {cerr << "Error: pyInstance is NULL" << endl; return list;}
fazekasgy@37: #endif
fazekasgy@37: 
fazekasgy@37: 	return genericMethodCall("getParameterDescriptors",list);
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: void PyPlugin::setParameter(std::string paramid, float newval)
fazekasgy@37: {
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 	genericMethodCallArgs<NoneType>("setParameter",paramid,newval);
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: float PyPlugin::getParameter(std::string paramid) const
fazekasgy@37: {
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 	return genericMethodCallArgs<float>("getParameter",paramid);
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: #ifdef _DEBUG_VALUES
fazekasgy@37: static int proccounter = 0;
fazekasgy@37: #endif
fazekasgy@37: 
fazekasgy@37: PyPlugin::FeatureSet
fazekasgy@37: PyPlugin::process(const float *const *inputBuffers,Vamp::RealTime timestamp)
fazekasgy@37: {
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37: 
fazekasgy@37: #ifdef _DEBUG_VALUES
fazekasgy@37: 	/// we only need this if we'd like to see what frame a set of values belong to 
fazekasgy@37: 	cerr << "[Vampy::call] process, frame:" << proccounter << endl;
fazekasgy@37: 	proccounter++;
fazekasgy@37: #endif
fazekasgy@37: 
fazekasgy@37:     if (m_blockSize == 0 || m_channels == 0) {
fazekasgy@37: 	cerr << "ERROR: PyPlugin::process: "
fazekasgy@37: 	     << "Plugin has not been initialised" << endl;
fazekasgy@37: 	return FeatureSet();
fazekasgy@37:     }
fazekasgy@37: 
fazekasgy@37: 	if (m_processType == not_implemented) {
fazekasgy@37: 	cerr << "ERROR: In Python plugin [" << m_class   
fazekasgy@37: 		 << "] No process implementation found. Returning empty feature set." << endl;
fazekasgy@37: 	return FeatureSet();
fazekasgy@37: 	}
fazekasgy@37: 	
fazekasgy@51: 	if (m_processFailure) return FeatureSet();
fazekasgy@51: 	
fazekasgy@37: 	return processMethodCall(inputBuffers,timestamp);
fazekasgy@37: 
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: PyPlugin::FeatureSet
fazekasgy@37: PyPlugin::getRemainingFeatures()
fazekasgy@37: {
fazekasgy@37: 	MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@51: 	if (m_processFailure) return FeatureSet();
fazekasgy@37: 	FeatureSet rValue;
fazekasgy@37: 	return genericMethodCall("getRemainingFeatures",rValue); 
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: bool
Chris@66: PyPlugin::getBooleanFlag(const char flagName[], bool defValue = false) const
fazekasgy@37: {
fazekasgy@37: 	bool rValue = defValue;
fazekasgy@37: 	if (PyObject_HasAttrString(m_pyInstance,flagName))
fazekasgy@37: 	{
fazekasgy@37: 		PyObject *pyValue = PyObject_GetAttrString(m_pyInstance,flagName);
fazekasgy@37: 		if (!pyValue) 
fazekasgy@37: 		{
fazekasgy@37: 			if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
fazekasgy@37: 		} else {
fazekasgy@37: 			rValue = m_ti.PyValue_To_Bool(pyValue);
fazekasgy@37: 			if (m_ti.error) { 
fazekasgy@37: 				Py_CLEAR(pyValue);
fazekasgy@37: 				typeErrorHandler(flagName);
fazekasgy@37: 				rValue = defValue;
fazekasgy@37: 			} else Py_DECREF(pyValue);
fazekasgy@37: 		}
fazekasgy@37: 	}
fazekasgy@37: 	if (m_debugFlag) cerr << FLAG_VALUE << endl;
fazekasgy@37: 	return rValue;
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: int
Chris@66: PyPlugin::getBinaryFlags(const char flagName[], eVampyFlags defValue = vf_NULL) const
fazekasgy@37: {
fazekasgy@37: 	int rValue = defValue;
fazekasgy@37: 	if (PyObject_HasAttrString(m_pyInstance,flagName))
fazekasgy@37: 	{
fazekasgy@37: 		PyObject *pyValue = PyObject_GetAttrString(m_pyInstance,flagName);
fazekasgy@37: 		if (!pyValue) 
fazekasgy@37: 		{
fazekasgy@37: 			if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
fazekasgy@37: 		} else {
fazekasgy@37: 			rValue |= (int) m_ti.PyValue_To_Size_t(pyValue);
fazekasgy@37: 			if (m_ti.error) { 
fazekasgy@37: 				Py_CLEAR(pyValue);
fazekasgy@37: 				typeErrorHandler(flagName);
fazekasgy@37: 				rValue = defValue;
fazekasgy@37: 			} else Py_DECREF(pyValue);
fazekasgy@37: 		}
fazekasgy@37: 	}
fazekasgy@37: 	if (m_debugFlag) cerr << FLAG_VALUE << endl;
fazekasgy@37: 	return rValue;
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: 
fazekasgy@37: void
fazekasgy@37: PyPlugin::setProcessType()
fazekasgy@37: {
fazekasgy@37: 	//quering process implementation type
fazekasgy@37: 	char legacyMethod[]="process";
fazekasgy@37: 	char numpyMethod[]="processN";
fazekasgy@51: 	m_processFailure = false;
fazekasgy@37: 
fazekasgy@37: 	if (PyObject_HasAttrString(m_pyInstance,legacyMethod) &&
fazekasgy@37: 	    m_processType == 0) 
fazekasgy@37: 	{ 
fazekasgy@37: 		m_processType = legacyProcess;
fazekasgy@37: 		m_pyProcess = PyString_FromString(legacyMethod);
fazekasgy@37: 		m_pyProcessCallable = PyObject_GetAttr(m_pyInstance,m_pyProcess);
fazekasgy@37: 	}
fazekasgy@37: 
fazekasgy@37: 	if (PyObject_HasAttrString(m_pyInstance,numpyMethod) &&
fazekasgy@37: 	    m_processType == 0)
fazekasgy@37: 	{
fazekasgy@37: 		m_processType = numpy_bufferProcess;
fazekasgy@37: 		m_pyProcess = PyString_FromString(numpyMethod);
fazekasgy@37: 		m_pyProcessCallable = PyObject_GetAttr(m_pyInstance,m_pyProcess);
fazekasgy@37: 	}
fazekasgy@37: 
fazekasgy@37: 	// These flags are optional. If provided, they override the
fazekasgy@37: 	// implementation type making the use of the odd processN() 
fazekasgy@37: 	// function redundant.
fazekasgy@37: 	// However, the code above provides backward compatibility.
fazekasgy@37: 
fazekasgy@37: 	if (m_vampyFlags & vf_BUFFER) {
fazekasgy@37: 		m_processType = numpy_bufferProcess;
fazekasgy@37: 		if (m_debugFlag) cerr << "Process using (numpy) buffer interface." << endl;
fazekasgy@37: 	}
fazekasgy@37: 
fazekasgy@37:     if (m_vampyFlags & vf_ARRAY) {
fazekasgy@37: #ifdef HAVE_NUMPY
fazekasgy@51: 		if (m_numpyInstalled) { m_processType = numpy_arrayProcess;
fazekasgy@51: 			if (m_debugFlag) 
fazekasgy@51: 				cerr << "Process using numpy array interface." << endl;
fazekasgy@51: 		}
fazekasgy@51: 		else {
fazekasgy@51: 			m_processFailure = true;
fazekasgy@51: 			char method[]="initialise::setProcessType";
fazekasgy@51: 			cerr << PLUGIN_ERROR
fazekasgy@51: 			<< "This plugin requests the Numpy array interface by setting "
fazekasgy@51: 			<< " the vf_ARRAY flag in its __init__() function." << endl 
fazekasgy@51: 			<< "However, we could not found a version of Numpy compatible with this build of Vampy." << endl
fazekasgy@51: 			<< "If you have a numerical library installed that supports the buffer interface, " << endl
fazekasgy@51: 			<< "you can request this interface instead by setting the vf_BUFFER flag." << endl;
fazekasgy@51: 		}
fazekasgy@37: #else
fazekasgy@51: 		m_processFailure = true;
fazekasgy@51: 		char method[]="initialise::setProcessType";
fazekasgy@51: 		cerr << PLUGIN_ERROR
fazekasgy@51: 		<< "Error: This version of vampy was compiled without numpy support, "
fazekasgy@51: 		<< "however the vf_ARRAY flag is set for plugin: " << m_class << endl
fazekasgy@51: 		<< "The default behaviour is: passing a python list of samples for each channel in process() "
fazekasgy@51: 		<< "or a list of memory buffers in processN(). " << endl 
fazekasgy@51: 		<< "This can be used create numpy arrays using the numpy.frombuffer() command." << endl;
fazekasgy@37: #endif		
fazekasgy@37: 	}
fazekasgy@37: 	
fazekasgy@51: 	if (!m_pyProcessCallable)
fazekasgy@37: 	{
fazekasgy@37: 		m_processType = not_implemented;
fazekasgy@37: 		m_pyProcess = NULL;
fazekasgy@37: 		char method[]="initialise::setProcessType";
fazekasgy@37: 		cerr << PLUGIN_ERROR << " No process implementation found. Plugin will do nothing." << endl;
fazekasgy@51: 		m_processFailure = true;
fazekasgy@37: 	}
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: void
Chris@66: PyPlugin::typeErrorHandler(const char *method, bool process) const
fazekasgy@37: {
fazekasgy@37: 	bool strict = false;
fazekasgy@37: 	while (m_ti.error) { 
fazekasgy@37: 		PyTypeInterface::ValueError e = m_ti.getError();
fazekasgy@51: #ifdef HAVE_NUMPY
fazekasgy@51: 		// disable the process completely if numpy types are returned 
fazekasgy@51: 		// but a compatible version was not loaded.
fazekasgy@51: 		// This is required because if an object is returned from
fazekasgy@51: 		// the wrong build, malloc complains about its size
fazekasgy@51: 		// (i.e. the interpreter doesn't free it properly)
fazekasgy@51: 		// and the process may be leaking.
fazekasgy@51: 		// Note: this only happens in the obscure situation when
fazekasgy@51: 		// someone forces to return wrong numpy types from an 
fazekasgy@51: 		// incompatible version using the buffer interface.
fazekasgy@51: 		// In this case the incampatible library is still usable,
fazekasgy@51: 		// but manual conversion to python builtins is required.
fazekasgy@51: 		// If the ARRAY interface is set but Numpy is not installed
fazekasgy@51: 		// the process will be disabled already at initialisation.
fazekasgy@51: 		if (process && !m_numpyInstalled && e.str().find("numpy")!=std::string::npos) 
fazekasgy@51: 		{
fazekasgy@51: 			m_processFailure = true;
fazekasgy@51: 			cerr << "Warning: incompatible numpy type encountered. Disabling process." << endl;
fazekasgy@51: 		}
fazekasgy@51: #endif		
fazekasgy@37: 		cerr << PLUGIN_ERROR << e.str() << endl;
fazekasgy@37: 		if (e.strict) strict = true;
fazekasgy@37: 		// e.print();
fazekasgy@37: 	}
fazekasgy@37: 	/// quit on hard errors like accessing NULL pointers or strict type conversion
fazekasgy@37: 	/// errors IF the user sets the quitOnErrorFlag in the plugin.
fazekasgy@37: 	/// Otherwise most errors will go unnoticed apart from
fazekasgy@37: 	/// a messages in the terminal.
fazekasgy@37: 	/// It would be best if hosts could catch an exception instead
fazekasgy@37: 	/// and display something meaningful to the user.
fazekasgy@37: 	if (strict && m_quitOnErrorFlag) exit(EXIT_FAILURE);
fazekasgy@51: 
fazekasgy@51: 	// this would disable all outputs even if some are valid
fazekasgy@51: 	// if (process) m_processFailure = true;
fazekasgy@51: 	
fazekasgy@37: }
fazekasgy@37: