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 #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(); // clear previously thrown exceptions.
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         // Set the "self" variable to point to the ScriptContainer
00107         // we are using for the script. That way we are able to
00108         // simply access the ScriptContainer itself from within
00109         // python scripting code.
00110         Py::Dict moduledict = d->m_module->getDict();
00111         moduledict["self"] = PythonExtension::toPyObject( m_scriptcontainer );
00112         //moduledict["parent"] = PythonExtension::toPyObject( m_manager );
00113 
00114 /*
00115         // Prepare the local context.
00116         QString s =
00117             //"import sys\n"
00118             "if self.has(\"stdout\"):\n"
00119             "  self.stdout = Redirect( self.get(\"stdout\") )\n"
00120             "if self.has(\"stderr\"):\n"
00121             "  self.stderr = Redirect( self.get(\"stderr\") )\n"
00122             ;
00123         Py::Dict mainmoduledict = ((PythonInterpreter*)m_interpreter)->mainModule()->getDict();
00124         PyObject* pyrun = PyRun_StringFlags((char*)s.latin1(), Py_file_input, mainmoduledict.ptr(), moduledict.ptr());
00125         if(! pyrun)
00126             throw Py::Exception(); // throw exception
00127         Py_XDECREF(pyrun); // free the reference.
00128 */
00129 
00130         // Compile the python script code. It will be later on request
00131         // executed. That way we cache the compiled code.
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             // Use the RestrictedPython module wrapped by the PythonSecurity class.
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             //PyCompilerFlags* cf = new PyCompilerFlags;
00148             //cf->cf_flags |= PyCF_SOURCE_IS_UTF8;
00149 
00150             // Just compile the code without any restrictions.
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(); // exception is handled. clear it now.
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(); // exception is handled. clear it now.
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"); //['args', 'filename', 'lineno', 'msg', 'offset', 'print_file_and_line', 'text']
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; // python linecount starts with 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(); //TODO catch exception
00241     return d->m_functions;
00242     /*
00243     QStringList list;
00244     Py::List l = d->m_module->getDict().keys();
00245     int length = l.length();
00246     for(Py::List::size_type i = 0; i < length; ++i)
00247         list.append( l[i].str().as_string().c_str() );
00248     return list;
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         // the main module dictonary.
00263         Py::Dict mainmoduledict = ((PythonInterpreter*)m_interpreter)->mainModule()->getDict();
00264         // the local context dictonary.
00265         Py::Dict moduledict( d->m_module->getDict().ptr() );
00266 
00267         // Initialize context before execution.
00268         QString s =
00269             "import sys\n"
00270             //"if self.has(\"stdout\"):\n"
00271             //"  sys.stdout = Redirect( self.get(\"stdout\") )\n"
00272             //"if self.has(\"stderr\"):\n"
00273             //"  sys.stderr = Redirect( self.get(\"stderr\") )\n"
00274             ;
00275 
00276         PyObject* pyrun = PyRun_String(s.latin1(), Py_file_input, mainmoduledict.ptr(), moduledict.ptr());
00277         if(! pyrun)
00278             throw Py::Exception(); // throw exception
00279         Py_XDECREF(pyrun); // free the reference.
00280 
00281         // Acquire interpreter lock*/
00282         PyGILState_STATE gilstate = PyGILState_Ensure();
00283 
00284         // Evaluate the already compiled code.
00285         PyObject* pyresult = PyEval_EvalCode(
00286             (PyCodeObject*)d->m_code->ptr(),
00287             mainmoduledict.ptr(),
00288             moduledict.ptr()
00289         );
00290 
00291         // Free interpreter lock
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) // at least string-exceptions have there errormessage in the type-object
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(); // exception is handled. clear it now.
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(); // exception is handled. clear it now.
00338             setException( exception );
00339         }
00340     }
00341     catch(Kross::Api::Exception::Ptr e) {
00342         setException(e);
00343     }
00344 
00345     return 0; // return nothing if exception got thrown.
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; // abort if we had an unresolved exception.
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         // Try to determinate the function we like to execute.
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); // the funcobject takes care of freeing our func pyobject.
00374 
00375         // Check if the object is really a function and therefore callable.
00376         if(! funcobject.isCallable())
00377             throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Function is not callable.")) );
00378 
00379         // Call the function.
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(); // exception is handled. clear it now.
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; // return nothing if exception got thrown.
00393 }
00394 
00395 const QStringList& PythonScript::getClassNames()
00396 {
00397     if(! d->m_module)
00398         initialize(); //TODO catch exception
00399     return d->m_classes;
00400 }
00401 
00402 Kross::Api::Object::Ptr PythonScript::classInstance(const QString& name)
00403 {
00404     if(hadException()) return 0; // abort if we had an unresolved exception.
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         // Try to determinate the class.
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);//aclarg, 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(); // exception is handled. clear it now.
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; // return nothing if exception got thrown.
00440 }
00441 
KDE Home | KDE Accessibility Home | Description of Access Keys