comparison PyPlugScanner.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 0120dac53a69
children 2f2292b029a4
comparison
equal deleted inserted replaced
91:c4510e5f7a17 92:a6718f9fe942
1 /* -*- c-basic-offset: 8 indent-tabs-mode: t -*- */
1 /* 2 /*
2 3
3 * Vampy : This plugin is a wrapper around the Vamp plugin API. 4 * Vampy : This plugin is a wrapper around the Vamp plugin API.
4 * It allows for writing Vamp plugins in Python. 5 * It allows for writing Vamp plugins in Python.
5 6
9 10
10 */ 11 */
11 12
12 13
13 #include "PyPlugScanner.h" 14 #include "PyPlugScanner.h"
15 #include "PyExtensionManager.h"
16 #include "Debug.h"
14 #include <algorithm> 17 #include <algorithm>
15 #include <cstdlib> 18 #include <cstdlib>
16 //#include "vamp-hostsdk/PluginHostAdapter.h" 19 //#include "vamp-hostsdk/PluginHostAdapter.h"
17 20
18 #ifdef _WIN32 21 #ifdef _WIN32
93 if (!script.empty()) { 96 if (!script.empty()) {
94 string classname=script.substr(0,script.rfind('.')); 97 string classname=script.substr(0,script.rfind('.'));
95 pluginKey=joinPath(m_path[i],script)+":"+classname; 98 pluginKey=joinPath(m_path[i],script)+":"+classname;
96 pyClass = getScriptClass(m_path[i],classname); 99 pyClass = getScriptClass(m_path[i],classname);
97 if (pyClass == NULL) 100 if (pyClass == NULL)
98 cerr << "Warning: Syntax error in VamPy plugin: " 101 cerr << "Warning: Syntax error or other problem in scanning VamPy plugin: "
99 << classname << ". Avoiding plugin." << endl; 102 << classname << ". Avoiding plugin." << endl;
100 else { 103 else {
101 pyPlugs.push_back(pluginKey); 104 pyPlugs.push_back(pluginKey);
102 m_pyClasses.push_back(pyClass); 105 m_pyClasses.push_back(pyClass);
103 } 106 }
104 //pyPlugs.push_back(pluginKey);
105 } 107 }
106 } 108 }
107 } 109 }
108 110
109 return pyPlugs; 111 return pyPlugs;
140 //Validate 142 //Validate
141 //This should not be called more than once! 143 //This should not be called more than once!
142 PyObject* 144 PyObject*
143 PyPlugScanner::getScriptClass(string path, string classname) 145 PyPlugScanner::getScriptClass(string path, string classname)
144 { 146 {
145
146 //Add plugin path to active Python Path 147 //Add plugin path to active Python Path
147 string pyCmd = "import sys\nsys.path.append('" + path + "')\n"; 148 string pyCmd = "import sys\nsys.path.append('" + path + "')\n";
148 PyRun_SimpleString(pyCmd.c_str()); 149 PyRun_SimpleString(pyCmd.c_str());
149 150
150 //Assign an object to the source code 151 //Assign an object to the source code
167 Py_DECREF(pyModule); 168 Py_DECREF(pyModule);
168 169
169 //Get the PluginClass from the module (borrowed reference) 170 //Get the PluginClass from the module (borrowed reference)
170 PyObject *pyClass = PyDict_GetItemString(pyDict, classname.c_str()); 171 PyObject *pyClass = PyDict_GetItemString(pyDict, classname.c_str());
171 172
172 //Check if class is present and a callable method is implemented 173 if (pyClass == Py_None) {
173 if (pyClass && PyCallable_Check(pyClass)) { 174 DSTREAM << "Vampy: class name " << classname
174 175 << " is None in module; assuming it was scrubbed "
175 return pyClass; 176 << "following an earlier load failure" << endl;
176 } 177 return NULL;
177 else { 178 }
179
180 // Check if class is present and a callable method is implemented
181 if (!pyClass || !PyCallable_Check(pyClass)) {
178 cerr << "ERROR: callable plugin class could not be found in source: " << classname << endl 182 cerr << "ERROR: callable plugin class could not be found in source: " << classname << endl
179 << "Hint: plugin source filename and plugin class name must be the same." << endl; 183 << "Hint: plugin source filename and plugin class name must be the same." << endl;
180 PyErr_Print(); 184 PyErr_Print();
185 return NULL;
186 }
187
188 bool acceptable = true;
189
190 // Check that the module doesn't have any name collisions with
191 // our own symbols
192
193 int i = 0;
194 while (PyExtensionManager::m_exposedNames[i]) {
195
196 const char* name = PyExtensionManager::m_exposedNames[i];
197 i++;
198
199 PyObject *item = PyDict_GetItemString(pyDict, name);
200 if (!item) continue;
201
202 if (item == Py_None) {
203 DSTREAM << "Vampy: name " << name << " is None "
204 << "in module " << classname
205 << "; assuming it was cleared on unload"
206 << endl;
207 continue;
208 }
209
210 PyTypeObject *metatype = Py_TYPE(item);
211
212 if (!strcmp(name, "frame2RealTime")) {
213 if (metatype != &PyCFunction_Type) {
214 cerr << "ERROR: plugin " << classname
215 << " redefines Vampy function name \""
216 << name << "\" (metatype is \""
217 << metatype->tp_name << "\")" << endl;
218 acceptable = false;
219 break;
220 } else {
221 continue;
222 }
223 }
224
225 if (metatype != &PyType_Type) {
226 cerr << "ERROR: plugin " << classname
227 << " uses Vampy reserved type name \"" << name
228 << "\" for non-type (metatype is \""
229 << metatype->tp_name << "\")" << endl;
230 acceptable = false;
231 break;
232 }
233
234 PyTypeObject *type = (PyTypeObject *)item;
235 if (type->tp_name == std::string("vampy.") + name) {
236 DSTREAM << "Vampy: acceptable Vampy type name "
237 << type->tp_name << " found in module" << endl;
238 } else {
239 cerr << "ERROR: plugin " << classname
240 << " redefines Vampy type \"" << name << "\"";
241 if (strcmp(type->tp_name, name)) {
242 cerr << " (as \"" << type->tp_name << "\")";
243 }
244 cerr << endl;
245 acceptable = false;
246 break;
247 }
248 }
249
250 if (acceptable) {
251 return pyClass;
252 } else {
253 PyObject *key = PyString_FromString(classname.c_str());
254 PyDict_SetItem(pyDict, key, Py_None);
255 Py_DECREF(key);
181 return NULL; 256 return NULL;
182 } 257 }
183 } 258 }
184 259
185 260