lib

pythonscript.cpp
1/***************************************************************************
2 * pythonscript.cpp
3 * This file is part of the KDE project
4 * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public License
15 * along with this program; see the file COPYING. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 ***************************************************************************/
19
20#include "pythonscript.h"
21#include "pythonmodule.h"
22#include "pythoninterpreter.h"
23#include "pythonsecurity.h"
24#include "../main/scriptcontainer.h"
25
26//#include <tdeapplication.h>
27
28using namespace Kross::Python;
29
30namespace Kross { namespace Python {
31
33 class PythonScriptPrivate
34 {
35 public:
36
41 Py::Module* m_module;
42
49 Py::Object* m_code;
50
54 TQStringList m_functions;
55
59 TQStringList m_classes;
60 };
61
62}}
63
65 : Kross::Api::Script(interpreter, scriptcontainer)
66 , d(new PythonScriptPrivate())
67{
68#ifdef KROSS_PYTHON_SCRIPT_CTOR_DEBUG
69 krossdebug("PythonScript::PythonScript() Constructor.");
70#endif
71 d->m_module = 0;
72 d->m_code = 0;
73}
74
76{
77#ifdef KROSS_PYTHON_SCRIPT_DTOR_DEBUG
78 krossdebug("PythonScript::~PythonScript() Destructor.");
79#endif
80 finalize();
81 delete d;
82}
83
84void PythonScript::initialize()
85{
86 finalize();
87 clearException(); // clear previously thrown exceptions.
88
89 try {
90 if(m_scriptcontainer->getCode().isNull())
91 throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(TQString("Invalid scripting code for script '%1'").arg( m_scriptcontainer->getName() )) );
92
93 if(m_scriptcontainer->getName().isNull())
94 throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(TQString("Name for the script is invalid!")) );
95
96 PyObject* pymod = PyModule_New( (char*) m_scriptcontainer->getName().latin1() );
97 d->m_module = new Py::Module(pymod, true);
98 if(! d->m_module)
99 throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(TQString("Failed to initialize local module context for script '%1'").arg( m_scriptcontainer->getName() )) );
100
101#ifdef KROSS_PYTHON_SCRIPT_INIT_DEBUG
102 krossdebug( TQString("PythonScript::initialize() module='%1' refcount='%2'").arg(d->m_module->as_string().c_str()).arg(d->m_module->reference_count()) );
103#endif
104
105 // Set the "self" variable to point to the ScriptContainer
106 // we are using for the script. That way we are able to
107 // simply access the ScriptContainer itself from within
108 // python scripting code.
109 Py::Dict moduledict = d->m_module->getDict();
110 moduledict["self"] = PythonExtension::toPyObject( (Kross::Api::Object::Ptr)m_scriptcontainer );
111 //moduledict["parent"] = PythonExtension::toPyObject( m_manager );
112
113/*
114 // Prepare the local context.
115 TQString s =
116 //"import sys\n"
117 "if self.has(\"stdout\"):\n"
118 " self.stdout = Redirect( self.get(\"stdout\") )\n"
119 "if self.has(\"stderr\"):\n"
120 " self.stderr = Redirect( self.get(\"stderr\") )\n"
121 ;
122 Py::Dict mainmoduledict = ((PythonInterpreter*)m_interpreter)->mainModule()->getDict();
123 PyObject* pyrun = PyRun_StringFlags((char*)s.latin1(), Py_file_input, mainmoduledict.ptr(), moduledict.ptr());
124 if(! pyrun)
125 throw Py::Exception(); // throw exception
126 Py_XDECREF(pyrun); // free the reference.
127*/
128
129 // Compile the python script code. It will be later on request
130 // executed. That way we cache the compiled code.
131 PyObject* code = 0;
132 bool restricted = m_scriptcontainer->getOption("restricted", TQVariant(false), true).toBool();
133
134 krossdebug( TQString("PythonScript::initialize() name=%1 restricted=%2").arg(m_scriptcontainer->getName()).arg(restricted) );
135 if(restricted) {
136
137 // Use the RestrictedPython module wrapped by the PythonSecurity class.
138 code = dynamic_cast<PythonInterpreter*>(m_interpreter)->securityModule()->compile_restricted(
141 "exec"
142 );
143
144 }
145 else {
146 //PyCompilerFlags* cf = new PyCompilerFlags;
147 //cf->cf_flags |= PyCF_SOURCE_IS_UTF8;
148
149 // Just compile the code without any restrictions.
150 code = Py_CompileString(
151 (char*) m_scriptcontainer->getCode().latin1(),
152 (char*) m_scriptcontainer->getName().latin1(),
153 Py_file_input
154 );
155 }
156
157 if(! code)
158 throw Py::Exception();
159 d->m_code = new Py::Object(code, true);
160 }
161 catch(Py::Exception& e) {
162 TQString err = Py::value(e).as_string().c_str();
163 Kross::Api::Exception::Ptr exception = toException( TQString("Failed to compile python code: %1").arg(err) );
164 e.clear(); // exception is handled. clear it now.
165 throw exception;
166 }
167}
168
169void PythonScript::finalize()
170{
171#ifdef KROSS_PYTHON_SCRIPT_FINALIZE_DEBUG
172 if(d->m_module)
173 krossdebug( TQString("PythonScript::finalize() module='%1' refcount='%2'").arg(d->m_module->as_string().c_str()).arg(d->m_module->reference_count()) );
174#endif
175
176 delete d->m_module; d->m_module = 0;
177 delete d->m_code; d->m_code = 0;
178 d->m_functions.clear();
179 d->m_classes.clear();
180}
181
182Kross::Api::Exception::Ptr PythonScript::toException(const TQString& error)
183{
184 long lineno = -1;
185 TQStringList errorlist;
186
187 PyObject *type, *value, *traceback;
188 PyErr_Fetch(&type, &value, &traceback);
189 Py_FlushLine();
190 PyErr_NormalizeException(&type, &value, &traceback);
191
192 if(traceback) {
193 Py::List tblist;
194 try {
195 Py::Module tbmodule( PyImport_Import(Py::String("traceback").ptr()), true );
196 Py::Dict tbdict = tbmodule.getDict();
197 Py::Callable tbfunc(tbdict.getItem("format_tb"));
198 Py::Tuple args(1);
199 args.setItem(0, Py::Object(traceback));
200 tblist = tbfunc.apply(args);
201 uint length = tblist.length();
202 for(Py::List::size_type i = 0; i < length; ++i)
203 errorlist.append( Py::Object(tblist[i]).as_string().c_str() );
204 }
205 catch(Py::Exception& e) {
206 TQString err = Py::value(e).as_string().c_str();
207 e.clear(); // exception is handled. clear it now.
208 krosswarning( TQString("Kross::Python::PythonScript::toException() Failed to fetch a traceback: %1").arg(err) );
209 }
210
211 PyObject *next;
212 while (traceback && traceback != Py_None) {
213 PyFrameObject *frame = (PyFrameObject*)PyObject_GetAttrString(traceback, "tb_frame");
214 Py_DECREF(frame);
215 {
216 PyObject *getobj = PyObject_GetAttrString(traceback, "tb_lineno");
217 lineno = PyInt_AsLong(getobj);
218 Py_DECREF(getobj);
219 }
220 if(Py_OptimizeFlag) {
221 PyObject *getobj = PyObject_GetAttrString(traceback, "tb_lasti");
222 int lasti = PyInt_AsLong(getobj);
223 Py_DECREF(getobj);
224 lineno = PyCode_Addr2Line(frame->f_code, lasti);
225 }
226
227 //const char* filename = PyString_AsString(frame->f_code->co_filename);
228 //const char* name = PyString_AsString(frame->f_code->co_name);
229 //errorlist.append( TQString("%1#%2: \"%3\"").arg(filename).arg(lineno).arg(name) );
230
231 next = PyObject_GetAttrString(traceback, "tb_next");
232 Py_DECREF(traceback);
233 traceback = next;
234 }
235 }
236
237 if(lineno < 0) {
238 if(value) {
239 PyObject *getobj = PyObject_GetAttrString(value, "lineno");
240 if(getobj) {
241 lineno = PyInt_AsLong(getobj);
242 Py_DECREF(getobj);
243 }
244 }
245 if(lineno < 0)
246 lineno = 0;
247 }
248
249 //PyErr_Restore(type, value, traceback);
250
251 Kross::Api::Exception::Ptr exception = new Kross::Api::Exception(error, lineno - 1);
252 if(errorlist.count() > 0)
253 exception->setTrace( errorlist.join("\n") );
254 return exception;
255}
256
258{
259 if(! d->m_module)
260 initialize(); //TODO catch exception
261 return d->m_functions;
262 /*
263 TQStringList list;
264 Py::List l = d->m_module->getDict().keys();
265 int length = l.length();
266 for(Py::List::size_type i = 0; i < length; ++i)
267 list.append( l[i].str().as_string().c_str() );
268 return list;
269 */
270}
271
273{
274#ifdef KROSS_PYTHON_SCRIPT_EXEC_DEBUG
275 krossdebug( TQString("PythonScript::execute()") );
276#endif
277
278 try {
279 if(! d->m_module)
280 initialize();
281
282 // the main module dictonary.
283 Py::Dict mainmoduledict = ((PythonInterpreter*)m_interpreter)->mainModule()->getDict();
284 // the local context dictonary.
285 Py::Dict moduledict( d->m_module->getDict().ptr() );
286
287 // Initialize context before execution.
288 TQString s =
289 "import sys\n"
290 //"if self.has(\"stdout\"):\n"
291 //" sys.stdout = Redirect( self.get(\"stdout\") )\n"
292 //"if self.has(\"stderr\"):\n"
293 //" sys.stderr = Redirect( self.get(\"stderr\") )\n"
294 ;
295
296 PyObject* pyrun = PyRun_String(s.latin1(), Py_file_input, mainmoduledict.ptr(), moduledict.ptr());
297 if(! pyrun)
298 throw Py::Exception(); // throw exception
299 Py_XDECREF(pyrun); // free the reference.
300
301 // Acquire interpreter lock*/
302 PyGILState_STATE gilstate = PyGILState_Ensure();
303
304 // Evaluate the already compiled code.
305 PyObject* pyresult = PyEval_EvalCode(
306 (PyCodeObject*)d->m_code->ptr(),
307 mainmoduledict.ptr(),
308 moduledict.ptr()
309 );
310
311 // Free interpreter lock
312 PyGILState_Release(gilstate);
313
314 if(! pyresult || PyErr_Occurred()) {
315 krosswarning("Kross::Python::PythonScript::execute(): Failed to PyEval_EvalCode");
316 throw Py::Exception();
317 }
318 Py::Object result(pyresult, true);
319
320#ifdef KROSS_PYTHON_SCRIPT_EXEC_DEBUG
321 krossdebug( TQString("PythonScript::execute() result=%1").arg(result.as_string().c_str()) );
322#endif
323
324 for(Py::Dict::iterator it = moduledict.begin(); it != moduledict.end(); ++it) {
325 Py::Dict::value_type vt(*it);
326 if(PyClass_Check( vt.second.ptr() )) {
327#ifdef KROSS_PYTHON_SCRIPT_EXEC_DEBUG
328 krossdebug( TQString("PythonScript::execute() class '%1' added.").arg(vt.first.as_string().c_str()) );
329#endif
330 d->m_classes.append( vt.first.as_string().c_str() );
331 }
332 else if(vt.second.isCallable()) {
333#ifdef KROSS_PYTHON_SCRIPT_EXEC_DEBUG
334 krossdebug( TQString("PythonScript::execute() function '%1' added.").arg(vt.first.as_string().c_str()) );
335#endif
336 d->m_functions.append( vt.first.as_string().c_str() );
337 }
338 }
339
340 Kross::Api::Object::Ptr r = PythonExtension::toObject(result);
341 return r;
342 }
343 catch(Py::Exception& e) {
344 try {
345 Py::Object errobj = Py::value(e);
346 if(errobj.ptr() == Py_None) // at least string-exceptions have there errormessage in the type-object
347 errobj = Py::type(e);
348 TQString err = errobj.as_string().c_str();
349
350 Kross::Api::Exception::Ptr exception = toException( TQString("Failed to execute python code: %1").arg(err) );
351 e.clear(); // exception is handled. clear it now.
352 setException( exception );
353 }
354 catch(Py::Exception& e) {
355 TQString err = Py::value(e).as_string().c_str();
356 Kross::Api::Exception::Ptr exception = toException( TQString("Failed to execute python code: %1").arg(err) );
357 e.clear(); // exception is handled. clear it now.
358 setException( exception );
359 }
360 }
362 setException(e);
363 }
364
365 return 0; // return nothing if exception got thrown.
366}
367
369{
370#ifdef KROSS_PYTHON_SCRIPT_CALLFUNC_DEBUG
371 krossdebug( TQString("PythonScript::callFunction(%1, %2)")
372 .arg(name)
373 .arg(args ? TQString::number(args->count()) : TQString("NULL")) );
374#endif
375
376 if(hadException()) return 0; // abort if we had an unresolved exception.
377
378 if(! d->m_module) {
379 setException( new Kross::Api::Exception(TQString("Script not initialized.")) );
380 return 0;
381 }
382
383 try {
384 Py::Dict moduledict = d->m_module->getDict();
385
386 // Try to determinate the function we like to execute.
387 PyObject* func = PyDict_GetItemString(moduledict.ptr(), name.latin1());
388
389 if( (! d->m_functions.contains(name)) || (! func) )
390 throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(TQString("No such function '%1'.").arg(name)) );
391
392 Py::Callable funcobject(func, true); // the funcobject takes care of freeing our func pyobject.
393
394 // Check if the object is really a function and therefore callable.
395 if(! funcobject.isCallable())
396 throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(TQString("Function is not callable.")) );
397
398 // Call the function.
399 Py::Object result = funcobject.apply(PythonExtension::toPyTuple(args));
400 return PythonExtension::toObject(result);
401 }
402 catch(Py::Exception& e) {
403 TQString err = Py::value(e).as_string().c_str();
404 e.clear(); // exception is handled. clear it now.
405 setException( new Kross::Api::Exception(TQString("Python Exception: %1").arg(err)) );
406 }
408 setException(e);
409 }
410
411 return 0; // return nothing if exception got thrown.
412}
413
414const TQStringList& PythonScript::getClassNames()
415{
416 if(! d->m_module)
417 initialize(); //TODO catch exception
418 return d->m_classes;
419}
420
422{
423 if(hadException()) return 0; // abort if we had an unresolved exception.
424
425 if(! d->m_module) {
426 setException( new Kross::Api::Exception(TQString("Script not initialized.")) );
427 return 0;
428 }
429
430 try {
431 Py::Dict moduledict = d->m_module->getDict();
432
433 // Try to determinate the class.
434 PyObject* pyclass = PyDict_GetItemString(moduledict.ptr(), name.latin1());
435 if( (! d->m_classes.contains(name)) || (! pyclass) )
436 throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(TQString("No such class '%1'.").arg(name)) );
437
438 PyObject *pyobj = PyInstance_New(pyclass, 0, 0);//aclarg, 0);
439 if(! pyobj)
440 throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(TQString("Failed to create instance of class '%1'.").arg(name)) );
441
442 Py::Object classobject(pyobj, true);
443
444#ifdef KROSS_PYTHON_SCRIPT_CLASSINSTANCE_DEBUG
445 krossdebug( TQString("PythonScript::classInstance() inst='%1'").arg(classobject.as_string().c_str()) );
446#endif
447 return PythonExtension::toObject(classobject);
448 }
449 catch(Py::Exception& e) {
450 TQString err = Py::value(e).as_string().c_str();
451 e.clear(); // exception is handled. clear it now.
453 }
455 setException(e);
456 }
457
458 return 0; // return nothing if exception got thrown.
459}
460
Common exception class used for representing exceptions in Kross.
Definition exception.h:39
TDESharedPtr< Exception > Ptr
Shared pointer to implement reference-counting.
Definition exception.h:45
Base class for interpreters.
TDESharedPtr< List > Ptr
Shared pointer to implement reference-counting.
Definition list.h:46
TDESharedPtr< Object > Ptr
Shared pointer to implement reference-counting.
Definition object.h:56
The ScriptContainer class is something like a single standalone scriptfile.
const TQString getName() const
TQVariant getOption(const TQString name, TQVariant defaultvalue=TQVariant(), bool recursive=false)
TQString getCode() const
Return the scriptcode this ScriptContainer holds.
bool hadException()
Definition script.cpp:40
Interpreter *const m_interpreter
The Interpreter used to create this Script instance.
Definition script.h:128
Script(Interpreter *const interpreter, ScriptContainer *const scriptcontainer)
Constructor.
Definition script.cpp:29
void setException(Exception::Ptr e)
Set a new exception this script throwed.
Definition script.cpp:50
void clearException()
Clear previous exceptions.
Definition script.cpp:55
ScriptContainer *const m_scriptcontainer
The ScriptContainer associated with this Script.
Definition script.h:130
Python interpreter bridge.
virtual const TQStringList & getFunctionNames()
Return a list of callable functionnames this script spends.
virtual ~PythonScript()
Destructor.
PythonScript(Kross::Api::Interpreter *interpreter, Kross::Api::ScriptContainer *scriptcontainer)
Constructor.
virtual Kross::Api::Object::Ptr execute()
Execute the script.
virtual Kross::Api::Object::Ptr callFunction(const TQString &name, Kross::Api::List::Ptr args)
Call a function.
virtual Kross::Api::Object::Ptr classInstance(const TQString &name)
Create and return a new class instance.
virtual const TQStringList & getClassNames()
Return a list of class types this script supports.
TDEAction * next(const TQObject *recvr, const char *slot, TDEActionCollection *parent, const char *name=0)
The common Kross API used as common codebase.
Definition callable.h:33
The Python plugin for the Kross scripting framework.
The Kross scripting bridge to embed scripting functionality into an application.
Definition callable.h:33
KOFFICE_EXPORT void krossdebug(const TQString &s)
Debugging function.
KOFFICE_EXPORT void krosswarning(const TQString &s)
Warning function.