annotate PyPlugin.cpp @ 53:7e59caea821b

* Make a better job of preloading Python, especially when it's in a framework. Go for the Python file in the frameworks directory in preference to any libpythonX.Y.dylib. Particularly, don't try to preload any library without an absolute path until we've exhausted all our framework possibilities (so as to avoid picking up an ancient system library).
author cannam
date Fri, 09 Oct 2009 13:48:25 +0000
parents c1e4f706ca9a
children 5664fe298af2
rev   line source
fazekasgy@37 1 /*
fazekasgy@37 2
fazekasgy@37 3 * Vampy : This plugin is a wrapper around the Vamp plugin API.
fazekasgy@37 4 * It allows for writing Vamp plugins in Python.
fazekasgy@37 5
fazekasgy@37 6 * Centre for Digital Music, Queen Mary University of London.
fazekasgy@37 7 * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources
fazekasgy@37 8 * for licence information.)
fazekasgy@37 9
fazekasgy@37 10 */
fazekasgy@37 11
fazekasgy@37 12 #include <Python.h>
fazekasgy@37 13 #include "PyPlugin.h"
fazekasgy@37 14 #include "PyTypeInterface.h"
fazekasgy@37 15 #include <stdlib.h>
fazekasgy@37 16 #include "PyExtensionModule.h"
fazekasgy@37 17
fazekasgy@37 18
fazekasgy@37 19 #ifdef _WIN32
fazekasgy@37 20 #define PATHSEP ('\\')
fazekasgy@37 21 #else
fazekasgy@37 22 #define PATHSEP ('/')
fazekasgy@37 23 #endif
fazekasgy@37 24
fazekasgy@37 25 using std::string;
fazekasgy@37 26 using std::vector;
fazekasgy@37 27 using std::cerr;
fazekasgy@37 28 using std::endl;
fazekasgy@37 29 using std::map;
fazekasgy@37 30
fazekasgy@37 31 Mutex PyPlugin::m_pythonInterpreterMutex;
fazekasgy@37 32
fazekasgy@51 33 PyPlugin::PyPlugin(std::string pluginKey, float inputSampleRate, PyObject *pyClass, int &instcount, bool &numpyInstalled) :
fazekasgy@37 34 Plugin(inputSampleRate),
fazekasgy@37 35 m_pyClass(pyClass),
fazekasgy@37 36 m_instcount(instcount),
fazekasgy@37 37 m_stepSize(0),
fazekasgy@37 38 m_blockSize(0),
fazekasgy@37 39 m_channels(0),
fazekasgy@37 40 m_plugin(pluginKey),
fazekasgy@37 41 m_class(pluginKey.substr(pluginKey.rfind(':')+1,pluginKey.size()-1)),
fazekasgy@37 42 m_path((pluginKey.substr(0,pluginKey.rfind(PATHSEP)))),
fazekasgy@37 43 m_processType(not_implemented),
fazekasgy@37 44 m_pyProcess(NULL),
fazekasgy@37 45 m_inputDomain(TimeDomain),
fazekasgy@37 46 m_quitOnErrorFlag(false),
fazekasgy@51 47 m_debugFlag(false),
fazekasgy@51 48 m_numpyInstalled(numpyInstalled),
fazekasgy@51 49 m_processFailure(false)
fazekasgy@37 50 {
fazekasgy@37 51 m_ti.setInputSampleRate(inputSampleRate);
fazekasgy@37 52 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 53 cerr << "Creating instance " << m_instcount << " of " << pluginKey << endl;
fazekasgy@37 54
fazekasgy@37 55 // Create an instance
fazekasgy@37 56 Py_INCREF(m_pyClass);
fazekasgy@37 57 PyObject *pyInputSampleRate = PyFloat_FromDouble(inputSampleRate);
fazekasgy@37 58 PyObject *args = PyTuple_Pack(1, pyInputSampleRate);
fazekasgy@37 59 m_pyInstance = PyObject_Call(m_pyClass, args, NULL);
fazekasgy@37 60
fazekasgy@37 61 if (!m_pyInstance || PyErr_Occurred()) {
fazekasgy@37 62 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
fazekasgy@37 63 Py_DECREF(m_pyClass);
fazekasgy@37 64 Py_CLEAR(args);
fazekasgy@37 65 Py_CLEAR(pyInputSampleRate);
fazekasgy@37 66 cerr << "PyPlugin::PyPlugin: Failed to create Python plugin instance for key \""
fazekasgy@37 67 << pluginKey << "\" (is the 1-arg class constructor from sample rate correctly provided?)" << endl;
fazekasgy@37 68 throw std::string("Constructor failed");
fazekasgy@37 69 }
fazekasgy@37 70 Py_INCREF(m_pyInstance);
fazekasgy@37 71 Py_DECREF(args);
fazekasgy@37 72 Py_DECREF(pyInputSampleRate);
fazekasgy@37 73
fazekasgy@37 74 m_instcount++;
fazekasgy@37 75
fazekasgy@37 76 // query and decode vampy flags
fazekasgy@37 77 m_vampyFlags = getBinaryFlags("vampy_flags",vf_NULL);
fazekasgy@37 78
fazekasgy@37 79 m_debugFlag = (bool) (m_vampyFlags & vf_DEBUG);
fazekasgy@37 80 m_quitOnErrorFlag = (bool) (m_vampyFlags & vf_QUIT);
fazekasgy@37 81 bool st_flag = (bool) (m_vampyFlags & vf_STRICT);
fazekasgy@37 82 m_useRealTimeFlag = (bool) (m_vampyFlags & vf_REALTIME);
fazekasgy@37 83
fazekasgy@37 84 if (m_debugFlag) cerr << "Debug messages ON for Vampy plugin: " << m_class << endl;
fazekasgy@37 85 else cerr << "Debug messages OFF for Vampy plugin: " << m_class << endl;
fazekasgy@37 86
fazekasgy@37 87 if (m_debugFlag && m_quitOnErrorFlag) cerr << "Quit on type error ON for: " << m_class << endl;
fazekasgy@37 88
fazekasgy@37 89 if (m_debugFlag && st_flag) cerr << "Strict type conversion ON for: " << m_class << endl;
fazekasgy@37 90 m_ti.setStrictTypingFlag(st_flag);
fazekasgy@51 91 m_ti.setNumpyInstalled(m_numpyInstalled);
fazekasgy@37 92
fazekasgy@37 93 }
fazekasgy@37 94
fazekasgy@37 95 PyPlugin::~PyPlugin()
fazekasgy@37 96 {
fazekasgy@37 97 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 98 m_instcount--;
fazekasgy@37 99 // cerr << "Deleting plugin instance. Count: " << m_instcount << endl;
fazekasgy@37 100
fazekasgy@37 101 if (m_pyInstance) Py_DECREF(m_pyInstance);
fazekasgy@37 102 //we increase the class refcount before creating an instance
fazekasgy@37 103 if (m_pyClass) Py_DECREF(m_pyClass);
fazekasgy@37 104 if (m_pyProcess) Py_CLEAR(m_pyProcess);
fazekasgy@37 105
fazekasgy@37 106 #ifdef _DEBUG
fazekasgy@37 107 cerr << "PyPlugin::PyPlugin:" << m_class << " instance " << m_instcount << " deleted." << endl;
fazekasgy@37 108 #endif
fazekasgy@37 109 }
fazekasgy@37 110
fazekasgy@37 111 string
fazekasgy@37 112 PyPlugin::getIdentifier() const
fazekasgy@37 113 {
fazekasgy@37 114 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 115 string rString="vampy-xxx";
fazekasgy@37 116 if (!m_debugFlag) return genericMethodCall("getIdentifier",rString);
fazekasgy@37 117
fazekasgy@37 118 rString = genericMethodCall("getIdentifier",rString);
fazekasgy@37 119 if (rString == "vampy-xxx")
fazekasgy@37 120 cerr << "Warning: Plugin must return a unique identifier." << endl;
fazekasgy@37 121 return rString;
fazekasgy@37 122 }
fazekasgy@37 123
fazekasgy@37 124 string
fazekasgy@37 125 PyPlugin::getName() const
fazekasgy@37 126 {
fazekasgy@37 127 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 128 string rString="VamPy Plugin (Noname)";
fazekasgy@37 129 return genericMethodCall("getName",rString);
fazekasgy@37 130 }
fazekasgy@37 131
fazekasgy@37 132 string
fazekasgy@37 133 PyPlugin::getDescription() const
fazekasgy@37 134 {
fazekasgy@37 135 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 136 string rString="Not given. (Hint: Implement getDescription method.)";
fazekasgy@37 137 return genericMethodCall("getDescription",rString);
fazekasgy@37 138 }
fazekasgy@37 139
fazekasgy@37 140
fazekasgy@37 141 string
fazekasgy@37 142 PyPlugin::getMaker() const
fazekasgy@37 143 {
fazekasgy@37 144 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 145 string rString="VamPy Plugin.";
fazekasgy@37 146 return genericMethodCall("getMaker",rString);
fazekasgy@37 147 }
fazekasgy@37 148
fazekasgy@37 149 int
fazekasgy@37 150 PyPlugin::getPluginVersion() const
fazekasgy@37 151 {
fazekasgy@37 152 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 153 size_t rValue=2;
fazekasgy@37 154 return genericMethodCall("getPluginVersion",rValue);
fazekasgy@37 155 }
fazekasgy@37 156
fazekasgy@37 157 string
fazekasgy@37 158 PyPlugin::getCopyright() const
fazekasgy@37 159 {
fazekasgy@37 160 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 161 string rString="Licence information not available.";
fazekasgy@37 162 return genericMethodCall("getCopyright",rString);
fazekasgy@37 163 }
fazekasgy@37 164
fazekasgy@37 165
fazekasgy@37 166 bool
fazekasgy@37 167 PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
fazekasgy@37 168 {
fazekasgy@37 169
fazekasgy@37 170 if (channels < getMinChannelCount() ||
fazekasgy@37 171 channels > getMaxChannelCount()) return false;
fazekasgy@37 172
fazekasgy@37 173 m_inputDomain = getInputDomain();
fazekasgy@37 174
fazekasgy@37 175 //Note: placing Mutex before the calls above causes deadlock !!
fazekasgy@37 176 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 177
fazekasgy@37 178 m_stepSize = stepSize;
fazekasgy@37 179 m_blockSize = blockSize;
fazekasgy@37 180 m_channels = channels;
fazekasgy@37 181
fazekasgy@37 182 //query the process implementation type
fazekasgy@37 183 //two optional flags can be used: 'use_numpy_interface' or 'use_legacy_interface'
fazekasgy@37 184 //if they are not provided, we fall back to the original method
fazekasgy@37 185 setProcessType();
fazekasgy@37 186
fazekasgy@37 187 return genericMethodCallArgs<bool>("initialise",channels,stepSize,blockSize);
fazekasgy@37 188 }
fazekasgy@37 189
fazekasgy@37 190 void
fazekasgy@37 191 PyPlugin::reset()
fazekasgy@37 192 {
fazekasgy@37 193 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@51 194 m_processFailure = false;
fazekasgy@37 195 genericMethodCall("reset");
fazekasgy@37 196 }
fazekasgy@37 197
fazekasgy@37 198 PyPlugin::InputDomain
fazekasgy@37 199 PyPlugin::getInputDomain() const
fazekasgy@37 200 {
fazekasgy@37 201 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 202 return genericMethodCall("getInputDomain",m_inputDomain);
fazekasgy@37 203 }
fazekasgy@37 204
fazekasgy@37 205 size_t
fazekasgy@37 206 PyPlugin::getPreferredBlockSize() const
fazekasgy@37 207 {
fazekasgy@37 208 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 209 size_t rValue = 0;
fazekasgy@37 210 return genericMethodCall("getPreferredBlockSize",rValue);
fazekasgy@37 211 }
fazekasgy@37 212
fazekasgy@37 213 size_t
fazekasgy@37 214 PyPlugin::getPreferredStepSize() const
fazekasgy@37 215 {
fazekasgy@37 216 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 217 size_t rValue = 0;
fazekasgy@37 218 return genericMethodCall("getPreferredStepSize",rValue);
fazekasgy@37 219 }
fazekasgy@37 220
fazekasgy@37 221 size_t
fazekasgy@37 222 PyPlugin::getMinChannelCount() const
fazekasgy@37 223 {
fazekasgy@37 224 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 225 size_t rValue = 1;
fazekasgy@37 226 return genericMethodCall("getMinChannelCount",rValue);
fazekasgy@37 227 }
fazekasgy@37 228
fazekasgy@37 229 size_t
fazekasgy@37 230 PyPlugin::getMaxChannelCount() const
fazekasgy@37 231 {
fazekasgy@37 232 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 233 size_t rValue = 1;
fazekasgy@37 234 return genericMethodCall("getMaxChannelCount",rValue);
fazekasgy@37 235 }
fazekasgy@37 236
fazekasgy@37 237 PyPlugin::OutputList
fazekasgy@37 238 PyPlugin::getOutputDescriptors() const
fazekasgy@37 239 {
fazekasgy@37 240 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 241 OutputList list;
fazekasgy@37 242 return genericMethodCall("getOutputDescriptors",list);
fazekasgy@37 243 }
fazekasgy@37 244
fazekasgy@37 245 PyPlugin::ParameterList
fazekasgy@37 246 PyPlugin::getParameterDescriptors() const
fazekasgy@37 247 {
fazekasgy@37 248 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 249 ParameterList list;
fazekasgy@37 250 #ifdef _DEBUG
fazekasgy@37 251 ///Note: This function is often called first by the host.
fazekasgy@37 252 if (!m_pyInstance) {cerr << "Error: pyInstance is NULL" << endl; return list;}
fazekasgy@37 253 #endif
fazekasgy@37 254
fazekasgy@37 255 return genericMethodCall("getParameterDescriptors",list);
fazekasgy@37 256 }
fazekasgy@37 257
fazekasgy@37 258 void PyPlugin::setParameter(std::string paramid, float newval)
fazekasgy@37 259 {
fazekasgy@37 260 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 261 genericMethodCallArgs<NoneType>("setParameter",paramid,newval);
fazekasgy@37 262 }
fazekasgy@37 263
fazekasgy@37 264 float PyPlugin::getParameter(std::string paramid) const
fazekasgy@37 265 {
fazekasgy@37 266 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 267 return genericMethodCallArgs<float>("getParameter",paramid);
fazekasgy@37 268 }
fazekasgy@37 269
fazekasgy@37 270 #ifdef _DEBUG_VALUES
fazekasgy@37 271 static int proccounter = 0;
fazekasgy@37 272 #endif
fazekasgy@37 273
fazekasgy@37 274 PyPlugin::FeatureSet
fazekasgy@37 275 PyPlugin::process(const float *const *inputBuffers,Vamp::RealTime timestamp)
fazekasgy@37 276 {
fazekasgy@37 277 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@37 278
fazekasgy@37 279 #ifdef _DEBUG_VALUES
fazekasgy@37 280 /// we only need this if we'd like to see what frame a set of values belong to
fazekasgy@37 281 cerr << "[Vampy::call] process, frame:" << proccounter << endl;
fazekasgy@37 282 proccounter++;
fazekasgy@37 283 #endif
fazekasgy@37 284
fazekasgy@37 285 if (m_blockSize == 0 || m_channels == 0) {
fazekasgy@37 286 cerr << "ERROR: PyPlugin::process: "
fazekasgy@37 287 << "Plugin has not been initialised" << endl;
fazekasgy@37 288 return FeatureSet();
fazekasgy@37 289 }
fazekasgy@37 290
fazekasgy@37 291 if (m_processType == not_implemented) {
fazekasgy@37 292 cerr << "ERROR: In Python plugin [" << m_class
fazekasgy@37 293 << "] No process implementation found. Returning empty feature set." << endl;
fazekasgy@37 294 return FeatureSet();
fazekasgy@37 295 }
fazekasgy@37 296
fazekasgy@51 297 if (m_processFailure) return FeatureSet();
fazekasgy@51 298
fazekasgy@37 299 return processMethodCall(inputBuffers,timestamp);
fazekasgy@37 300
fazekasgy@37 301 }
fazekasgy@37 302
fazekasgy@37 303 PyPlugin::FeatureSet
fazekasgy@37 304 PyPlugin::getRemainingFeatures()
fazekasgy@37 305 {
fazekasgy@37 306 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@51 307 if (m_processFailure) return FeatureSet();
fazekasgy@37 308 FeatureSet rValue;
fazekasgy@37 309 return genericMethodCall("getRemainingFeatures",rValue);
fazekasgy@37 310 }
fazekasgy@37 311
fazekasgy@37 312 bool
fazekasgy@37 313 PyPlugin::getBooleanFlag(char flagName[], bool defValue = false) const
fazekasgy@37 314 {
fazekasgy@37 315 bool rValue = defValue;
fazekasgy@37 316 if (PyObject_HasAttrString(m_pyInstance,flagName))
fazekasgy@37 317 {
fazekasgy@37 318 PyObject *pyValue = PyObject_GetAttrString(m_pyInstance,flagName);
fazekasgy@37 319 if (!pyValue)
fazekasgy@37 320 {
fazekasgy@37 321 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
fazekasgy@37 322 } else {
fazekasgy@37 323 rValue = m_ti.PyValue_To_Bool(pyValue);
fazekasgy@37 324 if (m_ti.error) {
fazekasgy@37 325 Py_CLEAR(pyValue);
fazekasgy@37 326 typeErrorHandler(flagName);
fazekasgy@37 327 rValue = defValue;
fazekasgy@37 328 } else Py_DECREF(pyValue);
fazekasgy@37 329 }
fazekasgy@37 330 }
fazekasgy@37 331 if (m_debugFlag) cerr << FLAG_VALUE << endl;
fazekasgy@37 332 return rValue;
fazekasgy@37 333 }
fazekasgy@37 334
fazekasgy@37 335 int
fazekasgy@37 336 PyPlugin::getBinaryFlags(char flagName[], eVampyFlags defValue = vf_NULL) const
fazekasgy@37 337 {
fazekasgy@37 338 int rValue = defValue;
fazekasgy@37 339 if (PyObject_HasAttrString(m_pyInstance,flagName))
fazekasgy@37 340 {
fazekasgy@37 341 PyObject *pyValue = PyObject_GetAttrString(m_pyInstance,flagName);
fazekasgy@37 342 if (!pyValue)
fazekasgy@37 343 {
fazekasgy@37 344 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
fazekasgy@37 345 } else {
fazekasgy@37 346 rValue |= (int) m_ti.PyValue_To_Size_t(pyValue);
fazekasgy@37 347 if (m_ti.error) {
fazekasgy@37 348 Py_CLEAR(pyValue);
fazekasgy@37 349 typeErrorHandler(flagName);
fazekasgy@37 350 rValue = defValue;
fazekasgy@37 351 } else Py_DECREF(pyValue);
fazekasgy@37 352 }
fazekasgy@37 353 }
fazekasgy@37 354 if (m_debugFlag) cerr << FLAG_VALUE << endl;
fazekasgy@37 355 return rValue;
fazekasgy@37 356 }
fazekasgy@37 357
fazekasgy@37 358
fazekasgy@37 359 void
fazekasgy@37 360 PyPlugin::setProcessType()
fazekasgy@37 361 {
fazekasgy@37 362 //quering process implementation type
fazekasgy@37 363 char legacyMethod[]="process";
fazekasgy@37 364 char numpyMethod[]="processN";
fazekasgy@51 365 m_processFailure = false;
fazekasgy@37 366
fazekasgy@37 367 if (PyObject_HasAttrString(m_pyInstance,legacyMethod) &&
fazekasgy@37 368 m_processType == 0)
fazekasgy@37 369 {
fazekasgy@37 370 m_processType = legacyProcess;
fazekasgy@37 371 m_pyProcess = PyString_FromString(legacyMethod);
fazekasgy@37 372 m_pyProcessCallable = PyObject_GetAttr(m_pyInstance,m_pyProcess);
fazekasgy@37 373 }
fazekasgy@37 374
fazekasgy@37 375 if (PyObject_HasAttrString(m_pyInstance,numpyMethod) &&
fazekasgy@37 376 m_processType == 0)
fazekasgy@37 377 {
fazekasgy@37 378 m_processType = numpy_bufferProcess;
fazekasgy@37 379 m_pyProcess = PyString_FromString(numpyMethod);
fazekasgy@37 380 m_pyProcessCallable = PyObject_GetAttr(m_pyInstance,m_pyProcess);
fazekasgy@37 381 }
fazekasgy@37 382
fazekasgy@37 383 // These flags are optional. If provided, they override the
fazekasgy@37 384 // implementation type making the use of the odd processN()
fazekasgy@37 385 // function redundant.
fazekasgy@37 386 // However, the code above provides backward compatibility.
fazekasgy@37 387
fazekasgy@37 388 if (m_vampyFlags & vf_BUFFER) {
fazekasgy@37 389 m_processType = numpy_bufferProcess;
fazekasgy@37 390 if (m_debugFlag) cerr << "Process using (numpy) buffer interface." << endl;
fazekasgy@37 391 }
fazekasgy@37 392
fazekasgy@37 393 if (m_vampyFlags & vf_ARRAY) {
fazekasgy@37 394 #ifdef HAVE_NUMPY
fazekasgy@51 395 if (m_numpyInstalled) { m_processType = numpy_arrayProcess;
fazekasgy@51 396 if (m_debugFlag)
fazekasgy@51 397 cerr << "Process using numpy array interface." << endl;
fazekasgy@51 398 }
fazekasgy@51 399 else {
fazekasgy@51 400 m_processFailure = true;
fazekasgy@51 401 char method[]="initialise::setProcessType";
fazekasgy@51 402 cerr << PLUGIN_ERROR
fazekasgy@51 403 << "This plugin requests the Numpy array interface by setting "
fazekasgy@51 404 << " the vf_ARRAY flag in its __init__() function." << endl
fazekasgy@51 405 << "However, we could not found a version of Numpy compatible with this build of Vampy." << endl
fazekasgy@51 406 << "If you have a numerical library installed that supports the buffer interface, " << endl
fazekasgy@51 407 << "you can request this interface instead by setting the vf_BUFFER flag." << endl;
fazekasgy@51 408 }
fazekasgy@37 409 #else
fazekasgy@51 410 m_processFailure = true;
fazekasgy@51 411 char method[]="initialise::setProcessType";
fazekasgy@51 412 cerr << PLUGIN_ERROR
fazekasgy@51 413 << "Error: This version of vampy was compiled without numpy support, "
fazekasgy@51 414 << "however the vf_ARRAY flag is set for plugin: " << m_class << endl
fazekasgy@51 415 << "The default behaviour is: passing a python list of samples for each channel in process() "
fazekasgy@51 416 << "or a list of memory buffers in processN(). " << endl
fazekasgy@51 417 << "This can be used create numpy arrays using the numpy.frombuffer() command." << endl;
fazekasgy@37 418 #endif
fazekasgy@37 419 }
fazekasgy@37 420
fazekasgy@51 421 if (!m_pyProcessCallable)
fazekasgy@37 422 {
fazekasgy@37 423 m_processType = not_implemented;
fazekasgy@37 424 m_pyProcess = NULL;
fazekasgy@37 425 char method[]="initialise::setProcessType";
fazekasgy@37 426 cerr << PLUGIN_ERROR << " No process implementation found. Plugin will do nothing." << endl;
fazekasgy@51 427 m_processFailure = true;
fazekasgy@37 428 }
fazekasgy@37 429 }
fazekasgy@37 430
fazekasgy@37 431 void
fazekasgy@51 432 PyPlugin::typeErrorHandler(char *method, bool process) const
fazekasgy@37 433 {
fazekasgy@37 434 bool strict = false;
fazekasgy@37 435 while (m_ti.error) {
fazekasgy@37 436 PyTypeInterface::ValueError e = m_ti.getError();
fazekasgy@51 437 #ifdef HAVE_NUMPY
fazekasgy@51 438 // disable the process completely if numpy types are returned
fazekasgy@51 439 // but a compatible version was not loaded.
fazekasgy@51 440 // This is required because if an object is returned from
fazekasgy@51 441 // the wrong build, malloc complains about its size
fazekasgy@51 442 // (i.e. the interpreter doesn't free it properly)
fazekasgy@51 443 // and the process may be leaking.
fazekasgy@51 444 // Note: this only happens in the obscure situation when
fazekasgy@51 445 // someone forces to return wrong numpy types from an
fazekasgy@51 446 // incompatible version using the buffer interface.
fazekasgy@51 447 // In this case the incampatible library is still usable,
fazekasgy@51 448 // but manual conversion to python builtins is required.
fazekasgy@51 449 // If the ARRAY interface is set but Numpy is not installed
fazekasgy@51 450 // the process will be disabled already at initialisation.
fazekasgy@51 451 if (process && !m_numpyInstalled && e.str().find("numpy")!=std::string::npos)
fazekasgy@51 452 {
fazekasgy@51 453 m_processFailure = true;
fazekasgy@51 454 cerr << "Warning: incompatible numpy type encountered. Disabling process." << endl;
fazekasgy@51 455 }
fazekasgy@51 456 #endif
fazekasgy@37 457 cerr << PLUGIN_ERROR << e.str() << endl;
fazekasgy@37 458 if (e.strict) strict = true;
fazekasgy@37 459 // e.print();
fazekasgy@37 460 }
fazekasgy@37 461 /// quit on hard errors like accessing NULL pointers or strict type conversion
fazekasgy@37 462 /// errors IF the user sets the quitOnErrorFlag in the plugin.
fazekasgy@37 463 /// Otherwise most errors will go unnoticed apart from
fazekasgy@37 464 /// a messages in the terminal.
fazekasgy@37 465 /// It would be best if hosts could catch an exception instead
fazekasgy@37 466 /// and display something meaningful to the user.
fazekasgy@37 467 if (strict && m_quitOnErrorFlag) exit(EXIT_FAILURE);
fazekasgy@51 468
fazekasgy@51 469 // this would disable all outputs even if some are valid
fazekasgy@51 470 // if (process) m_processFailure = true;
fazekasgy@51 471
fazekasgy@37 472 }
fazekasgy@37 473