annotate PyExtensionModule.cpp @ 92:a6718f9fe942

If a module appears to redefine one of our own types, refuse to load it. Also clear out the class dict for all refused modules now, so that we don't get stale names on the next scan due to not having cleared the module on unload
author Chris Cannam
date Mon, 14 Jan 2019 16:19:44 +0000
parents 146d14ab15e7
children
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 "PyExtensionModule.h"
fazekasgy@37 14 #include "PyRealTime.h"
fazekasgy@37 15 #include "PyFeature.h"
fazekasgy@37 16 #include "PyFeatureSet.h"
fazekasgy@37 17 #include "PyParameterDescriptor.h"
fazekasgy@37 18 #include "PyOutputDescriptor.h"
Chris@67 19 #include "Debug.h"
fazekasgy@37 20 #include "vamp/vamp.h"
fazekasgy@37 21 #include "vamp-sdk/Plugin.h"
fazekasgy@37 22
fazekasgy@37 23 using namespace std;
fazekasgy@37 24 using namespace Vamp;
fazekasgy@37 25 using Vamp::Plugin;
fazekasgy@37 26 using Vamp::RealTime;
fazekasgy@37 27
fazekasgy@37 28 /* Functions Exposed by Vampy */
fazekasgy@37 29
fazekasgy@37 30 /* Creating PyRealTime Objects from frame count */
fazekasgy@37 31
fazekasgy@37 32 /* New RealTime object from Frame (with given samplerate) */
fazekasgy@37 33 static PyObject *
fazekasgy@37 34 RealTime_frame2RealTime(PyObject *ignored, PyObject *args)
fazekasgy@37 35 {
fazekasgy@37 36 long frame;
fazekasgy@37 37 unsigned int sampleRate;
fazekasgy@37 38
fazekasgy@37 39 if (!(args && PyTuple_GET_SIZE(args) == 2)) {
fazekasgy@37 40 PyErr_SetString(PyExc_ValueError,"frame2RealTime requires two arguments: frame and sample rate.");
fazekasgy@37 41 return NULL;
fazekasgy@37 42 }
fazekasgy@37 43
fazekasgy@37 44 PyObject* pyFrame = PyTuple_GET_ITEM(args,0);
fazekasgy@37 45 PyObject* pySampleRate = PyTuple_GET_ITEM(args,1);
fazekasgy@37 46
fazekasgy@37 47 /// frame
fazekasgy@37 48 if (PyInt_Check(pyFrame)) frame = PyInt_AS_LONG(pyFrame);
fazekasgy@37 49 else if (PyLong_Check(pyFrame)) frame = PyLong_AsLong(pyFrame);
fazekasgy@37 50 else {
fazekasgy@37 51 PyErr_SetString(PyExc_ValueError,"frame2RealTime 'frame' argument must be long integer.");
fazekasgy@37 52 return NULL;
fazekasgy@37 53 }
fazekasgy@37 54
fazekasgy@37 55 /// sample rate
fazekasgy@37 56 if (PyInt_Check(pySampleRate))
fazekasgy@37 57 sampleRate = _long2uint(PyInt_AS_LONG(pySampleRate));
fazekasgy@37 58 else if (PyFloat_Check(pySampleRate))
fazekasgy@37 59 sampleRate = _dbl2uint(PyFloat_AS_DOUBLE(pySampleRate));
fazekasgy@37 60 else if (PyLong_Check(pySampleRate))
fazekasgy@37 61 sampleRate = _long2uint(PyLong_AsLong(pySampleRate));
fazekasgy@37 62 else {
fazekasgy@37 63 PyErr_SetString(PyExc_ValueError,"frame2RealTime 'sample rate' argument must be int, long or float.");
fazekasgy@37 64 return NULL;
fazekasgy@37 65 }
fazekasgy@37 66
fazekasgy@37 67 if (!sampleRate) {
fazekasgy@37 68 PyErr_SetString(PyExc_ValueError,"frame2RealTime 'sample rate' argument overflow error. Argument must be 0 < arg < UINT_MAX.");
fazekasgy@37 69 cerr << "Value: " << sampleRate << endl;
fazekasgy@37 70 return NULL;
fazekasgy@37 71 }
fazekasgy@37 72
fazekasgy@37 73 // simpler but slower:
fazekasgy@37 74 // if (!PyArg_ParseTuple(args, "lI:realtime.frame2RealTime ",
fazekasgy@37 75 // &frame,&sampleRate))
fazekasgy@37 76 // return NULL;
fazekasgy@37 77
fazekasgy@37 78 RealTimeObject *self;
fazekasgy@37 79 self = PyObject_New(RealTimeObject, &RealTime_Type);
fazekasgy@37 80 if (self == NULL) return NULL;
fazekasgy@37 81
fazekasgy@37 82 self->rt = new RealTime(
fazekasgy@37 83 RealTime::frame2RealTime(frame,sampleRate));
fazekasgy@37 84
fazekasgy@37 85 return (PyObject *) self;
fazekasgy@37 86 }
fazekasgy@37 87
fazekasgy@37 88 /*
fazekasgy@37 89
fazekasgy@37 90 Note: these functions are not very interesting on their own, but
fazekasgy@37 91 they can be used to make the semantics of the plugin clearer.
fazekasgy@37 92 They return ordinary Python list objects. All type checking
fazekasgy@37 93 is performed in the type interface.
fazekasgy@37 94
fazekasgy@37 95 */
fazekasgy@37 96
fazekasgy@37 97 /* New PyOutputList Objects */
fazekasgy@37 98 static PyObject *
fazekasgy@37 99 OutputList_new(PyObject *ignored, PyObject *args)
fazekasgy@37 100 {
cannam@46 101 if (args && PyTuple_Check(args))
fazekasgy@37 102 return PySequence_List(args);
fazekasgy@37 103 else return (PyObject *) PyList_New(0);
fazekasgy@37 104 }
fazekasgy@37 105
fazekasgy@37 106
fazekasgy@37 107 /* New PyParameterList Objects */
fazekasgy@37 108 static PyObject *
fazekasgy@37 109 ParameterList_new(PyObject *ignored, PyObject *args)
fazekasgy@37 110 {
cannam@46 111 if (args && PyTuple_Check(args))
fazekasgy@37 112 return PySequence_List(args);
fazekasgy@37 113 else return (PyObject *) PyList_New(0);
fazekasgy@37 114 }
fazekasgy@37 115
fazekasgy@37 116 /* New PyFeatureList Objects */
fazekasgy@37 117 static PyObject *
fazekasgy@37 118 FeatureList_new(PyObject *ignored, PyObject *args)
fazekasgy@37 119 {
cannam@46 120 if (args && PyTuple_Check(args))
fazekasgy@37 121 return PySequence_List(args);
fazekasgy@37 122 else return (PyObject *) PyList_New(0);
fazekasgy@37 123 }
fazekasgy@37 124
fazekasgy@37 125
fazekasgy@37 126 /* Declare the methods exposed by the vampy module */
fazekasgy@37 127
fazekasgy@37 128
fazekasgy@37 129 PyMethodDef VampyMethods[] = {
fazekasgy@37 130 /*NOTE: This is conventionally static, but limiting the scope
fazekasgy@37 131 here will cause seg fault if the declared functions are
fazekasgy@37 132 called back from a Python function wrapped in a C++ class.*/
fazekasgy@37 133
fazekasgy@37 134 {"frame2RealTime", (PyCFunction)RealTime_frame2RealTime, METH_VARARGS,
fazekasgy@37 135 PyDoc_STR("frame2RealTime((int64)frame, (uint32)sampleRate ) -> returns new RealTime object from frame.")},
fazekasgy@37 136
fazekasgy@37 137 {"OutputList", OutputList_new, METH_VARARGS,
fazekasgy@37 138 PyDoc_STR("OutputList() -> returns new OutputList object")},
fazekasgy@37 139
fazekasgy@37 140 {"ParameterList", ParameterList_new, METH_VARARGS,
fazekasgy@37 141 PyDoc_STR("ParameterList() -> returns new ParameterList object")},
fazekasgy@37 142
fazekasgy@37 143 {"FeatureList", FeatureList_new, METH_VARARGS,
fazekasgy@37 144 PyDoc_STR("FeatureList() -> returns new FeatureList object")},
fazekasgy@37 145
fazekasgy@37 146 {NULL, NULL, 0, NULL}
fazekasgy@37 147 };
fazekasgy@37 148
fazekasgy@37 149 /* Module Documentation */
fazekasgy@37 150 // PyDoc_STRVAR(vampy_doc,"This module exposes Vamp plugin data type wrappers.");
fazekasgy@37 151
fazekasgy@37 152 static int
Chris@66 153 setint(PyObject *d, const char *name, int value)
fazekasgy@37 154 {
fazekasgy@37 155 PyObject *v;
fazekasgy@37 156 int err;
fazekasgy@37 157 v = PyInt_FromLong((long)value);
fazekasgy@37 158 err = PyDict_SetItemString(d, name, v);
fazekasgy@37 159 Py_XDECREF(v);
fazekasgy@37 160 return err;
fazekasgy@37 161 }
fazekasgy@37 162
fazekasgy@37 163 static int
Chris@66 164 setdbl(PyObject *d, const char *name, double value)
fazekasgy@37 165 {
fazekasgy@37 166 PyObject *v;
fazekasgy@37 167 int err;
fazekasgy@37 168 v = PyFloat_FromDouble(value);
fazekasgy@37 169 err = PyDict_SetItemString(d, name, v);
fazekasgy@37 170 Py_XDECREF(v);
fazekasgy@37 171 return err;
fazekasgy@37 172 }
fazekasgy@37 173
fazekasgy@37 174 static int
Chris@66 175 setstr(PyObject *d, const char *name, const char *value)
fazekasgy@37 176 {
fazekasgy@37 177 PyObject *v;
fazekasgy@37 178 int err;
fazekasgy@37 179 v = PyString_FromString(value);
fazekasgy@37 180 err = PyDict_SetItemString(d, name, v);
fazekasgy@37 181 Py_XDECREF(v);
fazekasgy@37 182 return err;
fazekasgy@37 183 }
fazekasgy@37 184
fazekasgy@37 185
fazekasgy@37 186 PyMODINIT_FUNC
fazekasgy@37 187 initvampy(void)
fazekasgy@37 188 {
fazekasgy@37 189 PyObject *module, *mdict;
fazekasgy@37 190
fazekasgy@37 191 /* if (PyType_Ready(&Feature_Type) < 0) return;
fazekasgy@37 192 Note: Why do we get a segfault if this is initialised here?
fazekasgy@37 193 PyType_Ready adds these object to the GC.
fazekasgy@37 194 This is OK for an extension module, but it is a mistake here,
fazekasgy@37 195 because the adresses become invalid when the shared library
fazekasgy@37 196 is unloaded. When the GC tries to visit a these objects,
fazekasgy@37 197 it will fail.*/
fazekasgy@37 198
fazekasgy@37 199 RealTime_Type.ob_type = &PyType_Type;
fazekasgy@37 200 Feature_Type.ob_type = &PyType_Type;
fazekasgy@37 201 OutputDescriptor_Type.ob_type = &PyType_Type;
fazekasgy@37 202 ParameterDescriptor_Type.ob_type = &PyType_Type;
fazekasgy@37 203 initFeatureSetType(); // this is derived from the builtin dict
fazekasgy@37 204
fazekasgy@37 205 PyImport_AddModule("vampy");
fazekasgy@37 206 module = Py_InitModule("vampy", VampyMethods);
fazekasgy@37 207 if (!module) goto failure;
fazekasgy@37 208 mdict = PyModule_GetDict(module);
fazekasgy@37 209 if (!mdict) goto failure;
fazekasgy@37 210
fazekasgy@37 211 /// vampy plugin wrapper flags
fazekasgy@37 212 if (setint(mdict, "vf_NULL", vf_NULL) < 0) goto failure;
fazekasgy@37 213 if (setint(mdict, "vf_DEBUG", vf_DEBUG) < 0) goto failure;
fazekasgy@37 214 if (setint(mdict, "vf_STRICT", vf_STRICT) < 0) goto failure;
fazekasgy@37 215 if (setint(mdict, "vf_QUIT", vf_QUIT) < 0) goto failure;
fazekasgy@37 216 if (setint(mdict, "vf_REALTIME", vf_REALTIME) < 0) goto failure;
fazekasgy@37 217 if (setint(mdict, "vf_BUFFER", vf_BUFFER) < 0) goto failure;
fazekasgy@37 218 if (setint(mdict, "vf_ARRAY", vf_ARRAY) < 0) goto failure;
fazekasgy@37 219 if (setint(mdict, "vf_DEFAULT_V2", vf_DEFAULT_V2) < 0) goto failure;
fazekasgy@37 220
fazekasgy@37 221 /// Vamp enum types simulation
fazekasgy@37 222 if (setint(mdict, "OneSamplePerStep", Vamp::Plugin::OutputDescriptor::OneSamplePerStep) < 0) goto failure;
fazekasgy@37 223 if (setint(mdict, "FixedSampleRate", Vamp::Plugin::OutputDescriptor::FixedSampleRate) < 0) goto failure;
fazekasgy@37 224 if (setint(mdict, "VariableSampleRate", Vamp::Plugin::OutputDescriptor::VariableSampleRate) < 0) goto failure;
fazekasgy@37 225 if (setint(mdict, "TimeDomain", Vamp::Plugin::TimeDomain) < 0) goto failure;
fazekasgy@37 226 if (setint(mdict, "FrequencyDomain", Vamp::Plugin::FrequencyDomain) < 0) goto failure;
fazekasgy@37 227
fazekasgy@37 228 /// module attributes
fazekasgy@37 229 if (setstr(mdict, "__name__", "vampy") < 0) goto failure;
fazekasgy@37 230 if (setdbl(mdict, "__version__", 2.0) < 0) goto failure;
fazekasgy@37 231 if (setdbl(mdict, "__VAMP_API_VERSION__", (double) VAMP_API_VERSION) < 0) goto failure;
fazekasgy@37 232 #ifdef HAVE_NUMPY
fazekasgy@37 233 if (setint(mdict, "__numpy__", 1) < 0) goto failure;
fazekasgy@37 234 #else
fazekasgy@37 235 if (setint(mdict, "__numpy__", 0) < 0) goto failure;
fazekasgy@37 236 #endif
fazekasgy@37 237
fazekasgy@37 238 /// type objects
fazekasgy@37 239 Py_INCREF(&RealTime_Type);
fazekasgy@37 240 if (PyModule_AddObject(module,"RealTime",(PyObject*)&RealTime_Type) !=0) goto failure;
fazekasgy@37 241
fazekasgy@37 242 Py_INCREF((PyObject*)&Feature_Type);
fazekasgy@37 243 if (PyModule_AddObject(module,"Feature",(PyObject*)&Feature_Type) !=0) goto failure;
fazekasgy@37 244
fazekasgy@37 245 Py_INCREF((PyObject*)&FeatureSet_Type);
fazekasgy@37 246 if (PyModule_AddObject(module,"FeatureSet",(PyObject*)&FeatureSet_Type) !=0) goto failure;
fazekasgy@37 247
fazekasgy@37 248 Py_INCREF((PyObject*)&OutputDescriptor_Type);
fazekasgy@37 249 if (PyModule_AddObject(module,"OutputDescriptor",(PyObject*)&OutputDescriptor_Type) !=0) goto failure;
fazekasgy@37 250
fazekasgy@37 251 Py_INCREF((PyObject*)&ParameterDescriptor_Type);
fazekasgy@37 252 if (PyModule_AddObject(module,"ParameterDescriptor",(PyObject*)&ParameterDescriptor_Type) !=0) goto failure;
fazekasgy@37 253
Chris@67 254 DSTREAM << "Vampy: extension module initialised." << endl;
fazekasgy@37 255
fazekasgy@37 256 return;
fazekasgy@37 257
fazekasgy@37 258 failure :
fazekasgy@37 259 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
fazekasgy@37 260 cerr << "Vampy::PyExtensionModule::initvampy: Failed to initialise extension module." << endl;
fazekasgy@37 261 return;
fazekasgy@37 262 }
fazekasgy@37 263
fazekasgy@37 264