annotate PyExtensionManager.cpp @ 113:c7694d24500c

Fix crash when plugin library is loaded and unloaded without any plugins being queried (so extension manager is never initialised)
author Chris Cannam
date Tue, 26 Feb 2019 12:46:24 +0000
parents c4510e5f7a17
children
rev   line source
Chris@67 1 /* -*- c-basic-offset: 8 indent-tabs-mode: t -*- */
fazekasgy@37 2 /*
fazekasgy@37 3
fazekasgy@37 4 * Vampy : This plugin is a wrapper around the Vamp plugin API.
fazekasgy@37 5 * It allows for writing Vamp plugins in Python.
fazekasgy@37 6
fazekasgy@37 7 * Centre for Digital Music, Queen Mary University of London.
fazekasgy@37 8 * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources
fazekasgy@37 9 * for licence information.)
fazekasgy@37 10
fazekasgy@37 11 */
fazekasgy@37 12
fazekasgy@37 13 #include <Python.h>
fazekasgy@37 14 #include "vamp/vamp.h"
fazekasgy@37 15 #include "PyExtensionModule.h"
fazekasgy@37 16 #include "PyExtensionManager.h"
fazekasgy@37 17 #include <algorithm>
Chris@67 18 #include "Debug.h"
fazekasgy@37 19
fazekasgy@37 20 using std::cerr;
fazekasgy@37 21 using std::endl;
fazekasgy@37 22 using std::string;
fazekasgy@37 23 using std::vector;
fazekasgy@37 24 using std::find;
fazekasgy@37 25
fazekasgy@37 26 //static
Chris@66 27 const char* PyExtensionManager::m_exposedNames[] = {
Chris@91 28
fazekasgy@37 29 "ParameterDescriptor",
fazekasgy@37 30 "OutputDescriptor",
fazekasgy@37 31 "FeatureSet",
fazekasgy@37 32 "Feature",
fazekasgy@37 33 "RealTime",
fazekasgy@37 34 "frame2RealTime",
Chris@91 35
Chris@91 36 /* // using builtin objects:
Chris@91 37 "ParameterList",
Chris@91 38 "OutputList",
Chris@91 39 "FeatureList",
fazekasgy@37 40 "OneSamplePerStep",
fazekasgy@37 41 "FixedSampleRate",
fazekasgy@37 42 "VariableSampleRate",
fazekasgy@37 43 "TimeDomain",
fazekasgy@37 44 "FrequencyDomain",
fazekasgy@37 45 */
Chris@91 46
fazekasgy@37 47 NULL
fazekasgy@37 48 };
fazekasgy@37 49
Chris@113 50 PyExtensionManager::PyExtensionManager() :
Chris@113 51 m_pyGlobalNamespace(0),
Chris@113 52 m_pyVampyNamespace(0)
fazekasgy@37 53 {
Chris@67 54 DSTREAM << "Creating extension manager." << endl;
fazekasgy@37 55 }
fazekasgy@37 56
fazekasgy@37 57 bool
fazekasgy@37 58 PyExtensionManager::initExtension()
fazekasgy@37 59 {
Chris@67 60 DSTREAM << "Initialising extension module." << endl;
fazekasgy@37 61
fazekasgy@37 62 /// call the module initialiser first
fazekasgy@37 63 initvampy();
fazekasgy@37 64
fazekasgy@37 65 /// these references are all borrowed
fazekasgy@37 66 m_pyGlobalNamespace = PyImport_GetModuleDict();
fazekasgy@37 67 if (!m_pyGlobalNamespace)
fazekasgy@37 68 {cerr << "Vampy::PyExtensionManager::initExtension: GlobalNamespace failed." << endl; return false;}
fazekasgy@37 69 PyObject *pyVampyModule = PyDict_GetItemString(m_pyGlobalNamespace,"vampy");
fazekasgy@37 70 if (!pyVampyModule)
fazekasgy@37 71 {cerr << "Vampy::PyExtensionManager::initExtension: VampyModule failed." << endl; return false;}
fazekasgy@37 72 m_pyVampyNamespace = PyModule_GetDict(pyVampyModule);
fazekasgy@37 73 if (!m_pyVampyNamespace)
fazekasgy@37 74 {cerr << "Vampy::PyExtensionManager::initExtension: VampyNamespace failed." << endl; return false;}
fazekasgy@37 75
fazekasgy@37 76 /// initialise local namespaces
fazekasgy@37 77 updateAllLocals();
Chris@67 78
Chris@67 79 DSTREAM << "Vampy: Extension namespaces updated." << endl;
Chris@67 80
fazekasgy@37 81 return true;
fazekasgy@37 82 }
fazekasgy@37 83
fazekasgy@37 84
fazekasgy@37 85 PyExtensionManager::~PyExtensionManager()
fazekasgy@37 86 {
Chris@113 87 if (!m_pyVampyNamespace) {
Chris@113 88 DSTREAM << "Vampy::~PyExtensionManager: manager was never initialised, or initialisation did not complete: not attempting cleanup" << endl;
Chris@113 89 return;
Chris@113 90 }
Chris@113 91
Chris@67 92 DSTREAM << "Cleaning locals..." << endl;
fazekasgy@37 93
fazekasgy@37 94 cleanAllLocals();
fazekasgy@37 95
Chris@67 96 DSTREAM << "Cleaning module..." << endl;
fazekasgy@37 97
Chris@113 98 if (!cleanModule()) {
fazekasgy@37 99 cerr << "Vampy::~PyExtensionManager: failed to clean extension module." << endl;
Chris@113 100 } else {
Chris@113 101 DSTREAM << "Vampy::~PyExtensionManager: Extension module cleaned." << endl;
Chris@113 102 }
fazekasgy@37 103 }
fazekasgy@37 104
fazekasgy@37 105
fazekasgy@37 106
fazekasgy@37 107 void
fazekasgy@37 108 PyExtensionManager::setPlugModuleNames(vector<string> pyPlugs)
fazekasgy@37 109 {
fazekasgy@37 110 for (size_t i = 0; i < pyPlugs.size(); ++i) {
fazekasgy@37 111 string modName = pyPlugs[i];
fazekasgy@37 112 string tmp = modName.substr(modName.rfind(':')+1,modName.size()-1);
fazekasgy@37 113 m_plugModuleNames.push_back(tmp);
fazekasgy@37 114
Chris@67 115 DSTREAM << "Inserted module name: " << tmp << endl;
fazekasgy@37 116 }
fazekasgy@37 117 }
fazekasgy@37 118
fazekasgy@37 119 void
fazekasgy@37 120 PyExtensionManager::deleteModuleName(string plugKey)
fazekasgy@37 121 {
fazekasgy@37 122 string name = plugKey.substr(plugKey.rfind(':')+1,plugKey.size()-1);
fazekasgy@37 123 vector<string>::iterator it =
fazekasgy@37 124 find (m_plugModuleNames.begin(), m_plugModuleNames.end(), name);
fazekasgy@37 125 if (it != m_plugModuleNames.end()) m_plugModuleNames.erase(it);
Chris@67 126
Chris@67 127 DSTREAM << "PyExtensionManager::deleteModuleName: Deleted module name: " << name << endl;
fazekasgy@37 128 }
fazekasgy@37 129
fazekasgy@37 130
fazekasgy@37 131 void
fazekasgy@37 132 PyExtensionManager::cleanAllLocals() const
fazekasgy@37 133 {
fazekasgy@37 134 for (size_t i = 0; i < m_plugModuleNames.size(); ++i) {
fazekasgy@37 135 cleanLocalNamespace(m_plugModuleNames[i].c_str());
fazekasgy@37 136 }
fazekasgy@37 137 }
fazekasgy@37 138
fazekasgy@37 139 void
fazekasgy@37 140 PyExtensionManager::updateAllLocals() const
fazekasgy@37 141 {
fazekasgy@37 142 for (size_t i = 0; i < m_plugModuleNames.size(); ++i) {
fazekasgy@37 143 updateLocalNamespace(m_plugModuleNames[i].c_str());
fazekasgy@37 144 }
fazekasgy@37 145 }
fazekasgy@37 146
fazekasgy@37 147 void
fazekasgy@37 148 PyExtensionManager::cleanLocalNamespace(const char* plugModuleName) const
fazekasgy@37 149 {
Chris@91 150 DSTREAM << "Cleaning local namespace: " << plugModuleName << endl;
Chris@91 151
fazekasgy@37 152 /// these references are all borrowed
fazekasgy@37 153 PyObject *pyPlugModule = PyDict_GetItemString(m_pyGlobalNamespace,plugModuleName);
fazekasgy@37 154 if (!pyPlugModule) return;
fazekasgy@37 155 PyObject *pyPlugDict = PyModule_GetDict(pyPlugModule);
fazekasgy@37 156 if (!pyPlugDict) return;
fazekasgy@37 157
fazekasgy@37 158 int i = 0;
fazekasgy@37 159 while (PyExtensionManager::m_exposedNames[i]) {
Chris@66 160 const char* name = PyExtensionManager::m_exposedNames[i];
fazekasgy@37 161 i++;
fazekasgy@37 162 PyObject *key = PyString_FromString(name);
fazekasgy@37 163 if (!key) break;
fazekasgy@37 164 if (PyDict_Contains(pyPlugDict,key)) {
fazekasgy@37 165 if (PyDict_SetItem(pyPlugDict,key,Py_None) != 0)
fazekasgy@37 166 cerr << "Vampy::PyExtensionManager::cleanLocalNamespace: Failed: "
fazekasgy@37 167 << name << " of "<< plugModuleName << endl;
Chris@67 168 else DSTREAM << "Cleaned local name: " << name << endl;
fazekasgy@37 169 }
fazekasgy@37 170 Py_DECREF(key);
fazekasgy@37 171 }
fazekasgy@37 172 }
fazekasgy@37 173
fazekasgy@37 174 void
fazekasgy@37 175 PyExtensionManager::updateLocalNamespace(const char* plugModuleName) const
fazekasgy@37 176 {
Chris@91 177 DSTREAM << "Updating local namespace: " << plugModuleName << endl;
Chris@91 178
fazekasgy@37 179 /// this allows the use of common syntax like:
fazekasgy@37 180 /// from vampy import *
fazekasgy@37 181 /// even after several unload/reload cycles
fazekasgy@37 182
fazekasgy@37 183 /// these references are all borrowed
fazekasgy@37 184 PyObject *pyPlugModule = PyDict_GetItemString(m_pyGlobalNamespace,plugModuleName);
fazekasgy@37 185 if (!pyPlugModule) return;
fazekasgy@37 186 PyObject *pyPlugDict = PyModule_GetDict(pyPlugModule);
fazekasgy@37 187 if (!pyPlugDict) return;
fazekasgy@37 188
fazekasgy@37 189 int i = 0;
fazekasgy@37 190 while (PyExtensionManager::m_exposedNames[i]) {
fazekasgy@37 191 const char* name = PyExtensionManager::m_exposedNames[i];
fazekasgy@37 192 i++;
fazekasgy@37 193 PyObject *key = PyString_FromString(name);
fazekasgy@37 194 if (!key) break;
fazekasgy@37 195 if (PyDict_Contains(pyPlugDict,key)) {
fazekasgy@37 196 PyObject* item = PyDict_GetItem(m_pyVampyNamespace,key);
fazekasgy@37 197 if (PyDict_SetItem(pyPlugDict,key,item) != 0)
fazekasgy@37 198 cerr << "Vampy::PyExtensionManager::updateLocalNamespace: Failed: "
fazekasgy@37 199 << name << " of "<< plugModuleName << endl;
Chris@67 200 else DSTREAM << "Updated local name: " << name << endl;
Chris@89 201 } else {
Chris@89 202 DSTREAM << "Local namespace does not contain name: " << name << endl;
fazekasgy@37 203 }
fazekasgy@37 204 Py_DECREF(key);
fazekasgy@37 205 }
fazekasgy@37 206 }
fazekasgy@37 207
fazekasgy@37 208
fazekasgy@37 209 bool
fazekasgy@37 210 PyExtensionManager::cleanModule(void) const
fazekasgy@37 211 {
fazekasgy@37 212 PyObject *m = PyImport_AddModule("vampy");
fazekasgy@37 213 if (!m) {
fazekasgy@37 214 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
fazekasgy@37 215 cerr << "Vampy::PyExtensionManager::cleanModule: PyImport_AddModule returned NULL!" << endl;
fazekasgy@37 216 return false;
fazekasgy@37 217 } else {
fazekasgy@37 218 PyObject *dict = PyModule_GetDict(m);
fazekasgy@37 219 #ifdef _DEBUG
fazekasgy@37 220 Py_ssize_t ln = PyDict_Size(dict);
Chris@67 221 DSTREAM << "Vampy::PyExtensionManager::cleanModule: Size of module dict = " << (int) ln << endl;
fazekasgy@37 222 #endif
fazekasgy@37 223 /// Clean the module dictionary.
fazekasgy@37 224 // printDict(dict);
fazekasgy@37 225 PyDict_Clear(dict);
fazekasgy@37 226 if (PyErr_Occurred())
fazekasgy@37 227 { PyErr_Print(); PyErr_Clear(); return false; }
fazekasgy@37 228 PyObject *name = PyString_FromString("vampy");
fazekasgy@37 229 if (name) PyDict_SetItemString(dict,"__name__",name);
fazekasgy@37 230 Py_XDECREF(name);
fazekasgy@37 231 #ifdef _DEBUG
fazekasgy@37 232 ln = PyDict_Size(dict);
Chris@67 233 DSTREAM << "Vampy::PyExtensionManager::cleanModule: Size of module dict (cleaned) = " << (int) ln << endl;
fazekasgy@37 234 #endif
fazekasgy@37 235 return true;
fazekasgy@37 236 }
fazekasgy@37 237 }
fazekasgy@37 238
fazekasgy@37 239 void
fazekasgy@37 240 PyExtensionManager::printDict(PyObject* inDict) const
fazekasgy@37 241 {
fazekasgy@37 242 Py_ssize_t pyPos = 0;
fazekasgy@37 243 PyObject *pyKey, *pyDictValue;
fazekasgy@37 244 cerr << endl << endl << "Module dictionary contents: " << endl;
fazekasgy@37 245 while (PyDict_Next(inDict, &pyPos, &pyKey, &pyDictValue))
fazekasgy@37 246 {
fazekasgy@37 247 char *key = PyString_AS_STRING(pyKey);
fazekasgy@37 248 char *val = PyString_AS_STRING(PyObject_Str(pyDictValue));
fazekasgy@37 249 cerr << "key: [ '" << key << "' ] value: " << val << endl;
fazekasgy@37 250 }
fazekasgy@37 251 }
fazekasgy@37 252