00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "pythonscript.h"
00021 #include "pythonmodule.h"
00022 #include "pythoninterpreter.h"
00023 #include "pythonsecurity.h"
00024 #include "../main/scriptcontainer.h"
00025
00026
00027
00028 using namespace Kross::Python;
00029
00030 namespace Kross { namespace Python {
00031
00033 class PythonScriptPrivate
00034 {
00035 public:
00036
00041 Py::Module* m_module;
00042
00049 Py::Object* m_code;
00050
00054 QStringList m_functions;
00055
00059 QStringList m_classes;
00060 };
00061
00062 }}
00063
00064 PythonScript::PythonScript(Kross::Api::Interpreter* interpreter, Kross::Api::ScriptContainer* scriptcontainer)
00065 : Kross::Api::Script(interpreter, scriptcontainer)
00066 , d(new PythonScriptPrivate())
00067 {
00068 #ifdef KROSS_PYTHON_SCRIPT_CTOR_DEBUG
00069 krossdebug("PythonScript::PythonScript() Constructor.");
00070 #endif
00071 d->m_module = 0;
00072 d->m_code = 0;
00073 }
00074
00075 PythonScript::~PythonScript()
00076 {
00077 #ifdef KROSS_PYTHON_SCRIPT_DTOR_DEBUG
00078 krossdebug("PythonScript::~PythonScript() Destructor.");
00079 #endif
00080 finalize();
00081 delete d;
00082 }
00083
00084 void PythonScript::initialize()
00085 {
00086 finalize();
00087 clearException();
00088
00089 try {
00090 if(m_scriptcontainer->getCode().isNull())
00091 throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Invalid scripting code for script '%1'").arg( m_scriptcontainer->getName() )) );
00092
00093 if(m_scriptcontainer->getName().isNull())
00094 throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Name for the script is invalid!")) );
00095
00096 PyObject* pymod = PyModule_New( (char*) m_scriptcontainer->getName().latin1() );
00097 d->m_module = new Py::Module(pymod, true);
00098 if(! d->m_module)
00099 throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Failed to initialize local module context for script '%1'").arg( m_scriptcontainer->getName() )) );
00100
00101 #ifdef KROSS_PYTHON_SCRIPT_INIT_DEBUG
00102 krossdebug( QString("PythonScript::initialize() module='%1' refcount='%2'").arg(d->m_module->as_string().c_str()).arg(d->m_module->reference_count()) );
00103 #endif
00104
00105
00106
00107
00108
00109 Py::Dict moduledict = d->m_module->getDict();
00110 moduledict["self"] = PythonExtension::toPyObject( m_scriptcontainer );
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131 PyObject* code = 0;
00132 bool restricted = m_scriptcontainer->getOption("restricted", QVariant(false,0), true).toBool();
00133
00134 krossdebug( QString("PythonScript::initialize() name=%1 restricted=%2").arg(m_scriptcontainer->getName()).arg(restricted) );
00135 if(restricted) {
00136
00137
00138 code = dynamic_cast<PythonInterpreter*>(m_interpreter)->securityModule()->compile_restricted(
00139 m_scriptcontainer->getCode(),
00140 m_scriptcontainer->getName(),
00141 "exec"
00142 );
00143
00144 }
00145 else {
00146
00147
00148
00149
00150 code = Py_CompileString(
00151 (char*) m_scriptcontainer->getCode().latin1(),
00152 (char*) m_scriptcontainer->getName().latin1(),
00153 Py_file_input
00154 );
00155 }
00156
00157 if(! code)
00158 throw Py::Exception();
00159 d->m_code = new Py::Object(code, true);
00160 }
00161 catch(Py::Exception& e) {
00162 QString err = Py::value(e).as_string().c_str();
00163 Kross::Api::Exception::Ptr exception = toException( QString("Failed to compile python code: %1").arg(err) );
00164 e.clear();
00165 throw exception;
00166 }
00167 }
00168
00169 void PythonScript::finalize()
00170 {
00171 #ifdef KROSS_PYTHON_SCRIPT_FINALIZE_DEBUG
00172 if(d->m_module)
00173 krossdebug( QString("PythonScript::finalize() module='%1' refcount='%2'").arg(d->m_module->as_string().c_str()).arg(d->m_module->reference_count()) );
00174 #endif
00175
00176 delete d->m_module; d->m_module = 0;
00177 delete d->m_code; d->m_code = 0;
00178 d->m_functions.clear();
00179 d->m_classes.clear();
00180 }
00181
00182 Kross::Api::Exception::Ptr PythonScript::toException(const QString& error)
00183 {
00184 long lineno = -1;
00185 QStringList errorlist;
00186
00187 PyObject *type, *value, *traceback;
00188 PyErr_Fetch(&type, &value, &traceback);
00189 Py_FlushLine();
00190 PyErr_NormalizeException(&type, &value, &traceback);
00191
00192 if(traceback) {
00193 Py::List tblist;
00194 try {
00195 Py::Module tbmodule( PyImport_Import(Py::String("traceback").ptr()), true );
00196 Py::Dict tbdict = tbmodule.getDict();
00197 Py::Callable tbfunc(tbdict.getItem("format_tb"));
00198 Py::Tuple args(1);
00199 args.setItem(0, Py::Object(traceback));
00200 tblist = tbfunc.apply(args);
00201 uint length = tblist.length();
00202 for(Py::List::size_type i = 0; i < length; ++i)
00203 errorlist.append( Py::Object(tblist[i]).as_string().c_str() );
00204 }
00205 catch(Py::Exception& e) {
00206 QString err = Py::value(e).as_string().c_str();
00207 e.clear();
00208 krosswarning( QString("Kross::Python::PythonScript::toException() Failed to fetch a traceback: %1").arg(err) );
00209 }
00210
00211 PyObject *next;
00212 while (traceback && traceback != Py_None) {
00213 PyFrameObject *frame = (PyFrameObject*)PyObject_GetAttrString(traceback, "tb_frame");
00214 Py_DECREF(frame);
00215 {
00216 PyObject *getobj = PyObject_GetAttrString(traceback, "tb_lineno");
00217 lineno = PyInt_AsLong(getobj);
00218 Py_DECREF(getobj);
00219 }
00220 if(Py_OptimizeFlag) {
00221 PyObject *getobj = PyObject_GetAttrString(traceback, "tb_lasti");
00222 int lasti = PyInt_AsLong(getobj);
00223 Py_DECREF(getobj);
00224 lineno = PyCode_Addr2Line(frame->f_code, lasti);
00225 }
00226
00227
00228
00229
00230
00231 next = PyObject_GetAttrString(traceback, "tb_next");
00232 Py_DECREF(traceback);
00233 traceback = next;
00234 }
00235 }
00236
00237 if(lineno < 0) {
00238 if(value) {
00239 PyObject *getobj = PyObject_GetAttrString(value, "lineno");
00240 if(getobj) {
00241 lineno = PyInt_AsLong(getobj);
00242 Py_DECREF(getobj);
00243 }
00244 }
00245 if(lineno < 0)
00246 lineno = 0;
00247 }
00248
00249
00250
00251 Kross::Api::Exception::Ptr exception = new Kross::Api::Exception(error, lineno - 1);
00252 if(errorlist.count() > 0)
00253 exception->setTrace( errorlist.join("\n") );
00254 return exception;
00255 }
00256
00257 const QStringList& PythonScript::getFunctionNames()
00258 {
00259 if(! d->m_module)
00260 initialize();
00261 return d->m_functions;
00262
00263
00264
00265
00266
00267
00268
00269
00270 }
00271
00272 Kross::Api::Object::Ptr PythonScript::execute()
00273 {
00274 #ifdef KROSS_PYTHON_SCRIPT_EXEC_DEBUG
00275 krossdebug( QString("PythonScript::execute()") );
00276 #endif
00277
00278 try {
00279 if(! d->m_module)
00280 initialize();
00281
00282
00283 Py::Dict mainmoduledict = ((PythonInterpreter*)m_interpreter)->mainModule()->getDict();
00284
00285 Py::Dict moduledict( d->m_module->getDict().ptr() );
00286
00287
00288 QString s =
00289 "import sys\n"
00290
00291
00292
00293
00294 ;
00295
00296 PyObject* pyrun = PyRun_String(s.latin1(), Py_file_input, mainmoduledict.ptr(), moduledict.ptr());
00297 if(! pyrun)
00298 throw Py::Exception();
00299 Py_XDECREF(pyrun);
00300
00301
00302 PyGILState_STATE gilstate = PyGILState_Ensure();
00303
00304
00305 PyObject* pyresult = PyEval_EvalCode(
00306 (PyCodeObject*)d->m_code->ptr(),
00307 mainmoduledict.ptr(),
00308 moduledict.ptr()
00309 );
00310
00311
00312 PyGILState_Release(gilstate);
00313
00314 if(! pyresult || PyErr_Occurred()) {
00315 krosswarning("Kross::Python::PythonScript::execute(): Failed to PyEval_EvalCode");
00316 throw Py::Exception();
00317 }
00318 Py::Object result(pyresult, true);
00319
00320 #ifdef KROSS_PYTHON_SCRIPT_EXEC_DEBUG
00321 krossdebug( QString("PythonScript::execute() result=%1").arg(result.as_string().c_str()) );
00322 #endif
00323
00324 for(Py::Dict::iterator it = moduledict.begin(); it != moduledict.end(); ++it) {
00325 Py::Dict::value_type vt(*it);
00326 if(PyClass_Check( vt.second.ptr() )) {
00327 #ifdef KROSS_PYTHON_SCRIPT_EXEC_DEBUG
00328 krossdebug( QString("PythonScript::execute() class '%1' added.").arg(vt.first.as_string().c_str()) );
00329 #endif
00330 d->m_classes.append( vt.first.as_string().c_str() );
00331 }
00332 else if(vt.second.isCallable()) {
00333 #ifdef KROSS_PYTHON_SCRIPT_EXEC_DEBUG
00334 krossdebug( QString("PythonScript::execute() function '%1' added.").arg(vt.first.as_string().c_str()) );
00335 #endif
00336 d->m_functions.append( vt.first.as_string().c_str() );
00337 }
00338 }
00339
00340 Kross::Api::Object::Ptr r = PythonExtension::toObject(result);
00341 return r;
00342 }
00343 catch(Py::Exception& e) {
00344 try {
00345 Py::Object errobj = Py::value(e);
00346 if(errobj.ptr() == Py_None)
00347 errobj = Py::type(e);
00348 QString err = errobj.as_string().c_str();
00349
00350 Kross::Api::Exception::Ptr exception = toException( QString("Failed to execute python code: %1").arg(err) );
00351 e.clear();
00352 setException( exception );
00353 }
00354 catch(Py::Exception& e) {
00355 QString err = Py::value(e).as_string().c_str();
00356 Kross::Api::Exception::Ptr exception = toException( QString("Failed to execute python code: %1").arg(err) );
00357 e.clear();
00358 setException( exception );
00359 }
00360 }
00361 catch(Kross::Api::Exception::Ptr e) {
00362 setException(e);
00363 }
00364
00365 return 0;
00366 }
00367
00368 Kross::Api::Object::Ptr PythonScript::callFunction(const QString& name, Kross::Api::List::Ptr args)
00369 {
00370 #ifdef KROSS_PYTHON_SCRIPT_CALLFUNC_DEBUG
00371 krossdebug( QString("PythonScript::callFunction(%1, %2)")
00372 .arg(name)
00373 .arg(args ? QString::number(args->count()) : QString("NULL")) );
00374 #endif
00375
00376 if(hadException()) return 0;
00377
00378 if(! d->m_module) {
00379 setException( new Kross::Api::Exception(QString("Script not initialized.")) );
00380 return 0;
00381 }
00382
00383 try {
00384 Py::Dict moduledict = d->m_module->getDict();
00385
00386
00387 PyObject* func = PyDict_GetItemString(moduledict.ptr(), name.latin1());
00388
00389 if( (! d->m_functions.contains(name)) || (! func) )
00390 throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("No such function '%1'.").arg(name)) );
00391
00392 Py::Callable funcobject(func, true);
00393
00394
00395 if(! funcobject.isCallable())
00396 throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Function is not callable.")) );
00397
00398
00399 Py::Object result = funcobject.apply(PythonExtension::toPyTuple(args));
00400 return PythonExtension::toObject(result);
00401 }
00402 catch(Py::Exception& e) {
00403 QString err = Py::value(e).as_string().c_str();
00404 e.clear();
00405 setException( new Kross::Api::Exception(QString("Python Exception: %1").arg(err)) );
00406 }
00407 catch(Kross::Api::Exception::Ptr e) {
00408 setException(e);
00409 }
00410
00411 return 0;
00412 }
00413
00414 const QStringList& PythonScript::getClassNames()
00415 {
00416 if(! d->m_module)
00417 initialize();
00418 return d->m_classes;
00419 }
00420
00421 Kross::Api::Object::Ptr PythonScript::classInstance(const QString& name)
00422 {
00423 if(hadException()) return 0;
00424
00425 if(! d->m_module) {
00426 setException( new Kross::Api::Exception(QString("Script not initialized.")) );
00427 return 0;
00428 }
00429
00430 try {
00431 Py::Dict moduledict = d->m_module->getDict();
00432
00433
00434 PyObject* pyclass = PyDict_GetItemString(moduledict.ptr(), name.latin1());
00435 if( (! d->m_classes.contains(name)) || (! pyclass) )
00436 throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("No such class '%1'.").arg(name)) );
00437
00438 PyObject *pyobj = PyInstance_New(pyclass, 0, 0);
00439 if(! pyobj)
00440 throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Failed to create instance of class '%1'.").arg(name)) );
00441
00442 Py::Object classobject(pyobj, true);
00443
00444 #ifdef KROSS_PYTHON_SCRIPT_CLASSINSTANCE_DEBUG
00445 krossdebug( QString("PythonScript::classInstance() inst='%1'").arg(classobject.as_string().c_str()) );
00446 #endif
00447 return PythonExtension::toObject(classobject);
00448 }
00449 catch(Py::Exception& e) {
00450 QString err = Py::value(e).as_string().c_str();
00451 e.clear();
00452 setException( Kross::Api::Exception::Ptr( new Kross::Api::Exception(err) ) );
00453 }
00454 catch(Kross::Api::Exception::Ptr e) {
00455 setException(e);
00456 }
00457
00458 return 0;
00459 }
00460