PLearn 0.1
|
00001 // -*- C++ -*- 00002 00003 // PythonCodeSnippet.cc 00004 // 00005 // Copyright (C) 2005 Nicolas Chapados 00006 // 00007 // Redistribution and use in source and binary forms, with or without 00008 // modification, are permitted provided that the following conditions are met: 00009 // 00010 // 1. Redistributions of source code must retain the above copyright 00011 // notice, this list of conditions and the following disclaimer. 00012 // 00013 // 2. Redistributions in binary form must reproduce the above copyright 00014 // notice, this list of conditions and the following disclaimer in the 00015 // documentation and/or other materials provided with the distribution. 00016 // 00017 // 3. The name of the authors may not be used to endorse or promote 00018 // products derived from this software without specific prior written 00019 // permission. 00020 // 00021 // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 00022 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 00023 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 00024 // NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 00025 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 00026 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00027 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 00028 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00029 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00030 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00031 // 00032 // This file is part of the PLearn library. For more information on the PLearn 00033 // library, go to the PLearn Web site at www.plearn.org 00034 00035 /* ******************************************************* 00036 * $Id: PythonCodeSnippet.cc 2771 2005-08-11 22:06:13Z chapados $ 00037 ******************************************************* */ 00038 00039 // Authors: Nicolas Chapados 00040 00043 // Python stuff must be included first 00044 #include "PythonCodeSnippet.h" 00045 #include "PythonEmbedder.h" 00046 #include "PythonExtension.h" 00047 00048 // From PLearn 00049 #include <plearn/io/fileutils.h> 00050 #include <plearn/base/tostring.h> 00051 00052 #ifdef WIN32 00053 #include <plearn/base/stringutils.h> // For 'search_replace'. 00054 #endif 00055 00056 00057 namespace PLearn { 00058 using namespace std; 00059 00060 // #if sizeof(long) < sizeof(void*) 00061 // #error "Snippets' addresses need to be casted to long" 00062 // #endif 00063 00064 const char* PythonCodeSnippet::InjectSetupSnippet =\ 00065 // Redefines import statement behavior 00066 "from plearn.utilities import inject_import as _inject_import_\n" 00067 // The dictionnary in which to inject 00068 "__injected__ = {}\n"; 00069 00070 const char* PythonCodeSnippet::SetCurrentSnippetVar =\ 00071 // Completed by sprintf using the snippet's address hex value 00072 "_inject_import_.setCurrentSnippet(%p)\n"; 00073 00074 const char* PythonCodeSnippet::ResetCurrentSnippetVar = \ 00075 "_inject_import_.resetCurrentSnippet()\n"; 00076 00077 //##### PythonCodeSnippet ################################################### 00078 00079 PLEARN_IMPLEMENT_OBJECT( 00080 PythonCodeSnippet, 00081 "Enables embedded Python code to be called from PLearn/C++ code.", 00082 "This class enables an embedded Python code snippet to be compiled and\n" 00083 "called back later. It is not designed to be used by itself, but rather in\n" 00084 "conjunction with specific PLearn objects that understand the\n" 00085 "PythonCodeSnippet calling protocol.\n" 00086 "\n" 00087 "Note that global variables can be used, in the Python code, to keep a\n" 00088 "\"living state\", used to carry information across calls to Python functions.\n" 00089 "\n" 00090 "A note on exception behavior within the PythonCodeSnippet:\n" 00091 "\n" 00092 "- Exceptions that are raised within executed Python code are handled\n" 00093 " according to the 'remap_python_exceptions' option. Basically, client\n" 00094 " code to the PythonCodeSnippet has the choice of either generating a\n" 00095 " PLERROR from the Python Exception, or of remapping this exception into\n" 00096 " a C++ exception (of class PythonException, subclass of PLearnError).\n" 00097 "\n" 00098 "- C++ exceptions that are thrown from inside injected code functions\n" 00099 " are remapped into Python exceptions by the trampoline handler. These\n" 00100 " Python exceptions are then handled according to the behavior in the\n" 00101 " previous point. Note that, for now, all C++ exceptions are turned into\n" 00102 " a generic Python 'Exception' (base class for all exceptions).\n" 00103 "\n" 00104 "The current implementation of the PythonCodeSnippet is designed to be\n" 00105 "thread-safe, i.e. the Python Global Interpreter Lock is always acquired\n" 00106 "before sensitive operations are carried out.\n" 00107 ); 00108 00109 00110 PythonCodeSnippet::PythonCodeSnippet(const string& code, 00111 bool remap_python_exceptions) 00112 : inherited(), 00113 m_code(code), 00114 m_remap_python_exceptions(remap_python_exceptions), 00115 m_instance_params(), 00116 m_instance(), 00117 m_handle(this), 00118 m_compiled_code(), 00119 m_injected_functions(4), 00120 m_python_methods(4) 00121 { 00122 // NOTE: build() not called 00123 } 00124 00125 00126 PythonCodeSnippet::PythonCodeSnippet(const PythonObjectWrapper& instance, 00127 bool remap_python_exceptions) 00128 : inherited(), 00129 m_code(""), 00130 m_remap_python_exceptions(remap_python_exceptions), 00131 m_instance_params(), 00132 m_instance(instance), 00133 m_handle(this), 00134 m_compiled_code(), 00135 m_injected_functions(4), 00136 m_python_methods(4) 00137 { 00138 PyObject* compiled_code= 00139 PyObject_GetAttrString(m_instance.getPyObject(), const_cast<char*>("__dict__")); 00140 m_compiled_code= PythonObjectWrapper(compiled_code, PythonObjectWrapper::transfer_ownership); 00141 // NOTE: build() not called 00142 } 00143 00144 void PythonCodeSnippet::declareOptions(OptionList& ol) 00145 { 00146 declareOption( 00147 ol, "code", &PythonCodeSnippet::m_code, 00148 OptionBase::buildoption, 00149 "Python statement list that should be compiled at build time to provide\n" 00150 "the desired functions (defined by the client code to PythonCodeSnippet)\n" 00151 "and otherwise set up the Python global namespace. Note that the Python\n" 00152 "'__builtins__' module is always injected into the global namespace.\n" 00153 "You should also add the statement\n" 00154 "\n" 00155 " from numarray import *'\n" 00156 "\n" 00157 "to manipulate PLearn Vec and Mat.\n"); 00158 00159 declareOption( 00160 ol, "remap_python_exceptions", &PythonCodeSnippet::m_remap_python_exceptions, 00161 OptionBase::buildoption, 00162 "If true, Python exceptions raised during function execution are mapped\n" 00163 "to a C++ exception. If false, then a normal Python stack dump is\n" 00164 "output to stderr and a PLERROR is raised. Default=false."); 00165 00166 declareOption( 00167 ol, "instance_params", &PythonCodeSnippet::m_instance_params, 00168 OptionBase::buildoption, 00169 "If this snippet represents a python object, these are the\n" 00170 "parameters passed to the object's constructor."); 00171 00172 // Now call the parent class' declareOptions 00173 inherited::declareOptions(ol); 00174 } 00175 00176 void PythonCodeSnippet::build_() 00177 { 00178 static PythonEmbedder python; 00179 static bool numarray_initialized = false; 00180 if (! numarray_initialized) { 00181 // must be in each translation unit that makes use of libnumarray; 00182 // weird stuff related to table of function pointers that's being 00183 // initialized into a STATIC VARIABLE of the translation unit! 00184 import_libnumarray(); 00185 numarray_initialized = true; 00186 00187 PythonObjectWrapper::initializePython(); 00188 } 00189 00190 // Compile code into global environment 00191 if (m_code != ""){ 00192 // Here we don't call setCurrentSnippet() because it has to be called 00193 // between the setup and the m_code... Still have to call 00194 // resetCurrentSnippet() afterwards though. 00195 char set_current_snippet[100]; 00196 sprintf(set_current_snippet, SetCurrentSnippetVar, m_handle); 00197 m_compiled_code = compileGlobalCode(InjectSetupSnippet+ 00198 string(set_current_snippet)+m_code); 00199 if(m_instance.isNull()) 00200 resetCurrentSnippet(); 00201 } 00202 00203 // Forget about injected functions 00204 m_injected_functions.purge_memory(); 00205 m_python_methods.purge_memory(); 00206 } 00207 00208 // ### Nothing to add here, simply calls build_ 00209 void PythonCodeSnippet::build() 00210 { 00211 inherited::build(); 00212 build_(); 00213 } 00214 00215 void PythonCodeSnippet::makeDeepCopyFromShallowCopy( 00216 CopiesMap& copies) 00217 { 00218 inherited::makeDeepCopyFromShallowCopy(copies); 00219 00220 // Compile fresh code into global environment 00221 m_compiled_code = compileGlobalCode(m_code); 00222 00223 // Forget about injected functions (not necessarily the correct thing to do...) 00224 m_injected_functions.purge_memory(); 00225 m_python_methods.purge_memory(); 00226 } 00227 00228 00229 //##### Global Environment Interface ######################################## 00230 00231 PythonObjectWrapper 00232 PythonCodeSnippet::getGlobalObject(const string& object_name) const 00233 { 00234 PythonGlobalInterpreterLock gil; // For thread-safety 00235 PyObject* pyobj; 00236 if(!m_instance.isNull()) 00237 pyobj= PyObject_GetAttrString(m_instance.getPyObject(), 00238 const_cast<char*>(object_name.c_str())); 00239 else 00240 pyobj= PyDict_GetItemString(m_compiled_code.getPyObject(), 00241 object_name.c_str()); 00242 if (pyobj) { 00243 // pyobj == borrowed reference 00244 // Increment refcount to keep long-lived reference 00245 Py_XINCREF(pyobj); 00246 return PythonObjectWrapper(pyobj); 00247 } 00248 return PythonObjectWrapper(); // None 00249 } 00250 00251 void PythonCodeSnippet::setGlobalObject(const string& object_name, 00252 const PythonObjectWrapper& pow) 00253 { 00254 PythonGlobalInterpreterLock gil; // For thread-safety 00255 00256 // Note that PyDict_SetItemString increments the reference count for us 00257 int non_success = 0; 00258 if(!m_instance.isNull()) 00259 non_success= PyObject_SetAttrString(m_instance.getPyObject(), 00260 const_cast<char*>(object_name.c_str()), 00261 pow.getPyObject()); 00262 else if (! pow.isNull()) 00263 non_success = PyDict_SetItemString(m_compiled_code.getPyObject(), 00264 object_name.c_str(), 00265 pow.getPyObject()); 00266 else 00267 non_success = PyDict_SetItemString(m_compiled_code.getPyObject(), 00268 object_name.c_str(), 00269 Py_None); 00270 00271 if (non_success) 00272 PLERROR("PythonCodeSnippet::setGlobalObject: error inserting a global Python \n" 00273 "object under the name '%s'", object_name.c_str()); 00274 } 00275 00276 00277 //##### Function Call Interface ############################################# 00278 00279 bool PythonCodeSnippet::isInvokable(const char* function_name) const 00280 { 00281 PythonGlobalInterpreterLock gil; // For thread-safety 00282 00283 PyObject* pFunc= 0; 00284 bool instance_method= false; 00285 if(!m_instance.isNull()) 00286 { 00287 char* fn= new char[strlen(function_name)+1]; 00288 strcpy(fn, function_name); 00289 if(PyObject_HasAttrString(m_instance.getPyObject(), fn)) 00290 pFunc= PyObject_GetAttrString(m_instance.getPyObject(), fn); 00291 delete[] fn; 00292 } 00293 if(pFunc) 00294 instance_method= true; 00295 else 00296 pFunc= PyDict_GetItemString(m_compiled_code.getPyObject(), 00297 function_name); 00298 // pFunc: Borrowed reference if not instance_method 00299 bool ret= pFunc && PyCallable_Check(pFunc); 00300 if(instance_method) {Py_DECREF(pFunc);} 00301 return ret; 00302 } 00303 00304 00305 // Zero-argument function call 00306 PythonObjectWrapper 00307 PythonCodeSnippet::invoke(const char* function_name) const 00308 { 00309 PythonGlobalInterpreterLock gil; // For thread-safety 00310 00311 PyObject* pFunc= 0; 00312 bool instance_method= false; 00313 if(!m_instance.isNull()) 00314 { 00315 char* fn= new char[strlen(function_name)+1]; 00316 strcpy(fn, function_name); 00317 if(PyObject_HasAttrString(m_instance.getPyObject(), fn)) 00318 pFunc= PyObject_GetAttrString(m_instance.getPyObject(), fn); 00319 delete[] fn; 00320 } 00321 if(pFunc) 00322 instance_method= true; 00323 else 00324 pFunc= PyDict_GetItemString(m_compiled_code.getPyObject(), 00325 function_name); 00326 00327 // pFunc: Borrowed reference if not instance_method 00328 00329 PyObject* return_value = 0; 00330 if (pFunc && PyCallable_Check(pFunc)) { 00331 if(!instance_method) 00332 setCurrentSnippet(m_handle); 00333 00334 return_value = PyObject_CallObject(pFunc, NULL); 00335 if (! return_value) 00336 { 00337 if(instance_method){ 00338 Py_DECREF(pFunc); 00339 } 00340 handlePythonErrors(string("Error while calling function '") 00341 + function_name 00342 + "' with no params."); 00343 } 00344 00345 if(!instance_method) 00346 resetCurrentSnippet(); 00347 } 00348 else 00349 { 00350 if(instance_method) {Py_DECREF(pFunc);} 00351 PLERROR("PythonCodeSnippet::invoke: cannot call function '%s' (not callable).", 00352 function_name); 00353 } 00354 00355 if(instance_method) {Py_DECREF(pFunc);} 00356 //return PythonObjectWrapper(return_value); 00357 PythonObjectWrapper r(return_value); 00358 Py_DECREF(return_value); 00359 return r; 00360 } 00361 00362 00363 // N-argument function call 00364 PythonObjectWrapper 00365 PythonCodeSnippet::invoke(const char* function_name, 00366 const TVec<PythonObjectWrapper>& args) const 00367 { 00368 PythonGlobalInterpreterLock gil; // For thread-safety 00369 00370 PyObject* pFunc= 0; 00371 bool instance_method= false; 00372 if(!m_instance.isNull()) 00373 { 00374 char* fn= new char[strlen(function_name)+1]; 00375 strcpy(fn, function_name); 00376 if(PyObject_HasAttrString(m_instance.getPyObject(), fn)) 00377 pFunc= PyObject_GetAttrString(m_instance.getPyObject(), fn); 00378 delete[] fn; 00379 } 00380 if(pFunc) 00381 instance_method= true; 00382 else 00383 pFunc= PyDict_GetItemString(m_compiled_code.getPyObject(), 00384 function_name); 00385 00386 // pFunc: Borrowed reference if not instance_method 00387 00388 PyObject* return_value = 0; 00389 if (pFunc && PyCallable_Check(pFunc)) { 00390 if(!instance_method) 00391 setCurrentSnippet(m_handle); 00392 00393 // Create argument tuple. Warning: PyTuple_SetItem STEALS references. 00394 PyObject* pArgs = PyTuple_New(args.size()); 00395 for (int i=0, n=args.size() ; i<n ; ++i) 00396 { 00397 PyTuple_SetItem(pArgs, i, args[i].getPyObject()); 00398 Py_INCREF(args[i].getPyObject()); 00399 } 00400 00401 return_value = PyObject_CallObject(pFunc, pArgs); 00402 00403 Py_DECREF(pArgs); 00404 if (! return_value) 00405 { 00406 if(instance_method) 00407 {Py_DECREF(pFunc);} 00408 handlePythonErrors(string("Error while calling function '") 00409 + function_name 00410 + "' with " 00411 + tostring(args.length()) 00412 + " params."); 00413 } 00414 if(!instance_method) 00415 resetCurrentSnippet(); 00416 } 00417 else 00418 { 00419 if(instance_method) 00420 {Py_DECREF(pFunc);} 00421 PLERROR("PythonCodeSnippet::invoke: cannot call function '%s'", 00422 function_name); 00423 } 00424 00425 if(instance_method) 00426 {Py_DECREF(pFunc);} 00427 //return PythonObjectWrapper(return_value); 00428 PythonObjectWrapper r(return_value); 00429 Py_DECREF(return_value); 00430 return r; 00431 } 00432 00433 00434 //##### Function Injection Interface ######################################## 00435 00436 // This is the function actually called by Python. Be careful to remap 00437 // exceptions thrown by C++ into Python exceptions. 00438 PyObject* PythonCodeSnippet::pythonTrampoline(PyObject* self, PyObject* args) 00439 { 00440 PythonGlobalInterpreterLock gil; // For thread-safety 00441 try { 00442 // Transform the args tuple into a TVec of not-owned PythonObjectWrapper 00443 if (! PyTuple_Check(args)) 00444 PLERROR("PythonCodeSnippet.cc:python_trampoline: the Python interpreter " 00445 "did not pass a Tuple as the arguments object."); 00446 00447 int size = PyTuple_GET_SIZE(args); 00448 TVec<PythonObjectWrapper> args_tvec(size); 00449 for (int i=0 ; i<size ; ++i) { 00450 args_tvec[i]= 00451 PythonObjectWrapper(PyTuple_GET_ITEM(args,i)); 00452 } 00453 00454 // Now get the void* stored within the PyCObject of self 00455 StandaloneFunction* func = 00456 static_cast<StandaloneFunction*>(PyCObject_AsVoidPtr(self)); 00457 PythonObjectWrapper returned_value = (*func)(args_tvec); 00458 PyObject* to_return = returned_value.getPyObject(); 00459 Py_XINCREF(to_return); 00460 return to_return; 00461 } 00462 // Catch PLERROR and such 00463 catch (const PLearnError& e) { 00464 PyErr_SetString(PyExc_Exception, 00465 (string("PLearn Error: ")+e.message()).c_str()); 00466 return NULL; 00467 } 00468 // Catch C++ stdlib exceptions 00469 catch (const std::exception& e) { 00470 PyErr_SetString(PyExc_Exception, 00471 (string("C++ stdlib error: ")+e.what()).c_str()); 00472 return NULL; 00473 } 00474 // Catch any other unexpected exceptions 00475 catch (...) { 00476 PyErr_SetString(PyExc_Exception, 00477 "Caught unknown C++ exception while executing injected function " 00478 "inside a PythonCodeSnippet"); 00479 return NULL; 00480 } 00481 } 00482 00483 00484 // Bind "standalone functions" to a Python name 00485 void PythonCodeSnippet::injectInternal(const char* python_name, 00486 StandaloneFunction* function_ptr) 00487 { 00488 PythonGlobalInterpreterLock gil; // For thread-safety 00489 00490 // Wrap the function_ptr into a PyCObject 00491 PyObject* self = PyCObject_FromVoidPtr(function_ptr, NULL); 00492 00493 // Create a Python Function Object 00494 PyMethodDef* py_method = m_python_methods.allocate(); 00495 py_method->ml_name = const_cast<char*>(python_name); 00496 py_method->ml_meth = pythonTrampoline; 00497 py_method->ml_flags = METH_VARARGS; 00498 py_method->ml_doc = const_cast<char*>("injected-function-from-PythonCodeSnippet"); 00499 00500 PyObject* py_funcobj = PyCFunction_NewEx(py_method, 00501 self /* info for trampoline */, 00502 NULL /* module */); 00503 00504 if (py_funcobj) { 00505 // Inject into the running snippet. Note that when a 00506 // PythonObjectWrapper is constructed from a PyObject, it steals the 00507 // refcount, so we don't need to perform a Py_XDECREF on py_funcobj. 00508 this->setGlobalObject(python_name, py_funcobj); 00509 if(!m_instance.isNull()) 00510 { 00511 char* fn= new char[strlen(python_name)+1]; 00512 strcpy(fn, python_name); 00513 PyObject_SetAttrString(m_instance.getPyObject(), 00514 fn, py_funcobj); 00515 delete[] fn; 00516 } 00517 else 00518 { 00519 // Publish the injection in the '__injected__' dictionary for imported modules 00520 PythonObjectWrapper inj_dict = this->getGlobalObject("__injected__"); 00521 PyDict_SetItemString(inj_dict.getPyObject(), python_name, py_funcobj); 00522 00523 Py_XDECREF(self); 00524 } 00525 } 00526 else 00527 PLERROR("PythonCodeSnippet::injectInternal: failed to inject " 00528 "Python function '%s'", python_name); 00529 } 00530 00531 00532 // High-level injection interface 00533 void PythonCodeSnippet::inject(const char* python_name, 00534 StandaloneFunction function_ptr) 00535 { 00536 StandaloneFunction* pfunc = m_injected_functions.allocate(); 00537 new(pfunc) StandaloneFunction(function_ptr); // In-place copy constructor 00538 injectInternal(python_name, pfunc); 00539 } 00540 00541 00542 //##### Miscellaneous Functions ############################################# 00543 00544 PythonObjectWrapper PythonCodeSnippet::compileGlobalCode(const string& code) //const 00545 { 00546 PythonGlobalInterpreterLock gil; // For thread-safety 00547 00548 PyObject* globals = PyDict_New(); 00549 PyDict_SetItemString(globals, "__builtins__", PyEval_GetBuiltins()); 00550 00551 //always include EmbeddedCodeSnippet to check for an object to instantiate 00552 string extracode= "\nfrom plearn.pybridge.embedded_code_snippet " 00553 "import EmbeddedCodeSnippet\n" 00554 "from plearn.pybridge import pl_global_funcs\n"; 00555 00556 if (code != "") { 00557 #ifdef WIN32 00558 // Under Windows, it appears the Python code will not execute with 00559 // Windows carriage returns. Thus we first make a copy of the code and 00560 // replace any carriage return by a Unix one. 00561 string code_copy = code; 00562 PLearn::search_replace(code_copy, "\r\n", "\n"); 00563 #else 00564 const string& code_copy = code; 00565 #endif 00566 PyObject* res= PyRun_String((code_copy+extracode).c_str(), 00567 Py_file_input /* exec code block */, 00568 globals, globals); 00569 Py_XDECREF(res); 00570 if (PyErr_Occurred()) { 00571 Py_XDECREF(globals); 00572 PyErr_Print(); 00573 PLERROR("in PythonCodeSnippet::compileGlobalCode : error compiling " 00574 "Python code contained in the 'code' option."); 00575 } 00576 } 00577 00578 //get the global env. as an stl map 00579 PythonObjectWrapper wrapped_globals(globals); 00580 Py_XDECREF(globals); 00581 map<string, PyObject*> global_map= 00582 wrapped_globals.as<map<string, PyObject*> >(); 00583 00584 //inject global funcs, if not already done 00585 static bool global_funcs_injected= false; 00586 if(!global_funcs_injected) 00587 { 00588 map<string, PyObject*>::iterator it= 00589 global_map.find("pl_global_funcs"); 00590 if(it == global_map.end()) 00591 PLERROR("in PythonCodeSnippet::compileGlobalCode : " 00592 "plearn.pybridge.pl_global_funcs not present in global env!"); 00593 setPythonModuleAndInject(it->second); 00594 global_funcs_injected= true; 00595 } 00596 00597 //try to find an EmbeddedCodeSnippet to instantiate 00598 PyObject* snippet_found= 0; 00599 map<string, PyObject*>::iterator it_id= 00600 global_map.find("pl_embedded_code_snippet_type"); 00601 00602 if(it_id != global_map.end()) 00603 snippet_found= it_id->second; 00604 else //check for a single class deriving from EmbeddedCodeSnippet 00605 { 00606 list<pair<string, PyObject*> > classes_found; 00607 00608 //iter (find) 00609 PyTypeObject* embedded_code_snippet_type= 00610 (PyTypeObject*)global_map["EmbeddedCodeSnippet"]; 00611 00612 //find all classes deriving from EmbeddedCodeSnippet 00613 for(map<string, PyObject*>::iterator it= global_map.begin(); 00614 it != global_map.end(); ++it) 00615 { 00616 if(PyType_Check(it->second) 00617 && 0 != PyObject_Compare(it->second, 00618 (PyObject*)embedded_code_snippet_type) 00619 && PyType_IsSubtype((PyTypeObject*)it->second, 00620 embedded_code_snippet_type)) 00621 { 00622 classes_found.push_back(*it); 00623 } 00624 } 00625 00626 int nclasses= classes_found.size(); 00627 list<pair<string, PyObject*> >::iterator jt= classes_found.begin(); 00628 if(nclasses > 1) 00629 { 00630 string classes_list= jt->first; 00631 for(++jt; jt != classes_found.end(); ++jt) 00632 classes_list+= string(", ") + jt->first; 00633 PLERROR("in PythonCodeSnippet::compileGlobalCode : " 00634 "more than one class derives from EmbeddedCodeSnippet " 00635 "and pl_embedded_code_snippet_type is not defined. " 00636 "classes= [%s]", 00637 classes_list.c_str()); 00638 } 00639 if(nclasses == 1) 00640 snippet_found= jt->second; 00641 } 00642 00643 if(snippet_found) 00644 {//instantiate object of appropriate type 00645 PyObject* pyparams= PyDict_New(); 00646 if(!pyparams) 00647 handlePythonErrors(); 00648 for(map<string, string>::const_iterator it= m_instance_params.begin(); 00649 it != m_instance_params.end(); ++it) 00650 {// fill kwargs 00651 PyObject* val= PyString_FromString(it->second.c_str()); 00652 PyDict_SetItemString(pyparams, it->first.c_str(), val); 00653 Py_DECREF(val); 00654 } 00655 00656 if(!PyCallable_Check(snippet_found)) 00657 PLERROR("in PythonCodeSnippet::compileGlobalCode : " 00658 "found something that is not callable [not a class?]"); 00659 00660 PyObject* pargs= PyTuple_New(0); 00661 PyObject* the_obj= PyObject_Call(snippet_found, pargs, pyparams); 00662 Py_DECREF(pyparams); 00663 Py_DECREF(pargs); 00664 if(!the_obj) 00665 { 00666 if (PyErr_Occurred()) 00667 PyErr_Print(); 00668 PLERROR("in PythonCodeSnippet::compileGlobalCode : " 00669 "found subclass of EmbeddedCodeSnippet, but can't " 00670 "call constructor with given params. " 00671 "class='%s', params=%s", 00672 ((PyTypeObject*)snippet_found)->tp_name, 00673 tostring(m_instance_params).c_str()); 00674 } 00675 m_instance= PythonObjectWrapper(the_obj); 00676 } 00677 00678 return wrapped_globals; 00679 } 00680 00681 void PythonCodeSnippet::run() 00682 { 00683 if(m_instance.isNull()) 00684 PLERROR("in PythonCodeSnippet::run : this snippet is not " 00685 "an instance of EmbeddedCodeSnippet"); 00686 if(!PyCallable_Check(m_instance.getPyObject())) 00687 PLERROR("in PythonCodeSnippet::run : this instance of " 00688 "EmbeddedCodeSnippet is not callable."); 00689 PyObject* pargs= PyTuple_New(0); 00690 PyObject* res= PyObject_Call(m_instance.getPyObject(), pargs, 0); 00691 00692 Py_DECREF(pargs); 00693 if(!res) handlePythonErrors(); 00694 Py_XDECREF(res); 00695 } 00696 00697 void PythonCodeSnippet::setCurrentSnippet(const void* handle) const 00698 { 00699 PythonGlobalInterpreterLock gil; // For thread-safety 00700 00701 char set_current_snippet[100]; 00702 sprintf(set_current_snippet, SetCurrentSnippetVar, handle); 00703 PyObject* res= PyRun_String(set_current_snippet, 00704 Py_file_input /* exec code block */, 00705 m_compiled_code.getPyObject(), 00706 m_compiled_code.getPyObject()); 00707 00708 Py_XDECREF(res); 00709 if (PyErr_Occurred()) { 00710 Py_XDECREF(m_compiled_code.getPyObject()); 00711 PyErr_Print(); 00712 PLERROR("PythonCodeSnippet::setCurrentSnippet: error compiling " 00713 "Python code contained in the 'SetCurrentSnippetVar'." 00714 "\n\t'%s'", set_current_snippet); 00715 } 00716 } 00717 00718 void PythonCodeSnippet::resetCurrentSnippet() const 00719 { 00720 PythonGlobalInterpreterLock gil; // For thread-safety 00721 00722 PyObject* res= PyRun_String(ResetCurrentSnippetVar, 00723 Py_file_input /* exec code block */, 00724 m_compiled_code.getPyObject(), 00725 m_compiled_code.getPyObject()); 00726 Py_XDECREF(res); 00727 if (PyErr_Occurred()) { 00728 Py_XDECREF(m_compiled_code.getPyObject()); 00729 PyErr_Print(); 00730 PLERROR("PythonCodeSnippet::resetCurrentSnippet: error compiling " 00731 "Python code contained in the 'ResetCurrentSnippetVar'."); 00732 } 00733 } 00734 00735 void PythonCodeSnippet::handlePythonErrors(const string& extramsg) const 00736 { 00737 PythonGlobalInterpreterLock gil; // For thread-safety 00738 if (PyErr_Occurred()) { 00739 if (m_remap_python_exceptions) { 00740 00741 // format using cgitb, throw as PythonError (PLearnError) 00742 PyObject *exception, *v, *traceback; 00743 PyErr_Fetch(&exception, &v, &traceback); 00744 PyErr_NormalizeException(&exception, &v, &traceback); 00745 00746 if(!traceback) 00747 { 00748 //perr << "$$$$ before print" << endl; 00749 PyErr_Print(); 00750 //perr << "$$$$ after print" << endl; 00751 throw PythonException(string("PythonCodeSnippet: encountered Python " 00752 "exception but there is no traceback.\n") 00753 + extramsg); 00754 } 00755 00756 00757 PyObject* tbstr= 00758 PyString_FromString("plearn.utilities.pltraceback"); 00759 PyObject* tbmod= PyImport_Import(tbstr); 00760 Py_XDECREF(tbstr); 00761 if(!tbmod) 00762 throw PythonException("PythonCodeSnippet::handlePythonErrors :" 00763 " Unable to import cgitb module."); 00764 PyObject* tbdict= PyModule_GetDict(tbmod); 00765 Py_XDECREF(tbmod); 00766 PyObject* formatFunc= PyDict_GetItemString(tbdict, "text"); 00767 if(!formatFunc) 00768 throw PythonException("PythonCodeSnippet::handlePythonErrors :" 00769 " Can't find cgitb.text"); 00770 PyObject* args= Py_BuildValue(const_cast<char*>("((OOO))"), 00771 exception, v, traceback); 00772 if(!args) 00773 throw PythonException("PythonCodeSnippet::handlePythonErrors :" 00774 " Can't build args for cgitb.text"); 00775 PyObject* pystr= PyObject_CallObject(formatFunc, args); 00776 Py_XDECREF(args); 00777 if(!pystr) 00778 throw PythonException("PythonCodeSnippet::handlePythonErrors :" 00779 " call to cgitb.text failed"); 00780 string str= PyString_AsString(pystr); 00781 Py_XDECREF(pystr); 00782 00783 PyErr_Clear(); 00784 00785 Py_XDECREF(exception); 00786 Py_XDECREF(v); 00787 Py_XDECREF(traceback); 00788 throw PythonException(str+extramsg); 00789 } 00790 else { 00791 PyErr_Print(); 00792 PyErr_Clear(); 00793 PLERROR("PythonCodeSnippet: encountered Python exception.\n%s", 00794 extramsg.c_str()); 00795 } 00796 } 00797 } 00798 00799 00800 void PythonCodeSnippet::dumpPythonEnvironment() 00801 { 00802 PyObject_Print(m_compiled_code.getPyObject(), stderr, 0); 00803 } 00804 00805 00806 00807 } // end of namespace PLearn 00808 00809 00810 /* 00811 Local Variables: 00812 mode:c++ 00813 c-basic-offset:4 00814 c-file-style:"stroustrup" 00815 c-file-offsets:((innamespace . 0)(inline-open . 0)) 00816 indent-tabs-mode:nil 00817 fill-column:79 00818 End: 00819 */ 00820 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=79 :