lib

pythonscript.cpp

00001 /***************************************************************************
00002  * pythonscript.cpp
00003  * This file is part of the KDE project
00004  * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Library General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2 of the License, or (at your option) any later version.
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Library General Public License for more details.
00014  * You should have received a copy of the GNU Library General Public License
00015  * along with this program; see the file COPYING.  If not, write to
00016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
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 //#include <kapplication.h>
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(); // clear previously thrown exceptions.
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         // Set the "self" variable to point to the ScriptContainer
00106         // we are using for the script. That way we are able to
00107         // simply access the ScriptContainer itself from within
00108         // python scripting code.
00109         Py::Dict moduledict = d->m_module->getDict();
00110         moduledict["self"] = PythonExtension::toPyObject( m_scriptcontainer );
00111         //moduledict["parent"] = PythonExtension::toPyObject( m_manager );
00112 
00113 /*
00114         // Prepare the local context.
00115         QString s =
00116             //"import sys\n"
00117             "if self.has(\"stdout\"):\n"
00118             "  self.stdout = Redirect( self.get(\"stdout\") )\n"
00119             "if self.has(\"stderr\"):\n"
00120             "  self.stderr = Redirect( self.get(\"stderr\") )\n"
00121             ;
00122         Py::Dict mainmoduledict = ((PythonInterpreter*)m_interpreter)->mainModule()->getDict();
00123         PyObject* pyrun = PyRun_StringFlags((char*)s.latin1(), Py_file_input, mainmoduledict.ptr(), moduledict.ptr());
00124         if(! pyrun)
00125             throw Py::Exception(); // throw exception
00126         Py_XDECREF(pyrun); // free the reference.
00127 */
00128 
00129         // Compile the python script code. It will be later on request
00130         // executed. That way we cache the compiled code.
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             // Use the RestrictedPython module wrapped by the PythonSecurity class.
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             //PyCompilerFlags* cf = new PyCompilerFlags;
00147             //cf->cf_flags |= PyCF_SOURCE_IS_UTF8;
00148 
00149             // Just compile the code without any restrictions.
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(); // exception is handled. clear it now.
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(); // exception is handled. clear it now.
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             //const char* filename = PyString_AsString(frame->f_code->co_filename);
00228             //const char* name = PyString_AsString(frame->f_code->co_name);
00229             //errorlist.append( QString("%1#%2: \"%3\"").arg(filename).arg(lineno).arg(name) );
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     //PyErr_Restore(type, value, traceback);
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(); //TODO catch exception
00261     return d->m_functions;
00262     /*
00263     QStringList list;
00264     Py::List l = d->m_module->getDict().keys();
00265     int length = l.length();
00266     for(Py::List::size_type i = 0; i < length; ++i)
00267         list.append( l[i].str().as_string().c_str() );
00268     return list;
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         // the main module dictonary.
00283         Py::Dict mainmoduledict = ((PythonInterpreter*)m_interpreter)->mainModule()->getDict();
00284         // the local context dictonary.
00285         Py::Dict moduledict( d->m_module->getDict().ptr() );
00286 
00287         // Initialize context before execution.
00288         QString s =
00289             "import sys\n"
00290             //"if self.has(\"stdout\"):\n"
00291             //"  sys.stdout = Redirect( self.get(\"stdout\") )\n"
00292             //"if self.has(\"stderr\"):\n"
00293             //"  sys.stderr = Redirect( self.get(\"stderr\") )\n"
00294             ;
00295 
00296         PyObject* pyrun = PyRun_String(s.latin1(), Py_file_input, mainmoduledict.ptr(), moduledict.ptr());
00297         if(! pyrun)
00298             throw Py::Exception(); // throw exception
00299         Py_XDECREF(pyrun); // free the reference.
00300 
00301         // Acquire interpreter lock*/
00302         PyGILState_STATE gilstate = PyGILState_Ensure();
00303 
00304         // Evaluate the already compiled code.
00305         PyObject* pyresult = PyEval_EvalCode(
00306             (PyCodeObject*)d->m_code->ptr(),
00307             mainmoduledict.ptr(),
00308             moduledict.ptr()
00309         );
00310 
00311         // Free interpreter lock
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) // at least string-exceptions have there errormessage in the type-object
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(); // exception is handled. clear it now.
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(); // exception is handled. clear it now.
00358             setException( exception );
00359         }
00360     }
00361     catch(Kross::Api::Exception::Ptr e) {
00362         setException(e);
00363     }
00364 
00365     return 0; // return nothing if exception got thrown.
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; // abort if we had an unresolved exception.
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         // Try to determinate the function we like to execute.
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); // the funcobject takes care of freeing our func pyobject.
00393 
00394         // Check if the object is really a function and therefore callable.
00395         if(! funcobject.isCallable())
00396             throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Function is not callable.")) );
00397 
00398         // Call the function.
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(); // exception is handled. clear it now.
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; // return nothing if exception got thrown.
00412 }
00413 
00414 const QStringList& PythonScript::getClassNames()
00415 {
00416     if(! d->m_module)
00417         initialize(); //TODO catch exception
00418     return d->m_classes;
00419 }
00420 
00421 Kross::Api::Object::Ptr PythonScript::classInstance(const QString& name)
00422 {
00423     if(hadException()) return 0; // abort if we had an unresolved exception.
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         // Try to determinate the class.
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);//aclarg, 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(); // exception is handled. clear it now.
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; // return nothing if exception got thrown.
00459 }
00460 
KDE Home | KDE Accessibility Home | Description of Access Keys