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