Mercurial > hg > vampy
comparison PyPlugin.h @ 37:27bab3a16c9a vampy2final
new branch Vampy2final
| author | fazekasgy | 
|---|---|
| date | Mon, 05 Oct 2009 11:28:00 +0000 | 
| parents | |
| children | 8b2eddf686da | 
   comparison
  equal
  deleted
  inserted
  replaced
| -1:000000000000 | 37:27bab3a16c9a | 
|---|---|
| 1 /* | |
| 2 | |
| 3 * Vampy : This plugin is a wrapper around the Vamp plugin API. | |
| 4 * It allows for writing Vamp plugins in Python. | |
| 5 | |
| 6 * Centre for Digital Music, Queen Mary University of London. | |
| 7 * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources | |
| 8 * for licence information.) | |
| 9 | |
| 10 */ | |
| 11 | |
| 12 /* | |
| 13 Vamp | |
| 14 | |
| 15 An API for audio analysis and feature extraction plugins. | |
| 16 | |
| 17 Centre for Digital Music, Queen Mary, University of London. | |
| 18 Copyright 2006 Chris Cannam. | |
| 19 | |
| 20 Permission is hereby granted, free of charge, to any person | |
| 21 obtaining a copy of this software and associated documentation | |
| 22 files (the "Software"), to deal in the Software without | |
| 23 restriction, including without limitation the rights to use, copy, | |
| 24 modify, merge, publish, distribute, sublicense, and/or sell copies | |
| 25 of the Software, and to permit persons to whom the Software is | |
| 26 furnished to do so, subject to the following conditions: | |
| 27 | |
| 28 The above copyright notice and this permission notice shall be | |
| 29 included in all copies or substantial portions of the Software. | |
| 30 | |
| 31 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| 32 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| 33 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
| 34 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR | |
| 35 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
| 36 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
| 37 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| 38 | |
| 39 Except as contained in this notice, the names of the Centre for | |
| 40 Digital Music; Queen Mary, University of London; and Chris Cannam | |
| 41 shall not be used in advertising or otherwise to promote the sale, | |
| 42 use or other dealings in this Software without prior written | |
| 43 authorization. | |
| 44 */ | |
| 45 | |
| 46 #ifndef _PYTHON_WRAPPER_PLUGIN_H_ | |
| 47 #define _PYTHON_WRAPPER_PLUGIN_H_ | |
| 48 | |
| 49 #define _CLASS_METHOD_ m_class << "::" << method | |
| 50 #define PLUGIN_ERROR "ERROR: In Vampy plugin [" << _CLASS_METHOD_ << "]" << endl << "Cause: " | |
| 51 #define DEBUG_NAME "[Vampy::call] " << _CLASS_METHOD_ << " " | |
| 52 #define DEAFULT_RETURN "Method [" << _CLASS_METHOD_ << "] is not implemented. Returning default value." | |
| 53 #define FLAG_VALUE "Flag: " << flagName << ": " << ((rValue==0)?"False":"True") | |
| 54 | |
| 55 #include <Python.h> | |
| 56 #include "PyExtensionModule.h" | |
| 57 #include "PyTypeInterface.h" | |
| 58 #include "vamp-sdk/Plugin.h" | |
| 59 #include "Mutex.h" | |
| 60 | |
| 61 using std::string; | |
| 62 using std::cerr; | |
| 63 using std::endl; | |
| 64 | |
| 65 enum eProcessType { | |
| 66 not_implemented, | |
| 67 legacyProcess, | |
| 68 numpyProcess, | |
| 69 numpy_bufferProcess, | |
| 70 numpy_arrayProcess | |
| 71 }; | |
| 72 | |
| 73 class PyPlugin : public Vamp::Plugin | |
| 74 { | |
| 75 public: | |
| 76 PyPlugin(std::string plugin,float inputSampleRate, PyObject *pyClass, int &instcount); | |
| 77 virtual ~PyPlugin(); | |
| 78 | |
| 79 bool initialise(size_t channels, size_t stepSize, size_t blockSize); | |
| 80 void reset(); | |
| 81 | |
| 82 InputDomain getInputDomain() const; | |
| 83 size_t getPreferredBlockSize() const; | |
| 84 size_t getPreferredStepSize() const; | |
| 85 size_t getMinChannelCount() const; | |
| 86 size_t getMaxChannelCount() const; | |
| 87 | |
| 88 std::string getIdentifier() const; | |
| 89 std::string getName() const; | |
| 90 std::string getDescription() const; | |
| 91 std::string getMaker() const; | |
| 92 int getPluginVersion() const; | |
| 93 std::string getCopyright() const; | |
| 94 | |
| 95 OutputList getOutputDescriptors() const; | |
| 96 ParameterList getParameterDescriptors() const; | |
| 97 float getParameter(std::string paramid) const; | |
| 98 void setParameter(std::string paramid, float newval); | |
| 99 | |
| 100 FeatureSet process(const float *const *inputBuffers, | |
| 101 Vamp::RealTime timestamp); | |
| 102 | |
| 103 FeatureSet getRemainingFeatures(); | |
| 104 | |
| 105 protected: | |
| 106 static Mutex m_pythonInterpreterMutex; | |
| 107 PyObject *m_pyClass; | |
| 108 PyObject *m_pyInstance; | |
| 109 int &m_instcount; | |
| 110 size_t m_stepSize; | |
| 111 size_t m_blockSize; | |
| 112 size_t m_channels; | |
| 113 std::string m_plugin; | |
| 114 std::string m_class; | |
| 115 std::string m_path; | |
| 116 eProcessType m_processType; | |
| 117 PyObject *m_pyProcess; | |
| 118 PyObject *m_pyProcessCallable; | |
| 119 mutable InputDomain m_inputDomain; | |
| 120 PyTypeInterface m_ti; | |
| 121 int m_vampyFlags; | |
| 122 bool m_quitOnErrorFlag; | |
| 123 bool m_debugFlag; | |
| 124 bool m_useRealTimeFlag; | |
| 125 | |
| 126 void setProcessType(); | |
| 127 | |
| 128 FeatureSet processMethodCall(const float *const *inputBuffers,Vamp::RealTime timestamp); | |
| 129 | |
| 130 bool getBooleanFlag(char flagName[],bool) const; | |
| 131 int getBinaryFlags(char flagName[], eVampyFlags) const; | |
| 132 void typeErrorHandler(char *method) const; | |
| 133 | |
| 134 /// simple 'void return' call with no args | |
| 135 void genericMethodCall(char *method) const | |
| 136 { | |
| 137 if (m_debugFlag) cerr << DEBUG_NAME << endl; | |
| 138 if ( PyObject_HasAttrString(m_pyInstance,method) ) | |
| 139 { | |
| 140 PyObject *pyValue = PyObject_CallMethod(m_pyInstance, method, NULL); | |
| 141 if (!pyValue) { | |
| 142 cerr << PLUGIN_ERROR << "Failed to call method." << endl; | |
| 143 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} | |
| 144 } | |
| 145 } | |
| 146 } | |
| 147 | |
| 148 /// 'no arg with default return value' call | |
| 149 template<typename RET> | |
| 150 RET &genericMethodCall(char *method, RET &rValue) const | |
| 151 { | |
| 152 if (m_debugFlag) cerr << DEBUG_NAME << endl; | |
| 153 if ( PyObject_HasAttrString(m_pyInstance,method) ) | |
| 154 { | |
| 155 PyObject *pyValue = PyObject_CallMethod(m_pyInstance, method, NULL); | |
| 156 if (!pyValue) { | |
| 157 cerr << PLUGIN_ERROR << "Failed to call method." << endl; | |
| 158 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} | |
| 159 return rValue; | |
| 160 } | |
| 161 | |
| 162 /// convert the returned value | |
| 163 m_ti.PyValue_To_rValue(pyValue,rValue); | |
| 164 if (!m_ti.error) { | |
| 165 Py_DECREF(pyValue); | |
| 166 } else { | |
| 167 Py_CLEAR(pyValue); | |
| 168 typeErrorHandler(method); | |
| 169 } | |
| 170 return rValue; | |
| 171 } | |
| 172 if (m_debugFlag) cerr << DEAFULT_RETURN << endl; | |
| 173 return rValue; | |
| 174 } | |
| 175 | |
| 176 /// unary call | |
| 177 template<typename RET,typename A1> | |
| 178 RET genericMethodCallArgs(char *method, A1 arg1) const | |
| 179 { | |
| 180 RET rValue = RET(); | |
| 181 if (m_debugFlag) cerr << DEBUG_NAME << endl; | |
| 182 if (!PyObject_HasAttrString(m_pyInstance,method)) { | |
| 183 if (m_debugFlag) cerr << DEAFULT_RETURN << endl; | |
| 184 return rValue; | |
| 185 } | |
| 186 | |
| 187 /// prepare arguments for fast method call | |
| 188 PyObject *pyMethod = m_ti.PyValue_From_CValue(method); | |
| 189 PyObject *pyCallable = PyObject_GetAttr(m_pyInstance,pyMethod); | |
| 190 PyObject* pyArgs = PyTuple_New(1); | |
| 191 if (!(pyArgs && pyCallable && pyMethod)) { | |
| 192 cerr << PLUGIN_ERROR << "Failed to prepare argument for calling method." << endl; | |
| 193 Py_CLEAR(pyMethod); | |
| 194 Py_CLEAR(pyCallable); | |
| 195 Py_CLEAR(pyArgs); | |
| 196 return rValue; | |
| 197 } | |
| 198 | |
| 199 PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1); | |
| 200 if (m_ti.error) { | |
| 201 cerr << PLUGIN_ERROR << "Failed to convert argument for calling method." << endl; | |
| 202 typeErrorHandler(method); | |
| 203 Py_CLEAR(pyMethod); | |
| 204 Py_CLEAR(pyCallable); | |
| 205 Py_CLEAR(pyArg1); | |
| 206 Py_CLEAR(pyArgs); | |
| 207 return rValue; | |
| 208 } | |
| 209 | |
| 210 PyTuple_SET_ITEM(pyArgs, 0, pyArg1); | |
| 211 Py_INCREF(pyArg1); | |
| 212 | |
| 213 /// call the method | |
| 214 PyObject *pyValue = PyObject_Call(pyCallable,pyArgs,NULL); | |
| 215 if (!pyValue) | |
| 216 { | |
| 217 cerr << PLUGIN_ERROR << "Failed to call method." << endl; | |
| 218 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} | |
| 219 Py_CLEAR(pyMethod); | |
| 220 Py_CLEAR(pyCallable); | |
| 221 Py_CLEAR(pyArg1); | |
| 222 Py_CLEAR(pyArgs); | |
| 223 return rValue; | |
| 224 } | |
| 225 | |
| 226 Py_DECREF(pyMethod); | |
| 227 Py_DECREF(pyCallable); | |
| 228 Py_DECREF(pyArg1); | |
| 229 Py_DECREF(pyArgs); | |
| 230 | |
| 231 /// convert the returned value | |
| 232 m_ti.PyValue_To_rValue(pyValue,rValue); | |
| 233 if (!m_ti.error) { | |
| 234 Py_DECREF(pyValue); | |
| 235 } else { | |
| 236 Py_CLEAR(pyValue); | |
| 237 typeErrorHandler(method); | |
| 238 } | |
| 239 return rValue; | |
| 240 } | |
| 241 | |
| 242 /// binary call | |
| 243 template<typename RET,typename A1,typename A2> | |
| 244 RET genericMethodCallArgs(char *method, A1 arg1, A2 arg2) const | |
| 245 { | |
| 246 RET rValue = RET(); | |
| 247 if (m_debugFlag) cerr << DEBUG_NAME << endl; | |
| 248 if (!PyObject_HasAttrString(m_pyInstance,method)) { | |
| 249 if (m_debugFlag) cerr << DEAFULT_RETURN << endl; | |
| 250 return rValue; | |
| 251 } | |
| 252 | |
| 253 /// prepare arguments for fast method call | |
| 254 PyObject *pyMethod = m_ti.PyValue_From_CValue(method); | |
| 255 PyObject *pyCallable = PyObject_GetAttr(m_pyInstance,pyMethod); | |
| 256 PyObject* pyArgs = PyTuple_New(2); | |
| 257 if (!(pyArgs && pyCallable && pyMethod)) { | |
| 258 cerr << PLUGIN_ERROR << "Failed to prepare arguments for calling method." << endl; | |
| 259 Py_CLEAR(pyMethod); | |
| 260 Py_CLEAR(pyCallable); | |
| 261 Py_CLEAR(pyArgs); | |
| 262 return rValue; | |
| 263 } | |
| 264 | |
| 265 PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1); | |
| 266 PyObject *pyArg2 = m_ti.PyValue_From_CValue(arg2); | |
| 267 if (m_ti.error) { | |
| 268 cerr << PLUGIN_ERROR << "Failed to convert arguments for calling method." << endl; | |
| 269 typeErrorHandler(method); | |
| 270 Py_CLEAR(pyMethod); | |
| 271 Py_CLEAR(pyCallable); | |
| 272 Py_CLEAR(pyArg1); | |
| 273 Py_CLEAR(pyArg2); | |
| 274 Py_CLEAR(pyArgs); | |
| 275 return rValue; | |
| 276 } | |
| 277 | |
| 278 PyTuple_SET_ITEM(pyArgs, 0, pyArg1); | |
| 279 Py_INCREF(pyArg1); | |
| 280 PyTuple_SET_ITEM(pyArgs, 1, pyArg2); | |
| 281 Py_INCREF(pyArg2); | |
| 282 | |
| 283 // calls the method | |
| 284 PyObject *pyValue = PyObject_Call(pyCallable,pyArgs,NULL); | |
| 285 if (!pyValue) | |
| 286 { | |
| 287 cerr << PLUGIN_ERROR << "Failed to call method." << endl; | |
| 288 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} | |
| 289 Py_CLEAR(pyMethod); | |
| 290 Py_CLEAR(pyCallable); | |
| 291 Py_CLEAR(pyArg1); | |
| 292 Py_CLEAR(pyArg2); | |
| 293 Py_CLEAR(pyArgs); | |
| 294 return rValue; | |
| 295 } | |
| 296 | |
| 297 Py_DECREF(pyMethod); | |
| 298 Py_DECREF(pyCallable); | |
| 299 Py_DECREF(pyArg1); | |
| 300 Py_DECREF(pyArg2); | |
| 301 Py_DECREF(pyArgs); | |
| 302 | |
| 303 /// convert the returned value | |
| 304 m_ti.PyValue_To_rValue(pyValue,rValue); | |
| 305 if (!m_ti.error) { | |
| 306 Py_DECREF(pyValue); | |
| 307 } else { | |
| 308 Py_CLEAR(pyValue); | |
| 309 typeErrorHandler(method); | |
| 310 } | |
| 311 return rValue; | |
| 312 } | |
| 313 | |
| 314 /// trenary call | |
| 315 template<typename RET,typename A1,typename A2,typename A3> | |
| 316 RET genericMethodCallArgs(char *method, A1 arg1, A2 arg2, A3 arg3) const | |
| 317 { | |
| 318 RET rValue = RET(); | |
| 319 if (m_debugFlag) cerr << DEBUG_NAME << endl; | |
| 320 if (!PyObject_HasAttrString(m_pyInstance,method)) { | |
| 321 if (m_debugFlag) cerr << DEAFULT_RETURN << endl; | |
| 322 return rValue; | |
| 323 } | |
| 324 | |
| 325 /// prepare arguments for fast method call | |
| 326 PyObject *pyMethod = m_ti.PyValue_From_CValue(method); | |
| 327 PyObject *pyCallable = PyObject_GetAttr(m_pyInstance,pyMethod); | |
| 328 PyObject* pyArgs = PyTuple_New(3); | |
| 329 if (!(pyArgs && pyCallable && pyMethod)) { | |
| 330 cerr << PLUGIN_ERROR << "Failed to prepare arguments for calling method." << endl; | |
| 331 Py_CLEAR(pyMethod); | |
| 332 Py_CLEAR(pyCallable); | |
| 333 Py_CLEAR(pyArgs); | |
| 334 return rValue; | |
| 335 } | |
| 336 | |
| 337 PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1); | |
| 338 PyObject *pyArg2 = m_ti.PyValue_From_CValue(arg2); | |
| 339 PyObject *pyArg3 = m_ti.PyValue_From_CValue(arg3); | |
| 340 if (m_ti.error) { | |
| 341 cerr << PLUGIN_ERROR << "Failed to convert arguments for calling method." << endl; | |
| 342 typeErrorHandler(method); | |
| 343 Py_CLEAR(pyMethod); | |
| 344 Py_CLEAR(pyCallable); | |
| 345 Py_CLEAR(pyArg1); | |
| 346 Py_CLEAR(pyArg2); | |
| 347 Py_CLEAR(pyArg3); | |
| 348 Py_CLEAR(pyArgs); | |
| 349 return rValue; | |
| 350 } | |
| 351 | |
| 352 /// Optimization: Pack args in a tuple to avoid va_list parsing. | |
| 353 PyTuple_SET_ITEM(pyArgs, 0, pyArg1); | |
| 354 Py_INCREF(pyArg1); | |
| 355 PyTuple_SET_ITEM(pyArgs, 1, pyArg2); | |
| 356 Py_INCREF(pyArg2); | |
| 357 PyTuple_SET_ITEM(pyArgs, 2, pyArg3); | |
| 358 Py_INCREF(pyArg3); | |
| 359 | |
| 360 // PyObject *pyValue = PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyArg1,pyArg2,pyArg3,NULL); | |
| 361 /// fast method call | |
| 362 PyObject *pyValue = PyObject_Call(pyCallable,pyArgs,NULL); | |
| 363 if (!pyValue) | |
| 364 { | |
| 365 cerr << PLUGIN_ERROR << "Failed to call method." << endl; | |
| 366 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} | |
| 367 Py_CLEAR(pyMethod); | |
| 368 Py_CLEAR(pyCallable); | |
| 369 Py_CLEAR(pyArg1); | |
| 370 Py_CLEAR(pyArg2); | |
| 371 Py_CLEAR(pyArg3); | |
| 372 Py_CLEAR(pyArgs); | |
| 373 return rValue; | |
| 374 } | |
| 375 | |
| 376 Py_DECREF(pyMethod); | |
| 377 Py_DECREF(pyCallable); | |
| 378 Py_DECREF(pyArg1); | |
| 379 Py_DECREF(pyArg2); | |
| 380 Py_DECREF(pyArg3); | |
| 381 Py_DECREF(pyArgs); | |
| 382 | |
| 383 /// convert the returned value | |
| 384 m_ti.PyValue_To_rValue(pyValue,rValue); | |
| 385 if (!m_ti.error) { | |
| 386 Py_DECREF(pyValue); | |
| 387 } else { | |
| 388 Py_CLEAR(pyValue); | |
| 389 typeErrorHandler(method); | |
| 390 } | |
| 391 return rValue; | |
| 392 } | |
| 393 | |
| 394 }; | |
| 395 | |
| 396 /// optimised process call | |
| 397 inline PyPlugin::FeatureSet | |
| 398 PyPlugin::processMethodCall(const float *const *inputBuffers,Vamp::RealTime timestamp) | |
| 399 { | |
| 400 | |
| 401 /// Optimizations: 1) we avoid ...ObjArg functions since we know | |
| 402 /// the number of arguments, and we don't like va_list parsing | |
| 403 /// in the process. 2) Also: we're supposed to incref args, | |
| 404 /// but instead, we let the arguments tuple steal the references | |
| 405 /// and decref them when it is deallocated. | |
| 406 /// 3) all conversions are now using the fast sequence protocol | |
| 407 /// (indexing the underlying object array). | |
| 408 | |
| 409 FeatureSet rFeatureSet; | |
| 410 PyObject *pyChannelList = NULL; | |
| 411 | |
| 412 if (m_processType == numpy_bufferProcess) { | |
| 413 pyChannelList = m_ti.InputBuffers_As_SharedMemoryList(inputBuffers,m_channels,m_blockSize); | |
| 414 } | |
| 415 | |
| 416 if (m_processType == legacyProcess) { | |
| 417 pyChannelList = m_ti.InputBuffers_As_PythonLists(inputBuffers,m_channels,m_blockSize,m_inputDomain); | |
| 418 } | |
| 419 | |
| 420 #ifdef HAVE_NUMPY | |
| 421 if (m_processType == numpy_arrayProcess) { | |
| 422 pyChannelList = m_ti.InputBuffers_As_NumpyArray(inputBuffers,m_channels,m_blockSize,m_inputDomain); | |
| 423 } | |
| 424 #endif | |
| 425 | |
| 426 /// we don't expect these to fail unless out of memory (which is very unlikely on modern systems) | |
| 427 #ifdef _DEBUG | |
| 428 if (!pyChannelList) { | |
| 429 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} | |
| 430 std::string method = PyString_AsString(m_pyProcess); | |
| 431 cerr << PLUGIN_ERROR << "Failed to create channel list." << endl; | |
| 432 return rFeatureSet; | |
| 433 } | |
| 434 #endif | |
| 435 | |
| 436 PyObject *pyTimeStamp = NULL; | |
| 437 | |
| 438 if (m_useRealTimeFlag) { | |
| 439 //(1) pass TimeStamp as PyRealTime object | |
| 440 pyTimeStamp = PyRealTime_FromRealTime(timestamp); | |
| 441 | |
| 442 } else { | |
| 443 //(2) pass TimeStamp as frame count (long Sample Count) | |
| 444 pyTimeStamp = PyLong_FromLong(Vamp::RealTime::realTime2Frame | |
| 445 (timestamp, (unsigned int) m_inputSampleRate)); | |
| 446 } | |
| 447 | |
| 448 | |
| 449 #ifdef _DEBUG | |
| 450 if (!pyTimeStamp) { | |
| 451 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} | |
| 452 std::string method = PyString_AsString(m_pyProcess); | |
| 453 cerr << PLUGIN_ERROR << "Failed to create RealTime time stamp." << endl; | |
| 454 Py_DECREF(pyChannelList); | |
| 455 return rFeatureSet; | |
| 456 } | |
| 457 #endif | |
| 458 | |
| 459 /// Old method: Call python process (returns new reference) | |
| 460 /// PyObject *pyValue = PyObject_CallMethodObjArgs | |
| 461 /// (m_pyInstance,m_pyProcess,pyChannelList,pyTimeStamp,NULL); | |
| 462 | |
| 463 PyObject *pyArgs = PyTuple_New(2); | |
| 464 PyTuple_SET_ITEM(pyArgs, 0, pyChannelList); | |
| 465 PyTuple_SET_ITEM(pyArgs, 1, pyTimeStamp); | |
| 466 | |
| 467 /// Call python process (returns new reference) {kwArgs = NULL} | |
| 468 PyObject *pyValue = PyObject_Call(m_pyProcessCallable,pyArgs,NULL); | |
| 469 | |
| 470 if (!pyValue) { | |
| 471 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} | |
| 472 std::string method = PyString_AsString(m_pyProcess); | |
| 473 cerr << PLUGIN_ERROR << "An error occurred while evaluating Python process." << endl; | |
| 474 Py_CLEAR(pyValue); | |
| 475 Py_CLEAR(pyArgs); | |
| 476 return rFeatureSet; | |
| 477 } | |
| 478 | |
| 479 rFeatureSet = m_ti.PyValue_To_FeatureSet(pyValue); | |
| 480 if (!m_ti.error) { | |
| 481 Py_DECREF(pyValue); | |
| 482 Py_DECREF(pyArgs); | |
| 483 } else { | |
| 484 typeErrorHandler(PyString_AsString(m_pyProcess)); | |
| 485 Py_CLEAR(pyValue); | |
| 486 Py_CLEAR(pyArgs); | |
| 487 } | |
| 488 return rFeatureSet; | |
| 489 } | |
| 490 | |
| 491 #endif | 
