annotate PyExtensionManager.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 c4510e5f7a17
children c7694d24500c
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
fazekasgy@37 50 PyExtensionManager::PyExtensionManager()
fazekasgy@37 51 {
Chris@67 52 DSTREAM << "Creating extension manager." << endl;
fazekasgy@37 53 }
fazekasgy@37 54
fazekasgy@37 55 bool
fazekasgy@37 56 PyExtensionManager::initExtension()
fazekasgy@37 57 {
Chris@67 58 DSTREAM << "Initialising extension module." << endl;
fazekasgy@37 59
fazekasgy@37 60 /// call the module initialiser first
fazekasgy@37 61 initvampy();
fazekasgy@37 62
fazekasgy@37 63 /// these references are all borrowed
fazekasgy@37 64 m_pyGlobalNamespace = PyImport_GetModuleDict();
fazekasgy@37 65 if (!m_pyGlobalNamespace)
fazekasgy@37 66 {cerr << "Vampy::PyExtensionManager::initExtension: GlobalNamespace failed." << endl; return false;}
fazekasgy@37 67 PyObject *pyVampyModule = PyDict_GetItemString(m_pyGlobalNamespace,"vampy");
fazekasgy@37 68 if (!pyVampyModule)
fazekasgy@37 69 {cerr << "Vampy::PyExtensionManager::initExtension: VampyModule failed." << endl; return false;}
fazekasgy@37 70 m_pyVampyNamespace = PyModule_GetDict(pyVampyModule);
fazekasgy@37 71 if (!m_pyVampyNamespace)
fazekasgy@37 72 {cerr << "Vampy::PyExtensionManager::initExtension: VampyNamespace failed." << endl; return false;}
fazekasgy@37 73
fazekasgy@37 74 /// initialise local namespaces
fazekasgy@37 75 updateAllLocals();
Chris@67 76
Chris@67 77 DSTREAM << "Vampy: Extension namespaces updated." << endl;
Chris@67 78
fazekasgy@37 79 return true;
fazekasgy@37 80 }
fazekasgy@37 81
fazekasgy@37 82
fazekasgy@37 83 PyExtensionManager::~PyExtensionManager()
fazekasgy@37 84 {
Chris@67 85 DSTREAM << "Cleaning locals..." << endl;
fazekasgy@37 86
fazekasgy@37 87 cleanAllLocals();
fazekasgy@37 88
Chris@67 89 DSTREAM << "Cleaning module..." << endl;
fazekasgy@37 90
fazekasgy@37 91 if (!cleanModule())
fazekasgy@37 92 cerr << "Vampy::~PyExtensionManager: failed to clean extension module." << endl;
Chris@67 93 DSTREAM << "Vampy::~PyExtensionManager: Extension module cleaned." << endl;
fazekasgy@37 94 }
fazekasgy@37 95
fazekasgy@37 96
fazekasgy@37 97
fazekasgy@37 98 void
fazekasgy@37 99 PyExtensionManager::setPlugModuleNames(vector<string> pyPlugs)
fazekasgy@37 100 {
fazekasgy@37 101 for (size_t i = 0; i < pyPlugs.size(); ++i) {
fazekasgy@37 102 string modName = pyPlugs[i];
fazekasgy@37 103 string tmp = modName.substr(modName.rfind(':')+1,modName.size()-1);
fazekasgy@37 104 m_plugModuleNames.push_back(tmp);
fazekasgy@37 105
Chris@67 106 DSTREAM << "Inserted module name: " << tmp << endl;
fazekasgy@37 107 }
fazekasgy@37 108 }
fazekasgy@37 109
fazekasgy@37 110 void
fazekasgy@37 111 PyExtensionManager::deleteModuleName(string plugKey)
fazekasgy@37 112 {
fazekasgy@37 113 string name = plugKey.substr(plugKey.rfind(':')+1,plugKey.size()-1);
fazekasgy@37 114 vector<string>::iterator it =
fazekasgy@37 115 find (m_plugModuleNames.begin(), m_plugModuleNames.end(), name);
fazekasgy@37 116 if (it != m_plugModuleNames.end()) m_plugModuleNames.erase(it);
Chris@67 117
Chris@67 118 DSTREAM << "PyExtensionManager::deleteModuleName: Deleted module name: " << name << endl;
fazekasgy@37 119 }
fazekasgy@37 120
fazekasgy@37 121
fazekasgy@37 122 void
fazekasgy@37 123 PyExtensionManager::cleanAllLocals() const
fazekasgy@37 124 {
fazekasgy@37 125 for (size_t i = 0; i < m_plugModuleNames.size(); ++i) {
fazekasgy@37 126 cleanLocalNamespace(m_plugModuleNames[i].c_str());
fazekasgy@37 127 }
fazekasgy@37 128 }
fazekasgy@37 129
fazekasgy@37 130 void
fazekasgy@37 131 PyExtensionManager::updateAllLocals() const
fazekasgy@37 132 {
fazekasgy@37 133 for (size_t i = 0; i < m_plugModuleNames.size(); ++i) {
fazekasgy@37 134 updateLocalNamespace(m_plugModuleNames[i].c_str());
fazekasgy@37 135 }
fazekasgy@37 136 }
fazekasgy@37 137
fazekasgy@37 138 void
fazekasgy@37 139 PyExtensionManager::cleanLocalNamespace(const char* plugModuleName) const
fazekasgy@37 140 {
Chris@91 141 DSTREAM << "Cleaning local namespace: " << plugModuleName << endl;
Chris@91 142
fazekasgy@37 143 /// these references are all borrowed
fazekasgy@37 144 PyObject *pyPlugModule = PyDict_GetItemString(m_pyGlobalNamespace,plugModuleName);
fazekasgy@37 145 if (!pyPlugModule) return;
fazekasgy@37 146 PyObject *pyPlugDict = PyModule_GetDict(pyPlugModule);
fazekasgy@37 147 if (!pyPlugDict) return;
fazekasgy@37 148
fazekasgy@37 149 int i = 0;
fazekasgy@37 150 while (PyExtensionManager::m_exposedNames[i]) {
Chris@66 151 const char* name = PyExtensionManager::m_exposedNames[i];
fazekasgy@37 152 i++;
fazekasgy@37 153 PyObject *key = PyString_FromString(name);
fazekasgy@37 154 if (!key) break;
fazekasgy@37 155 if (PyDict_Contains(pyPlugDict,key)) {
fazekasgy@37 156 if (PyDict_SetItem(pyPlugDict,key,Py_None) != 0)
fazekasgy@37 157 cerr << "Vampy::PyExtensionManager::cleanLocalNamespace: Failed: "
fazekasgy@37 158 << name << " of "<< plugModuleName << endl;
Chris@67 159 else DSTREAM << "Cleaned local name: " << name << endl;
fazekasgy@37 160 }
fazekasgy@37 161 Py_DECREF(key);
fazekasgy@37 162 }
fazekasgy@37 163 }
fazekasgy@37 164
fazekasgy@37 165 void
fazekasgy@37 166 PyExtensionManager::updateLocalNamespace(const char* plugModuleName) const
fazekasgy@37 167 {
Chris@91 168 DSTREAM << "Updating local namespace: " << plugModuleName << endl;
Chris@91 169
fazekasgy@37 170 /// this allows the use of common syntax like:
fazekasgy@37 171 /// from vampy import *
fazekasgy@37 172 /// even after several unload/reload cycles
fazekasgy@37 173
fazekasgy@37 174 /// these references are all borrowed
fazekasgy@37 175 PyObject *pyPlugModule = PyDict_GetItemString(m_pyGlobalNamespace,plugModuleName);
fazekasgy@37 176 if (!pyPlugModule) return;
fazekasgy@37 177 PyObject *pyPlugDict = PyModule_GetDict(pyPlugModule);
fazekasgy@37 178 if (!pyPlugDict) return;
fazekasgy@37 179
fazekasgy@37 180 int i = 0;
fazekasgy@37 181 while (PyExtensionManager::m_exposedNames[i]) {
fazekasgy@37 182 const char* name = PyExtensionManager::m_exposedNames[i];
fazekasgy@37 183 i++;
fazekasgy@37 184 PyObject *key = PyString_FromString(name);
fazekasgy@37 185 if (!key) break;
fazekasgy@37 186 if (PyDict_Contains(pyPlugDict,key)) {
fazekasgy@37 187 PyObject* item = PyDict_GetItem(m_pyVampyNamespace,key);
fazekasgy@37 188 if (PyDict_SetItem(pyPlugDict,key,item) != 0)
fazekasgy@37 189 cerr << "Vampy::PyExtensionManager::updateLocalNamespace: Failed: "
fazekasgy@37 190 << name << " of "<< plugModuleName << endl;
Chris@67 191 else DSTREAM << "Updated local name: " << name << endl;
Chris@89 192 } else {
Chris@89 193 DSTREAM << "Local namespace does not contain name: " << name << endl;
fazekasgy@37 194 }
fazekasgy@37 195 Py_DECREF(key);
fazekasgy@37 196 }
fazekasgy@37 197 }
fazekasgy@37 198
fazekasgy@37 199
fazekasgy@37 200 bool
fazekasgy@37 201 PyExtensionManager::cleanModule(void) const
fazekasgy@37 202 {
fazekasgy@37 203 PyObject *m = PyImport_AddModule("vampy");
fazekasgy@37 204 if (!m) {
fazekasgy@37 205 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
fazekasgy@37 206 cerr << "Vampy::PyExtensionManager::cleanModule: PyImport_AddModule returned NULL!" << endl;
fazekasgy@37 207 return false;
fazekasgy@37 208 } else {
fazekasgy@37 209 PyObject *dict = PyModule_GetDict(m);
fazekasgy@37 210 #ifdef _DEBUG
fazekasgy@37 211 Py_ssize_t ln = PyDict_Size(dict);
Chris@67 212 DSTREAM << "Vampy::PyExtensionManager::cleanModule: Size of module dict = " << (int) ln << endl;
fazekasgy@37 213 #endif
fazekasgy@37 214 /// Clean the module dictionary.
fazekasgy@37 215 // printDict(dict);
fazekasgy@37 216 PyDict_Clear(dict);
fazekasgy@37 217 if (PyErr_Occurred())
fazekasgy@37 218 { PyErr_Print(); PyErr_Clear(); return false; }
fazekasgy@37 219 PyObject *name = PyString_FromString("vampy");
fazekasgy@37 220 if (name) PyDict_SetItemString(dict,"__name__",name);
fazekasgy@37 221 Py_XDECREF(name);
fazekasgy@37 222 #ifdef _DEBUG
fazekasgy@37 223 ln = PyDict_Size(dict);
Chris@67 224 DSTREAM << "Vampy::PyExtensionManager::cleanModule: Size of module dict (cleaned) = " << (int) ln << endl;
fazekasgy@37 225 #endif
fazekasgy@37 226 return true;
fazekasgy@37 227 }
fazekasgy@37 228 }
fazekasgy@37 229
fazekasgy@37 230 void
fazekasgy@37 231 PyExtensionManager::printDict(PyObject* inDict) const
fazekasgy@37 232 {
fazekasgy@37 233 Py_ssize_t pyPos = 0;
fazekasgy@37 234 PyObject *pyKey, *pyDictValue;
fazekasgy@37 235 cerr << endl << endl << "Module dictionary contents: " << endl;
fazekasgy@37 236 while (PyDict_Next(inDict, &pyPos, &pyKey, &pyDictValue))
fazekasgy@37 237 {
fazekasgy@37 238 char *key = PyString_AS_STRING(pyKey);
fazekasgy@37 239 char *val = PyString_AS_STRING(PyObject_Str(pyDictValue));
fazekasgy@37 240 cerr << "key: [ '" << key << "' ] value: " << val << endl;
fazekasgy@37 241 }
fazekasgy@37 242 }
fazekasgy@37 243