From 8362bf63dea22bbf6736609b0f49c152f975eb63 Mon Sep 17 00:00:00 2001 From: tpearson Date: Wed, 20 Jan 2010 01:29:50 +0000 Subject: Added old abandoned KDE3 version of koffice git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- lib/kross/CHANGES | 20 + lib/kross/Makefile.am | 22 + lib/kross/Makefile.global | 2 + lib/kross/api/Makefile.am | 47 + lib/kross/api/callable.cpp | 139 + lib/kross/api/callable.h | 147 + lib/kross/api/class.h | 89 + lib/kross/api/dict.cpp | 47 + lib/kross/api/dict.h | 69 + lib/kross/api/event.h | 229 ++ lib/kross/api/eventaction.cpp | 77 + lib/kross/api/eventaction.h | 86 + lib/kross/api/eventscript.cpp | 47 + lib/kross/api/eventscript.h | 65 + lib/kross/api/eventsignal.cpp | 66 + lib/kross/api/eventsignal.h | 79 + lib/kross/api/eventslot.cpp | 222 ++ lib/kross/api/eventslot.h | 123 + lib/kross/api/exception.cpp | 71 + lib/kross/api/exception.h | 102 + lib/kross/api/function.h | 132 + lib/kross/api/interpreter.cpp | 151 ++ lib/kross/api/interpreter.h | 197 ++ lib/kross/api/list.cpp | 69 + lib/kross/api/list.h | 167 ++ lib/kross/api/module.h | 80 + lib/kross/api/object.cpp | 63 + lib/kross/api/object.h | 152 ++ lib/kross/api/proxy.h | 342 +++ lib/kross/api/qtobject.cpp | 235 ++ lib/kross/api/qtobject.h | 135 + lib/kross/api/script.cpp | 59 + lib/kross/api/script.h | 140 + lib/kross/api/value.h | 91 + lib/kross/api/variant.cpp | 168 ++ lib/kross/api/variant.h | 207 ++ lib/kross/configure.in.bot | 16 + lib/kross/configure.in.in | 108 + lib/kross/main/Makefile.am | 33 + lib/kross/main/krossconfig.cpp | 36 + lib/kross/main/krossconfig.h | 116 + lib/kross/main/mainmodule.cpp | 117 + lib/kross/main/mainmodule.h | 181 ++ lib/kross/main/manager.cpp | 248 ++ lib/kross/main/manager.h | 168 ++ lib/kross/main/scriptaction.cpp | 247 ++ lib/kross/main/scriptaction.h | 309 +++ lib/kross/main/scriptcontainer.cpp | 293 ++ lib/kross/main/scriptcontainer.h | 210 ++ lib/kross/main/scriptguiclient.cpp | 384 +++ lib/kross/main/scriptguiclient.h | 217 ++ lib/kross/main/wdgscriptsmanager.cpp | 354 +++ lib/kross/main/wdgscriptsmanager.h | 60 + lib/kross/main/wdgscriptsmanagerbase.ui | 247 ++ lib/kross/python/Makefile.am | 33 + lib/kross/python/cxx/Config.hxx | 74 + lib/kross/python/cxx/Exception.hxx | 212 ++ lib/kross/python/cxx/Extensions.hxx | 756 ++++++ lib/kross/python/cxx/IndirectPythonInterface.cxx | 550 ++++ lib/kross/python/cxx/IndirectPythonInterface.hxx | 156 ++ lib/kross/python/cxx/Legal.html | 40 + lib/kross/python/cxx/Makefile.am | 19 + lib/kross/python/cxx/Objects.hxx | 2804 ++++++++++++++++++++ lib/kross/python/cxx/PyCXX.html | 2131 +++++++++++++++ lib/kross/python/cxx/README.html | 436 +++ lib/kross/python/cxx/Readme.Kross.txt | 16 + lib/kross/python/cxx/Version.txt | 1 + lib/kross/python/cxx/cxx_extensions.cxx | 1287 +++++++++ lib/kross/python/cxx/cxxextensions.c | 19 + lib/kross/python/cxx/cxxsupport.cxx | 142 + lib/kross/python/pythonconfig.h | 105 + lib/kross/python/pythonextension.cpp | 445 ++++ lib/kross/python/pythonextension.h | 221 ++ lib/kross/python/pythoninterpreter.cpp | 255 ++ lib/kross/python/pythoninterpreter.h | 90 + lib/kross/python/pythonmodule.cpp | 100 + lib/kross/python/pythonmodule.h | 76 + lib/kross/python/pythonobject.cpp | 94 + lib/kross/python/pythonobject.h | 95 + lib/kross/python/pythonscript.cpp | 460 ++++ lib/kross/python/pythonscript.h | 98 + lib/kross/python/pythonsecurity.cpp | 181 ++ lib/kross/python/pythonsecurity.h | 109 + lib/kross/python/scripts/Makefile.am | 4 + lib/kross/python/scripts/RestrictedPython/Eval.py | 118 + .../python/scripts/RestrictedPython/Guards.py | 136 + .../python/scripts/RestrictedPython/Limits.py | 46 + .../python/scripts/RestrictedPython/Makefile.am | 4 + .../scripts/RestrictedPython/MutatingWalker.py | 74 + .../scripts/RestrictedPython/PrintCollector.py | 23 + .../python/scripts/RestrictedPython/RCompile.py | 235 ++ .../scripts/RestrictedPython/RestrictionMutator.py | 372 +++ .../scripts/RestrictedPython/SelectCompiler.py | 28 + .../python/scripts/RestrictedPython/Utilities.py | 77 + .../python/scripts/RestrictedPython/__init__.py | 19 + lib/kross/python/scripts/gui.py | 396 +++ lib/kross/readme.dox | 28 + lib/kross/ruby/Makefile.am | 16 + lib/kross/ruby/rubyconfig.h | 42 + lib/kross/ruby/rubyextension.cpp | 378 +++ lib/kross/ruby/rubyextension.h | 162 ++ lib/kross/ruby/rubyinterpreter.cpp | 149 ++ lib/kross/ruby/rubyinterpreter.h | 73 + lib/kross/ruby/rubymodule.cpp | 68 + lib/kross/ruby/rubymodule.h | 73 + lib/kross/ruby/rubyscript.cpp | 193 ++ lib/kross/ruby/rubyscript.h | 98 + lib/kross/runner/Makefile.am | 11 + lib/kross/runner/main.cpp | 144 + lib/kross/test/Makefile.am | 17 + lib/kross/test/main.cpp | 190 ++ lib/kross/test/testaction.cpp | 49 + lib/kross/test/testaction.h | 50 + lib/kross/test/testcase.py | 144 + lib/kross/test/testcase.rb | 15 + lib/kross/test/testgui.py | 149 ++ lib/kross/test/testkexidb.py | 214 ++ lib/kross/test/testobject.cpp | 96 + lib/kross/test/testobject.h | 64 + lib/kross/test/testperformance.py | 75 + lib/kross/test/testplugin.cpp | 126 + lib/kross/test/testplugin.h | 75 + lib/kross/test/testscripting.rc | 33 + lib/kross/test/testwindow.cpp | 110 + lib/kross/test/testwindow.h | 55 + 125 files changed, 22915 insertions(+) create mode 100644 lib/kross/CHANGES create mode 100644 lib/kross/Makefile.am create mode 100644 lib/kross/Makefile.global create mode 100644 lib/kross/api/Makefile.am create mode 100644 lib/kross/api/callable.cpp create mode 100644 lib/kross/api/callable.h create mode 100644 lib/kross/api/class.h create mode 100644 lib/kross/api/dict.cpp create mode 100644 lib/kross/api/dict.h create mode 100644 lib/kross/api/event.h create mode 100644 lib/kross/api/eventaction.cpp create mode 100644 lib/kross/api/eventaction.h create mode 100644 lib/kross/api/eventscript.cpp create mode 100644 lib/kross/api/eventscript.h create mode 100644 lib/kross/api/eventsignal.cpp create mode 100644 lib/kross/api/eventsignal.h create mode 100644 lib/kross/api/eventslot.cpp create mode 100644 lib/kross/api/eventslot.h create mode 100644 lib/kross/api/exception.cpp create mode 100644 lib/kross/api/exception.h create mode 100644 lib/kross/api/function.h create mode 100644 lib/kross/api/interpreter.cpp create mode 100644 lib/kross/api/interpreter.h create mode 100644 lib/kross/api/list.cpp create mode 100644 lib/kross/api/list.h create mode 100644 lib/kross/api/module.h create mode 100644 lib/kross/api/object.cpp create mode 100644 lib/kross/api/object.h create mode 100644 lib/kross/api/proxy.h create mode 100644 lib/kross/api/qtobject.cpp create mode 100644 lib/kross/api/qtobject.h create mode 100644 lib/kross/api/script.cpp create mode 100644 lib/kross/api/script.h create mode 100644 lib/kross/api/value.h create mode 100644 lib/kross/api/variant.cpp create mode 100644 lib/kross/api/variant.h create mode 100644 lib/kross/configure.in.bot create mode 100644 lib/kross/configure.in.in create mode 100644 lib/kross/main/Makefile.am create mode 100644 lib/kross/main/krossconfig.cpp create mode 100644 lib/kross/main/krossconfig.h create mode 100644 lib/kross/main/mainmodule.cpp create mode 100644 lib/kross/main/mainmodule.h create mode 100644 lib/kross/main/manager.cpp create mode 100644 lib/kross/main/manager.h create mode 100644 lib/kross/main/scriptaction.cpp create mode 100644 lib/kross/main/scriptaction.h create mode 100644 lib/kross/main/scriptcontainer.cpp create mode 100644 lib/kross/main/scriptcontainer.h create mode 100644 lib/kross/main/scriptguiclient.cpp create mode 100644 lib/kross/main/scriptguiclient.h create mode 100644 lib/kross/main/wdgscriptsmanager.cpp create mode 100644 lib/kross/main/wdgscriptsmanager.h create mode 100644 lib/kross/main/wdgscriptsmanagerbase.ui create mode 100644 lib/kross/python/Makefile.am create mode 100644 lib/kross/python/cxx/Config.hxx create mode 100644 lib/kross/python/cxx/Exception.hxx create mode 100644 lib/kross/python/cxx/Extensions.hxx create mode 100644 lib/kross/python/cxx/IndirectPythonInterface.cxx create mode 100644 lib/kross/python/cxx/IndirectPythonInterface.hxx create mode 100755 lib/kross/python/cxx/Legal.html create mode 100644 lib/kross/python/cxx/Makefile.am create mode 100644 lib/kross/python/cxx/Objects.hxx create mode 100644 lib/kross/python/cxx/PyCXX.html create mode 100644 lib/kross/python/cxx/README.html create mode 100644 lib/kross/python/cxx/Readme.Kross.txt create mode 100644 lib/kross/python/cxx/Version.txt create mode 100644 lib/kross/python/cxx/cxx_extensions.cxx create mode 100644 lib/kross/python/cxx/cxxextensions.c create mode 100644 lib/kross/python/cxx/cxxsupport.cxx create mode 100644 lib/kross/python/pythonconfig.h create mode 100644 lib/kross/python/pythonextension.cpp create mode 100644 lib/kross/python/pythonextension.h create mode 100644 lib/kross/python/pythoninterpreter.cpp create mode 100644 lib/kross/python/pythoninterpreter.h create mode 100644 lib/kross/python/pythonmodule.cpp create mode 100644 lib/kross/python/pythonmodule.h create mode 100644 lib/kross/python/pythonobject.cpp create mode 100644 lib/kross/python/pythonobject.h create mode 100644 lib/kross/python/pythonscript.cpp create mode 100644 lib/kross/python/pythonscript.h create mode 100644 lib/kross/python/pythonsecurity.cpp create mode 100644 lib/kross/python/pythonsecurity.h create mode 100644 lib/kross/python/scripts/Makefile.am create mode 100644 lib/kross/python/scripts/RestrictedPython/Eval.py create mode 100644 lib/kross/python/scripts/RestrictedPython/Guards.py create mode 100644 lib/kross/python/scripts/RestrictedPython/Limits.py create mode 100644 lib/kross/python/scripts/RestrictedPython/Makefile.am create mode 100644 lib/kross/python/scripts/RestrictedPython/MutatingWalker.py create mode 100644 lib/kross/python/scripts/RestrictedPython/PrintCollector.py create mode 100644 lib/kross/python/scripts/RestrictedPython/RCompile.py create mode 100644 lib/kross/python/scripts/RestrictedPython/RestrictionMutator.py create mode 100644 lib/kross/python/scripts/RestrictedPython/SelectCompiler.py create mode 100644 lib/kross/python/scripts/RestrictedPython/Utilities.py create mode 100644 lib/kross/python/scripts/RestrictedPython/__init__.py create mode 100755 lib/kross/python/scripts/gui.py create mode 100644 lib/kross/readme.dox create mode 100644 lib/kross/ruby/Makefile.am create mode 100644 lib/kross/ruby/rubyconfig.h create mode 100644 lib/kross/ruby/rubyextension.cpp create mode 100644 lib/kross/ruby/rubyextension.h create mode 100644 lib/kross/ruby/rubyinterpreter.cpp create mode 100644 lib/kross/ruby/rubyinterpreter.h create mode 100644 lib/kross/ruby/rubymodule.cpp create mode 100644 lib/kross/ruby/rubymodule.h create mode 100644 lib/kross/ruby/rubyscript.cpp create mode 100644 lib/kross/ruby/rubyscript.h create mode 100644 lib/kross/runner/Makefile.am create mode 100644 lib/kross/runner/main.cpp create mode 100644 lib/kross/test/Makefile.am create mode 100644 lib/kross/test/main.cpp create mode 100644 lib/kross/test/testaction.cpp create mode 100644 lib/kross/test/testaction.h create mode 100644 lib/kross/test/testcase.py create mode 100644 lib/kross/test/testcase.rb create mode 100644 lib/kross/test/testgui.py create mode 100644 lib/kross/test/testkexidb.py create mode 100644 lib/kross/test/testobject.cpp create mode 100644 lib/kross/test/testobject.h create mode 100755 lib/kross/test/testperformance.py create mode 100644 lib/kross/test/testplugin.cpp create mode 100644 lib/kross/test/testplugin.h create mode 100644 lib/kross/test/testscripting.rc create mode 100644 lib/kross/test/testwindow.cpp create mode 100644 lib/kross/test/testwindow.h (limited to 'lib/kross') diff --git a/lib/kross/CHANGES b/lib/kross/CHANGES new file mode 100644 index 00000000..096aff0e --- /dev/null +++ b/lib/kross/CHANGES @@ -0,0 +1,20 @@ +== Kross CHANGES == + +1.6 +* Added krossdebug and krosswarning. +* Integrated ProxyFunction's. +* Python: Added unittest. +* Removed Argument class. +* Refactor of the tree-handling to improve the performance. +* Tweaks on Object and Value to improve the performance. +* Python: Fixed crasher in PyEval_EvalCode + +1.5.2 +* API: Fix possible crash in Manager::getInterpreternameForFile() + +1.5.1 +* Python: Fix compile with gcc 3.3.6 + +1.5 +* Ruby: Fix 124649: libruby-devel lost when build. +* Ruby: Fix crash cause of invalid rNode. diff --git a/lib/kross/Makefile.am b/lib/kross/Makefile.am new file mode 100644 index 00000000..6e0fa847 --- /dev/null +++ b/lib/kross/Makefile.am @@ -0,0 +1,22 @@ +# Compile python plugin only if python is installed. Kross itself doesn't +# care of it cause it doesn't depend directly on the python plugin. Kross +# determinates at runtime if it's avaiable and if that's the case it just +# uses the plugin. So, we are able to don't compile the python plugin now +# and at a later stage once we like to use it, just compile+install the +# python plugin and let Kross use it without the need to recompiling whole +# Kross. So, for packagers it's recommed to move the python plugin into +# an own package that could be optional installed. +if compile_kross_python + PYTHONSUBDIR = python +endif + +# Also only compile the ruby plugin if ruby is installed. +if compile_kross_ruby + RUBYSUBDIR = ruby +endif + +SUBDIRS = api main $(PYTHONSUBDIR) $(RUBYSUBDIR) runner + +# Don't compile test by default. To use test, just cd into the directory +# and do a "make && ./krosstest" manualy. +#SUBDIRS += test diff --git a/lib/kross/Makefile.global b/lib/kross/Makefile.global new file mode 100644 index 00000000..db6d32df --- /dev/null +++ b/lib/kross/Makefile.global @@ -0,0 +1,2 @@ +KDE_CXXFLAGS = $(USE_EXCEPTIONS) +VER_INFO = -version-info 1:0:0 diff --git a/lib/kross/api/Makefile.am b/lib/kross/api/Makefile.am new file mode 100644 index 00000000..166e1bfa --- /dev/null +++ b/lib/kross/api/Makefile.am @@ -0,0 +1,47 @@ +include $(top_srcdir)/lib/kross/Makefile.global +apiincludedir=$(includedir)/kross/api + +apiinclude_HEADERS = \ + callable.h \ + class.h \ + dict.h \ + eventaction.h \ + event.h \ + eventscript.h \ + eventsignal.h \ + eventslot.h \ + exception.h \ + function.h \ + interpreter.h \ + list.h \ + module.h \ + object.h \ + proxy.h \ + qtobject.h \ + script.h \ + value.h \ + variant.h + +lib_LTLIBRARIES = libkrossapi.la + +libkrossapi_la_SOURCES = \ + object.cpp \ + variant.cpp \ + list.cpp \ + dict.cpp \ + exception.cpp \ + callable.cpp \ + eventaction.cpp \ + eventsignal.cpp \ + eventslot.cpp \ + eventscript.cpp \ + qtobject.cpp \ + script.cpp \ + interpreter.cpp + +libkrossapi_la_LDFLAGS = $(all_libraries) $(VER_INFO) -Wnounresolved +libkrossapi_la_LIBADD = $(LIB_QT) $(LIB_KDECORE) + +METASOURCES = AUTO +SUBDIRS = . +INCLUDES = $(KROSS_INCLUDES) $(all_includes) diff --git a/lib/kross/api/callable.cpp b/lib/kross/api/callable.cpp new file mode 100644 index 00000000..261a1bf0 --- /dev/null +++ b/lib/kross/api/callable.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + * callable.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "callable.h" +#include "variant.h" +#include "dict.h" + +#include "../main/krossconfig.h" + +using namespace Kross::Api; + +Callable::Callable(const QString& name) + : Object() + , m_name(name) +{ +} + +Callable::~Callable() +{ +} + +const QString Callable::getName() const +{ + return m_name; +} + +const QString Callable::getClassName() const +{ + return "Kross::Api::Callable"; +} + +bool Callable::hasChild(const QString& name) const +{ + return m_children.contains(name); +} + +Object::Ptr Callable::getChild(const QString& name) const +{ + return m_children[name]; +} + +QMap Callable::getChildren() const +{ + return m_children; +} + +bool Callable::addChild(const QString& name, Object* object) +{ +#ifdef KROSS_API_OBJECT_ADDCHILD_DEBUG + krossdebug( QString("Kross::Api::Callable::addChild() object.name='%1' object.classname='%2'") + .arg(name).arg(object->getClassName()) ); +#endif + m_children.replace(name, Object::Ptr(object)); + return true; +} + +bool Callable::addChild(Callable* object) +{ + return addChild(object->getName(), object); +} + +void Callable::removeChild(const QString& name) +{ +#ifdef KROSS_API_OBJECT_REMCHILD_DEBUG + krossdebug( QString("Kross::Api::Callable::removeChild() name='%1'").arg(name) ); +#endif + m_children.remove(name); +} + +void Callable::removeAllChildren() +{ +#ifdef KROSS_API_OBJECT_REMCHILD_DEBUG + krossdebug( "Kross::Api::Callable::removeAllChildren()" ); +#endif + m_children.clear(); +} + +Object::Ptr Callable::call(const QString& name, List::Ptr args) +{ +#ifdef KROSS_API_CALLABLE_CALL_DEBUG + krossdebug( QString("Kross::Api::Callable::call() name=%1 getName()=%2 arguments=%3").arg(name).arg(getName()).arg(args ? args->toString() : QString("")) ); +#endif + + if(name.isEmpty()) // return a self-reference if no functionname is defined. + return this; + + // if name is defined try to get the matching child and pass the call to it. + Object::Ptr object = getChild(name); + if(object) { + //TODO handle namespace, e.g. "mychild1.mychild2.myfunction" + return object->call(name, args); + } + + if(name == "get") { + QString s = Variant::toString(args->item(0)); + Object::Ptr obj = getChild(s); + if(! obj) + throw Exception::Ptr( new Exception(QString("The object '%1' has no child object '%2'").arg(getName()).arg(s)) ); + return obj; + } + else if(name == "has") { + return new Variant( hasChild( Variant::toString(args->item(0)) ) ); + } + else if(name == "call") { + //TODO should we remove first args-item? + return Object::call(Variant::toString(args->item(0)), args); + } + else if(name == "list") { + QStringList list; + QMap children = getChildren(); + QMap::Iterator it( children.begin() ); + for(; it != children.end(); ++it) + list.append( it.key() ); + return new Variant(list); + } + else if(name == "dict") { + return new Dict( getChildren() ); + } + + // If there exists no such object return NULL. + krossdebug( QString("Object '%1' has no callable object named '%2'.").arg(getName()).arg(name) ); + return 0; +} diff --git a/lib/kross/api/callable.h b/lib/kross/api/callable.h new file mode 100644 index 00000000..4d25bd91 --- /dev/null +++ b/lib/kross/api/callable.h @@ -0,0 +1,147 @@ +/*************************************************************************** + * callable.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_CALLABLE_H +#define KROSS_API_CALLABLE_H + +#include "object.h" +#include "list.h" +//#include "exception.h" + +#include +#include +#include + +namespace Kross { namespace Api { + + /** + * Base class for callable objects. Classes like + * \a Event or \a Class are inherited from this class + * and implement the \a Object::call() method to handle + * the call. + */ + class Callable : public Object + { + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr Ptr; + + /** + * Constructor. + * + * \param name The name this callable object has and + * it is reachable as via \a getChild() . + */ + Callable(const QString& name); + + /** + * Destructor. + */ + virtual ~Callable(); + + /** + * \return the name this object has. Each callable object + * has a name which is used e.g. on \a addChild to be able + * to identify the object itself. + */ + const QString getName() const; + + /** + * Return the class name. This could be something + * like "Kross::Api::Callable" for this object. The + * value is mainly used for display purposes. + * + * \return The name of this class. + */ + virtual const QString getClassName() const; + + /** + * Returns if the defined child is avaible. + * + * \return true if child exists else false. + */ + bool hasChild(const QString& name) const; + + /** + * Return the defined child or NULL if there is + * no such object with that name avaible. + * + * \param name The name of the Object to return. + * \return The Object matching to the defined + * name or NULL if there is no such Object. + */ + Object::Ptr getChild(const QString& name) const; + + /** + * Return all children. + * + * \return A \a ObjectMap of children this Object has. + */ + QMap getChildren() const; + + /** + * Add a new child. Replaces a possible already existing + * child with such a name. + * + * \param name the name of the child + * \param object The Object to add. + * \return true if the Object was added successfully + * else false. + */ + bool addChild(const QString& name, Object* object); + + /** + * Same as the \a addChild method above but for callable + * objects which define there own name. + */ + bool addChild(Callable* object); + + /** + * Remove an existing child. + * + * \param name The name of the Object to remove. + * If there doesn't exists an Object with + * such name just nothing will be done. + */ + void removeChild(const QString& name); + + /** + * Remove all children. + */ + void removeAllChildren(); + + /** + * Call the object. + */ + virtual Object::Ptr call(const QString& name, List::Ptr arguments); + + private: + /// Name this callable object has. + QString m_name; + /// A list of childobjects. + QMap m_children; + }; + +}} + +#endif + diff --git a/lib/kross/api/class.h b/lib/kross/api/class.h new file mode 100644 index 00000000..afcfe425 --- /dev/null +++ b/lib/kross/api/class.h @@ -0,0 +1,89 @@ +/*************************************************************************** + * class.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_CLASS_H +#define KROSS_API_CLASS_H + +#include +//#include +//#include +#include + +//#include "../main/krossconfig.h" +#include "object.h" +#include "event.h" +#include "list.h" +#include "exception.h" +#include "variant.h" + +namespace Kross { namespace Api { + + /** + * From \a Event inherited template-class to represent + * class-structures. Classes implemetating this template + * are able to dynamicly define \a Event methodfunctions + * accessible from within scripts. + */ + template + class Class : public Event + { + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr Ptr; + + /** + * Constructor. + * + * \param name The name this class has. + */ + Class(const QString& name) + : Event(name) + { + } + + /** + * Destructor. + */ + virtual ~Class() + { + } + + template + static Object::Ptr toObject(TYPE t) { return t; } + + operator T* () { return (T*)this; } + //operator Ptr () { return (T*)this; } + + /* + virtual Object::Ptr call(const QString& name, List::Ptr arguments) + { + krossdebug( QString("Class::call(%1)").arg(name) ); + return Event::call(name, arguments); + } + */ + + }; + +}} + +#endif + diff --git a/lib/kross/api/dict.cpp b/lib/kross/api/dict.cpp new file mode 100644 index 00000000..b9fa5ddc --- /dev/null +++ b/lib/kross/api/dict.cpp @@ -0,0 +1,47 @@ +/*************************************************************************** + * dict.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "dict.h" +//#include "exception.h" + +using namespace Kross::Api; + +Dict::Dict(const QMap value) + : Value< List, QMap >(value) +{ +} + +Dict::~Dict() +{ +} + +const QString Dict::getClassName() const +{ + return "Kross::Api::Dict"; +} + +const QString Dict::toString() +{ + QString s = "["; + QMap list = getValue(); + for(QMap::Iterator it = list.begin(); it != list.end(); ++it) + s += "'" + it.key() + "' = '" + it.data()->toString() + "', "; + return (s.endsWith(", ") ? s.left(s.length() - 2) : s) + "]"; +} + diff --git a/lib/kross/api/dict.h b/lib/kross/api/dict.h new file mode 100644 index 00000000..773f36ac --- /dev/null +++ b/lib/kross/api/dict.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * dict.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_DICT_H +#define KROSS_API_DICT_H + +#include +#include + +#include "object.h" +#include "value.h" + +namespace Kross { namespace Api { + + /** + * The Dict class implementates \a Value to handle + * key=value base dictonaries/maps. + */ + class Dict : public Value< List, QMap > + { + friend class Value< List, QMap >; + public: + + /** + * Constructor. + * + * @param value The map of \a Object instances accessible + * via there string-identifier that is in this + * \a Dict intialy. + */ + explicit Dict(const QMap value); + + /** + * Destructor. + */ + virtual ~Dict(); + + /// \see Kross::Api::Object::getClassName() + virtual const QString getClassName() const; + + /** + * \return a string representation of the whole dictonary. + * + * \see Kross::Api::Object::toString() + */ + virtual const QString toString(); + + }; + +}} + +#endif + diff --git a/lib/kross/api/event.h b/lib/kross/api/event.h new file mode 100644 index 00000000..2df5a331 --- /dev/null +++ b/lib/kross/api/event.h @@ -0,0 +1,229 @@ +/*************************************************************************** + * event.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_EVENT_H +#define KROSS_API_EVENT_H + +#include "../main/krossconfig.h" +#include "object.h" +#include "callable.h" +#include "list.h" +#include "exception.h" +#include "function.h" +#include "proxy.h" +#include "variant.h" + +#include +#include +#include + +namespace Kross { namespace Api { + + /** + * Template class for all kinds of callable events. An + * event is the abstract base for callable objects like + * methodfunctions in \a Class instances or \a EventSlot + * and \a EventSignal to access Qt signals and slots. + */ + template + class Event : public Callable + { + private: + + /** + * Definition of function-pointers. + */ + typedef Object::Ptr(T::*FunctionPtr)(List::Ptr); + + /** + * List of memberfunctions. Each function is accessible + * by the functionname. + */ + QMap m_functions; + + public: + + /** + * Constructor. + * + * \param name The name this \a Event has. + */ + Event(const QString& name) + : Callable(name) + { + } + + /** + * Destructor. + */ + virtual ~Event() + { + QMapConstIterator endit = m_functions.constEnd(); + for(QMapConstIterator it = m_functions.constBegin(); it != endit; ++it) + delete it.data(); + } + + /** + * Add a \a Callable methodfunction to the list of functions + * this Object supports. + * + * The FunctionPtr points to the concret + * Object::Ptr myfuncname(List::Ptr) + * method in the class defined with template T. + * + * \param name The functionname. Each function this object + * holds should have an unique name to be + * still accessable. + * \param function A pointer to the methodfunction that + * should handle calls. + * + * \todo Remove this method as soon as there is no code using it + */ + inline void addFunction(const QString& name, FunctionPtr function) + { + m_functions.replace(name, new Function0(static_cast(this), function)); + } + + /** + * Add a methodfunction to the list of functions this Object + * supports. + * + * \param name The functionname. Each function this object + * holds should have an unique name to be + * still accessable. + * \param function A \a Function instance which defines + * the methodfunction. This \a Event will be the + * owner of the \a Function instance and will take + * care of deleting it if this \a Event got deleted. + */ + inline void addFunction(const QString& name, Function* function) + { + m_functions.replace(name, function); + } + + /** + * Template function to add a \a ProxyFunction as builtin-function + * to this \a Event instance. + */ + template + inline void addFunction4(const QString& name, INSTANCE* instance, METHOD method, ARG1OBJ* arg1 = 0, ARG2OBJ* arg2 = 0, ARG3OBJ* arg3 = 0, ARG4OBJ* arg4 = 0) + { + m_functions.replace(name, + new Kross::Api::ProxyFunction + (instance, method, arg1, arg2, arg3, arg4) + ); + } + + /// Same as above with three arguments. + template + inline void addFunction3(const QString& name, INSTANCE* instance, METHOD method, ARG1OBJ* arg1 = 0, ARG2OBJ* arg2 = 0, ARG3OBJ* arg3 = 0) + { + m_functions.replace(name, + new Kross::Api::ProxyFunction + (instance, method, arg1, arg2, arg3) + ); + } + + /// Same as above with two arguments. + template + inline void addFunction2(const QString& name, INSTANCE* instance, METHOD method, ARG1OBJ* arg1 = 0, ARG2OBJ* arg2 = 0) + { + m_functions.replace(name, + new Kross::Api::ProxyFunction + (instance, method, arg1, arg2) + ); + } + + /// Same as above, but with one argument. + template + inline void addFunction1(const QString& name, INSTANCE* instance, METHOD method, ARG1OBJ* arg1 = 0) + { + m_functions.replace(name, + new Kross::Api::ProxyFunction + (instance, method, arg1) + ); + } + + /// Same as above with no arguments. + template + inline void addFunction0(const QString& name, INSTANCE* instance, METHOD method) + { + m_functions.replace(name, + new Kross::Api::ProxyFunction + (instance, method) + ); + } + + /** + * Check if a function is a member of this \a Callable + * \param name the function name + * \return true if the function is available in this \a Callable + */ + bool isAFunction(const QString & name) const + { + return m_functions.contains(name); + } + + /** + * Overloaded method to handle function-calls. + * + * \throw AttributeException if argumentparameters + * arn't valid. + * \throw RuntimeException if the functionname isn't + * valid. + * \param name The functionname. Each function this + * Object holds should have a different + * name cause they are access by they name. + * If name is QString::null or empty, a + * self-reference to this instance is + * returned. + * \param arguments The list of arguments. + * \return An Object representing the call result + * or NULL if there doesn't exists such a + * function with defined name. + */ + virtual Object::Ptr call(const QString& name, List::Ptr arguments) + { +#ifdef KROSS_API_EVENT_CALL_DEBUG + krossdebug( QString("Event::call() name='%1' getName()='%2'").arg(name).arg(getName()) ); +#endif + + Function* function = m_functions[name]; + if(function) { +#ifdef KROSS_API_EVENT_CALL_DEBUG + krossdebug( QString("Event::call() name='%1' is a builtin function.").arg(name) ); +#endif + return function->call(arguments); + } + + if(name.isNull()) { + // If no name is defined, we return a reference to our instance. + return this; + } + + // Redirect the call to the Kross::Api::Callable we are inherited from. + return Callable::call(name, arguments); + } + + }; + +}} + +#endif + diff --git a/lib/kross/api/eventaction.cpp b/lib/kross/api/eventaction.cpp new file mode 100644 index 00000000..fffaec5c --- /dev/null +++ b/lib/kross/api/eventaction.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** + * eventaction.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "eventaction.h" +#include "variant.h" + +//#include +//#include + +using namespace Kross::Api; + +EventAction::EventAction(const QString& name, KAction* action) + : Event(name.isEmpty() ? action->name() : name) + , m_action(action) +{ + addFunction("getText", &EventAction::getText); + addFunction("setText", &EventAction::setText); + + addFunction("isEnabled", &EventAction::isEnabled); + addFunction("setEnabled", &EventAction::setEnabled); + + addFunction("activate", &EventAction::activate); +} + +EventAction::~EventAction() +{ +} + +const QString EventAction::getClassName() const +{ + return "Kross::Api::EventAction"; +} + +Object::Ptr EventAction::getText(List::Ptr) +{ + return new Variant(m_action->text()); +} + +Object::Ptr EventAction::setText(List::Ptr args) +{ + m_action->setText( Variant::toString(args->item(0)) ); + return 0; +} + +Object::Ptr EventAction::isEnabled(List::Ptr) +{ + return new Variant(m_action->isEnabled()); +} + +Object::Ptr EventAction::setEnabled(List::Ptr args) +{ + m_action->setEnabled( Variant::toBool(args->item(0)) ); + return 0; +} + +Object::Ptr EventAction::activate(List::Ptr) +{ + m_action->activate(); + return 0; +} + diff --git a/lib/kross/api/eventaction.h b/lib/kross/api/eventaction.h new file mode 100644 index 00000000..8f09c116 --- /dev/null +++ b/lib/kross/api/eventaction.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * eventaction.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_EVENTACTION_H +#define KROSS_API_EVENTACTION_H + +#include +//#include +#include +#include + +#include "event.h" + +namespace Kross { namespace Api { + + // Forward declarations. + class ScriptContainer; + + /** + * The EventAction class is used to wrap KAction instances + * into the Kross object hierachy and provide access to + * them. + */ + class EventAction : public Event + { + + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr Ptr; + + /** + * Constructor. + */ + EventAction(const QString& name, KAction* action); + + /** + * Destructor. + */ + virtual ~EventAction(); + + /// \see Kross::Api::Object::getClassName() + virtual const QString getClassName() const; + + /// \see Kross::Api::Event::call() + //virtual Object::Ptr call(const QString& name, KSharedPtr arguments); + + private: + KAction* m_action; + + /// \return the text associated with this action. + Kross::Api::Object::Ptr getText(Kross::Api::List::Ptr); + /// Sets the text associated with this action. + Kross::Api::Object::Ptr setText(Kross::Api::List::Ptr); + + /// \return true if this action is enabled else false. + Kross::Api::Object::Ptr isEnabled(Kross::Api::List::Ptr); + /// Enables or disables this action. + Kross::Api::Object::Ptr setEnabled(Kross::Api::List::Ptr); + + /// Activates the action. + Kross::Api::Object::Ptr activate(Kross::Api::List::Ptr); + }; + +}} + +#endif + diff --git a/lib/kross/api/eventscript.cpp b/lib/kross/api/eventscript.cpp new file mode 100644 index 00000000..39ceb59d --- /dev/null +++ b/lib/kross/api/eventscript.cpp @@ -0,0 +1,47 @@ +/*************************************************************************** + * eventscript.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "eventscript.h" +//#include "object.h" +//#include "variant.h" +//#include "../main/scriptcontainer.h" + +using namespace Kross::Api; + +EventScript::EventScript(const QString& name) + : Event(name) +{ +} + +EventScript::~EventScript() +{ +} + +const QString EventScript::getClassName() const +{ + return "Kross::Api::EventScript"; +} + +Object::Ptr EventScript::call(const QString& name, KSharedPtr arguments) +{ + krossdebug( QString("EventScript::call() name=%1 arguments=%2").arg(name).arg(arguments->toString()) ); + //TODO + return 0; +} + diff --git a/lib/kross/api/eventscript.h b/lib/kross/api/eventscript.h new file mode 100644 index 00000000..8ef9b7eb --- /dev/null +++ b/lib/kross/api/eventscript.h @@ -0,0 +1,65 @@ +/*************************************************************************** + * eventscript.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_EVENTSCRIPT_H +#define KROSS_API_EVENTSCRIPT_H + +#include +#include + +#include "event.h" + +namespace Kross { namespace Api { + + /** + * \todo implement EventScript ?! + */ + class EventScript : public Event + { + + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr Ptr; + + /** + * Constructor. + */ + EventScript(const QString& name); + + /** + * Destructor. + */ + virtual ~EventScript(); + + virtual const QString getClassName() const; + + virtual Object::Ptr call(const QString& name, KSharedPtr arguments); + + private: + //ScriptContainer* m_scriptcontainer; + //Callable* m_callable; + }; + +}} + +#endif + diff --git a/lib/kross/api/eventsignal.cpp b/lib/kross/api/eventsignal.cpp new file mode 100644 index 00000000..455ca61f --- /dev/null +++ b/lib/kross/api/eventsignal.cpp @@ -0,0 +1,66 @@ +/*************************************************************************** + * eventsignal.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "eventsignal.h" + +#include "variant.h" +#include "qtobject.h" + +#include +#include // for the Qt QUObject API. + +using namespace Kross::Api; + +EventSignal::EventSignal(const QString& name, QObject* sender, QCString signal) + : Event(name) + , m_sender(sender) + , m_signal(signal) //QObject::normalizeSignalSlot(signal) +{ +} + +EventSignal::~EventSignal() +{ +} + +const QString EventSignal::getClassName() const +{ + return "Kross::Api::EventSignal"; +} + +Object::Ptr EventSignal::call(const QString& /*name*/, KSharedPtr arguments) +{ +#ifdef KROSS_API_EVENTSIGNAL_CALL_DEBUG + krossdebug( QString("EventSignal::call() m_signal=%1 arguments=%2").arg(m_signal).arg(arguments->toString()) ); +#endif + + QString n = m_signal; + + if(n.startsWith("2")) // Remove prefix of SIGNAL-macros + n.remove(0,1); + + int signalid = m_sender->metaObject()->findSignal(n.latin1(), false); + if(signalid < 0) + throw new Exception(QString("No such signal '%1'.").arg(n)); + + QUObject* uo = QtObject::toQUObject(n, arguments); + m_sender->qt_emit(signalid, uo); // emit the signal + delete [] uo; + + return new Variant( QVariant(true,0) ); +} diff --git a/lib/kross/api/eventsignal.h b/lib/kross/api/eventsignal.h new file mode 100644 index 00000000..aea58b12 --- /dev/null +++ b/lib/kross/api/eventsignal.h @@ -0,0 +1,79 @@ +/*************************************************************************** + * eventsignal.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_EVENTSIGNAL_H +#define KROSS_API_EVENTSIGNAL_H + +//#include +//#include +//#include +//#include +//#include +//#include +#include +#include + +#include "event.h" + +namespace Kross { namespace Api { + + /** + * Each Qt signal and slot connection between a QObject + * instance and a functionname is represented with + * a EventSignal and handled by \a EventManager. + */ + class EventSignal : public Event + { + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr Ptr; + + /** + * Constructor. + */ + EventSignal(const QString& name, QObject* sender, QCString signal); + + /** + * Destructor. + */ + virtual ~EventSignal(); + + virtual const QString getClassName() const; + + virtual Object::Ptr call(const QString& name, KSharedPtr arguments); + +/* + signals: + void callback(); + void callback(const QString&); + void callback(int); + void callback(bool); +*/ + private: + QObject* m_sender; + QCString m_signal; + }; + +}} + +#endif + diff --git a/lib/kross/api/eventslot.cpp b/lib/kross/api/eventslot.cpp new file mode 100644 index 00000000..df70c9c8 --- /dev/null +++ b/lib/kross/api/eventslot.cpp @@ -0,0 +1,222 @@ +/*************************************************************************** + * eventslot.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "eventslot.h" + +#include "variant.h" +#include "qtobject.h" + +#include +#include // for the Qt QUObject API. + +using namespace Kross::Api; + +EventSlot::EventSlot(const QString& name, QObject* receiver, QCString slot) + : Event(name) + , m_receiver(receiver) + , m_slot(slot) //QObject::normalizeSignalSlot(slot) +{ +} + +EventSlot::~EventSlot() +{ +} + +const QString EventSlot::getClassName() const +{ + return "Kross::Api::EventSlot"; +} + +Object::Ptr EventSlot::call(const QString& /*name*/, List::Ptr arguments) +{ +#ifdef KROSS_API_EVENTSLOT_CALL_DEBUG + krossdebug( QString("EventSlot::call() m_slot=%1 arguments=%2").arg(m_slot).arg(arguments->toString()) ); +#endif + + QString n = m_slot; //TODO name; //Variant::toString(args->item(0)); + + if(n.startsWith("1")) // Remove prefix of SLOT-macros + n.remove(0,1); + + int slotid = m_receiver->metaObject()->findSlot(n.latin1(), false); + if(slotid < 0) + throw Exception::Ptr( new Exception(QString("No such slot '%1'.").arg(n)) ); + + QUObject* uo = QtObject::toQUObject(n, arguments); + m_receiver->qt_invoke(slotid, uo); // invoke the slot + delete [] uo; + + return new Variant( QVariant(true,0) ); +} + +/* +QCString EventSlot::getSlot(const QCString& signal) +{ + QString signature = QString(signal).mid(1); + int startpos = signature.find("("); + int endpos = signature.findRev(")"); + if(startpos < 0 || startpos > endpos) { + krosswarning( QString("EventSlot::getSlot(%1) Invalid signal.").arg(signal) ); + return QCString(); + } + QString signalname = signature.left(startpos); + QString params = signature.mid(startpos + 1, endpos - startpos - 1); + //QStringList paramlist = QStringList::split(",", params); + QCString slot = QString("callback(" + params + ")").latin1(); //normalizeSignalSlot(); + + QMetaObject* mo = metaObject(); + int slotid = mo->findSlot(slot, false); + if(slotid < 0) { + krossdebug( QString("EventSlot::getSlot(%1) No such slot '%2' avaiable.").arg(signal).arg(slot) ); + return QCString(); + } + + const QMetaData* md = mo->slot(slotid, false); + if(md->access != QMetaData::Public) { + krossdebug( QString("EventSlot::getSlot(%1) The slot '%2' is not public.").arg(signal).arg(slot) ); + return QCString(); + } + +//QMember* member = md->member; +//const QUMethod *method = md->method; + + krossdebug( QString("signal=%1 slot=%2 slotid=%3 params=%4 mdname=%5") + .arg(signal).arg(slot).arg(slotid).arg(params).arg(md->name) ); + return QCString("1" + slot); // Emulate the SLOT(...) macro by adding as first char a "1". +} + +bool EventSlot::connect(EventManager* eventmanager, QObject* senderobj, const QCString& signal, QString function, const QCString& slot) +{ + if(m_sender && ! disconnect()) + return false; + + const QCString& myslot = slot.isEmpty() ? getSlot(signal) : slot; + if(! myslot) + return false; + + if(! m_eventmanager) { + EventSlot* eventslot = create(eventmanager); + eventslot->connect(eventmanager, senderobj, signal, function, slot); + m_slots.append(eventslot); + krossdebug( QString("EventSlot::connect(%1, %2, %3) added child EventSlot !!!").arg(senderobj->name()).arg(signal).arg(function) ); + } + else { + m_sender = senderobj; + m_signal = signal; + m_function = function; + m_slot = myslot; + if(! QObject::connect((QObject*)senderobj, signal, this, myslot)) { + krossdebug( QString("EventSlot::connect(%1, %2, %3) failed.").arg(senderobj->name()).arg(signal).arg(function) ); + return false; + } + krossdebug( QString("EventSlot::connect(%1, %2, %3) successfully connected.").arg(senderobj->name()).arg(signal).arg(function) ); + } + return true; +} + +bool EventSlot::disconnect() +{ + if(! m_sender) return false; + QObject::disconnect((QObject*)m_sender, m_signal, this, m_slot); + m_sender = 0; + m_signal = 0; + m_slot = 0; + m_function = QString::null; + return true; +} + +void EventSlot::call(const QVariant& variant) +{ + krossdebug( QString("EventSlot::call() sender='%1' signal='%2' function='%3'") + .arg(m_sender->name()).arg(m_signal).arg(m_function) ); + + Kross::Api::List* arglist = 0; + + QValueList args; + if(variant.isValid()) { + args.append(Kross::Api::Variant::create(variant)); + arglist = Kross::Api::List::create(args); + } + + try { + m_eventmanager->m_scriptcontainer->callFunction(m_function, arglist); + } + catch(Exception& e) { + //TODO add hadError(), getError() and setError() + krossdebug( QString("EXCEPTION in EventSlot::call('%1') type='%2' description='%3'").arg(variant.toString()).arg(e.type()).arg(e.description()) ); + } +} + +void EventSlot::callback() { + call(QVariant()); } +void EventSlot::callback(short s) { + call(QVariant(s)); } +void EventSlot::callback(int i) { + call(QVariant(i)); } +void EventSlot::callback(int i1, int i2) { + call(QVariant( QValueList() << i1 << i2 )); } +void EventSlot::callback(int i1, int i2, int i3) { + call(QVariant( QValueList() << i1 << i2 << i3 )); } +void EventSlot::callback(int i1, int i2, int i3, int i4) { + call(QVariant( QValueList() << i1 << i2 << i3 << i4 )); } +void EventSlot::callback(int i1, int i2, int i3, int i4, int i5) { + call(QVariant( QValueList() << i1 << i2 << i3 << i4 << i5 )); } +void EventSlot::callback(int i1, int i2, int i3, int i4, bool b) { + call(QVariant( QValueList() << i1 << i2 << i3 << i4 << b )); } +void EventSlot::callback(int i1, bool b) { + call(QVariant( QValueList() << i1 << b )); } +void EventSlot::callback(int i1, int i2, bool b) { + call(QVariant( QValueList() << i1 << i2 << b )); } +void EventSlot::callback(int i1, int i2, const QString& s) { + call(QVariant( QValueList() << i1 << i2 << s )); } +void EventSlot::callback(uint i) { + call(QVariant(i)); } +void EventSlot::callback(long l) { + call(QVariant((Q_LLONG)l)); } +void EventSlot::callback(ulong l) { + call(QVariant((Q_ULLONG)l)); } +void EventSlot::callback(double d) { + call(QVariant(d)); } +void EventSlot::callback(const char* c) { + call(QVariant(c)); } +void EventSlot::callback(bool b) { + call(QVariant(b)); } +void EventSlot::callback(const QString& s) { + call(QVariant(s)); } +void EventSlot::callback(const QString& s, int i) { + call(QVariant( QValueList() << s << i )); } +void EventSlot::callback(const QString& s, int i1, int i2) { + call(QVariant( QValueList() << s << i1 << i2 )); } +void EventSlot::callback(const QString& s, uint i) { + call(QVariant( QValueList() << s << i )); } +void EventSlot::callback(const QString& s, bool b) { + call(QVariant( QValueList() << s << b )); } +void EventSlot::callback(const QString& s, bool b1, bool b2) { + call(QVariant( QValueList() << s << b1 << b2 )); } +void EventSlot::callback(const QString& s, bool b, int i) { + call(QVariant( QValueList() << s << b << i )); } +void EventSlot::callback(const QString& s1, const QString& s2) { + call(QVariant( QValueList() << s1 << s2 )); } +void EventSlot::callback(const QString& s1, const QString& s2, const QString& s3) { + call(QVariant( QValueList() << s1 << s2 << s3 )); } +void EventSlot::callback(const QStringList& sl) { + call(QVariant(sl)); } +void EventSlot::callback(const QVariant& variant) { + call(variant); } +*/ diff --git a/lib/kross/api/eventslot.h b/lib/kross/api/eventslot.h new file mode 100644 index 00000000..8f564103 --- /dev/null +++ b/lib/kross/api/eventslot.h @@ -0,0 +1,123 @@ +/*************************************************************************** + * eventslot.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_EVENTSLOT_H +#define KROSS_API_EVENTSLOT_H + +#include +#include +#include + +#include "event.h" + +namespace Kross { namespace Api { + + /** + * Each Qt signal and slot connection between a QObject + * instance and a functionname is represented with + * a EventSlot and handled by the \a EventManager. + */ + class EventSlot : public Event + { + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr Ptr; + + /** + * Constructor. + * + * \param name The name of the EventSlot. The EventSlot + * will be accessible by that unique name via + * it's parent. + * \param receiver The receiver of the event. + * \param slot The slot of the receiver which this + * EventSlot points to. + */ + EventSlot(const QString& name, QObject* receiver, QCString slot); + + /** + * Destructor. + */ + virtual ~EventSlot(); + + /// \see Kross::Api::Object::getClassName() + virtual const QString getClassName() const; + + /// \see Kross::Api::Event::call() + virtual Object::Ptr call(const QString& name, KSharedPtr arguments); + +/* + private: + EventManager* m_eventmanager; + QGuardedPtr m_sender; + QCString m_signal; + QCString m_slot; + QString m_function; + QValueList m_slots; + protected: + void call(const QVariant&); + public slots: + // Stupid signals and slots. To get the passed + // arguments we need to have all cases of slots + // avaiable for EventManager::connect() signals. + void callback(); + void callback(short); + void callback(int); + void callback(int, int); + void callback(int, int, int); + void callback(int, int, int, int); + void callback(int, int, int, int, int); + void callback(int, int, int, int, bool); + void callback(int, bool); + void callback(int, int, bool); + void callback(int, int, const QString&); + void callback(uint); + void callback(long); + void callback(ulong); + void callback(double); + void callback(const char*); + void callback(bool); + void callback(const QString&); + void callback(const QString&, int); + void callback(const QString&, int, int); + void callback(const QString&, uint); + void callback(const QString&, bool); + void callback(const QString&, bool, bool); + void callback(const QString&, bool, int); + void callback(const QString&, const QString&); + void callback(const QString&, const QString&, const QString&); + void callback(const QStringList&); + void callback(const QVariant&); + // The following both slots are more generic to + // handle Kross::Api::Object instances. + //void callback(Kross::Api::Object*); + //void callback(Kross::Api::List*); +*/ + private: + QObject* m_receiver; + QCString m_slot; + }; + +}} + +#endif + diff --git a/lib/kross/api/exception.cpp b/lib/kross/api/exception.cpp new file mode 100644 index 00000000..a54bae88 --- /dev/null +++ b/lib/kross/api/exception.cpp @@ -0,0 +1,71 @@ +/*************************************************************************** + * exception.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "object.h" +#include "exception.h" + +//#include +//#include + +using namespace Kross::Api; + +Exception::Exception(const QString& error, long lineno) + : Object() + , m_error(error) + , m_lineno(lineno) +{ + krosswarning( QString("Kross::Api::Exception error='%1' lineno='%3'").arg(m_error).arg(m_lineno) ); +} + +Exception::~Exception() +{ +} + +const QString Exception::getClassName() const +{ + return "Kross::Api::Exception"; +} + +const QString Exception::toString() +{ + return (m_lineno != -1) + ? QString("Exception at line %1: %2").arg(m_lineno).arg(m_error) + : QString("Exception: %1").arg(m_error); +} + +const QString Exception::getError() const +{ + return m_error; +} + +const QString Exception::getTrace() const +{ + return m_trace; +} + +void Exception::setTrace(const QString& tracemessage) +{ + m_trace = tracemessage; +} + +long Exception::getLineNo() const +{ + return m_lineno; +} + diff --git a/lib/kross/api/exception.h b/lib/kross/api/exception.h new file mode 100644 index 00000000..6ee9597b --- /dev/null +++ b/lib/kross/api/exception.h @@ -0,0 +1,102 @@ +/*************************************************************************** + * exception.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_EXCEPTION_H +#define KROSS_API_EXCEPTION_H + +#include "object.h" + +#include +#include + +namespace Kross { namespace Api { + + /** + * Common exception class used for representing exceptions + * in Kross. + * + * Internal we use \a Exception instances to throw and handle + * exceptions. Those exceptions are inherited from \a Object + * and therefore they are first class citizens in Kross. + */ + class Exception : public Object + { + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr Ptr; + + /** + * Constructor. + * + * \param error The error message. + * \param lineno The liner number in the scripting + * code where this exception got thrown. + */ + Exception(const QString& error, long lineno = -1); + + /** + * Destructor. + */ + virtual ~Exception(); + + /// \see Kross::Api::Object::getClassName() + virtual const QString getClassName() const; + + /// \see Kross::Api::Object::toString() + virtual const QString toString(); + + /** + * \return the error message. + */ + const QString getError() const; + + /** + * \return a more detailed tracemessage or QString::null if + * there is no trace avaiable. + */ + const QString getTrace() const; + + /** + * Set a more detailed tracemessage. + */ + void setTrace(const QString& tracemessage); + + /** + * \return the line number in the scripting code + * where the exception got thrown or -1 if there + * was no line number defined. + */ + long getLineNo() const; + + private: + /// The error message. + QString m_error; + /// The trace message. + QString m_trace; + /// The line number where the exception got thrown + long m_lineno; + }; + +}} + +#endif + diff --git a/lib/kross/api/function.h b/lib/kross/api/function.h new file mode 100644 index 00000000..6b75752a --- /dev/null +++ b/lib/kross/api/function.h @@ -0,0 +1,132 @@ +/*************************************************************************** + * function.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_FUNCTION_H +#define KROSS_API_FUNCTION_H + +#include "../main/krossconfig.h" +#include "object.h" +#include "list.h" + +#include + +namespace Kross { namespace Api { + + /** + * The base class for functions. Classes like \a Function0 and + * \a ProxyFunction inheritate this class. + */ + class Function + { + public: + + /** + * Each function needs to implement the call-method which will + * be executed if the function itself should be executed. + */ + virtual Object::Ptr call(List::Ptr) = 0; + + }; + + /** + * This class implements the most abstract way to work with functions. It + * implements pointing to functions of the form + * @code + * Kross::Api::Object::Ptr myfunc(Kross::Api::List::Ptr) + * @endcode + * where a low-level \a Object got returned that represents the returnvalue + * of the function-call, and a \a List instance is passed that may contain + * optional \a Object instances as parameters. + */ + template + class Function0 : public Function + { + private: + typedef Object::Ptr(INSTANCE::*Method)(List::Ptr); + INSTANCE* m_instance; + Method m_method; + public: + Function0(INSTANCE* instance, Method method) + : m_instance(instance), m_method(method) {} + Object::Ptr call(List::Ptr args) + { return (m_instance->*m_method)(args); } + }; + + /** + * Specialization of the \a Function0 which takes as additional parameter + * a const-value. This const-value will be hidden for the scripting backend + * and is only passed through on function-call. + * + * So, this class could be as example used to point to a function like; + * @code + * Kross::Api::Object::Ptr myfunc(Kross::Api::List::Ptr, int myinteger) + * @endcode + * and then we are able to point to the function with something like + * @code + * this->addFunction("myfunctionname", + * new Kross::Api::Function1< MYCLASS, int >( + * this, // pointer to an instance of MYCLASS + * &MYCLASS::myfunction, // the method which should be wrapped + * 17 // the const-value we like to pass to the function. + * ) ); + * @endcode + * The defined integer myinteger which has the value 17 will be passed + * transparently to myfunc. The scripting-backend won't know that there + * exists such an additional integer at all. So, it's hidden and the user + * aka the scripting code won't be able to manipulate that additional + * value. + */ + template + class Function1 : public Function + { + private: + typedef Object::Ptr(INSTANCE::*Method)(List::Ptr, P1); + INSTANCE* m_instance; + Method m_method; + P1 m_p1; + public: + Function1(INSTANCE* instance, Method method, P1 p1) + : m_instance(instance), m_method(method), m_p1(p1) {} + Object::Ptr call(List::Ptr args) + { return (m_instance->*m_method)(args, m_p1); } + }; + + /** + * Same as \a Function1 but with 2 additional parameters. + */ + template + class Function2 : public Function + { + private: + typedef Object::Ptr(INSTANCE::*Method)(List::Ptr, P1, P2); + INSTANCE* m_instance; + Method m_method; + P1 m_p1; + P2 m_p2; + public: + Function2(INSTANCE* instance, Method method, P1 p1, P2 p2) + : m_instance(instance), m_method(method), m_p1(p1), m_p2(p2) {} + Object::Ptr call(List::Ptr args) + { return (m_instance->*m_method)(args, m_p1, m_p2); } + }; + +}} + +#endif + diff --git a/lib/kross/api/interpreter.cpp b/lib/kross/api/interpreter.cpp new file mode 100644 index 00000000..439063cd --- /dev/null +++ b/lib/kross/api/interpreter.cpp @@ -0,0 +1,151 @@ +/*************************************************************************** + * interpreter.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "interpreter.h" +#include "script.h" +#include "../main/manager.h" +#include "../main/scriptcontainer.h" + +#include + +extern "C" +{ + typedef int (*def_interpreter_func)(Kross::Api::InterpreterInfo*); +} + +using namespace Kross::Api; + +/************************************************************************* + * InterpreterInfo + */ + +InterpreterInfo::InterpreterInfo(const QString& interpretername, const QString& library, const QString& wildcard, QStringList mimetypes, Option::Map options) + : m_interpretername(interpretername) + , m_library(library) + , m_wildcard(wildcard) + , m_mimetypes(mimetypes) + , m_options(options) + , m_interpreter(0) +{ +} + +InterpreterInfo::~InterpreterInfo() +{ + for(Option::Map::Iterator it = m_options.begin(); it != m_options.end(); ++it) + delete it.data(); + + delete m_interpreter; + m_interpreter = 0; +} + +const QString InterpreterInfo::getInterpretername() +{ + return m_interpretername; +} + +const QString InterpreterInfo::getWildcard() +{ + return m_wildcard; +} + +const QStringList InterpreterInfo::getMimeTypes() +{ + return m_mimetypes; +} + +bool InterpreterInfo::hasOption(const QString& key) +{ + return m_options.contains(key); +} + +InterpreterInfo::Option* InterpreterInfo::getOption(const QString name) +{ + return m_options[name]; +} + +const QVariant InterpreterInfo::getOptionValue(const QString name, QVariant defaultvalue) +{ + Option* o = m_options[name]; + return o ? o->value : defaultvalue; +} + +InterpreterInfo::Option::Map InterpreterInfo::getOptions() +{ + return m_options; +} + +Interpreter* InterpreterInfo::getInterpreter() +{ + if(m_interpreter) // buffered + return m_interpreter; + + krossdebug( QString("Loading the interpreter library for %1").arg(m_interpretername) ); + + // Load the krosspython library. + KLibLoader *libloader = KLibLoader::self(); + + KLibrary* library = libloader->globalLibrary( m_library.latin1() ); + if(! library) { + /* + setException( + new Exception( QString("Could not load library \"%1\" for the \"%2\" interpreter.").arg(m_library).arg(m_interpretername) ) + ); + */ + krosswarning( QString("Could not load library \"%1\" for the \"%2\" interpreter.").arg(m_library).arg(m_interpretername) ); + return 0; + } + + // Get the extern "C" krosspython_instance function. + def_interpreter_func interpreter_func; + interpreter_func = (def_interpreter_func) library->symbol("krossinterpreter"); + if(! interpreter_func) { + //setException( new Exception("Failed to load symbol in krosspython library.") ); + krosswarning("Failed to load the 'krossinterpreter' symbol from the library."); + } + else { + // and execute the extern krosspython_instance function. + m_interpreter = (Interpreter*) (interpreter_func)(this); + if(! m_interpreter) { + krosswarning("Failed to load the Interpreter instance from library."); + } + else { + // Job done. The library is loaded and our Interpreter* points + // to the external Kross::Python::Interpreter* instance. + krossdebug("Successfully loaded Interpreter instance from library."); + } + } + + // finally unload the library. + library->unload(); + + return m_interpreter; +} + +/************************************************************************* + * Interpreter + */ + +Interpreter::Interpreter(InterpreterInfo* info) + : m_interpreterinfo(info) +{ +} + +Interpreter::~Interpreter() +{ +} diff --git a/lib/kross/api/interpreter.h b/lib/kross/api/interpreter.h new file mode 100644 index 00000000..5c73c303 --- /dev/null +++ b/lib/kross/api/interpreter.h @@ -0,0 +1,197 @@ +/*************************************************************************** + * interpreter.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_INTERPRETER_H +#define KROSS_API_INTERPRETER_H + +#include +#include + +#include "object.h" + +namespace Kross { namespace Api { + + // Forward declaration. + class Manager; + class ScriptContainer; + class Script; + class Interpreter; + + /** + * While the \a Interpreter is the implemented interpreter this class + * is used to provide some abstract informations about each interpreter + * we are able to use within the \a Manager singelton. + */ + class InterpreterInfo + { + public: + + /** + * Each interpreter is able to define options we could + * use to manipulate the interpreter behaviour. + */ + class Option + { + public: + + /** + * Map of options. + */ + typedef QMap Map; + + /** + * Constructor. + * + * \param name The name the option has. This is the + * displayed title and isn't used internaly. + * \param comment A comment that describes the option. + * \param value The QVariant value this option has. + */ + Option(const QString& name, const QString& comment, const QVariant& value) + : name(name), comment(comment), value(value) {} + + /// The short name of the option. + QString name; + + /// A description of the option. + QString comment; + + /// The value the option has. + QVariant value; + }; + + /** + * Constructor. + */ + InterpreterInfo(const QString& interpretername, const QString& library, const QString& wildcard, QStringList mimetypes, Option::Map options); + + /** + * Destructor. + */ + ~InterpreterInfo(); + + /** + * \return the name of the interpreter. For example "python" or "kjs". + */ + const QString getInterpretername(); + + /** + * \return the file-wildcard used to determinate by this interpreter + * used scriptingfiles. Those filter will be used e.g. with + * KGlobal::dirs()->findAllResources() as filtermask. For example + * python just defines it as "*py". + */ + const QString getWildcard(); + + /** + * List of mimetypes this interpreter supports. + * + * \return QStringList with mimetypes like + * "application/x-javascript". + */ + const QStringList getMimeTypes(); + + /** + * \return true if an \a Option with that \p key exists else false. + */ + bool hasOption(const QString& key); + + /** + * \return the option defined with \p name . + */ + Option* getOption(const QString name); + + /** + * \return the value of the option defined with \p name . If there + * doesn't exists an option with such a name, the \p defaultvalue + * is returned. + */ + const QVariant getOptionValue(const QString name, QVariant defaultvalue = QVariant()); + + /** + * \return a map of options. + */ + Option::Map getOptions(); + + /** + * \return the \a Interpreter instance this \a InterpreterInfo + * is the describer for. + */ + Interpreter* getInterpreter(); + + private: + /// The name the interpreter has. Could be something like "python" or "kjs". + QString m_interpretername; + /// The name of the library to load for the interpreter. + QString m_library; + /// The file wildcard used to determinate extensions. + QString m_wildcard; + /// List of mimetypes this interpreter supports. + QStringList m_mimetypes; + /// A \a Option::Map with options. + Option::Map m_options; + /// The \a Interpreter instance. + Interpreter* m_interpreter; + }; + + /** + * Base class for interpreters. + * + * Each scripting backend needs to inheritate it's own + * interpreter from this class and implementate there + * backend related stuff. + * The Interpreter will be managed by the \a Kross::Manager + * class. + */ + class Interpreter + { + public: + + /** + * Constructor. + * + * \param info is the \a InterpreterInfo instance + * that describes this interpreter. + */ + Interpreter(InterpreterInfo* info); + + /** + * Destructor. + */ + virtual ~Interpreter(); + + /** + * Create and return a new interpreter dependend + * \a Script instance. + * + * \param scriptcontainer The \a ScriptContainer + * to use for the \a Script instance. + * \return The from \a Script inherited instance. + */ + virtual Script* createScript(ScriptContainer* scriptcontainer) = 0; + + protected: + /// The \a InterpreterInfo instance this interpreter belongs to. + InterpreterInfo* m_interpreterinfo; + }; + +}} + +#endif + diff --git a/lib/kross/api/list.cpp b/lib/kross/api/list.cpp new file mode 100644 index 00000000..ad901e5e --- /dev/null +++ b/lib/kross/api/list.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** + * list.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "list.h" +#include "exception.h" + +using namespace Kross::Api; + +List::List(QValueList value) + : Value< List, QValueList >(value) +{ +} + +List::~List() +{ +} + +const QString List::getClassName() const +{ + return "Kross::Api::List"; +} + +const QString List::toString() +{ + QString s = "["; + QValueList list = getValue(); + for(QValueList::Iterator it = list.begin(); it != list.end(); ++it) + s += "'" + (*it)->toString() + "', "; + return (s.endsWith(", ") ? s.left(s.length() - 2) : s) + "]"; +} + +Object::Ptr List::item(uint idx, Object* defaultobject) +{ + QValueList& list = getValue(); + if(idx >= list.count()) { + if(defaultobject) + return defaultobject; + krossdebug( QString("List::item index=%1 is out of bounds. Raising TypeException.").arg(idx) ); + throw Exception::Ptr( new Exception(QString("List-index %1 out of bounds.").arg(idx)) ); + } + return list[idx]; +} + +uint List::count() +{ + return getValue().count(); +} + +void List::append(Object::Ptr object) +{ + getValue().append(object); +} + diff --git a/lib/kross/api/list.h b/lib/kross/api/list.h new file mode 100644 index 00000000..d74246a0 --- /dev/null +++ b/lib/kross/api/list.h @@ -0,0 +1,167 @@ +/*************************************************************************** + * list.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_LIST_H +#define KROSS_API_LIST_H + +#include +#include +#include + +#include "object.h" +#include "value.h" + +namespace Kross { namespace Api { + + /** + * The List class implementates \a Value to handle + * lists and collections. + */ + class List : public Value< List, QValueList > + { + friend class Value< List, QValueList >; + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr Ptr; + +/* + operator QStringList () { + //QValueList getValue() + krossdebug("999999999999 ..........................."); + return QStringList(); + } +*/ + + /** + * Constructor. + * + * \param value The list of \a Object instances this + * list has initialy. + */ + List(QValueList value = QValueList()); + + /** + * Destructor. + */ + virtual ~List(); + + /** + * See \see Kross::Api::Object::getClassName() + */ + virtual const QString getClassName() const; + + /** + * \return a string representation of the whole list. + * + * \see Kross::Api::Object::toString() + */ + virtual const QString toString(); + + /** + * Return the \a Object with defined index from the + * QValueList this list holds. + * + * \throw TypeException If index is out of bounds. + * \param idx The QValueList-index. + * \param defaultobject The default \a Object which should + * be used if there exists no item with such an + * index. This \a Object instance will be returned + * if not NULL and if the index is out of bounds. If + * its NULL a \a TypeException will be thrown. + * \return The \a Object instance. + */ + Object::Ptr item(uint idx, Object* defaultobject = 0); + + /** + * Return the number of items in the QValueList this + * list holds. + * + * \return The number of items. + */ + uint count(); + + /** + * Append an \a Kross::Api::Object to the list. + * + * \param object The \a Kross::Api::Object instance to + * append to this list. + */ + void append(Object::Ptr object); + + template + static Object::Ptr toObject(TYPE t) { return t; } + }; + + /** + * This template class extends the \a List class with + * generic functionality to deal with lists. + */ + template< class OBJECT > + class ListT : public List + { + public: + ListT() : List() {} + ListT(QValueList values) : List(values) {} + + template< typename TYPE > + ListT(QValueList values) : List() + { + QValueListIterator it(values.begin()), end(values.end()); + for(; it != end; ++it) + this->append( new OBJECT(*it) ); + } + + template< typename TYPE > + ListT(QIntDict values) : List() + { + QIntDictIterator it( values ); + TYPE *t; + while( (t = it.current()) != 0 ) { + this->append( new OBJECT(t) ); + ++it; + } + } + + template< typename TYPE > + ListT(const QPtrList values) : List() + { + QPtrListIterator it(values); + TYPE *t; + while( (t = it.current()) != 0 ) { + this->append( new OBJECT(t) ); + ++it; + } + } + + virtual ~ListT() {} + + template + static Object::Ptr toObject(TYPE t) + { + return new ListT(t); + } + }; + +}} + +#endif + diff --git a/lib/kross/api/module.h b/lib/kross/api/module.h new file mode 100644 index 00000000..127609e0 --- /dev/null +++ b/lib/kross/api/module.h @@ -0,0 +1,80 @@ +/*************************************************************************** + * module.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_MODULE_H +#define KROSS_API_MODULE_H + +#include + +#include "class.h" + +namespace Kross { namespace Api { + + /** + * The Module class. Modules are managed in the \a Manager singleton + * instance and are implemented as in scripting plugins as main + * entry point to load and work with them. + */ + class Module : public Class + { + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr Ptr; + + /** + * Constructor. + * + * \param name The name of this module. + * Each module needs a unique name cause + * the application using Kross identifies + * modules with there names. + */ + explicit Module(const QString& name) + : Class(name) + { + krossdebug( QString("Kross::Api::Module %1 created").arg(name) ); + } + + /** + * Destructor. + */ + virtual ~Module() + { + krossdebug( QString("Kross::Api::Module %1 destroyed").arg(getName()) ); + } + + /** + * Method to load from \a Kross::Api::Object inherited classes + * this module implements from within other modules. + */ + virtual Kross::Api::Object::Ptr get(const QString& /*name*/, void* /*pointer*/ = 0) + { + return 0; + } + + }; + + +}} + +#endif + diff --git a/lib/kross/api/object.cpp b/lib/kross/api/object.cpp new file mode 100644 index 00000000..ff2e2fbd --- /dev/null +++ b/lib/kross/api/object.cpp @@ -0,0 +1,63 @@ +/*************************************************************************** + * object.cpp + * This file is part of the KDE project + * copyright (C)2004-2006 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "object.h" +#include "list.h" +#include "variant.h" +//#include "function.h" +#include "event.h" +#include "exception.h" + +using namespace Kross::Api; + +Object::Object() + : KShared() +{ +#ifdef KROSS_API_OBJECT_CTOR_DEBUG + krossdebug( QString("Kross::Api::Object::Constructor() name='%1' refcount='%2'").arg(m_name).arg(_KShared_count()) ); +#endif +} + +Object::~Object() +{ +#ifdef KROSS_API_OBJECT_DTOR_DEBUG + krossdebug( QString("Kross::Api::Object::Destructor() name='%1' refcount='%2'").arg(m_name).arg(_KShared_count()) ); +#endif + //removeAllChildren(); // not needed cause we use KShared to handle ref-couting and freeing. +} + +const QString Object::toString() +{ + return QString("%1").arg(getClassName()); +} + +Object::Ptr Object::call(const QString& name, List::Ptr arguments) +{ + Q_UNUSED(arguments); + +#ifdef KROSS_API_OBJECT_CALL_DEBUG + krossdebug( QString("Kross::Api::Object::call(%1) name=%2 class=%3").arg(name).arg(getName()).arg(getClassName()) ); +#endif + + if(name.isEmpty()) // return a self-reference if no functionname is defined. + return this; + + throw Exception::Ptr( new Exception(QString("No callable object named '%2'").arg(name)) ); +} + diff --git a/lib/kross/api/object.h b/lib/kross/api/object.h new file mode 100644 index 00000000..3c86ca4b --- /dev/null +++ b/lib/kross/api/object.h @@ -0,0 +1,152 @@ +/*************************************************************************** + * object.h + * This file is part of the KDE project + * copyright (C)2004-2006 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_OBJECT_H +#define KROSS_API_OBJECT_H + +#include +#include +#include +#include +//#include +#include + +#include "../main/krossconfig.h" + +namespace Kross { namespace Api { + + // Forward declaration. + class List; + + /** + * The common Object class all other object-classes are + * inheritated from. + * + * The Object class is used as base class to provide + * common functionality. It's similar to what we know + * in Python as PyObject or in Qt as QObject. + * + * Inherited from e.g. \a Value, \a Module and \a Class . + * + * This class implementates reference counting for shared + * objects. So, no need to take care of freeing objects. + */ + class Object : public KShared + { + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr Ptr; + + /** + * Constructor. + */ + explicit Object(); + + /** + * Destructor. + */ + virtual ~Object(); + + /** + * Return the class name. This could be something + * like "Kross::Api::Object" for this object. The + * value is mainly used for display purposes. + * + * \return The name of this class. + */ + virtual const QString getClassName() const = 0; + + /** + * \return a string representation of the object or + * it's content. This method is mainly used for + * debugging and testing purposes. + */ + virtual const QString toString(); + + /** + * Pass a call to the object and evaluated it recursive + * down the object-hierachy. Objects like \a Class are able + * to handle call's by just implementing this function. + * If the call is done the \a called() method will be + * executed recursive from bottom up the call hierachy. + * + * \throws TypeException if the object or the name + * is not callable. + * \param name Each call has a name that says what + * should be called. In the case of a \a Class + * the name is the functionname. + * \param arguments The list of arguments passed to + * the call. + * \return The call-result as \a Object::Ptr instance or + * NULL if the call has no result. + */ + virtual Object::Ptr call(const QString& name, KSharedPtr arguments); + + /** + * Return a list of supported callable objects. + * + * \return List of supported calls. + */ + virtual QStringList getCalls() { return QStringList(); } + //FIXME replace function with getChildren() functionality ? + + /** + * Try to convert the \a Object instance to the + * template class T. + * + * \throw TypeException if the cast failed. + * \param object The Object to cast. + * \return The to a instance from template type T + * casted Object. + */ + template static T* fromObject(Object::Ptr object); + + /** + * This method got used by the \a ProxyFunction classes + * to translate an unknown \p TYPE to a \a Object instance. + * Classes like \a Value or \a ListT or \a Class are + * overwriting this method to transparently translate these + * passed type while this method just assumes that the + * type is already a \a Object instance. + */ + template + static Object::Ptr toObject(TYPE t) { return t; } + }; + +}} + +#include "exception.h" + +namespace Kross { namespace Api { + +template inline T* Object::fromObject(Object::Ptr object) +{ + T* t = (T*) object.data(); + if(! t) + throw KSharedPtr( new Exception(QString("Object \"%1\" invalid.").arg(object ? object->getClassName() : "")) ); + return t; +} + +}} + +#endif + diff --git a/lib/kross/api/proxy.h b/lib/kross/api/proxy.h new file mode 100644 index 00000000..f25a4845 --- /dev/null +++ b/lib/kross/api/proxy.h @@ -0,0 +1,342 @@ +/*************************************************************************** + * proxy.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_PROXY_H +#define KROSS_API_PROXY_H + +#include "../main/krossconfig.h" +#include "object.h" +#include "variant.h" +#include "list.h" + +#include + +namespace Kross { namespace Api { + + /** + * \internal used struct to translate an argument-value dynamicly. + */ + template + struct ProxyArgTranslator { + OBJ* m_object; + ProxyArgTranslator(Kross::Api::Object* obj) { + m_object = Kross::Api::Object::fromObject(obj); + } + template + inline operator T () { + return m_object->operator T(); + } + }; + + /** + * \internal used struct to translate a return-value dynamicly. + */ + struct ProxyRetTranslator { + template + inline static Object::Ptr cast(TYPE t) { + return RETURNOBJ::toObject(t); + } + }; + + /** + * The ProxyFunction template-class is used to publish any C/C++ + * method (not only slots) of a struct or class instance as a + * a \a Function to Kross. + * + * With this class we don't need to have a method-wrapper for + * each single function what a) should reduce the code needed for + * wrappers and b) is more typesafe cause the connection to the + * C/C++ method is done on compiletime. + * + * Example how a ProxyFunction may got used; + * @code + * #include "../api/class.h" + * #include "../api/proxy.h" + * // The class which should be published. + * class MyClass : public Kross::Api::Class { + * public: + * MyClass(const QString& name) : Kross::Api::Class(name) { + * // publish the function myfunc, so that scripting-code is able + * // to call that method. + * this->addProxyFunction < + * Kross::Api::Variant, // the uint returnvalue is handled with Variant. + * Kross::Api::Variant, // the QString argument is handled with Variant too. + * MyClass // the MyClass* is handled implicit by our class. + * > ( "myfuncname", // the name the function should be published as. + * this, // pointer to the class-instance which has the method. + * &TestPluginObject::myfunc ); // pointer to the method itself. + * } + * virtual ~MyClass() {} + * virtual const QString getClassName() const { return "MyClass"; } + * private: + * uint myfunc(const QCString&, MyClass* myotherclass) { + * // This method will be published to the scripting backend. So, scripting + * // code is able to call this method. + * } + * } + * @endcode + */ + template< class INSTANCE, // the objectinstance + typename METHOD, // the method-signature + class RETURNOBJ,// = Kross::Api::Object, // return-value + class ARG1OBJ = Kross::Api::Object, // first parameter-value + class ARG2OBJ = Kross::Api::Object, // second parameter-value + class ARG3OBJ = Kross::Api::Object, // theird parameter-value + class ARG4OBJ = Kross::Api::Object // forth parameter-value + > + class ProxyFunction : public Function + { + template + friend struct ProxyFunctionCaller; + private: + /// Pointer to the objectinstance which method should be called. + INSTANCE* m_instance; + /// Pointer to the method which should be called. + const METHOD m_method; + + /// First default argument. + KSharedPtr m_defarg1; + /// Second default argument. + KSharedPtr m_defarg2; + /// Theird default argument. + KSharedPtr m_defarg3; + /// Forth default argument. + KSharedPtr m_defarg4; + + /** + * \internal used struct that does the execution of the wrapped + * method. + */ + template + struct ProxyFunctionCaller { + inline static Object::Ptr exec(PROXYFUNC* self, Kross::Api::Object* arg1, Kross::Api::Object* arg2, Kross::Api::Object* arg3, Kross::Api::Object* arg4) { + return ProxyRetTranslator::cast( + ( (self->m_instance)->*(self->m_method) )( ProxyArgTranslator(arg1), ProxyArgTranslator(arg2), ProxyArgTranslator(arg3), ProxyArgTranslator(arg4) ) + ); + } + }; + + /** + * \internal template-specialization of the \a ProxyFunctionCaller + * above which handles void-returnvalues. We need to handle this + * special case seperatly cause compilers deny to return void :-/ + */ + template + struct ProxyFunctionCaller { + inline static Object::Ptr exec(PROXYFUNC* self, Kross::Api::Object* arg1, Kross::Api::Object* arg2, Kross::Api::Object* arg3, Kross::Api::Object* arg4) { + ( (self->m_instance)->*(self->m_method) )( ProxyArgTranslator(arg1), ProxyArgTranslator(arg2), ProxyArgTranslator(arg3), ProxyArgTranslator(arg4) ); + return 0; // void return-value + } + }; + + public: + + /** + * Constructor. + * + * \param instance The objectinstance to which the \p method + * belongs to. + * \param method The method-pointer. + */ + ProxyFunction(INSTANCE* instance, const METHOD& method, ARG1OBJ* defarg1 = 0, ARG2OBJ* defarg2 = 0, ARG3OBJ* defarg3 = 0, ARG4OBJ* defarg4 = 0) + : m_instance(instance), m_method(method), m_defarg1(defarg1), m_defarg2(defarg2), m_defarg3(defarg3), m_defarg4(defarg4) {} + + /** + * This method got called if the wrapped method should be executed. + * + * \param args The optional list of arguments passed to the + * execution-call. + * \return The returnvalue of the functioncall. It will be NULL if + * the functioncall doesn't provide us a returnvalue (e.g. + * if the function has void as returnvalue). + */ + Object::Ptr call(List::Ptr args) { + return ProxyFunctionCaller::exec(this, + args->item(0, m_defarg1), + args->item(1, m_defarg2), + args->item(2, m_defarg3), + args->item(3, m_defarg4) + ); + } + }; + + /** + * Template-specialization of the \a ProxyFunction above with three arguments. + */ + template + class ProxyFunction : public Function + { + template + friend struct ProxyFunctionCaller; + private: + INSTANCE* m_instance; + const METHOD m_method; + KSharedPtr m_defarg1; + KSharedPtr m_defarg2; + KSharedPtr m_defarg3; + + template + struct ProxyFunctionCaller { + inline static Object::Ptr exec(PROXYFUNC* self, Kross::Api::Object* arg1, Kross::Api::Object* arg2, Kross::Api::Object* arg3) { + return ProxyRetTranslator::cast( + ( (self->m_instance)->*(self->m_method) )( ProxyArgTranslator(arg1), ProxyArgTranslator(arg2), ProxyArgTranslator(arg3) ) + ); + } + }; + + template + struct ProxyFunctionCaller { + inline static Object::Ptr exec(PROXYFUNC* self, Kross::Api::Object* arg1, Kross::Api::Object* arg2, Kross::Api::Object* arg3) { + ( (self->m_instance)->*(self->m_method) )( ProxyArgTranslator(arg1), ProxyArgTranslator(arg2), ProxyArgTranslator(arg3) ); + return 0; + } + }; + + public: + ProxyFunction(INSTANCE* instance, const METHOD& method, ARG1OBJ* defarg1 = 0, ARG2OBJ* defarg2 = 0, ARG3OBJ* defarg3 = 0) + : m_instance(instance), m_method(method), m_defarg1(defarg1), m_defarg2(defarg2), m_defarg3(defarg3) {} + Object::Ptr call(List::Ptr args) { + return ProxyFunctionCaller::exec(this, + args->item(0, m_defarg1), args->item(1, m_defarg2), args->item(2, m_defarg3) + ); + } + }; + + /** + * Template-specialization of the \a ProxyFunction above with two arguments. + */ + template + class ProxyFunction : public Function + { + template + friend struct ProxyFunctionCaller; + private: + INSTANCE* m_instance; + const METHOD m_method; + KSharedPtr m_defarg1; + KSharedPtr m_defarg2; + + template + struct ProxyFunctionCaller { + inline static Object::Ptr exec(PROXYFUNC* self, Kross::Api::Object* arg1, Kross::Api::Object* arg2) { + return ProxyRetTranslator::cast( + ( (self->m_instance)->*(self->m_method) )( ProxyArgTranslator(arg1), ProxyArgTranslator(arg2) ) + ); + } + }; + + template + struct ProxyFunctionCaller { + inline static Object::Ptr exec(PROXYFUNC* self, Kross::Api::Object* arg1, Kross::Api::Object* arg2) { + ( (self->m_instance)->*(self->m_method) )( ProxyArgTranslator(arg1), ProxyArgTranslator(arg2) ); + return 0; + } + }; + + public: + ProxyFunction(INSTANCE* instance, const METHOD& method, ARG1OBJ* defarg1 = 0, ARG2OBJ* defarg2 = 0) + : m_instance(instance), m_method(method), m_defarg1(defarg1), m_defarg2(defarg2) {} + Object::Ptr call(List::Ptr args) { + return ProxyFunctionCaller::exec(this, + args->item(0, m_defarg1), args->item(1, m_defarg2) + ); + } + }; + + /** + * Template-specialization of the \a ProxyFunction above with one argument. + */ + template + class ProxyFunction : public Function + { + template + friend struct ProxyFunctionCaller; + private: + INSTANCE* m_instance; + const METHOD m_method; + KSharedPtr m_defarg1; + + template + struct ProxyFunctionCaller { + inline static Object::Ptr exec(PROXYFUNC* self, Kross::Api::Object* arg1) { + return ProxyRetTranslator::cast( + ( (self->m_instance)->*(self->m_method) )( ProxyArgTranslator(arg1) ) + ); + } + }; + + template + struct ProxyFunctionCaller { + inline static Object::Ptr exec(PROXYFUNC* self, Kross::Api::Object* arg1) { + ( (self->m_instance)->*(self->m_method) )( ProxyArgTranslator(arg1) ); + return 0; + } + }; + + public: + ProxyFunction(INSTANCE* instance, const METHOD& method, ARG1OBJ* defarg1 = 0) + : m_instance(instance), m_method(method), m_defarg1(defarg1) {} + Object::Ptr call(List::Ptr args) { + return ProxyFunctionCaller::exec(this, + args->item(0, m_defarg1) + ); + } + }; + + /** + * Template-specialization of the \a ProxyFunction above with no arguments. + */ + template + class ProxyFunction : public Function + { + template + friend struct ProxyFunctionCaller; + private: + INSTANCE* m_instance; + const METHOD m_method; + + template + struct ProxyFunctionCaller { + inline static Object::Ptr exec(PROXYFUNC* self) { + return ProxyRetTranslator::cast( + ( (self->m_instance)->*(self->m_method) )() + ); + } + }; + + template + struct ProxyFunctionCaller { + inline static Object::Ptr exec(PROXYFUNC* self) { + ( (self->m_instance)->*(self->m_method) )(); + return 0; + } + }; + + public: + ProxyFunction(INSTANCE* instance, const METHOD& method) + : m_instance(instance), m_method(method) {} + Object::Ptr call(List::Ptr) { + return ProxyFunctionCaller::exec(this); + } + }; + +}} + +#endif + diff --git a/lib/kross/api/qtobject.cpp b/lib/kross/api/qtobject.cpp new file mode 100644 index 00000000..c6eb082a --- /dev/null +++ b/lib/kross/api/qtobject.cpp @@ -0,0 +1,235 @@ +/*************************************************************************** + * qtobject.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "qtobject.h" +#include "object.h" +#include "variant.h" +#include "event.h" + +#include "../main/manager.h" +#include "eventslot.h" +#include "eventsignal.h" + +#include +#include +//#include +//#include +#include +#include // for the Qt QUObject API. + +using namespace Kross::Api; + +QtObject::QtObject(QObject* object, const QString& name) + : Kross::Api::Class(name.isEmpty() ? object->name() : name) + , m_object(object) +{ + // Walk through the signals and slots the QObject has + // and attach them as events to this QtObject. + + QStrList slotnames = m_object->metaObject()->slotNames(false); + for(char* c = slotnames.first(); c; c = slotnames.next()) { + QCString s = c; + addChild(s, new EventSlot(s, object, s) ); + } + + QStrList signalnames = m_object->metaObject()->signalNames(false); + for(char* c = signalnames.first(); c; c = signalnames.next()) { + QCString s = c; + addChild(s, new EventSignal(s, object, s) ); + } + + // Add functions to wrap QObject methods into callable + // Kross objects. + + addFunction("propertyNames", &QtObject::propertyNames); + addFunction("hasProperty", &QtObject::hasProperty); + addFunction("getProperty", &QtObject::getProperty); + addFunction("setProperty", &QtObject::setProperty); + + addFunction("slotNames", &QtObject::slotNames); + addFunction("hasSlot", &QtObject::hasSlot); + addFunction("slot", &QtObject::callSlot); + + addFunction("signalNames", &QtObject::signalNames); + addFunction("hasSignal", &QtObject::hasSignal); + addFunction("signal", &QtObject::emitSignal); + + addFunction("connect", &QtObject::connectSignal); + addFunction("disconnect", &QtObject::disconnectSignal); +} + +QtObject::~QtObject() +{ +} + +const QString QtObject::getClassName() const +{ + return "Kross::Api::QtObject"; +} + +QObject* QtObject::getObject() +{ + return m_object; +} + +QUObject* QtObject::toQUObject(const QString& signature, List::Ptr arguments) +{ + int startpos = signature.find("("); + int endpos = signature.findRev(")"); + if(startpos < 0 || startpos > endpos) + throw Exception::Ptr( new Exception(QString("Invalid Qt signal or slot signature '%1'").arg(signature)) ); + + //QString sig = signature.left(startpos); + QString params = signature.mid(startpos + 1, endpos - startpos - 1); + QStringList paramlist = QStringList::split(",", params); // this will fail on something like myslot(QMap arg), but we don't care jet. + uint paramcount = paramlist.size(); + + // The first item in the QUObject-array is for the returnvalue + // while everything >=1 are the passed parameters. + QUObject* uo = new QUObject[ paramcount + 1 ]; + uo[0] = QUObject(); // empty placeholder for the returnvalue. + +//QString t; +//for(int j=0; jitem(j)) + "' "; +//krossdebug( QString("1 ---------------------: (%1) %2").arg(argcount).arg(t) ); + + // Fill parameters. + uint argcount = arguments ? arguments->count() : 0; + for(uint i = 0; i < paramcount; i++) { + if(paramlist[i].find("QString") >= 0) { + const QString s = (argcount > i) ? Variant::toString(arguments->item(i)) : QString::null; + //krossdebug(QString("EventSlot::toQUObject s=%1").arg(s)); + static_QUType_QString.set( &(uo[i + 1]), s ); + } + //TODO handle int, long, char*, QStringList, etc. + else { + throw Exception::Ptr( new Exception(QString("Unknown Qt signal or slot argument '%1' in signature '%2'.").arg(paramlist[i]).arg(signature)) ); + } + } + + return uo; +} + +Kross::Api::Object::Ptr QtObject::propertyNames(Kross::Api::List::Ptr) +{ + return new Kross::Api::Variant( + QStringList::fromStrList(m_object->metaObject()->propertyNames(false))); +} + +Kross::Api::Object::Ptr QtObject::hasProperty(Kross::Api::List::Ptr args) +{ + return new Kross::Api::Variant( + m_object->metaObject()->findProperty(Kross::Api::Variant::toString(args->item(0)).latin1(), false)); +} + +Kross::Api::Object::Ptr QtObject::getProperty(Kross::Api::List::Ptr args) +{ + QVariant variant = m_object->property(Kross::Api::Variant::toString(args->item(0)).latin1()); + if(variant.type() == QVariant::Invalid) + return 0; + return new Kross::Api::Variant(variant); +} + +Kross::Api::Object::Ptr QtObject::setProperty(Kross::Api::List::Ptr args) +{ + return new Kross::Api::Variant( + m_object->setProperty( + Kross::Api::Variant::toString(args->item(0)).latin1(), + Kross::Api::Variant::toVariant(args->item(1)) + )); +} + +Kross::Api::Object::Ptr QtObject::slotNames(Kross::Api::List::Ptr) +{ + return new Kross::Api::Variant( + QStringList::fromStrList(m_object->metaObject()->slotNames(false))); +} + +Kross::Api::Object::Ptr QtObject::hasSlot(Kross::Api::List::Ptr args) +{ + return new Kross::Api::Variant( + bool(m_object->metaObject()->slotNames(false).find( + Kross::Api::Variant::toString(args->item(0)).latin1() + ) != -1)); +} + +Kross::Api::Object::Ptr QtObject::callSlot(Kross::Api::List::Ptr args) +{ +//TODO just call the child event ?! + QString name = Kross::Api::Variant::toString(args->item(0)); + int slotid = m_object->metaObject()->findSlot(name.latin1(), false); + if(slotid < 0) + throw Exception::Ptr( new Exception(QString("No such slot '%1'.").arg(name)) ); + + QUObject* uo = QtObject::toQUObject(name, args); + m_object->qt_invoke(slotid, uo); + delete [] uo; + + return new Variant( QVariant(true,0) ); +} + +Kross::Api::Object::Ptr QtObject::signalNames(Kross::Api::List::Ptr) +{ + return new Kross::Api::Variant( + QStringList::fromStrList(m_object->metaObject()->signalNames(false))); +} + +Kross::Api::Object::Ptr QtObject::hasSignal(Kross::Api::List::Ptr args) +{ + return new Kross::Api::Variant( + bool(m_object->metaObject()->signalNames(false).find( + Kross::Api::Variant::toString(args->item(0)).latin1() + ) != -1)); +} + +Kross::Api::Object::Ptr QtObject::emitSignal(Kross::Api::List::Ptr args) +{ + QString name = Kross::Api::Variant::toString(args->item(0)); + int signalid = m_object->metaObject()->findSignal(name.latin1(), false); + if(signalid < 0) + throw Exception::Ptr( new Exception(QString("No such signal '%1'.").arg(name)) ); + m_object->qt_invoke(signalid, 0); //TODO convert Kross::Api::List::Ptr => QUObject* + return 0; +} + +Kross::Api::Object::Ptr QtObject::connectSignal(Kross::Api::List::Ptr args) +{ + QString signalname = Kross::Api::Variant::toString(args->item(0)); + QString signalsignatur = QString("2%1").arg(signalname); + const char* signalsig = signalsignatur.latin1(); + + QtObject* obj = Kross::Api::Object::fromObject(args->item(1)); + QObject* o = obj->getObject(); + if(! o) + throw Exception::Ptr( new Exception(QString("No such QObject receiver in '%1'.").arg(obj->getName())) ); + + QString slotname = Kross::Api::Variant::toString(args->item(2)); + QString slotsignatur = QString("1%1").arg(slotname); + const char* slotsig = slotsignatur.latin1(); + + return new Kross::Api::Variant( + QObject::connect(m_object, signalsig, o, slotsig)); +} + +Kross::Api::Object::Ptr QtObject::disconnectSignal(Kross::Api::List::Ptr) +{ + //TODO + return 0; +} + diff --git a/lib/kross/api/qtobject.h b/lib/kross/api/qtobject.h new file mode 100644 index 00000000..29f493a4 --- /dev/null +++ b/lib/kross/api/qtobject.h @@ -0,0 +1,135 @@ +/*************************************************************************** + * qtobject.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_QTOBJECT_H +#define KROSS_API_QTOBJECT_H + +#include "class.h" + +#include +#include + +// Forward-declaration of the builtin Qt QUObject struct. +struct QUObject; + +namespace Kross { namespace Api { + + // Forward declarations. + class Object; + class Variant; + class ScriptContainer; + class ScriptContrainer; + + /** + * Class to wrap \a QObject or inherited instances. + * + * This class publishs all SIGNAL's, SLOT's and Q_PROPERTY's + * the QObject has. + */ + class QtObject : public Kross::Api::Class + { + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr Ptr; + + /** + * Constructor. + * + * \param object The \a QObject instance this + * class wraps. + * \param name The unique name this \a QtObject + * instance has. If not defined then the + * \a QObject::name() will be used. + */ + QtObject(QObject* object, const QString& name = QString::null); + + /** + * Destructor. + */ + virtual ~QtObject(); + + /// \see Kross::Api::Object::getClassName() + virtual const QString getClassName() const; + + /** + * Return the \a QObject instance this class wraps. + * + * \return The wrapped QObject. + */ + QObject* getObject(); + + /** + * Build a Qt QUObject struct out of the Qt signal or + * slot signature and the passed \a List arguments. + * + * \throw RuntimeException If the try to translate \p arguments + * failed. + * \param signature The Qt signal or slot signature. + * \param arguments The optional \a List of arguments. + * \return A QUObject array. + */ + static QUObject* toQUObject(const QString& signature, List::Ptr arguments); + + private: + /// The wrapped QObject. + QObject* m_object; + + // QProperty's + + /// Return a list of property names. + Kross::Api::Object::Ptr propertyNames(Kross::Api::List::Ptr); + /// Return true if the property exists else false. + Kross::Api::Object::Ptr hasProperty(Kross::Api::List::Ptr); + /// Return a property. + Kross::Api::Object::Ptr getProperty(Kross::Api::List::Ptr); + /// Set a property. + Kross::Api::Object::Ptr setProperty(Kross::Api::List::Ptr); + + // Slots + + /// Return a list of slot names. + Kross::Api::Object::Ptr slotNames(Kross::Api::List::Ptr); + /// Return true if the slot exists else false. + Kross::Api::Object::Ptr hasSlot(Kross::Api::List::Ptr); + /// Call a slot. + Kross::Api::Object::Ptr callSlot(Kross::Api::List::Ptr); + + // Signals + + /// Return a list of signal names. + Kross::Api::Object::Ptr signalNames(Kross::Api::List::Ptr); + /// Return true if the signal exists else false. + Kross::Api::Object::Ptr hasSignal(Kross::Api::List::Ptr); + /// Emit a signal. + Kross::Api::Object::Ptr emitSignal(Kross::Api::List::Ptr); + + /// Connect signal with a QObject slot. + Kross::Api::Object::Ptr connectSignal(Kross::Api::List::Ptr); + /// Disconnect signal from QObject slot. + Kross::Api::Object::Ptr disconnectSignal(Kross::Api::List::Ptr); + + }; + +}} + +#endif + diff --git a/lib/kross/api/script.cpp b/lib/kross/api/script.cpp new file mode 100644 index 00000000..d960ba43 --- /dev/null +++ b/lib/kross/api/script.cpp @@ -0,0 +1,59 @@ +/*************************************************************************** + * script.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "script.h" +#include "object.h" +#include "list.h" +#include "interpreter.h" +#include "exception.h" +#include "../main/scriptcontainer.h" + +using namespace Kross::Api; + +Script::Script(Interpreter* const interpreter, ScriptContainer* const scriptcontainer) + : m_interpreter(interpreter) + , m_scriptcontainer(scriptcontainer) + , m_exception(0) +{ +} + +Script::~Script() +{ +} + +bool Script::hadException() +{ + return m_exception != 0; +} + +Exception::Ptr Script::getException() +{ + return m_exception; +} + +void Script::setException(Exception::Ptr e) +{ + m_exception = e; +} + +void Script::clearException() +{ + m_exception = 0; +} + diff --git a/lib/kross/api/script.h b/lib/kross/api/script.h new file mode 100644 index 00000000..4f7d0c04 --- /dev/null +++ b/lib/kross/api/script.h @@ -0,0 +1,140 @@ +/*************************************************************************** + * script.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_SCRIPT_H +#define KROSS_API_SCRIPT_H + +#include +#include + +#include "class.h" + +namespace Kross { namespace Api { + + // Forward declarations. + class Object; + class Interpreter; + class ScriptContainer; + class List; + class Exception; + + /** + * Base class for interpreter dependend functionality + * each script provides. + * + * Each \a ScriptContainer holds a pointer to a class + * that implements the \a Script functionality for the + * defined \a Interpreter . + */ + class Script + { + public: + + /** + * Constructor. + * + * \param interpreter The \a Interpreter instance + * that uses this \a Script instance. + * \param scriptcontainer The \a ScriptContainer instance + * this script is associated with. + */ + Script(Interpreter* const interpreter, ScriptContainer* const scriptcontainer); + + /** + * Destructor. + */ + virtual ~Script(); + + /** + * \return true if the script throwed an exception + * else false. + */ + bool hadException(); + + /** + * \return the \a Exception the script throwed. + */ + Exception::Ptr getException(); + + /** + * Set a new exception this script throwed. + * + * \param e The \a Exception . + */ + void setException(Exception::Ptr e); + + /** + * Clear previous exceptions. If called \a hadException() + * will return false again. + */ + void clearException(); + + /** + * Execute the script. + * + * \throws Exception on error. + * \return The execution result. Could be NULL too. + */ + virtual Kross::Api::Object::Ptr execute() = 0; + + /** + * \return a list of callable functionnames this + * script spends. + */ + virtual const QStringList& getFunctionNames() = 0; + + /** + * Call a function. + * + * \throws Exception on error. + * \param name The name of the function to execute. + * \param args Optional arguments passed to the function. + * \return The result of the called function. Could be NULL. + */ + virtual Kross::Api::Object::Ptr callFunction(const QString& name, Kross::Api::List::Ptr args) = 0; + + /** + * \return a list of classnames. + */ + virtual const QStringList& getClassNames() = 0; + + /** + * Create and return a new class instance. + * + * \throws Exception on error. + * \param name The name of the class to create a instance of. + * \return The new classinstance. + */ + virtual Kross::Api::Object::Ptr classInstance(const QString& name) = 0; + + protected: + /// The \a Interpreter used to create this Script instance. + Interpreter* const m_interpreter; + /// The \a ScriptContainer associated with this Script. + ScriptContainer* const m_scriptcontainer; + + private: + /// The \a Exception this script throwed. + Exception::Ptr m_exception; + }; + +}} + +#endif + diff --git a/lib/kross/api/value.h b/lib/kross/api/value.h new file mode 100644 index 00000000..544d4a36 --- /dev/null +++ b/lib/kross/api/value.h @@ -0,0 +1,91 @@ +/*************************************************************************** + * value.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_VALUE_H +#define KROSS_API_VALUE_H + +#include +#include + +#include "object.h" + +namespace Kross { namespace Api { + + /** + * Template class to represent values. + * + * Classes like \a Variant or \a List are implementing this + * class. That way we have a common base for all kind of + * values. + */ + template + class Value : public Object + { + public: + + /** + * Constructor. + * + * \param value The initial value this + * Value has. + */ + Value(V value) + : Object() + , m_value(value) {} + + /** + * Destructor. + */ + virtual ~Value() {} + + //operator V&() const { return m_value; } + + /** + * Return the value. + * + * \return The value this Value-class holds. + */ + V& getValue() { return m_value; } + //operator V& () { return m_value; } + + template + static Object::Ptr toObject(TYPE t) { return new T(t); } + +#if 0 +//do we need it anyway? + /** + * Set the value. + * The value is call-by-value cause it may + * contain some KShared and therefore + * we need to keep a local copy to keep + * it from disappearing. + * + * \param value The value to set. + */ + void setValue(V& value) { m_value = value; } +#endif + + private: + V m_value; + }; + +}} + +#endif + diff --git a/lib/kross/api/variant.cpp b/lib/kross/api/variant.cpp new file mode 100644 index 00000000..92c0e3cb --- /dev/null +++ b/lib/kross/api/variant.cpp @@ -0,0 +1,168 @@ +/*************************************************************************** + * variant.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "variant.h" +#include "list.h" + +#include + +using namespace Kross::Api; + +Variant::Variant(const QVariant& value) + : Value(value) +{ +} + +Variant::~Variant() +{ +} + +const QString Variant::getClassName() const +{ + return "Kross::Api::Variant"; +} + +const QString Variant::toString() +{ + return getValue().toString(); +} + +/* +const QString Variant::getVariantType(Object::Ptr object) +{ + switch( toVariant(object).type() ) { + + case QVariant::CString: + case QVariant::String: + return "Kross::Api::Variant::String"; + case QVariant::Map: + return "Kross::Api::Variant::Dict"; + case QVariant::StringList: + case QVariant::List: + return "Kross::Api::Variant::List"; + case QVariant::Double: + //return "Kross::Api::Variant::Double"; + case QVariant::UInt: + //return "Kross::Api::Variant::UInt"; // python isn't able to differ between int and uint :-( + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::Int: + return "Kross::Api::Variant::Integer"; + case QVariant::Bool: + return "Kross::Api::Variant::Bool"; + default: //Date, Time, DateTime, ByteArray, BitArray, Rect, Size, Color, Invalid, etc. + return "Kross::Api::Variant"; + } +} +*/ + +const QVariant& Variant::toVariant(Object::Ptr object) +{ + return Object::fromObject( object.data() )->getValue(); +} + +const QString Variant::toString(Object::Ptr object) +{ + const QVariant& variant = toVariant(object); + if(! variant.canCast(QVariant::String)) + throw Exception::Ptr( new Exception(i18n("Kross::Api::Variant::String expected, but got %1.").arg(variant.typeName()).latin1()) ); + return variant.toString(); +} + +int Variant::toInt(Object::Ptr object) +{ + const QVariant& variant = toVariant(object); + if(! variant.canCast(QVariant::Int)) + throw Exception::Ptr( new Exception(i18n("Kross::Api::Variant::Int expected, but got %1.").arg(variant.typeName()).latin1()) ); + return variant.toInt(); +} + +uint Variant::toUInt(Object::Ptr object) +{ + const QVariant& variant = toVariant(object); + if(! variant.canCast(QVariant::UInt)) + throw Exception::Ptr( new Exception(i18n("Kross::Api::Variant::UInt expected, but got %1.").arg(variant.typeName()).latin1()) ); + return variant.toUInt(); +} + +double Variant::toDouble(Object::Ptr object) +{ + const QVariant& variant = toVariant(object); + if(! variant.canCast(QVariant::Double)) + throw Exception::Ptr( new Exception(i18n("Kross::Api::Variant::Double expected, but got %1.").arg(variant.typeName()).latin1()) ); + return variant.toDouble(); +} + +Q_LLONG Variant::toLLONG(Object::Ptr object) +{ + const QVariant& variant = toVariant(object); + if(! variant.canCast(QVariant::LongLong)) + throw Exception::Ptr( new Exception(i18n("Kross::Api::Variant::LLONG expected, but got %1.").arg(variant.typeName()).latin1()) ); + return variant.toLongLong(); +} + +Q_ULLONG Variant::toULLONG(Object::Ptr object) +{ + const QVariant& variant = toVariant(object); + if(! variant.canCast(QVariant::ULongLong)) + throw Exception::Ptr( new Exception(i18n("Kross::Api::Variant::ULLONG expected, but got %1.").arg(variant.typeName()).latin1()) ); + return variant.toULongLong(); +} + +bool Variant::toBool(Object::Ptr object) +{ + const QVariant& variant = toVariant(object); + if(! variant.canCast(QVariant::Bool)) + throw Exception::Ptr( new Exception(i18n("Kross::Api::Variant::Bool expected, but got %1.").arg(variant.typeName()).latin1()) ); + return variant.toBool(); +} + +QStringList Variant::toStringList(Object::Ptr object) +{ + List* list = dynamic_cast< List* >( object.data() ); + if(list) { + QStringList l; + QValueList valuelist = list->getValue(); + QValueList::Iterator it(valuelist.begin()), end(valuelist.end()); + for(; it != end; ++it) + l.append( toString(*it) ); + return l; + } + const QVariant& variant = toVariant(object); + if(! variant.canCast(QVariant::StringList)) + throw Exception::Ptr( new Exception(QString("Kross::Api::Variant::StringList expected, but got '%1'.").arg(variant.typeName()).latin1()) ); + return variant.toStringList(); +} + +QValueList Variant::toList(Object::Ptr object) +{ + List* list = dynamic_cast< List* >( object.data() ); + if(list) { + QValueList l; + QValueList valuelist = list->getValue(); + QValueList::Iterator it(valuelist.begin()), end(valuelist.end()); + for(; it != end; ++it) + l.append( toVariant(*it) ); + return l; + } + const QVariant& variant = toVariant(object); + if(! variant.canCast(QVariant::List)) + throw Exception::Ptr( new Exception(i18n("Kross::Api::Variant::List expected, but got '%1'.").arg(variant.typeName()).latin1()) ); + return variant.toList(); +} diff --git a/lib/kross/api/variant.h b/lib/kross/api/variant.h new file mode 100644 index 00000000..020e6e51 --- /dev/null +++ b/lib/kross/api/variant.h @@ -0,0 +1,207 @@ +/*************************************************************************** + * variant.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_VARIANT_H +#define KROSS_API_VARIANT_H + +#include +#include + +#include "object.h" +#include "value.h" +#include "exception.h" + +namespace Kross { namespace Api { + + class List; + + /** + * Variant value to wrap a QVariant into a \a Kross::Api::Value + * to enable primitive types like strings or numerics. + */ + class Variant : public Value + { + friend class Value; + public: + + /** + * Constructor. + * + * \param value The initial QVariant-value + * this Variant-Object has. + * \param name The name this Value has. + */ + Variant(const QVariant& value); + + inline operator bool () { return getValue().toBool(); } + inline operator int () { return getValue().toInt(); } + inline operator uint () { return getValue().toUInt(); } + inline operator double () { return getValue().toDouble(); } + inline operator const char* () { return getValue().toString().latin1(); } + + inline operator QString () { return getValue().toString(); } + inline operator const QString () { return getValue().toString(); } + inline operator const QString& () { return getValue().asString(); } + + inline operator QCString () { return getValue().toCString(); } + inline operator const QCString () { return getValue().toCString(); } + inline operator const QCString& () { return getValue().asCString(); } + + inline operator QVariant () { return getValue(); } + inline operator const QVariant () { return getValue(); } + inline operator const QVariant& () { return getValue(); } + + /** + * Operator to return a QStringList. + * + * We can not just use getValue().toStringList() here cause maybe + * this Kross::Api::Variant is a Kross::Api::List which could be + * internaly used for list of strings as well. So, we use the + * toStringList() function which will take care of translating a + * Kross::Api::List to a QStringList if possible or to throw an + * exception if the Kross::Api::List isn't a QStringList. + */ + inline operator QStringList () { + return toStringList(this); + } + inline operator QValueList () { + return toList(this); + } + + /** + * Destructor. + */ + virtual ~Variant(); + + /// \see Kross::Api::Object::getClassName() + virtual const QString getClassName() const; + + /** + * \return a string representation of the variant. + * + * \see Kross::Api::Object::toString() + */ + virtual const QString toString(); + + /** + * Try to convert the given \a Object into + * a QVariant. + * + * \throw TypeException If the convert failed. + * \param object The object to convert. + * \return The to a QVariant converted object. + */ + static const QVariant& toVariant(Object::Ptr object); + + /** + * Try to convert the given \a Object into + * a QString. + * + * \throw TypeException If the convert failed. + * \param object The object to convert. + * \return The to a QString converted object. + */ + static const QString toString(Object::Ptr object); + + /** + * Try to convert the given \a Object into + * a int. + * + * \throw TypeException If the convert failed. + * \param object The object to convert. + * \return The to a int converted object. + */ + static int toInt(Object::Ptr object); + + /** + * Try to convert the given \a Object into + * a uint. + * + * \throw TypeException If the convert failed. + * \param object The object to convert. + * \return The to a uint converted object. + */ + static uint toUInt(Object::Ptr object); + + /** + * Try to convert the given \a Object into + * a uint. + * + * \throw TypeException If the convert failed. + * \param object The object to convert. + * \return The to a uint converted object. + */ + static double toDouble(Object::Ptr object); + + /** + * Try to convert the given \a Object into + * a Q_LLONG. + * + * \throw TypeException If the convert failed. + * \param object The object to convert. + * \return The to a Q_LLONG converted object. + */ + static Q_LLONG toLLONG(Object::Ptr object); + + /** + * Try to convert the given \a Object into + * a Q_ULLONG. + * + * \throw TypeException If the convert failed. + * \param object The object to convert. + * \return The to a Q_ULLONG converted object. + */ + static Q_ULLONG toULLONG(Object::Ptr object); + + /** + * Try to convert the given \a Object into + * a boolean value. + * + * \throw TypeException If the convert failed. + * \param object The object to convert. + * \return The to a bool converted object. + */ + static bool toBool(Object::Ptr object); + + /** + * Try to convert the given \a Object into + * a QStringList. + * + * \throw TypeException If the convert failed. + * \param object The object to convert. + * \return The to a QValueList converted object. + */ + static QStringList toStringList(Object::Ptr object); + + /** + * Try to convert the given \a Object into + * a QValueList of QVariant's. + * + * \throw TypeException If the convert failed. + * \param object The object to convert. + * \return The to a QValueList converted object. + */ + static QValueList toList(Object::Ptr object); + + }; + +}} + +#endif + diff --git a/lib/kross/configure.in.bot b/lib/kross/configure.in.bot new file mode 100644 index 00000000..1d20501f --- /dev/null +++ b/lib/kross/configure.in.bot @@ -0,0 +1,16 @@ +if test -z "$RUBY_LIBDIR" -a "x$compile_kross" = "xyes" ; then + echo "" + echo "Ruby development files were not found, or Ruby <= 1.8.1 was found," + echo "Ruby scripting support for KOffice will not be built. If you don't" + echo "need Ruby scripting, you can ignore this message." + echo "" +fi + +if test -z "$LIBPYTHON" -a -z "$PYTHONINC" -a $"x$compile_kross" = "xyes"; then + echo "" + echo "Python developement files were not found, Python scripting support for" + echo "KOffice will not be built. If you don't need Python scripting, you" + echo "can ignore this message" + echo "" +fi + diff --git a/lib/kross/configure.in.in b/lib/kross/configure.in.in new file mode 100644 index 00000000..47c2d2d3 --- /dev/null +++ b/lib/kross/configure.in.in @@ -0,0 +1,108 @@ +AC_ARG_ENABLE(scripting, + AC_HELP_STRING([--enable-scripting], + [build scripting library (Kross) [default=yes]]), + compile_kross=$enableval, compile_kross=yes) +AM_CONDITIONAL(compile_kross, test "x$compile_kross" = "xyes") + +############################### +# Check if Python is installed. + +if test "x$compile_kross" = "xyes" ; then + #KDE_CHECK_PYTHON(2.3) + KDE_CHECK_PYTHON +fi + +# Compile the Kross python plugin only if both, $LIBPYTHON and +# $PYTHONINC, are defined. +AM_CONDITIONAL(compile_kross_python, + test -n "$LIBPYTHON" && test -n "$PYTHONINC") + +############################### +# Check for Ruby + +if test "x$compile_kross" = "xyes" ; then + AC_CHECK_PROGS([RUBY], [ruby ruby1.8 ruby18], ruby) + + if test -n "$RUBY"; then + AC_MSG_CHECKING(for Ruby dirs) + RUBY_ARCHDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"archdir"@:>@)'` + RUBY_SITEARCHDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"sitearchdir"@:>@)'` + RUBY_SITEDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"sitelibdir"@:>@)'` + RUBY_INCLUDEDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"rubyincludedir"@:>@)'` + RUBY_LIBDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"libdir"@:>@)'` + RUBY_LIBRUBYARG=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"LIBRUBYARG_SHARED"@:>@)'` + RUBY_ENABLESHARED=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"ENABLE_SHARED"@:>@)'` + AC_MSG_RESULT([ + archdir $RUBY_ARCHDIR, + sitearchdir $RUBY_SITEARCHDIR, + sitedir $RUBY_SITEDIR, + includedir $RUBY_INCLUDEDIR, + libdir $RUBY_LIBDIR, + librubyarg $RUBY_LIBRUBYARG, + rubysharedenabled $RUBY_ENABLESHARED]) + AC_SUBST(RUBY_ARCHDIR) + AC_SUBST(RUBY_SITEARCHDIR) + AC_SUBST(RUBY_SITEDIR) + AC_SUBST(RUBY_INCLUDEDIR) + AC_SUBST(RUBY_LIBDIR) + AC_SUBST(RUBY_LIBRUBYARG) + AC_SUBST(RUBY_ENABLESHARED) + + AC_MSG_CHECKING(for Ruby header) + + if test ! -r $RUBY_INCLUDEDIR/ruby.h; then + # if $RUBY_INCLUDEDIR is not valid try to use $RUBY_ARCHDIR + RUBY_INCLUDEDIR=$RUBY_ARCHDIR + fi + + if test ! -r $RUBY_INCLUDEDIR/ruby.h; then + RUBY_LIBDIR="" + AC_MSG_RESULT([not found]) + else + AC_MSG_RESULT([found]) # header + + AC_MSG_CHECKING(Ruby shared library) + if test "x$RUBY_ENABLESHARED" != "xyes" ; then + AC_MSG_RESULT([shared library not found]) + RUBY_LIBDIR="" + else + if test -z "$RUBY_LIBRUBYARG" ; then + AC_MSG_RESULT([link argument not found]) + RUBY_LIBDIR="" + else + AC_MSG_RESULT([found]) # shared library link arg + + AC_MSG_CHECKING([if C++ program with ruby can be compiled]) + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -I$RUBY_INCLUDEDIR" + AC_CACHE_VAL(ruby_build, + [ + AC_TRY_COMPILE([ + #include + #include + ],[ + +#if(RUBY_VERSION_MAJOR==1 && RUBY_VERSION_MINOR == 8 && RUBY_VERSION_TEENY <= 1) +#error "need at least ruby 1.8.2\n" +#endif + + ruby_init(); + return 0; + ], ruby_build=yes, + ruby_build=no) + ]) + AC_MSG_RESULT($ruby_build) + if test "$ruby_build" = "no"; then + RUBY_LIBDIR="" + fi + CXXFLAGS="$ac_save_CXXFLAGS" + AC_LANG_RESTORE + fi # have ruby shared lib argument + fi # have shared lib + fi # have ruby header + fi # have ruby +fi # compiling kross + +AM_CONDITIONAL(compile_kross_ruby, test -n "$RUBY_LIBDIR") diff --git a/lib/kross/main/Makefile.am b/lib/kross/main/Makefile.am new file mode 100644 index 00000000..0ab90117 --- /dev/null +++ b/lib/kross/main/Makefile.am @@ -0,0 +1,33 @@ +include $(top_srcdir)/lib/kross/Makefile.global + +lib_LTLIBRARIES = libkrossmain.la + +libkrossmain_la_SOURCES = krossconfig.cpp mainmodule.cpp scriptcontainer.cpp manager.cpp \ + scriptaction.cpp scriptguiclient.cpp wdgscriptsmanagerbase.ui wdgscriptsmanager.cpp + +libkrossmain_la_LDFLAGS = $(all_libraries) $(VER_INFO) -Wnounresolved + +mainincludedir=$(includedir)/kross/main + +maininclude_HEADERS = \ + krossconfig.h \ + mainmodule.h \ + manager.h \ + scriptaction.h \ + scriptcontainer.h \ + scriptguiclient.h \ + wdgscriptsmanager.h \ + wdgscriptsmanagerbase.h + +libkrossmain_la_LIBADD = \ + $(LIB_QT) \ + $(LIB_KDECORE) \ + $(LIB_KFILE) \ + $(LIB_KDEUI) \ + $(LIB_KNEWSTUFF) \ + $(LIB_KROSS_API) + +METASOURCES = AUTO +SUBDIRS = . +INCLUDES = $(KROSS_INCLUDES) $(all_includes) +noinst_HEADERS = wdgscriptsmanager.h diff --git a/lib/kross/main/krossconfig.cpp b/lib/kross/main/krossconfig.cpp new file mode 100644 index 00000000..61984532 --- /dev/null +++ b/lib/kross/main/krossconfig.cpp @@ -0,0 +1,36 @@ +/*************************************************************************** + * krossconfig.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "krossconfig.h" + +#ifdef KROSS_DEBUG_ENABLED + +#include + +void Kross::krossdebug(const QString &s) +{ + kdDebug() << "Kross: " << s << endl; +} + +void Kross::krosswarning(const QString &s) +{ + kdWarning() << "Kross: " << s << endl; +} + +#endif diff --git a/lib/kross/main/krossconfig.h b/lib/kross/main/krossconfig.h new file mode 100644 index 00000000..6b8bb25d --- /dev/null +++ b/lib/kross/main/krossconfig.h @@ -0,0 +1,116 @@ +/*************************************************************************** + * krossconfig.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_MAIN_KROSSCONFIG_H +#define KROSS_MAIN_KROSSCONFIG_H + +#include + +/** + * The Kross scripting bridge to embed scripting functionality + * into an application. + * + * - abstract API to access the scripting functionality. + * - interpreter independend to be able to decide on runtime + * if we like to use the python, kjs (KDE JavaScript) or + * whatever scripting interpreter. + * - flexibility by beeing able to connect different + * scripting interpreters together into something like + * a "working chain" (e.g. python-script script1 spends + * some functionality the kjs-script script2 likes to + * use. + * - transparently bridge functionality wrappers like + * \a Kross::KexiDB together with interpreters like \a Kross::Python. + * - Introspection where needed to be able to manipulate + * behaviours and functionality on runtime. + * - Qt/KDE based, so use the extended techs both spends. + * - integrate nicly as powerfull scripting system into the + * Kexi application. + * + * \author Sebastian Sauer + * \sa http://www.koffice.org/kexi + * \sa http://www.dipe.org/kross + */ +namespace Kross { + + /// Debugging enabled. + #define KROSS_DEBUG_ENABLED + + #ifdef KROSS_DEBUG_ENABLED + + /** + * Debugging function. + */ + void krossdebug(const QString &s); + + /** + * Warning function. + */ + void krosswarning(const QString &s); + + #else + // Define these to an empty statement if debugging is disabled. + #define krossdebug(x) + #define krosswarning(x) + #endif + + /** + * The common Kross API used as common codebase. + * + * The API spends \a Kross::Api::Object and more specialized + * classes to bridge other Kross parts together. Interaction + * between objects got wrapped at runtime and introspection- + * functionality enables dynamic manipulations. + * The proxy functionality prevents cross-dependencies + * between Kross parts like the \a Kross::Python implementation + * and the \a Kross::KexiDB wrapper. + * + * \author Sebastian Sauer + */ + namespace Api { + + //#define KROSS_API_OBJECT_CTOR_DEBUG + //#define KROSS_API_OBJECT_DTOR_DEBUG + //#define KROSS_API_OBJECT_ADDCHILD_DEBUG + //#define KROSS_API_OBJECT_REMCHILD_DEBUG + //#define KROSS_API_OBJECT_CALL_DEBUG + + //#define KROSS_API_EVENT_CALL_DEBUG + + //#define KROSS_API_CALLABLE_CALL_DEBUG + //#define KROSS_API_CALLABLE_CHECKARG_DEBUG + + //#define KROSS_API_EVENTSLOT_CALL_DEBUG + //#define KROSS_API_EVENTSIGNAL_CALL_DEBUG + + // The name of the interpreter's library. Those library got loaded + // dynamicly during runtime. Comment out to disable compiling of + // the interpreter-plugin or to hardcode the location of the lib + // like I did at the following line. + + //#define KROSS_PYTHON_LIBRARY "/home/snoopy/cvs/kde/trunk/koffice/lib/kross/python/krosspython.la" + #define KROSS_PYTHON_LIBRARY "krosspython" + #define KROSS_RUBY_LIBRARY "krossruby" + + } + +} + +#endif + diff --git a/lib/kross/main/mainmodule.cpp b/lib/kross/main/mainmodule.cpp new file mode 100644 index 00000000..0630e81a --- /dev/null +++ b/lib/kross/main/mainmodule.cpp @@ -0,0 +1,117 @@ +/*************************************************************************** + * mainmodule.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "mainmodule.h" + +using namespace Kross::Api; + +namespace Kross { namespace Api { + + /// \internal + class MainModulePrivate + { + public: + /** + * The \a Exception this \a MainModule throwed or + * NULL if we don't had an exception. + */ + Exception::Ptr exception; + }; + +}} + +MainModule::MainModule(const QString& name) + : Module(name) + , d(new MainModulePrivate()) +{ + d->exception = 0; +} + +MainModule::~MainModule() +{ + delete d; +} + +const QString MainModule::getClassName() const +{ + return "Kross::Api::MainModule"; +} + +bool MainModule::hadException() +{ + return d->exception != 0; +} + +Exception::Ptr MainModule::getException() +{ + return d->exception; +} + +void MainModule::setException(Exception::Ptr exception) +{ + d->exception = exception; +} + +#if 0 +bool MainModule::hasChild(const QString& name) const +{ + return Callable::hasChild(name); +} +#endif + +EventSignal::Ptr MainModule::addSignal(const QString& name, QObject* sender, QCString signal) +{ + EventSignal* event = new EventSignal(name, sender, signal); + if(! addChild(name, event)) { + krosswarning( QString("Failed to add signal name='%1' signature='%2'").arg(name).arg(signal) ); + return 0; + } + return event; +} + +EventSlot::Ptr MainModule::addSlot(const QString& name, QObject* receiver, QCString slot) +{ + EventSlot* event = new EventSlot(name, receiver, slot); + if(! addChild(name, event)) { + krosswarning( QString("Failed to add slot name='%1' signature='%2'").arg(name).arg(slot) ); + return 0; + } + return event; +} + +QtObject::Ptr MainModule::addQObject(QObject* object, const QString& name) +{ + QtObject* qtobject = new QtObject(object, name); + if(! addChild(name, qtobject)) { + krosswarning( QString("Failed to add QObject name='%1'").arg(object->name()) ); + return 0; + } + return qtobject; +} + +EventAction::Ptr MainModule::addKAction(KAction* action, const QString& name) +{ + EventAction* event = new EventAction(name, action); + if(! addChild(name, event)) { + krosswarning( QString("Failed to add KAction name='%1'").arg(action->name()) ); + return 0; + } + return event; +} + diff --git a/lib/kross/main/mainmodule.h b/lib/kross/main/mainmodule.h new file mode 100644 index 00000000..116e098d --- /dev/null +++ b/lib/kross/main/mainmodule.h @@ -0,0 +1,181 @@ +/*************************************************************************** + * mainmodule.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_MAINMODULE_H +#define KROSS_API_MAINMODULE_H + +#include "../api/object.h" +#include "../api/variant.h" +#include "../api/module.h" +#include "../api/event.h" +#include "../api/eventsignal.h" +#include "../api/eventslot.h" +#include "../api/qtobject.h" +#include "../api/eventaction.h" + +#include +#include +#include + +#include +#include + +namespace Kross { namespace Api { + + // Forward declarations. + class MainModulePrivate; + + /** + * This class implements \a Module for the global + * \a Manager singleton and local \a ScriptContainer + * instances. + * + * The MainModule class provides base functionality + * for a root node in a tree of \a Kross::Api::Object + * instances. + */ + class MainModule : public Module + { + public: + + /// Shared pointer to implement reference-counting. + typedef KSharedPtr Ptr; + + /** + * Constructor. + * + * \param name the name of the \a Module . While the + * global manager module has the name "Kross" + * the \a ScriptContainer instances are accessible + * by there \a ScriptContainer::getName() name. + */ + explicit MainModule(const QString& name); + + /** + * Destructor. + */ + virtual ~MainModule(); + + /// \see Kross::Api::Object::getClassName() + virtual const QString getClassName() const; + + /** + * \return true if the script throwed an exception + * else false. + */ + bool hadException(); + + /** + * \return the \a Exception this module throwed. + */ + Exception::Ptr getException(); + + /** + * Set the \a Exception this module throwed. + * + * \param exception The \a Exception this module throws or + * NULL if you like to clear exception and to let + * \a hadException() return false. + */ + void setException(Exception::Ptr exception); + +#if 0 + /** + * Returns if the defined child is avaible. + * + * \return true if child exists else false. + */ + bool hasChild(const QString& name) const; +#endif + + /** + * Add a Qt signal to the \a Module by creating + * an \a EventSignal for it. + * + * \param name the name the \a EventSignal is + * reachable as + * \param sender the QObject instance which + * is the sender of the \p signal + * \param signal the Qt signal macro the \p sender + * emits to call the \a EventSignal + * \return the newly added \a EventSignal instance + * which is now a child of this \a MainModule + */ + EventSignal::Ptr addSignal(const QString& name, QObject* sender, QCString signal); + + /** + * Add a Qt slot to the \a Module by creating + * an \a EventSlot for it. + * + * \param name the name the \a EventSlot is + * reachable as + * \param receiver the QObject instance which + * is the receiver of the \p signal + * \param slot the Qt slot macro of the \p receiver + * to invoke if the \a EventSlot got called. + * \return the newly added \a EventSlot instance + * which is now a child of this \a MainModule + */ + EventSlot::Ptr addSlot(const QString& name, QObject* receiver, QCString slot); + + /** + * Add a \a QObject to the eventcollection. All + * signals and slots the QObject has will be + * added to a new \a EventCollection instance + * which is child of this \a EventCollection + * instance. + * + * \param object the QObject instance that should + * be added to this \a MainModule + * \param name the name under which this QObject instance + * should be registered as + * \return the newly added \a QtObject instance + * which is now a child of this \a MainModule + */ + QtObject::Ptr addQObject(QObject* object, const QString& name = QString::null); + + /** + * Add a \a KAction to the eventcollection. The + * KAction will be wrapped by a \a EventAction + * and will be added to this collection. + * + * \param name name to identify the \a action by + * \param action the KAction instance that should + * be added to this \a MainModule + * \return the newly added \a EventAction instance + * which is now a child of this \a MainModule + * + * \todo check \a name dox. + */ + EventAction::Ptr addKAction(KAction* action, const QString& name = QString::null); + + //typedef QValueList EventList; + //EventList getEvents(); + //const QString& serializeToXML(); + //void unserializeFromXML(const QString& xml); + + private: + /// Private d-pointer class. + MainModulePrivate* d; + }; + +}} + +#endif + diff --git a/lib/kross/main/manager.cpp b/lib/kross/main/manager.cpp new file mode 100644 index 00000000..cdb4d363 --- /dev/null +++ b/lib/kross/main/manager.cpp @@ -0,0 +1,248 @@ +/*************************************************************************** + * manager.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "manager.h" + +#include "../api/interpreter.h" +//#include "../api/qtobject.h" +#include "../api/eventslot.h" +#include "../api/eventsignal.h" +//#include "../api/script.h" + +#include "krossconfig.h" +#include "scriptcontainer.h" + +#include +#include +#include + +#include +#include +#include + +extern "C" +{ + typedef Kross::Api::Object* (*def_module_func)(Kross::Api::Manager*); +} + +using namespace Kross::Api; + +namespace Kross { namespace Api { + + /// @internal + class ManagerPrivate + { + public: + /// List of \a InterpreterInfo instances. + QMap interpreterinfos; + + /// Loaded modules. + QMap modules; + }; + + /** + * The Manager-singleton instance is NULL by default till the + * Manager::scriptManager() method got called first time. + */ + static KSharedPtr m_manager = KSharedPtr(0); + +}} + +Manager* Manager::scriptManager() +{ + if(! m_manager.data()) { + // Create the Manager-singleton on demand. + m_manager = KSharedPtr( new Manager() ); + } + + // and finally return the singleton. + return m_manager.data(); +} + +Manager::Manager() + : MainModule("Kross") // the manager has the name "Kross" + , d( new ManagerPrivate() ) +{ +#ifdef KROSS_PYTHON_LIBRARY + QString pythonlib = QFile::encodeName( KLibLoader::self()->findLibrary(KROSS_PYTHON_LIBRARY) ); + if(! pythonlib.isEmpty()) { // If the Kross Python plugin exists we offer it as supported scripting language. + InterpreterInfo::Option::Map pythonoptions; + pythonoptions.replace("restricted", + new InterpreterInfo::Option("Restricted", "Restricted Python interpreter", QVariant(false,0)) + ); + d->interpreterinfos.replace("python", + new InterpreterInfo("python", + pythonlib, // library + "*.py", // file filter-wildcard + QStringList() << /* "text/x-python" << */ "application/x-python", // mimetypes + pythonoptions // options + ) + ); + } +#endif +#ifdef KROSS_RUBY_LIBRARY + QString rubylib = QFile::encodeName( KLibLoader::self()->findLibrary(KROSS_RUBY_LIBRARY) ); + if(! rubylib.isEmpty()) { // If the Kross Ruby plugin exists we offer it as supported scripting language. + InterpreterInfo::Option::Map rubyoptions; + rubyoptions.replace("safelevel", + new InterpreterInfo::Option("safelevel", "Level of safety of the Ruby interpreter", QVariant(0)) // 0 -> unsafe, 4 -> very safe + ); + d->interpreterinfos.replace("ruby", + new InterpreterInfo("ruby", + rubylib, // library + "*.rb", // file filter-wildcard + QStringList() << /* "text/x-ruby" << */ "application/x-ruby", // mimetypes + rubyoptions // options + ) + ); + } else { + krossdebug("Ruby interpreter for kross in unavailable"); + } +#endif +} + +Manager::~Manager() +{ + for(QMap::Iterator it = d->interpreterinfos.begin(); it != d->interpreterinfos.end(); ++it) + delete it.data(); + delete d; +} + +QMap Manager::getInterpreterInfos() +{ + return d->interpreterinfos; +} + +bool Manager::hasInterpreterInfo(const QString& interpretername) const +{ + return d->interpreterinfos.contains(interpretername); +} + +InterpreterInfo* Manager::getInterpreterInfo(const QString& interpretername) +{ + return d->interpreterinfos[interpretername]; +} + +const QString Manager::getInterpreternameForFile(const QString& file) +{ + QRegExp rx; + rx.setWildcard(true); + for(QMap::Iterator it = d->interpreterinfos.begin(); it != d->interpreterinfos.end(); ++it) { + rx.setPattern((*it)->getWildcard()); + if( file.find(rx) >= 0 ) + return (*it)->getInterpretername(); + } + return QString::null; +} + +ScriptContainer::Ptr Manager::getScriptContainer(const QString& scriptname) +{ + //TODO at the moment we don't share ScriptContainer instances. + + //if(d->m_scriptcontainers.contains(scriptname)) + // return d->m_scriptcontainers[scriptname]; + ScriptContainer* scriptcontainer = new ScriptContainer(scriptname); + //ScriptContainer script(this, scriptname); + //d->m_scriptcontainers.replace(scriptname, scriptcontainer); + + return scriptcontainer; +} + +Interpreter* Manager::getInterpreter(const QString& interpretername) +{ + setException(0); // clear previous exceptions + + if(! d->interpreterinfos.contains(interpretername)) { + setException( new Exception(i18n("No such interpreter '%1'").arg(interpretername)) ); + return 0; + } + + return d->interpreterinfos[interpretername]->getInterpreter(); +} + +const QStringList Manager::getInterpreters() +{ + QStringList list; + + QMap::Iterator it( d->interpreterinfos.begin() ); + for(; it != d->interpreterinfos.end(); ++it) + list << it.key(); + +//list << "TestCase"; + + return list; +} + +bool Manager::addModule(Module::Ptr module) +{ + QString name = module->getName(); + //if( d->modules.contains(name) ) return false; + d->modules.replace(name, module); + return true; +} + +Module::Ptr Manager::loadModule(const QString& modulename) +{ + Module::Ptr module = 0; + + if(d->modules.contains(modulename)) { + module = d->modules[modulename]; + if(module) + return module; + else + krossdebug( QString("Manager::loadModule(%1) =======> Modulename registered, but module is invalid!").arg(modulename) ); + } + + KLibLoader* loader = KLibLoader::self(); + KLibrary* lib = loader->globalLibrary( modulename.latin1() ); + if(! lib) { + krosswarning( QString("Failed to load module '%1': %2").arg(modulename).arg(loader->lastErrorMessage()) ); + return 0; + } + krossdebug( QString("Successfully loaded module '%1'").arg(modulename) ); + + def_module_func func; + func = (def_module_func) lib->symbol("init_module"); + + if(! func) { + krosswarning( QString("Failed to determinate init function in module '%1'").arg(modulename) ); + return 0; + } + + try { + module = (Kross::Api::Module*) (func)(this); + } + catch(Kross::Api::Exception::Ptr e) { + krosswarning( e->toString() ); + module = 0; + } + lib->unload(); + + if(! module) { + krosswarning( QString("Failed to load module '%1'").arg(modulename) ); + return 0; + } + + // Don't remember module cause we like to have freeing it handled by the caller. + //d->modules.replace(modulename, module); + + //krossdebug( QString("Kross::Api::Manager::loadModule modulename='%1' module='%2'").arg(modulename).arg(module->toString()) ); + return module; +} + diff --git a/lib/kross/main/manager.h b/lib/kross/main/manager.h new file mode 100644 index 00000000..0de5833f --- /dev/null +++ b/lib/kross/main/manager.h @@ -0,0 +1,168 @@ +/*************************************************************************** + * manager.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_MANAGER_H +#define KROSS_API_MANAGER_H + +#include +#include +#include +//#include +#include + +class QObject; + +#include "../api/object.h" +#include "mainmodule.h" + +namespace Kross { namespace Api { + + // Forward declarations. + class Interpreter; + class Object; + class EventSlot; + class EventSignal; + class ScriptContainer; + class ManagerPrivate; + class InterpreterInfo; + + /** + * The Manager class is the main entry point to work with + * Kross. It spends an abstraction layer between what is + * under the hood of Kross and the functionality you need + * to access. + * Use \a Interpreter to just work with some implementated + * interpreter like python. While \a Script spends a more + * flexible container. + */ + class KDE_EXPORT Manager : public MainModule + { + protected: + + /** + * Constructor. Use \a scriptManager() to access + * the Manager singleton instance. + */ + Manager(); + + public: + + /** + * Destructor. + */ + ~Manager(); + + /** + * Return the Manager instance. Always use this + * function to access the Manager singleton. + */ + static Manager* scriptManager(); + + /** + * \return a map with \a InterpreterInfo* instances + * used to describe interpreters. + */ + QMap getInterpreterInfos(); + + /** + * \return true if there exists an interpreter with the + * name \p interpretername else false. + */ + bool hasInterpreterInfo(const QString& interpretername) const; + + /** + * \return the \a InterpreterInfo* matching to the defined + * \p interpretername or NULL if there does not exists such + * a interpreter. + */ + InterpreterInfo* getInterpreterInfo(const QString& interpretername); + + /** + * \return the name of the \a Interpreter that feels responsible + * for the defined \p file . + * + * \param file The filename we should try to determinate the + * interpretername for. + * \return The name of the \a Interpreter which will be used + * to execute the file or QString::null if we failed + * to determinate a matching interpreter for the file. + */ + const QString getInterpreternameForFile(const QString& file); + + /** + * Return the existing \a ScriptContainer with scriptname + * or create a new \a ScriptContainer instance and associate + * the passed scriptname with it. + * + * \param scriptname The name of the script. This + * should be unique for each \a Script and + * could be something like the filename. + * \return The \a ScriptContainer instance matching to + * scriptname. + */ + KSharedPtr getScriptContainer(const QString& scriptname); + + /** + * Return the \a Interpreter instance defined by + * the interpretername. + * + * \param interpretername The name of the interpreter. + * e.g. "python" or "kjs". + * \return The Interpreter instance or NULL if there + * does not exists an interpreter with such + * an interpretername. + */ + Interpreter* getInterpreter(const QString& interpretername); + + /** + * \return a list of names of the at the backend + * supported interpreters. + */ + const QStringList getInterpreters(); + + /** + * Add the an external module to the global shared list of + * loaded modules. + * + * @param module The @a Module instace to add. + * @return true if the module was added successfully else + * false. + */ + bool addModule(Module::Ptr module); + + /** + * Load an external module and return it. + * + * \param modulename The name of the library we should try to + * load. Those library needs to be a valid kross module. + * \return The loaded \a Object or NULL if loading + * failed. The loaded Module isn't added to the global + * shared list of modules. + */ + Module::Ptr loadModule(const QString& modulename); + + private: + /// Private d-pointer class. + ManagerPrivate* d; + }; + +}} + +#endif + diff --git a/lib/kross/main/scriptaction.cpp b/lib/kross/main/scriptaction.cpp new file mode 100644 index 00000000..06ee9dd7 --- /dev/null +++ b/lib/kross/main/scriptaction.cpp @@ -0,0 +1,247 @@ +/*************************************************************************** + * scriptaction.cpp + * This file is part of the KDE project + * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "scriptaction.h" +#include "manager.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace Kross::Api; + +namespace Kross { namespace Api { + + /// @internal + class ScriptActionPrivate + { + public: + /** + * The packagepath is the directory that belongs to this + * \a ScriptAction instance. If this \a ScriptAction points + * to a scriptfile the packagepath will be the directory + * the scriptfile is located in. + */ + QString packagepath; + + /** + * List of logs this \a ScriptAction has. Initialization, + * execution and finalization should be logged for + * example. So, the logs are usuabled to provide some + * more detailed visual information to the user what + * our \a ScriptAction did so far. + */ + QStringList logs; + + /** + * The versionnumber this \a ScriptAction has. We are using + * the version to handle \a ScriptAction instances which + * have the same unique \a ScriptAction::name() . If the name + * is the same, we are able to use the version to determinate + * which \a ScriptAction is newer / replaces the other. + */ + int version; + + /** + * The description used to provide a way to the user to describe + * the \a ScriptAction with a longer string. + */ + QString description; + + /** + * List of \a ScriptActionCollection instances this \a ScriptAction + * is attached to. + */ + QValueList collections; + + /** + * Constructor. + */ + explicit ScriptActionPrivate() : version(0) {} + }; + +}} + +ScriptAction::ScriptAction(const QString& file) + : KAction(0, file.latin1()) + , Kross::Api::ScriptContainer(file) + , d( new ScriptActionPrivate() ) // initialize d-pointer class +{ + KURL url(file); + if(url.isLocalFile()) { + setFile(file); + setText(url.fileName()); + setIcon(KMimeType::iconForURL(url)); + } + else { + setText(file); + } + + setDescription(file); + setEnabled(false); +} + +ScriptAction::ScriptAction(const QString& scriptconfigfile, const QDomElement& element) + : KAction() + , Kross::Api::ScriptContainer() + , d( new ScriptActionPrivate() ) // initialize d-pointer class +{ + QString name = element.attribute("name"); + QString text = element.attribute("text"); + QString description = element.attribute("description"); + QString file = element.attribute("file"); + QString icon = element.attribute("icon"); + + QString version = element.attribute("version"); + bool ok; + int v = version.toInt(&ok); + if(ok) d->version = v; + + if(file.isEmpty()) { + if(text.isEmpty()) + text = name; + } + else { + if(name.isEmpty()) + name = file; + if(text.isEmpty()) + text = file; + } + + //d->scriptcontainer = Manager::scriptManager()->getScriptContainer(name); + + QString interpreter = element.attribute("interpreter"); + if(interpreter.isNull()) + setEnabled(false); + else + setInterpreterName( interpreter ); + + if(file.isNull()) { + setCode( element.text().stripWhiteSpace() ); + if(description.isNull()) + description = text; + ScriptContainer::setName(name); + } + else { + QDir dir = QFileInfo(scriptconfigfile).dir(true); + d->packagepath = dir.absPath(); + QFileInfo fi(dir, file); + file = fi.absFilePath(); + setEnabled(fi.exists()); + setFile(file); + if(icon.isNull()) + icon = KMimeType::iconForURL( KURL(file) ); + if(description.isEmpty()) + description = QString("%1
%2").arg(text.isEmpty() ? name : text).arg(file); + else + description += QString("
%1").arg(file); + ScriptContainer::setName(file); + } + + KAction::setName(name.latin1()); + KAction::setText(text); + setDescription(description); + KAction::setIcon(icon); + + // connect signal + connect(this, SIGNAL(activated()), this, SLOT(activate())); +} + +ScriptAction::~ScriptAction() +{ + detachAll(); + delete d; +} + +int ScriptAction::version() const +{ + return d->version; +} + +const QString ScriptAction::getDescription() const +{ + return d->description; +} + +void ScriptAction::setDescription(const QString& description) +{ + d->description = description; + setToolTip( description ); + setWhatsThis( description ); +} + +void ScriptAction::setInterpreterName(const QString& name) +{ + setEnabled( Manager::scriptManager()->hasInterpreterInfo(name) ); + Kross::Api::ScriptContainer::setInterpreterName(name); +} + +const QString ScriptAction::getPackagePath() const +{ + return d->packagepath; +} + +const QStringList& ScriptAction::getLogs() const +{ + return d->logs; +} + +void ScriptAction::attach(ScriptActionCollection* collection) +{ + d->collections.append( collection ); +} + +void ScriptAction::detach(ScriptActionCollection* collection) +{ + d->collections.remove( collection ); +} + +void ScriptAction::detachAll() +{ + for(QValueList::Iterator it = d->collections.begin(); it != d->collections.end(); ++it) + (*it)->detach( this ); +} + +void ScriptAction::activate() +{ + emit activated(this); + Kross::Api::ScriptContainer::execute(); + if( Kross::Api::ScriptContainer::hadException() ) { + QString errormessage = Kross::Api::ScriptContainer::getException()->getError(); + QString tracedetails = Kross::Api::ScriptContainer::getException()->getTrace(); + d->logs << QString("%1
%2") + .arg( QStyleSheet::escape(errormessage) ) + .arg( QStyleSheet::escape(tracedetails) ); + emit failed(errormessage, tracedetails); + } + else { + emit success(); + } +} + +void ScriptAction::finalize() +{ + Kross::Api::ScriptContainer::finalize(); +} + +#include "scriptaction.moc" diff --git a/lib/kross/main/scriptaction.h b/lib/kross/main/scriptaction.h new file mode 100644 index 00000000..22bb37ec --- /dev/null +++ b/lib/kross/main/scriptaction.h @@ -0,0 +1,309 @@ +/*************************************************************************** + * scriptaction.h + * This file is part of the KDE project + * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_SCRIPTACTION_H +#define KROSS_API_SCRIPTACTION_H + +#include +#include + +#include "scriptcontainer.h" + +namespace Kross { namespace Api { + + // Forward declarations. + class ScriptContainer; + class ScriptActionCollection; + class ScriptActionPrivate; + + /** + * A ScriptAction extends a KAction by providing a wrapper around + * a \a ScriptContainer to execute scripting code on activation. + */ + class ScriptAction + : public KAction + , public Kross::Api::ScriptContainer + { + Q_OBJECT + + /// The name of the interpreter used to execute the scripting code. + //Q_PROPERTY(QString interpretername READ getInterpreterName WRITE setInterpreterName) + + /// The scripting code which should be executed. + //Q_PROPERTY(QString code READ getCode WRITE setCode) + + /// The scriptfile which should be executed. + //Q_PROPERTY(QString file READ getFile WRITE setFile) + + /// The description for this \a ScriptAction . + Q_PROPERTY(QString description READ getDescription WRITE setDescription) + + public: + + /// Shared pointer to implement reference-counting. + typedef KSharedPtr Ptr; + + /// A list of \a ScriptAction instances. + //typedef QValueList List; + + /** + * Constructor. + * + * \param file The KURL scriptfile this \a ScriptAction + * points to. + */ + explicit ScriptAction(const QString& file); + + /** + * Constructor. + * + * \param scriptconfigfile The XML-configurationfile + * the DOM-element was readed from. + * \param element The QDomElement which will be used + * to setup the \a ScriptAction attributes. + */ + explicit ScriptAction(const QString& scriptconfigfile, const QDomElement& element); + + /** + * Destructor. + */ + virtual ~ScriptAction(); + + /** + * \return the version this script has. Versions are used + * to be able to manage different versions of the same + * script. The version is 0 by default if not defined to + * something else in the rc-file. + */ + int version() const; + + /** + * \return the description for this \a ScriptAction has. + */ + const QString getDescription() const; + + /** + * Set the description \p description for this \a ScriptAction . + */ + void setDescription(const QString& description); + + /** + * Set the name of the interpreter which will be used + * on activation to execute the scripting code. + * + * \param name The name of the \a Interpreter . This + * could be e.g. "python". + */ + void setInterpreterName(const QString& name); + + /** + * \return the path of the package this \a ScriptAction + * belongs to or QString::null if it doesn't belong to + * any package. + */ + const QString getPackagePath() const; + + /** + * \return a list of all kind of logs this \a ScriptAction + * does remember. + */ + const QStringList& getLogs() const; + + /** + * Attach this \a ScriptAction to the \a ScriptActionCollection + * \p collection . + */ + void attach(ScriptActionCollection* collection); + + /** + * Detach this \a ScriptAction from the \a ScriptActionCollection + * \p collection . + */ + void detach(ScriptActionCollection* collection); + + /** + * Detach this \a ScriptAction from all \a ScriptActionCollection + * instance his \a ScriptAction is attached to. + */ + void detachAll(); + + public slots: + + /** + * If the \a ScriptAction got activated the \a ScriptContainer + * got executed. Once this slot got executed it will emit a + * \a success() or \a failed() signal. + */ + virtual void activate(); + + /** + * This slot finalizes the \a ScriptContainer and tries to clean + * any still running script. + */ + void finalize(); + + signals: + + /** + * This signal got emitted when this action is emitted before execution. + */ + void activated(const Kross::Api::ScriptAction*); + + /** + * This signal got emitted after this \a ScriptAction got + * executed successfully. + */ + void success(); + + /** + * This signal got emitted after the try to execute this + * \a ScriptAction failed. The \p errormessage contains + * the error message. + */ + void failed(const QString& errormessage, const QString& tracedetails); + + private: + /// Internaly used private d-pointer. + ScriptActionPrivate* d; + }; + + /** + * A collection to store \a ScriptAction shared pointers. + * + * A \a ScriptAction instance could be stored within + * multiple \a ScriptActionCollection instances. + */ + class ScriptActionCollection + { + private: + + /** + * The list of \a ScriptAction shared pointers. + */ + QValueList m_list; + + /** + * A map of \a ScriptAction shared pointers used to access + * the actions with there name. + */ + QMap m_actions; + + /** + * A KActionMenu which could be used to display the + * content of this \a ScriptActionCollection instance. + */ + KActionMenu* m_actionmenu; + + /** + * Boolean value used to represent the modified-state. Will + * be true if this \a ScriptActionCollection is modified + * aka dirty and e.g. the \a m_actionmenu needs to be + * updated else its false. + */ + bool m_dirty; + + /** + * Copy-constructor. The cctor is private cause instances + * of this class shouldn't be copied. If that changes one + * day, don't forgot that it's needed to copy the private + * member variables as well or we may end in dirty + * crashes :) + */ + ScriptActionCollection(const ScriptActionCollection&) {} + + public: + + /** + * Constructor. + * + * \param text The text used to display some describing caption. + * \param ac The KActionCollection which should be used to as + * initial content for the KActionMenu \a m_actionmenu . + * \param name The internal name. + */ + ScriptActionCollection(const QString& text, KActionCollection* ac, const char* name) + : m_actionmenu( new KActionMenu(text, ac, name) ) + , m_dirty(true) {} + + + /** + * Destructor. + */ + ~ScriptActionCollection() { + for(QValueList::Iterator it = m_list.begin(); it != m_list.end(); ++it) + (*it)->detach(this); + } + + /** + * \return the \a ScriptAction instance which has the name \p name + * or NULL if there exists no such action. + */ + ScriptAction::Ptr action(const QCString& name) { return m_actions[name]; } + + /** + * \return a list of actions. + */ + QValueList actions() { return m_list; } + + /** + * \return the KActionMenu \a m_actionmenu . + */ + KActionMenu* actionMenu() { return m_actionmenu; } + + /** + * Attach a \a ScriptAction instance to this \a ScriptActionCollection . + */ + void attach(ScriptAction::Ptr action) { + m_dirty = true; + m_actions[ action->name() ] = action; + m_list.append(action); + m_actionmenu->insert(action); + action->attach(this); + } + + /** + * Detach a \a ScriptAction instance from this \a ScriptActionCollection . + */ + void detach(ScriptAction::Ptr action) { + m_dirty = true; + m_actions.remove(action->name()); + m_list.remove(action); + m_actionmenu->remove(action); + action->detach(this); + } + + /** + * Clear this \a ScriptActionCollection . The collection + * will be empty and there are no actions attach any longer. + */ + void clear() { + for(QValueList::Iterator it = m_list.begin(); it != m_list.end(); ++it) { + m_actionmenu->remove(*it); + (*it)->detach(this); + } + m_list.clear(); + m_actions.clear(); + } + + }; + +}} + +#endif + diff --git a/lib/kross/main/scriptcontainer.cpp b/lib/kross/main/scriptcontainer.cpp new file mode 100644 index 00000000..56c9bb8e --- /dev/null +++ b/lib/kross/main/scriptcontainer.cpp @@ -0,0 +1,293 @@ +/*************************************************************************** + * scriptcontainer.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "scriptcontainer.h" +#include "../api/object.h" +#include "../api/list.h" +#include "../api/interpreter.h" +#include "../api/script.h" +#include "../main/manager.h" +#include "mainmodule.h" + +#include + +#include + +using namespace Kross::Api; + +namespace Kross { namespace Api { + + /// @internal + class ScriptContainerPrivate + { + public: + + /** + * The \a Script instance the \a ScriptContainer uses + * if initialized. It will be NULL as long as we + * didn't initialized it what will be done on + * demand. + */ + Script* script; + + /** + * The unique name the \a ScriptContainer is + * reachable as. + */ + QString name; + + /** + * The scripting code. + */ + QString code; + + /** + * The name of the interpreter. This could be + * something like "python" for the python + * binding. + */ + QString interpretername; + + /** + * The name of the scriptfile that should be + * executed. Those scriptfile will be readed + * and the content will be used to set the + * scripting code and, if not defined, the + * used interpreter. + */ + QString scriptfile; + + /** + * Map of options that overwritte the \a InterpreterInfo::Option::Map + * standard options. + */ + QMap options; + + }; + +}} + +ScriptContainer::ScriptContainer(const QString& name) + : MainModule(name) + , d( new ScriptContainerPrivate() ) // initialize d-pointer class +{ + //krossdebug( QString("ScriptContainer::ScriptContainer() Ctor name='%1'").arg(name) ); + + d->script = 0; + d->name = name; +} + +ScriptContainer::~ScriptContainer() +{ + //krossdebug( QString("ScriptContainer::~ScriptContainer() Dtor name='%1'").arg(d->name) ); + + finalize(); + delete d; +} + +const QString ScriptContainer::getName() const +{ + return d->name; +} + +void ScriptContainer::setName(const QString& name) +{ + d->name = name; +} + +QString ScriptContainer::getCode() const +{ + return d->code; +} + +void ScriptContainer::setCode(const QString& code) +{ + finalize(); + d->code = code; +} + +QString ScriptContainer::getInterpreterName() const +{ + return d->interpretername; +} + +void ScriptContainer::setInterpreterName(const QString& interpretername) +{ + finalize(); + d->interpretername = interpretername; +} + +QString ScriptContainer::getFile() const +{ + return d->scriptfile; +} + +void ScriptContainer::setFile(const QString& scriptfile) +{ + finalize(); + d->scriptfile = scriptfile; +} + +QMap& ScriptContainer::getOptions() +{ + return d->options; +} + +QVariant ScriptContainer::getOption(const QString name, QVariant defaultvalue, bool /*recursive*/) +{ + if(d->options.contains(name)) + return d->options[name]; + Kross::Api::InterpreterInfo* info = Kross::Api::Manager::scriptManager()->getInterpreterInfo( d->interpretername ); + return info ? info->getOptionValue(name, defaultvalue) : defaultvalue; +} + +bool ScriptContainer::setOption(const QString name, const QVariant& value) +{ + Kross::Api::InterpreterInfo* info = Kross::Api::Manager::scriptManager()->getInterpreterInfo( d->interpretername ); + if(info) { + if(info->hasOption(name)) { + d->options.replace(name, value); + return true; + } else krosswarning( QString("Kross::Api::ScriptContainer::setOption(%1, %2): No such option").arg(name).arg(value.toString()) ); + } else krosswarning( QString("Kross::Api::ScriptContainer::setOption(%1, %2): No such interpreterinfo").arg(name).arg(value.toString()) ); + return false; +} + +Object::Ptr ScriptContainer::execute() +{ + if(! d->script) + if(! initialize()) + return 0; + + if(hadException()) + return 0; + + Object::Ptr r = d->script->execute(); + if(d->script->hadException()) { + setException( d->script->getException() ); + finalize(); + return 0; + } + return r; +} + +const QStringList ScriptContainer::getFunctionNames() +{ + return d->script ? d->script->getFunctionNames() : QStringList(); //FIXME init before if needed? +} + +Object::Ptr ScriptContainer::callFunction(const QString& functionname, List::Ptr arguments) +{ + if(! d->script) + if(! initialize()) + return 0; + + if(hadException()) + return 0; + + if(functionname.isEmpty()) { + setException( new Exception(i18n("No functionname defined for ScriptContainer::callFunction().")) ); + finalize(); + return 0; + } + + Object::Ptr r = d->script->callFunction(functionname, arguments); + if(d->script->hadException()) { + setException( d->script->getException() ); + finalize(); + return 0; + } + return r; +} + +QStringList ScriptContainer::getClassNames() +{ + return d->script ? d->script->getClassNames() : QStringList(); //FIXME init before if needed? +} + +Object::Ptr ScriptContainer::classInstance(const QString& classname) +{ + if(! d->script) + if(! initialize()) + return 0; + + if(hadException()) + return 0; + + Object::Ptr r = d->script->classInstance(classname); + if(d->script->hadException()) { + setException( d->script->getException() ); + finalize(); + return 0; + } + return r; +} + +bool ScriptContainer::initialize() +{ + finalize(); + + if(! d->scriptfile.isNull()) { + krossdebug( QString("Kross::Api::ScriptContainer::initialize() file=%1").arg(d->scriptfile) ); + + if(d->interpretername.isNull()) { + d->interpretername = Manager::scriptManager()->getInterpreternameForFile( d->scriptfile ); + if(d->interpretername.isNull()) { + setException( new Exception(i18n("Failed to determinate interpreter for scriptfile '%1'").arg(d->scriptfile)) ); + return false; + } + } + + QFile f( d->scriptfile ); + if(! f.open(IO_ReadOnly)) { + setException( new Exception(i18n("Failed to open scriptfile '%1'").arg(d->scriptfile)) ); + return false; + } + d->code = QString( f.readAll() ); + f.close(); + } + + Interpreter* interpreter = Manager::scriptManager()->getInterpreter(d->interpretername); + if(! interpreter) { + setException( new Exception(i18n("Unknown interpreter '%1'").arg(d->interpretername)) ); + return false; + } + + d->script = interpreter->createScript(this); + if(! d->script) { + setException( new Exception(i18n("Failed to create script for interpreter '%1'").arg(d->interpretername)) ); + return false; + } + if(d->script->hadException()) { + setException( d->script->getException() ); + finalize(); + return false; + } + setException( 0 ); // clear old exception + + return true; +} + +void ScriptContainer::finalize() +{ + delete d->script; + d->script = 0; +} + + diff --git a/lib/kross/main/scriptcontainer.h b/lib/kross/main/scriptcontainer.h new file mode 100644 index 00000000..a66293a2 --- /dev/null +++ b/lib/kross/main/scriptcontainer.h @@ -0,0 +1,210 @@ +/*************************************************************************** + * scriptcontainer.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_SCRIPTCONTAINER_H +#define KROSS_API_SCRIPTCONTAINER_H + +#include "mainmodule.h" + +#include +#include +#include +#include + +namespace Kross { namespace Api { + + // Forward declarations. + class Object; + class List; + class ScriptContainerPrivate; + + /** + * The ScriptContainer class is something like a single + * standalone scriptfile. + * + * Once you've such a ScriptContainer instance you're + * able to perform actions with it like to execute + * scripting code. The \a Manager takes care of + * handling the ScriptContainer instances application + * width. + * + * The class \a ScriptAction provides a higher level class + * to work with a \a ScriptContainer instances. + */ + class ScriptContainer : public MainModule + { + // We protected the constructor cause ScriptContainer + // instances should be created only within the + // Manager::getScriptContainer() method. + friend class Manager; + + protected: + + /** + * Constructor. + * + * The constructor is protected cause only with the + * \a ScriptManager it's possible to access + * \a ScriptContainer instances. + * + * \param name The unique name this ScriptContainer + * has. It's used e.g. at the \a Manager to + * identify the ScriptContainer. + */ + explicit ScriptContainer(const QString& name = QString::null); + + public: + + /// Shared pointer to implement reference-counting. + typedef KSharedPtr Ptr; + + /** + * Destructor. + */ + virtual ~ScriptContainer(); + + /** + * \return the unique name this ScriptContainer is + * reachable as. + */ + const QString getName() const; + + /** + * Set the name this ScriptContainer is reachable as. + */ + void setName(const QString& name); + + /** + * Return the scriptcode this ScriptContainer holds. + */ + QString getCode() const; + + /** + * Set the scriptcode this ScriptContainer holds. + */ + void setCode(const QString& code); + + /** + * \return the name of the interpreter used + * on \a execute. + */ + QString getInterpreterName() const; + + /** + * Set the name of the interpreter used + * on \a execute. + */ + void setInterpreterName(const QString& interpretername); + + /** + * \return the filename which will be executed + * on \a execute. + */ + QString getFile() const; + + /** + * Set the filename which will be executed + * on \a execute. The \p scriptfile needs to + * be a valid local file or QString::null if + * you don't like to use a file rather then + * the with \a setCode() defined scripting code. + */ + void setFile(const QString& scriptfile); + + /** + * \return a map of options this \a ScriptContainer defines. + * The options are returned call-by-ref, so you are able to + * manipulate them. + */ + QMap& getOptions(); + + /** + * \return the value of the option defined with \p name . + * If there doesn't exists an option with such a name, + * the \p defaultvalue is returned. If \p recursive is + * true then first the \a ScriptContainer options are + * seeked for the matching \p name and if not found + * the \a Manager options are seeked for the \p name and + * if not found either the \p defaultvalue is returned. + */ + QVariant getOption(const QString name, QVariant defaultvalue = QVariant(), bool recursive = false); + + /** + * Set the \a Interpreter::Option value. + */ + bool setOption(const QString name, const QVariant& value); + + /** + * Execute the script container. + */ + Object::Ptr execute(); + + /** + * Return a list of functionnames the with + * \a setCode defined scriptcode spends. + */ + const QStringList getFunctionNames(); + + /** + * Call a function in the script container. + * + * \param functionname The name of the function + * to call. + * \param arguments Optional list of arguments + * passed to the function. + * \return \a Object instance representing + * the functioncall returnvalue. + */ + KSharedPtr callFunction(const QString& functionname, KSharedPtr arguments = 0); + + /** + * Return a list of classes. + */ + QStringList getClassNames(); + + /** + * Create and return a new class instance. + */ + KSharedPtr classInstance(const QString& classname); + + /** + * Initialize the \a Script instance. + * + * Normaly it's not needed to call this function direct cause + * if will be internaly called if needed (e.g. on \a execute ). + */ + bool initialize(); + + /** + * Finalize the \a Script instance and free's any cached or still + * running executions. Normaly it's not needed to call this + * function direct cause the \a ScriptContainer will take care + * of calling it if needed. + */ + void finalize(); + + private: + /// Internaly used private d-pointer. + ScriptContainerPrivate* d; + }; + +}} + +#endif + diff --git a/lib/kross/main/scriptguiclient.cpp b/lib/kross/main/scriptguiclient.cpp new file mode 100644 index 00000000..28a89015 --- /dev/null +++ b/lib/kross/main/scriptguiclient.cpp @@ -0,0 +1,384 @@ +/*************************************************************************** + * scriptguiclient.cpp + * This file is part of the KDE project + * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "scriptguiclient.h" +#include "manager.h" +#include "../api/interpreter.h" +#include "wdgscriptsmanager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace Kross::Api; + +namespace Kross { namespace Api { + + /// @internal + class ScriptGUIClientPrivate + { + public: + /** + * The \a KXMLGUIClient that is parent of the \a ScriptGUIClient + * instance. + */ + KXMLGUIClient* guiclient; + + /** + * The optional parent QWidget widget. + */ + QWidget* parent; + + /** + * Map of \a ScriptActionCollection instances the \a ScriptGUIClient + * is attached to. + */ + QMap collections; + }; + +}} + +ScriptGUIClient::ScriptGUIClient(KXMLGUIClient* guiclient, QWidget* parent) + : QObject( parent ) + , KXMLGUIClient( guiclient ) + , d( new ScriptGUIClientPrivate() ) // initialize d-pointer class +{ + krossdebug( QString("ScriptGUIClient::ScriptGUIClient() Ctor") ); + + d->guiclient = guiclient; + d->parent = parent; + + setInstance( ScriptGUIClient::instance() ); + + // action to execute a scriptfile. + new KAction(i18n("Execute Script File..."), 0, 0, this, SLOT(executeScriptFile()), actionCollection(), "executescriptfile"); + + // acion to show the ScriptManagerGUI dialog. + new KAction(i18n("Scripts Manager..."), 0, 0, this, SLOT(showScriptManager()), actionCollection(), "configurescripts"); + + // The predefined ScriptActionCollection's this ScriptGUIClient provides. + d->collections.replace("installedscripts", + new ScriptActionCollection(i18n("Scripts"), actionCollection(), "installedscripts") ); + d->collections.replace("loadedscripts", + new ScriptActionCollection(i18n("Loaded"), actionCollection(), "loadedscripts") ); + d->collections.replace("executedscripts", + new ScriptActionCollection(i18n("History"), actionCollection(), "executedscripts") ); + + reloadInstalledScripts(); +} + +ScriptGUIClient::~ScriptGUIClient() +{ + krossdebug( QString("ScriptGUIClient::~ScriptGUIClient() Dtor") ); + for(QMap::Iterator it = d->collections.begin(); it != d->collections.end(); ++it) + delete it.data(); + delete d; +} + +bool ScriptGUIClient::hasActionCollection(const QString& name) +{ + return d->collections.contains(name); +} + +ScriptActionCollection* ScriptGUIClient::getActionCollection(const QString& name) +{ + return d->collections[name]; +} + +QMap ScriptGUIClient::getActionCollections() +{ + return d->collections; +} + +void ScriptGUIClient::addActionCollection(const QString& name, ScriptActionCollection* collection) +{ + removeActionCollection(name); + d->collections.replace(name, collection); +} + +bool ScriptGUIClient::removeActionCollection(const QString& name) +{ + if(d->collections.contains(name)) { + ScriptActionCollection* c = d->collections[name]; + d->collections.remove(name); + delete c; + return true; + } + return false; +} + +void ScriptGUIClient::reloadInstalledScripts() +{ + ScriptActionCollection* installedcollection = d->collections["installedscripts"]; + if(installedcollection) + installedcollection->clear(); + + QCString partname = d->guiclient->instance()->instanceName(); + QStringList files = KGlobal::dirs()->findAllResources("data", partname + "/scripts/*/*.rc"); + //files.sort(); + for(QStringList::iterator it = files.begin(); it != files.end(); ++it) + loadScriptConfigFile(*it); +} + +bool ScriptGUIClient::installScriptPackage(const QString& scriptpackagefile) +{ + krossdebug( QString("Install script package: %1").arg(scriptpackagefile) ); + KTar archive( scriptpackagefile ); + if(! archive.open(IO_ReadOnly)) { + KMessageBox::sorry(0, i18n("Could not read the package \"%1\".").arg(scriptpackagefile)); + return false; + } + + QCString partname = d->guiclient->instance()->instanceName(); + QString destination = KGlobal::dirs()->saveLocation("data", partname + "/scripts/", true); + //QString destination = KGlobal::dirs()->saveLocation("appdata", "scripts", true); + if(destination.isNull()) { + krosswarning("ScriptGUIClient::installScriptPackage() Failed to determinate location where the scriptpackage should be installed to!"); + return false; + } + + QString packagename = QFileInfo(scriptpackagefile).baseName(); + destination += packagename; // add the packagename to the name of the destination-directory. + + if( QDir(destination).exists() ) { + if( KMessageBox::warningContinueCancel(0, + i18n("A script package with the name \"%1\" already exists. Replace this package?" ).arg(packagename), + i18n("Replace")) != KMessageBox::Continue ) + return false; + + if(! KIO::NetAccess::del(destination, 0) ) { + KMessageBox::sorry(0, i18n("Could not uninstall this script package. You may not have sufficient permissions to delete the folder \"%1\".").arg(destination)); + return false; + } + } + + krossdebug( QString("Copy script-package to destination directory: %1").arg(destination) ); + const KArchiveDirectory* archivedir = archive.directory(); + archivedir->copyTo(destination, true); + + reloadInstalledScripts(); + return true; +} + +bool ScriptGUIClient::uninstallScriptPackage(const QString& scriptpackagepath) +{ + if(! KIO::NetAccess::del(scriptpackagepath, 0) ) { + KMessageBox::sorry(0, i18n("Could not uninstall this script package. You may not have sufficient permissions to delete the folder \"%1\".").arg(scriptpackagepath)); + return false; + } + reloadInstalledScripts(); + return true; +} + +bool ScriptGUIClient::loadScriptConfigFile(const QString& scriptconfigfile) +{ + krossdebug( QString("ScriptGUIClient::loadScriptConfig file=%1").arg(scriptconfigfile) ); + + QDomDocument domdoc; + QFile file(scriptconfigfile); + if(! file.open(IO_ReadOnly)) { + krosswarning( QString("ScriptGUIClient::loadScriptConfig(): Failed to read scriptconfigfile: %1").arg(scriptconfigfile) ); + return false; + } + bool ok = domdoc.setContent(&file); + file.close(); + if(! ok) { + krosswarning( QString("ScriptGUIClient::loadScriptConfig(): Failed to parse scriptconfigfile: %1").arg(scriptconfigfile) ); + return false; + } + + return loadScriptConfigDocument(scriptconfigfile, domdoc); +} + +bool ScriptGUIClient::loadScriptConfigDocument(const QString& scriptconfigfile, const QDomDocument &document) +{ + ScriptActionCollection* installedcollection = d->collections["installedscripts"]; + QDomNodeList nodelist = document.elementsByTagName("ScriptAction"); + uint nodelistcount = nodelist.count(); + for(uint i = 0; i < nodelistcount; i++) { + ScriptAction::Ptr action = new ScriptAction(scriptconfigfile, nodelist.item(i).toElement()); + + if(installedcollection) { + ScriptAction::Ptr otheraction = installedcollection->action( action->name() ); + if(otheraction) { + // There exists already an action with the same name. Use the versionnumber + // to see if one of them is newer and if that's the case display only + // the newer aka those with the highest version. + if(action->version() < otheraction->version() && action->version() >= 0) { + // Just don't do anything with the above created action. The + // shared pointer will take care of freeing the instance. + continue; + } + else if(action->version() > otheraction->version() && otheraction->version() >= 0) { + // The previously added scriptaction isn't up-to-date any + // longer. Remove it from the list of installed scripts. + otheraction->finalize(); + installedcollection->detach(otheraction); + //otheraction->detachAll() //FIXME: why it crashes with detachAll() ? + } + else { + // else just print a warning and fall through (so, install the action + // and don't care any longer of the duplicated name)... + krosswarning( QString("Kross::Api::ScriptGUIClient::loadScriptConfigDocument: There exists already a scriptaction with name \"%1\". Added anyway...").arg(action->name()) ); + } + } + installedcollection->attach( action ); + } + + connect(action.data(), SIGNAL( failed(const QString&, const QString&) ), + this, SLOT( executionFailed(const QString&, const QString&) )); + connect(action.data(), SIGNAL( success() ), + this, SLOT( successfullyExecuted() )); + connect(action.data(), SIGNAL( activated(const Kross::Api::ScriptAction*) ), SIGNAL( executionStarted(const Kross::Api::ScriptAction*))); + } + emit collectionChanged(installedcollection); + return true; +} + +void ScriptGUIClient::setXMLFile(const QString& file, bool merge, bool setXMLDoc) +{ + KXMLGUIClient::setXMLFile(file, merge, setXMLDoc); +} + +void ScriptGUIClient::setDOMDocument(const QDomDocument &document, bool merge) +{ + ScriptActionCollection* installedcollection = d->collections["installedscripts"]; + if(! merge && installedcollection) + installedcollection->clear(); + + KXMLGUIClient::setDOMDocument(document, merge); + loadScriptConfigDocument(xmlFile(), document); +} + +void ScriptGUIClient::successfullyExecuted() +{ + const ScriptAction* action = dynamic_cast< const ScriptAction* >( QObject::sender() ); + if(action) { + emit executionFinished(action); + ScriptActionCollection* executedcollection = d->collections["executedscripts"]; + if(executedcollection) { + ScriptAction* actionptr = const_cast< ScriptAction* >( action ); + executedcollection->detach(actionptr); + executedcollection->attach(actionptr); + emit collectionChanged(executedcollection); + } + } +} + +void ScriptGUIClient::executionFailed(const QString& errormessage, const QString& tracedetails) +{ + const ScriptAction* action = dynamic_cast< const ScriptAction* >( QObject::sender() ); + if(action) + emit executionFinished(action); + if(tracedetails.isEmpty()) + KMessageBox::error(0, errormessage); + else + KMessageBox::detailedError(0, errormessage, tracedetails); +} + +KURL ScriptGUIClient::openScriptFile(const QString& caption) +{ + QStringList mimetypes; + QMap infos = Manager::scriptManager()->getInterpreterInfos(); + for(QMap::Iterator it = infos.begin(); it != infos.end(); ++it) + mimetypes.append( it.data()->getMimeTypes().join(" ").stripWhiteSpace() ); + + KFileDialog* filedialog = new KFileDialog( + QString::null, // startdir + mimetypes.join(" "), // filter + 0, // parent widget + "ScriptGUIClientFileDialog", // name + true // modal + ); + if(! caption.isNull()) + filedialog->setCaption(caption); + if( filedialog->exec() ) + return filedialog->selectedURL(); + return KURL(); +} + +bool ScriptGUIClient::loadScriptFile() +{ + KURL url = openScriptFile( i18n("Load Script File") ); + if(url.isValid()) { + ScriptActionCollection* loadedcollection = d->collections["loadedscripts"]; + if(loadedcollection) { + ScriptAction::Ptr action = new ScriptAction( url.path() ); + connect(action.data(), SIGNAL( failed(const QString&, const QString&) ), + this, SLOT( executionFailed(const QString&, const QString&) )); + connect(action.data(), SIGNAL( success() ), + this, SLOT( successfullyExecuted() )); + connect(action.data(), SIGNAL( activated(const Kross::Api::ScriptAction*) ), SIGNAL( executionStarted(const Kross::Api::ScriptAction*))); + + loadedcollection->detach(action); + loadedcollection->attach(action); + return true; + } + } + return false; +} + +bool ScriptGUIClient::executeScriptFile() +{ + KURL url = openScriptFile( i18n("Execute Script File") ); + if(url.isValid()) + return executeScriptFile( url.path() ); + return false; +} + +bool ScriptGUIClient::executeScriptFile(const QString& file) +{ + krossdebug( QString("Kross::Api::ScriptGUIClient::executeScriptFile() file='%1'").arg(file) ); + + ScriptAction::Ptr action = new ScriptAction(file); + return executeScriptAction(action); +} + +bool ScriptGUIClient::executeScriptAction(ScriptAction::Ptr action) +{ + connect(action.data(), SIGNAL( failed(const QString&, const QString&) ), + this, SLOT( executionFailed(const QString&, const QString&) )); + connect(action.data(), SIGNAL( success() ), + this, SLOT( successfullyExecuted() )); + connect(action.data(), SIGNAL( activated(const Kross::Api::ScriptAction*) ), SIGNAL( executionStarted(const Kross::Api::ScriptAction*))); + action->activate(); + bool ok = action->hadException(); + action->finalize(); // execution is done. + return ok; +} + +void ScriptGUIClient::showScriptManager() +{ + KDialogBase* dialog = new KDialogBase(d->parent, "", true, i18n("Scripts Manager"), KDialogBase::Close); + WdgScriptsManager* wsm = new WdgScriptsManager(this, dialog); + dialog->setMainWidget(wsm); + dialog->resize( QSize(360, 320).expandedTo(dialog->minimumSizeHint()) ); + dialog->show(); +} + +#include "scriptguiclient.moc" diff --git a/lib/kross/main/scriptguiclient.h b/lib/kross/main/scriptguiclient.h new file mode 100644 index 00000000..955b55d9 --- /dev/null +++ b/lib/kross/main/scriptguiclient.h @@ -0,0 +1,217 @@ +/*************************************************************************** + * scriptguiclient.h + * This file is part of the KDE project + * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_API_SCRIPTGUICLIENT_H +#define KROSS_API_SCRIPTGUICLIENT_H + +#include "scriptcontainer.h" +#include "scriptaction.h" + +#include +#include +#include +#include + +class QWdiget; + +namespace Kross { namespace Api { + + // Forward declarations. + class ScriptAction; + class ScriptGUIClientPrivate; + + /** + * The ScriptGUIClient class provides abstract access to + * scripting code used to extend an applications functionality. + */ + class KDE_EXPORT ScriptGUIClient + : public QObject + , public KXMLGUIClient + { + Q_OBJECT + //Q_PROPERTY(QString configfile READ getConfigFile WRITE setConfigFile) + + public: + + /// List of KAction instances. + typedef QPtrList List; + + /** + * Constructor. + * + * \param guiclient The KXMLGUIClient this \a ScriptGUIClient + * is a child of. + * \param parent The parent QWidget. If defined Qt will handle + * freeing this \a ScriptGUIClient instance else the + * caller has to take care of freeing this instance + * if not needed any longer. + */ + explicit ScriptGUIClient(KXMLGUIClient* guiclient, QWidget* parent = 0); + + /** + * Destructor. + */ + virtual ~ScriptGUIClient(); + + /** + * \return true if this \a ScriptGUIClient has a \a ScriptActionCollection + * with the name \p name else false is returned. + */ + bool hasActionCollection(const QString& name); + + /** + * \return the \a ScriptActionCollection which has the name \p name + * or NULL if there exists no such \a ScriptActionCollection . + */ + ScriptActionCollection* getActionCollection(const QString& name); + + /** + * \return a map of all avaiable \a ScriptActionCollection instances + * this \a ScriptGUIClient knows about. + * Per default there are 2 collections avaiable; + * 1. "installedscripts" The installed collection of scripts. + * 2. "loadedscripts" The loaded scripts. + */ + QMap getActionCollections(); + + /** + * Add a new \a ScriptActionCollection with the name \p name to + * our map of actioncollections. + */ + void addActionCollection(const QString& name, ScriptActionCollection* collection); + + /** + * Remove the \a ScriptActionCollection defined with name \p name. + */ + bool removeActionCollection(const QString& name); + + /** + * Reload the list of installed scripts. + */ + void reloadInstalledScripts(); + + /** + * Install the packagefile \p scriptpackagefile . Those + * packagefile should be a tar.gz-archive which will be + * extracted and to the users script-directory. + */ + bool installScriptPackage(const QString& scriptpackagefile); + + /** + * Uninstall the scriptpackage located in the path + * \p scriptpackagepath . This just deletes the whole + * directory. + */ + bool uninstallScriptPackage(const QString& scriptpackagepath); + + /** + * Load the scriptpackage's configurationfile + * \p scriptconfigfile and add the defined \a ScriptAction + * instances to the list of installed scripts. + */ + bool loadScriptConfigFile(const QString& scriptconfigfile); + + /** + * Load the \p document DOM-document from the scriptpackage's + * XML-configfile \p scriptconfigfile and add the defined + * \a ScriptAction instances to the list of installed scripts. + */ + bool loadScriptConfigDocument(const QString& scriptconfigfile, const QDomDocument &document); + + /// KXMLGUIClient overloaded method to set the XML file. + virtual void setXMLFile(const QString& file, bool merge = false, bool setXMLDoc = true); + /// KXMLGUIClient overloaded method to set the XML DOM-document. + virtual void setDOMDocument(const QDomDocument &document, bool merge = false); + + public slots: + + /** + * A KFileDialog will be displayed to let the user choose + * a scriptfile. The choosen file will be returned as KURL. + */ + KURL openScriptFile(const QString& caption = QString::null); + + /** + * A KFileDialog will be displayed to let the user choose + * a scriptfile that should be loaded. + * Those loaded \a ScriptAction will be added to the + * \a ScriptActionCollection of loaded scripts. + */ + bool loadScriptFile(); + + /** + * A KFileDialog will be displayed to let the user choose + * the scriptfile that should be executed. + * The executed \a ScriptAction will be added to the + * \a ScriptActionCollection of executed scripts. + */ + bool executeScriptFile(); + + /** + * Execute the scriptfile \p file . Internaly we try to use + * the defined filename to auto-detect the \a Interpreter which + * should be used for the execution. + */ + bool executeScriptFile(const QString& file); + + /** + * This method executes the \a ScriptAction \p action . + * Internaly we just call \a ScriptAction::activate and + * redirect the success/failed signals to our internal slots. + */ + bool executeScriptAction(ScriptAction::Ptr action); + + /** + * The \a ScriptManagerGUI dialog will be displayed to + * let the user manage the scriptfiles. + */ + void showScriptManager(); + + private slots: + + /** + * Called if execution of this \a ScriptAction failed and + * displays an errormessage-dialog. + */ + void executionFailed(const QString& errormessage, const QString& tracedetails); + + /** + * Called if execution of this \a ScriptAction was + * successfully. The \a ScriptAction will be added + * to the history-collection of successfully executed + * \a ScriptAction instances. + */ + void successfullyExecuted(); + + signals: + /// Emitted if a \a ScriptActionCollection instances changed. + void collectionChanged(ScriptActionCollection*); + /// This signal is emited when the execution of a script is started + void executionStarted(const Kross::Api::ScriptAction* ); + /// This signal is emited when the execution of a script is finished + void executionFinished(const Kross::Api::ScriptAction* ); + private: + /// Internaly used private d-pointer. + ScriptGUIClientPrivate* d; + }; + +}} + +#endif + diff --git a/lib/kross/main/wdgscriptsmanager.cpp b/lib/kross/main/wdgscriptsmanager.cpp new file mode 100644 index 00000000..10924519 --- /dev/null +++ b/lib/kross/main/wdgscriptsmanager.cpp @@ -0,0 +1,354 @@ +/* + * This file is part of the KDE project + * + * Copyright (c) 2005 Cyrille Berger + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "wdgscriptsmanager.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if KDE_IS_VERSION(3, 4, 0) + // The KNewStuffSecure we use internaly for the GetHotNewStuff-functionality + // was introduced with KDE 3.4. + #define KROSS_SUPPORT_NEWSTUFF +#endif + +#ifdef KROSS_SUPPORT_NEWSTUFF + #include + #include + #include + #include +#endif + +#include "scriptguiclient.h" +#include "scriptaction.h" + +namespace Kross { namespace Api { + +#ifdef KROSS_SUPPORT_NEWSTUFF +class ScriptNewStuff : public KNewStuffSecure +{ + public: + ScriptNewStuff(ScriptGUIClient* scripguiclient, const QString& type, QWidget *parentWidget = 0) + : KNewStuffSecure(type, parentWidget) + , m_scripguiclient(scripguiclient) {} + virtual ~ScriptNewStuff() {} + private: + ScriptGUIClient* m_scripguiclient; + virtual void installResource() { m_scripguiclient->installScriptPackage( m_tarName ); } +}; +#endif + +class ListItem : public QListViewItem +{ + private: + ScriptActionCollection* m_collection; + ScriptAction::Ptr m_action; + public: + ListItem(QListView* parentview, ScriptActionCollection* collection) + : QListViewItem(parentview), m_collection(collection), m_action(0) {} + + ListItem(ListItem* parentitem, QListViewItem* afteritem, ScriptAction::Ptr action) + : QListViewItem(parentitem, afteritem), m_collection( parentitem->collection() ), m_action(action) {} + + ScriptAction::Ptr action() const { return m_action; } + ScriptActionCollection* collection() const { return m_collection; } + //ScriptActionMenu* actionMenu() const { return m_menu; } +}; + +class ToolTip : public QToolTip +{ + public: + ToolTip(KListView* parent) : QToolTip(parent->viewport()), m_parent(parent) {} + virtual ~ToolTip () { remove(m_parent->viewport()); } + protected: + virtual void maybeTip(const QPoint& p) { + ListItem* item = dynamic_cast( m_parent->itemAt(p) ); + if(item) { + QRect r( m_parent->itemRect(item) ); + if(r.isValid() && item->action()) { + tip(r, QString("%1").arg(item->action()->toolTip())); + } + } + } + private: + KListView* m_parent; +}; + +class WdgScriptsManagerPrivate +{ + friend class WdgScriptsManager; + ScriptGUIClient* m_scripguiclient; + ToolTip* m_tooltip; +#ifdef KROSS_SUPPORT_NEWSTUFF + ScriptNewStuff* newstuff; +#endif + //enum { LoadBtn = 0, UnloadBtn, InstallBtn, UninstallBtn, ExecBtn, NewStuffBtn }; +}; + +WdgScriptsManager::WdgScriptsManager(ScriptGUIClient* scr, QWidget* parent, const char* name, WFlags fl ) + : WdgScriptsManagerBase(parent, name, fl) + , d(new WdgScriptsManagerPrivate) +{ + d->m_scripguiclient = scr; + d->m_tooltip = new ToolTip(scriptsList); +#ifdef KROSS_SUPPORT_NEWSTUFF + d->newstuff = 0; +#endif + + scriptsList->header()->hide(); + //scriptsList->header()->setClickEnabled(false); + scriptsList->setAllColumnsShowFocus(true); + //scriptsList->setRootIsDecorated(true); + scriptsList->setSorting(-1); + scriptsList->addColumn("text"); + //scriptsList->setColumnWidthMode(1, QListView::Manual); + + slotFillScriptsList(); + + slotSelectionChanged(0); + connect(scriptsList, SIGNAL(selectionChanged(QListViewItem*)), this, SLOT(slotSelectionChanged(QListViewItem*))); + + btnExec->setIconSet(KGlobal::instance()->iconLoader()->loadIconSet( "exec", KIcon::MainToolbar, 16 )); + connect(btnExec, SIGNAL(clicked()), this, SLOT(slotExecuteScript())); + btnLoad->setIconSet(KGlobal::instance()->iconLoader()->loadIconSet( "fileopen", KIcon::MainToolbar, 16 )); + connect(btnLoad, SIGNAL(clicked()), this, SLOT(slotLoadScript())); + btnUnload->setIconSet(KGlobal::instance()->iconLoader()->loadIconSet( "fileclose", KIcon::MainToolbar, 16 )); + connect(btnUnload, SIGNAL(clicked()), this, SLOT(slotUnloadScript())); + btnInstall->setIconSet(KGlobal::instance()->iconLoader()->loadIconSet( "fileimport", KIcon::MainToolbar, 16 )); + connect(btnInstall, SIGNAL(clicked()), this, SLOT(slotInstallScript())); + btnUninstall->setIconSet(KGlobal::instance()->iconLoader()->loadIconSet( "fileclose", KIcon::MainToolbar, 16 )); + connect(btnUninstall, SIGNAL(clicked()), this, SLOT(slotUninstallScript())); +#ifdef KROSS_SUPPORT_NEWSTUFF + btnNewStuff->setIconSet(KGlobal::instance()->iconLoader()->loadIconSet( "knewstuff", KIcon::MainToolbar, 16 )); + connect(btnNewStuff, SIGNAL(clicked()), this, SLOT(slotGetNewScript())); +#endif +/* + toolBar->setIconText( KToolBar::IconTextRight ); + + toolBar->insertButton("exec", WdgScriptsManagerPrivate::ExecBtn, false, i18n("Execute")); + toolBar->addConnection(WdgScriptsManagerPrivate::ExecBtn, SIGNAL(clicked()), this, SLOT(slotExecuteScript())); + toolBar->insertLineSeparator(); + toolBar->insertButton("fileopen", WdgScriptsManagerPrivate::LoadBtn, true, i18n("Load")); + toolBar->addConnection(WdgScriptsManagerPrivate::LoadBtn, SIGNAL(clicked()), this, SLOT(slotLoadScript())); + toolBar->insertButton("fileclose", WdgScriptsManagerPrivate::UnloadBtn, false, i18n("Unload")); + toolBar->addConnection(WdgScriptsManagerPrivate::UnloadBtn, SIGNAL(clicked()), this, SLOT(slotUnloadScript())); + toolBar->insertLineSeparator(); + toolBar->insertButton("fileimport", WdgScriptsManagerPrivate::InstallBtn, true, i18n("Install")); + toolBar->addConnection(WdgScriptsManagerPrivate::InstallBtn, SIGNAL(clicked()), this, SLOT(slotInstallScript())); + toolBar->insertButton("fileclose", WdgScriptsManagerPrivate::UninstallBtn, false, i18n("Uninstall")); + toolBar->addConnection(WdgScriptsManagerPrivate::UninstallBtn, SIGNAL(clicked()), this, SLOT(slotUninstallScript())); +#ifdef KROSS_SUPPORT_NEWSTUFF + toolBar->insertLineSeparator(); + toolBar->insertButton("knewstuff", WdgScriptsManagerPrivate::NewStuffBtn, true, i18n("Get More Scripts")); + toolBar->addConnection(WdgScriptsManagerPrivate::NewStuffBtn, SIGNAL(clicked()), this, SLOT(slotGetNewScript())); +#endif +*/ + connect(scr, SIGNAL( collectionChanged(ScriptActionCollection*) ), + this, SLOT( slotFillScriptsList() )); +} + +WdgScriptsManager::~WdgScriptsManager() +{ + delete d->m_tooltip; + delete d; +} + +void WdgScriptsManager::slotFillScriptsList() +{ + scriptsList->clear(); + + addItem( d->m_scripguiclient->getActionCollection("executedscripts") ); + addItem( d->m_scripguiclient->getActionCollection("loadedscripts") ); + addItem( d->m_scripguiclient->getActionCollection("installedscripts") ); +} + +void WdgScriptsManager::addItem(ScriptActionCollection* collection) +{ + if(! collection) + return; + + ListItem* i = new ListItem(scriptsList, collection); + i->setText(0, collection->actionMenu()->text()); + i->setOpen(true); + + QValueList list = collection->actions(); + QListViewItem* lastitem = 0; + for(QValueList::Iterator it = list.begin(); it != list.end(); ++it) + lastitem = addItem(*it, i, lastitem); +} + +QListViewItem* WdgScriptsManager::addItem(ScriptAction::Ptr action, QListViewItem* parentitem, QListViewItem* afteritem) +{ + if(! action) + return 0; + + ListItem* i = new ListItem(dynamic_cast(parentitem), afteritem, action); + i->setText(0, action->text()); // FIXME: i18nise it for ko2.0 + //i->setText(1, action->getDescription()); // FIXME: i18nise it for ko2.0 + //i->setText(2, action->name()); + + QPixmap pm; + if(action->hasIcon()) { + KIconLoader* icons = KGlobal::iconLoader(); + pm = icons->loadIconSet(action->icon(), KIcon::Small).pixmap(QIconSet::Small, QIconSet::Active); + } + else { + pm = action->iconSet(KIcon::Small, 16).pixmap(QIconSet::Small, QIconSet::Active); + } + if(! pm.isNull()) + i->setPixmap(0, pm); // display the icon + + return i; +} + +void WdgScriptsManager::slotSelectionChanged(QListViewItem* item) +{ + ListItem* i = dynamic_cast(item); + Kross::Api::ScriptActionCollection* installedcollection = d->m_scripguiclient->getActionCollection("installedscripts"); + + //toolBar->setItemEnabled(WdgScriptsManagerPrivate::ExecBtn, i && i->action()); + //toolBar->setItemEnabled(WdgScriptsManagerPrivate::UninstallBtn, i && i->action() && i->collection() == installedcollection); + //toolBar->setItemEnabled(WdgScriptsManagerPrivate::UnloadBtn, i && i->action() && i->collection() != installedcollection); + btnExec->setEnabled(i && i->action()); + btnUnload->setEnabled(i && i->action() && i->collection() != installedcollection); + btnUninstall->setEnabled(i && i->action() && i->collection() == installedcollection); +} + +void WdgScriptsManager::slotLoadScript() +{ + if(d->m_scripguiclient->loadScriptFile()) + slotFillScriptsList(); +} + +void WdgScriptsManager::slotInstallScript() +{ + KFileDialog* filedialog = new KFileDialog( + QString::null, // startdir + "*.tar.gz *.tgz *.bz2", // filter + this, // parent widget + "WdgScriptsManagerInstallFileDialog", // name + true // modal + ); + filedialog->setCaption( i18n("Install Script Package") ); + + if(! filedialog->exec()) + return; + + if(! d->m_scripguiclient->installScriptPackage( filedialog->selectedURL().path() )) { + krosswarning("Failed to install scriptpackage"); + return; + } + + slotFillScriptsList(); +} + +void WdgScriptsManager::slotUninstallScript() +{ + ListItem* item = dynamic_cast( scriptsList->currentItem() ); + if( !item || !item->action() ) + return; + + Kross::Api::ScriptActionCollection* installedcollection = d->m_scripguiclient->getActionCollection("installedscripts"); + if( !item->collection() || item->collection() != installedcollection) + return; + + const QString packagepath = item->action()->getPackagePath(); + if( !packagepath) + return; + + if( KMessageBox::warningContinueCancel(0, + i18n("Uninstall the script package \"%1\" and delete the package's folder \"%2\"?") + .arg(item->action()->text()).arg(packagepath), + i18n("Uninstall")) != KMessageBox::Continue ) + { + return; + } + + if(! d->m_scripguiclient->uninstallScriptPackage(packagepath)) { + krosswarning("Failed to uninstall scriptpackage"); + return; + } + + slotFillScriptsList(); +} + +void WdgScriptsManager::slotExecuteScript() +{ + ListItem* item = dynamic_cast( scriptsList->currentItem() ); + if(item && item->action()) + item->action()->activate(); +} + +void WdgScriptsManager::slotUnloadScript() +{ + ListItem* item = dynamic_cast( scriptsList->currentItem() ); + if(item && item->action()) { + item->collection()->detach( item->action() ); + slotFillScriptsList(); + } +} + +void WdgScriptsManager::slotGetNewScript() +{ +#ifdef KROSS_SUPPORT_NEWSTUFF + const QString appname = KApplication::kApplication()->name(); + const QString type = QString("%1/script").arg(appname); + + if(! d->newstuff) { + d->newstuff = new ScriptNewStuff(d->m_scripguiclient, type); + connect(d->newstuff, SIGNAL(installFinished()), this, SLOT(slotResourceInstalled())); + } + + KNS::Engine *engine = new KNS::Engine(d->newstuff, type, this); + KNS::DownloadDialog *d = new KNS::DownloadDialog( engine, this ); + d->setType(type); + + KNS::ProviderLoader *p = new KNS::ProviderLoader(this); + QObject::connect(p, SIGNAL(providersLoaded(Provider::List*)), + d, SLOT(slotProviders(Provider::List*))); + + p->load(type, QString("http://download.kde.org/khotnewstuff/%1scripts-providers.xml").arg(appname)); + d->exec(); +#endif +} + +void WdgScriptsManager::slotResourceInstalled() +{ + // Delete KNewStuff's configuration entries. These entries reflect what has + // already been installed. As we cannot yet keep them in sync after uninstalling + // scripts, we deactivate the check marks entirely. + KGlobal::config()->deleteGroup("KNewStuffStatus"); +} + +}} + +#include "wdgscriptsmanager.moc" diff --git a/lib/kross/main/wdgscriptsmanager.h b/lib/kross/main/wdgscriptsmanager.h new file mode 100644 index 00000000..031d5b3c --- /dev/null +++ b/lib/kross/main/wdgscriptsmanager.h @@ -0,0 +1,60 @@ +/* + * This file is part of the KDE project + * + * Copyright (c) 2005-2006 Cyrille Berger + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef WDGSCRIPTSMANAGER_H +#define WDGSCRIPTSMANAGER_H + +#include "main/scriptaction.h" +#include "main/wdgscriptsmanagerbase.h" + +class Scripting; + +namespace Kross { namespace Api { + +class ScriptGUIClient; +class WdgScriptsManagerPrivate; + +/** +@author Cyrille Berger +*/ +class WdgScriptsManager : public WdgScriptsManagerBase +{ + Q_OBJECT + public: + WdgScriptsManager(ScriptGUIClient* scr, QWidget* parent = 0, const char* name = 0, WFlags fl = 0); + ~WdgScriptsManager(); + public slots: + void slotLoadScript(); + void slotInstallScript(); + void slotUninstallScript(); + void slotExecuteScript(); + void slotUnloadScript(); + void slotGetNewScript(); + void slotSelectionChanged(QListViewItem*); + private slots: + void slotFillScriptsList(); + void slotResourceInstalled(); + private: + WdgScriptsManagerPrivate* d; + void addItem(ScriptActionCollection* collection); + QListViewItem* addItem(ScriptAction::Ptr, QListViewItem* parentitem, QListViewItem* afteritem); +}; + +}} + +#endif diff --git a/lib/kross/main/wdgscriptsmanagerbase.ui b/lib/kross/main/wdgscriptsmanagerbase.ui new file mode 100644 index 00000000..18ab2b23 --- /dev/null +++ b/lib/kross/main/wdgscriptsmanagerbase.ui @@ -0,0 +1,247 @@ + +WdgScriptsManagerBase + + + WdgScriptsManagerBase + + + + 0 + 0 + 181 + 467 + + + + + 1 + 1 + 0 + 0 + + + + + 0 + 0 + + + + Scripts Manager + + + + unnamed + + + 0 + + + + scriptsList + + + + 2 + 2 + 0 + 0 + + + + + + toolBar + + + + + layout1 + + + + unnamed + + + + btnExec + + + + 20 + 0 + + + + + + + Execute + + + + + line2 + + + VLine + + + Sunken + + + Vertical + + + + + btnLoad + + + + 20 + 0 + + + + + + + Load + + + + + btnUnload + + + + 20 + 0 + + + + + + + Unload + + + + + line3 + + + VLine + + + Sunken + + + Vertical + + + + + btnInstall + + + + 20 + 0 + + + + + + + Install + + + + + btnUninstall + + + + 20 + 0 + + + + + + + Uninstall + + + + + line4 + + + VLine + + + Sunken + + + Vertical + + + + + btnNewStuff + + + + 20 + 0 + + + + + + + Get More Scripts + + + + + + + + + KToolBar +
ktoolbar.h
+ + 20 + 100 + + 0 + + 5 + 5 + 0 + 0 + + image0 +
+
+ + + 789c534e494dcbcc4b554829cdcdad8c2fcf4c29c95030e0524611cd48cd4ccf28010a1797249664262b2467241641a592324b8aa363156c15aab914146aadb90067111b1f + + + + + klistview.h + ktoolbar.h + kpushbutton.h + kpushbutton.h + kpushbutton.h + kpushbutton.h + kpushbutton.h + kpushbutton.h + +
diff --git a/lib/kross/python/Makefile.am b/lib/kross/python/Makefile.am new file mode 100644 index 00000000..5eff2abe --- /dev/null +++ b/lib/kross/python/Makefile.am @@ -0,0 +1,33 @@ +include $(top_srcdir)/lib/kross/Makefile.global + +kde_module_LTLIBRARIES = krosspython.la + +krosspython_la_LDFLAGS = $(KDE_PLUGIN) $(all_libraries) $(LIBPYTHON) $(PYTHONLIB) -module $(VER_INFO) + +noinst_HEADERS = \ + pythonextension.h \ + pythonmodule.h \ + pythonobject.h \ + pythonscript.h \ + pythonsecurity.h \ + pythoninterpreter.h \ + pythonconfig.h + +krosspython_la_SOURCES = \ + pythonextension.cpp \ + pythonmodule.cpp \ + pythonobject.cpp \ + pythonscript.cpp \ + pythonsecurity.cpp \ + pythoninterpreter.cpp + +krosspython_la_LIBADD = \ + $(LIB_QT) \ + $(LIB_KDECORE) \ + cxx/libkrosspythoncxx.la \ + ../api/libkrossapi.la \ + ../main/libkrossmain.la + +INCLUDES = $(KROSS_INCLUDES) $(PYTHONINC) $(all_includes) +METASOURCES = AUTO +SUBDIRS = cxx scripts . diff --git a/lib/kross/python/cxx/Config.hxx b/lib/kross/python/cxx/Config.hxx new file mode 100644 index 00000000..65bbc2d3 --- /dev/null +++ b/lib/kross/python/cxx/Config.hxx @@ -0,0 +1,74 @@ +#ifndef __PyCXX_config_hh__ +#define __PyCXX_config_hh__ + +// +// Microsoft VC++ 6.0 has no traits +// +#if defined( _MSC_VER ) + +# define STANDARD_LIBRARY_HAS_ITERATOR_TRAITS 1 + +#elif defined( __GNUC__ ) +# if __GNUC__ >= 3 +# define STANDARD_LIBRARY_HAS_ITERATOR_TRAITS 1 +# else +# define STANDARD_LIBRARY_HAS_ITERATOR_TRAITS 0 +#endif + +// +// Assume all other compilers do +// +#else + +// Macros to deal with deficiencies in compilers +# define STANDARD_LIBRARY_HAS_ITERATOR_TRAITS 1 +#endif + +#if STANDARD_LIBRARY_HAS_ITERATOR_TRAITS +# define random_access_iterator_parent(itemtype) std::iterator +#else +# define random_access_iterator_parent(itemtype) std::random_access_iterator +#endif + +// +// Which C++ standard is in use? +// +#if defined( _MSC_VER ) +# if _MSC_VER <= 1200 +// MSVC++ 6.0 +# define PYCXX_ISO_CPP_LIB 0 +# define STR_STREAM +# define TEMPLATE_TYPENAME class +# else +# define PYCXX_ISO_CPP_LIB 1 +# define STR_STREAM +# define TEMPLATE_TYPENAME typename +# endif +#elif defined( __GNUC__ ) +# if __GNUC__ >= 3 +# define PYCXX_ISO_CPP_LIB 1 +# define STR_STREAM +# define TEMPLATE_TYPENAME typename +# else +# define PYCXX_ISO_CPP_LIB 0 +# define STR_STREAM +# define TEMPLATE_TYPENAME class +# endif +#endif + +#if PYCXX_ISO_CPP_LIB +# define STR_STREAM +# define OSTRSTREAM ostringstream +# define EXPLICIT_TYPENAME typename +# define EXPLICIT_CLASS class +# define TEMPLATE_TYPENAME typename +#else +# define STR_STREAM +# define OSTRSTREAM ostrstream +# define EXPLICIT_TYPENAME +# define EXPLICIT_CLASS +# define TEMPLATE_TYPENAME class +#endif + + +#endif // __PyCXX_config_hh__ diff --git a/lib/kross/python/cxx/Exception.hxx b/lib/kross/python/cxx/Exception.hxx new file mode 100644 index 00000000..afcff489 --- /dev/null +++ b/lib/kross/python/cxx/Exception.hxx @@ -0,0 +1,212 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 1998 The Regents of the University of California. +// All rights reserved. See LEGAL.LLNL for full text and disclaimer. +//---------------------------------------------------------------------------// + +#ifndef __CXX_Exception_h +#define __CXX_Exception_h + +#include "Python.h" +#include "Config.hxx" +#include "IndirectPythonInterface.hxx" + +#include +#include + +// This mimics the Python structure, in order to minimize confusion +namespace Py + { + class ExtensionExceptionType; + + class Exception + { + public: + Exception( ExtensionExceptionType &exception, const std::string& reason ); + + explicit Exception () + {} + + Exception (const std::string& reason) + { + PyErr_SetString (Py::_Exc_RuntimeError(), reason.c_str()); + } + + Exception (PyObject* exception, const std::string& reason) + { + PyErr_SetString (exception, reason.c_str()); + } + + + void clear() // clear the error + // technically but not philosophically const + { + PyErr_Clear(); + } + }; + + + // Abstract + class StandardError: public Exception + { + protected: + explicit StandardError() + {} + }; + + class LookupError: public StandardError + { + protected: + explicit LookupError() + {} + }; + + class ArithmeticError: public StandardError + { + protected: + explicit ArithmeticError() + {} + }; + + class EnvironmentError: public StandardError + { + protected: + explicit EnvironmentError() + {} + }; + + // Concrete + + class TypeError: public StandardError + { + public: + TypeError (const std::string& reason) + : StandardError() + { + PyErr_SetString (Py::_Exc_TypeError(),reason.c_str()); + } + }; + + class IndexError: public LookupError + { + public: + IndexError (const std::string& reason) + : LookupError() + { + PyErr_SetString (Py::_Exc_IndexError(), reason.c_str()); + } + }; + + class AttributeError: public StandardError + { + public: + AttributeError (const std::string& reason) + : StandardError() + { + PyErr_SetString (Py::_Exc_AttributeError(), reason.c_str()); + } + }; + + class NameError: public StandardError + { + public: + NameError (const std::string& reason) + : StandardError() + { + PyErr_SetString (Py::_Exc_NameError(), reason.c_str()); + } + }; + + class RuntimeError: public StandardError + { + public: + RuntimeError (const std::string& reason) + : StandardError() + { + PyErr_SetString (Py::_Exc_RuntimeError(), reason.c_str()); + } + }; + + class SystemError: public StandardError + { + public: + SystemError (const std::string& reason) + : StandardError() + { + PyErr_SetString (Py::_Exc_SystemError(),reason.c_str()); + } + }; + + class KeyError: public LookupError + { + public: + KeyError (const std::string& reason) + : LookupError() + { + PyErr_SetString (Py::_Exc_KeyError(),reason.c_str()); + } + }; + + + class ValueError: public StandardError + { + public: + ValueError (const std::string& reason) + : StandardError() + { + PyErr_SetString (Py::_Exc_ValueError(), reason.c_str()); + } + }; + + class OverflowError: public ArithmeticError + { + public: + OverflowError (const std::string& reason) + : ArithmeticError() + { + PyErr_SetString (Py::_Exc_OverflowError(), reason.c_str()); + } + }; + + class ZeroDivisionError: public ArithmeticError + { + public: + ZeroDivisionError (const std::string& reason) + : ArithmeticError() + { + PyErr_SetString (Py::_Exc_ZeroDivisionError(), reason.c_str()); + } + }; + + class FloatingPointError: public ArithmeticError + { + public: + FloatingPointError (const std::string& reason) + : ArithmeticError() + { + PyErr_SetString (Py::_Exc_FloatingPointError(), reason.c_str()); + } + }; + + class MemoryError: public StandardError + { + public: + MemoryError (const std::string& reason) + : StandardError() + { + PyErr_SetString (Py::_Exc_MemoryError(), reason.c_str()); + } + }; + + class SystemExit: public StandardError + { + public: + SystemExit (const std::string& reason) + : StandardError() + { + PyErr_SetString (Py::_Exc_SystemExit(),reason.c_str()); + } + }; + + }// Py + +#endif diff --git a/lib/kross/python/cxx/Extensions.hxx b/lib/kross/python/cxx/Extensions.hxx new file mode 100644 index 00000000..69ce9a14 --- /dev/null +++ b/lib/kross/python/cxx/Extensions.hxx @@ -0,0 +1,756 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 1998 The Regents of the University of California. +// All rights reserved. See LEGAL.LLNL for full text and disclaimer. +//---------------------------------------------------------------------------// + +#ifndef __CXX_Extensions__h +#define __CXX_Extensions__h + + +#ifdef _MSC_VER +// disable warning C4786: symbol greater than 255 character, +// okay to ignore +#pragma warning(disable: 4786) +#endif + + +#include "Config.hxx" +#include "Objects.hxx" + +extern "C" + { + extern PyObject py_object_initializer; + } + +#include +#include + +namespace Py + { + class ExtensionModuleBase; + + // Make an Exception Type for use in raising custom exceptions + class ExtensionExceptionType : public Object + { + public: + ExtensionExceptionType(); + virtual ~ExtensionExceptionType(); + + // call init to create the type + void init( ExtensionModuleBase &module, const std::string& name ); + }; + + + class MethodTable + { + public: + MethodTable(); + virtual ~MethodTable(); + + void add(const char* method_name, PyCFunction f, const char* doc="", int flag=1); + PyMethodDef* table(); + + protected: + std::vector t; // accumulator of PyMethodDef's + PyMethodDef *mt; // Actual method table produced when full + + static PyMethodDef method (const char* method_name, PyCFunction f, int flags = 1, const char* doc=""); + + private: + // + // prevent the compiler generating these unwanted functions + // + MethodTable(const MethodTable& m); //unimplemented + void operator=(const MethodTable& m); //unimplemented + + }; // end class MethodTable + + extern "C" + { + typedef PyObject *(*method_varargs_call_handler_t)( PyObject *_self, PyObject *_args ); + typedef PyObject *(*method_keyword_call_handler_t)( PyObject *_self, PyObject *_args, PyObject *_dict ); + } + + template + class MethodDefExt : public PyMethodDef + { + public: + typedef Object (T::*method_varargs_function_t)( const Tuple &args ); + typedef Object (T::*method_keyword_function_t)( const Tuple &args, const Dict &kws ); + + MethodDefExt + ( + const char *_name, + method_varargs_function_t _function, + method_varargs_call_handler_t _handler, + const char *_doc + ) + { + ext_meth_def.ml_name = const_cast(_name); + ext_meth_def.ml_meth = _handler; + ext_meth_def.ml_flags = METH_VARARGS; + ext_meth_def.ml_doc = const_cast(_doc); + + ext_varargs_function = _function; + ext_keyword_function = NULL; + } + + MethodDefExt + ( + const char *_name, + method_keyword_function_t _function, + method_keyword_call_handler_t _handler, + const char *_doc + ) + { + ext_meth_def.ml_name = const_cast(_name); + ext_meth_def.ml_meth = method_varargs_call_handler_t( _handler ); + ext_meth_def.ml_flags = METH_VARARGS|METH_KEYWORDS; + ext_meth_def.ml_doc = const_cast(_doc); + + ext_varargs_function = NULL; + ext_keyword_function = _function; + } + + ~MethodDefExt() + {} + + PyMethodDef ext_meth_def; + method_varargs_function_t ext_varargs_function; + method_keyword_function_t ext_keyword_function; + }; + + class ExtensionModuleBase + { + public: + ExtensionModuleBase( const char *name ); + virtual ~ExtensionModuleBase(); + + Module module(void) const; // only valid after initialize() has been called + Dict moduleDictionary(void) const; // only valid after initialize() has been called + + virtual Object invoke_method_keyword( const std::string &_name, const Tuple &_args, const Dict &_keywords ) = 0; + virtual Object invoke_method_varargs( const std::string &_name, const Tuple &_args ) = 0; + + const std::string &name() const; + const std::string &fullName() const; + + protected: + // Initialize the module + void initialize( const char *module_doc ); + + const std::string module_name; + const std::string full_module_name; + MethodTable method_table; + + private: + + // + // prevent the compiler generating these unwanted functions + // + ExtensionModuleBase( const ExtensionModuleBase & ); //unimplemented + void operator=( const ExtensionModuleBase & ); //unimplemented + + }; + + extern "C" PyObject *method_keyword_call_handler( PyObject *_self_and_name_tuple, PyObject *_args, PyObject *_keywords ); + extern "C" PyObject *method_varargs_call_handler( PyObject *_self_and_name_tuple, PyObject *_args ); + extern "C" void do_not_dealloc( void * ); + + + template + class ExtensionModule : public ExtensionModuleBase + { + public: + ExtensionModule( const char *name ) + : ExtensionModuleBase( name ) + {} + virtual ~ExtensionModule() + {} + + protected: + typedef Object (T::*method_varargs_function_t)( const Tuple &args ); + typedef Object (T::*method_keyword_function_t)( const Tuple &args, const Dict &kws ); + typedef std::map *> method_map_t; + + static void add_varargs_method( const char *name, method_varargs_function_t function, const char *doc="" ) + { + method_map_t &mm = methods(); + + MethodDefExt *method_definition = new MethodDefExt + ( + name, + function, + method_varargs_call_handler, + doc + ); + + mm[std::string( name )] = method_definition; + } + + static void add_keyword_method( const char *name, method_keyword_function_t function, const char *doc="" ) + { + method_map_t &mm = methods(); + + MethodDefExt *method_definition = new MethodDefExt + ( + name, + function, + method_keyword_call_handler, + doc + ); + + mm[std::string( name )] = method_definition; + } + + void initialize( const char *module_doc="" ) + { + ExtensionModuleBase::initialize( module_doc ); + Dict dict( moduleDictionary() ); + + // + // put each of the methods into the modules dictionary + // so that we get called back at the function in T. + // + method_map_t &mm = methods(); + EXPLICIT_TYPENAME method_map_t::iterator i; + + for( i=mm.begin(); i != mm.end(); ++i ) + { + MethodDefExt *method_definition = (*i).second; + + static PyObject *self = PyCObject_FromVoidPtr( this, do_not_dealloc ); + + Tuple args( 2 ); + args[0] = Object( self ); + args[1] = String( (*i).first ); + + PyObject *func = PyCFunction_New + ( + &method_definition->ext_meth_def, + new_reference_to( args ) + ); + + dict[ (*i).first ] = Object( func ); + } + } + + protected: // Tom Malcolmson reports that derived classes need access to these + + static method_map_t &methods(void) + { + static method_map_t *map_of_methods = NULL; + if( map_of_methods == NULL ) + map_of_methods = new method_map_t; + + return *map_of_methods; + } + + + // this invoke function must be called from within a try catch block + virtual Object invoke_method_keyword( const std::string &name, const Tuple &args, const Dict &keywords ) + { + method_map_t &mm = methods(); + MethodDefExt *meth_def = mm[ name ]; + if( meth_def == NULL ) + { + std::string error_msg( "CXX - cannot invoke keyword method named " ); + error_msg += name; + throw RuntimeError( error_msg ); + } + + // cast up to the derived class + T *self = static_cast(this); + + return (self->*meth_def->ext_keyword_function)( args, keywords ); + } + + // this invoke function must be called from within a try catch block + virtual Object invoke_method_varargs( const std::string &name, const Tuple &args ) + { + method_map_t &mm = methods(); + MethodDefExt *meth_def = mm[ name ]; + if( meth_def == NULL ) + { + std::string error_msg( "CXX - cannot invoke varargs method named " ); + error_msg += name; + throw RuntimeError( error_msg ); + } + + // cast up to the derived class + T *self = static_cast(this); + + return (self->*meth_def->ext_varargs_function)( args ); + } + + private: + // + // prevent the compiler generating these unwanted functions + // + ExtensionModule( const ExtensionModule & ); //unimplemented + void operator=( const ExtensionModule & ); //unimplemented + }; + + + class PythonType + { + public: + // if you define one sequence method you must define + // all of them except the assigns + + PythonType (size_t base_size, int itemsize, const char *default_name ); + virtual ~PythonType (); + + const char *getName () const; + const char *getDoc () const; + + PyTypeObject* type_object () const; + void name (const char* nam); + void doc (const char* d); + void dealloc(void (*f)(PyObject*)); + + void supportPrint(void); + void supportGetattr(void); + void supportSetattr(void); + void supportGetattro(void); + void supportSetattro(void); + void supportCompare(void); + void supportRepr(void); + void supportStr(void); + void supportHash(void); + void supportCall(void); + + void supportSequenceType(void); + void supportMappingType(void); + void supportNumberType(void); + void supportBufferType(void); + + protected: + PyTypeObject *table; + PySequenceMethods *sequence_table; + PyMappingMethods *mapping_table; + PyNumberMethods *number_table; + PyBufferProcs *buffer_table; + + void init_sequence(); + void init_mapping(); + void init_number(); + void init_buffer(); + + private: + // + // prevent the compiler generating these unwanted functions + // + PythonType (const PythonType& tb); // unimplemented + void operator=(const PythonType& t); // unimplemented + + }; // end of PythonType + + + + // Class PythonExtension is what you inherit from to create + // a new Python extension type. You give your class itself + // as the template paramter. + + // There are two ways that extension objects can get destroyed. + // 1. Their reference count goes to zero + // 2. Someone does an explicit delete on a pointer. + // In (1) the problem is to get the destructor called + // We register a special deallocator in the Python type object + // (see behaviors()) to do this. + // In (2) there is no problem, the dtor gets called. + + // PythonExtension does not use the usual Python heap allocator, + // instead using new/delete. We do the setting of the type object + // and reference count, usually done by PyObject_New, in the + // base class ctor. + + // This special deallocator does a delete on the pointer. + + + class PythonExtensionBase : public PyObject + { + public: + PythonExtensionBase(); + virtual ~PythonExtensionBase(); + + public: + virtual int print( FILE *, int ); + virtual Object getattr( const char * ) = 0; + virtual int setattr( const char *, const Object & ); + virtual Object getattro( const Object & ); + virtual int setattro( const Object &, const Object & ); + virtual int compare( const Object & ); + virtual Object repr(); + virtual Object str(); + virtual long hash(); + virtual Object call( const Object &, const Object & ); + + // Sequence methods + virtual int sequence_length(); + virtual Object sequence_concat( const Object & ); + virtual Object sequence_repeat( int ); + virtual Object sequence_item( int ); + virtual Object sequence_slice( int, int ); + virtual int sequence_ass_item( int, const Object & ); + virtual int sequence_ass_slice( int, int, const Object & ); + + // Mapping + virtual int mapping_length(); + virtual Object mapping_subscript( const Object & ); + virtual int mapping_ass_subscript( const Object &, const Object & ); + + // Number + virtual int number_nonzero(); + virtual Object number_negative(); + virtual Object number_positive(); + virtual Object number_absolute(); + virtual Object number_invert(); + virtual Object number_int(); + virtual Object number_float(); + virtual Object number_long(); + virtual Object number_oct(); + virtual Object number_hex(); + virtual Object number_add( const Object & ); + virtual Object number_subtract( const Object & ); + virtual Object number_multiply( const Object & ); + virtual Object number_divide( const Object & ); + virtual Object number_remainder( const Object & ); + virtual Object number_divmod( const Object & ); + virtual Object number_lshift( const Object & ); + virtual Object number_rshift( const Object & ); + virtual Object number_and( const Object & ); + virtual Object number_xor( const Object & ); + virtual Object number_or( const Object & ); + virtual Object number_power( const Object &, const Object & ); + + // Buffer + virtual int buffer_getreadbuffer( int, void** ); + virtual int buffer_getwritebuffer( int, void** ); + virtual int buffer_getsegcount( int* ); + + private: + void missing_method( void ); + static PyObject *method_call_handler( PyObject *self, PyObject *args ); + }; + + template + class PythonExtension: public PythonExtensionBase + { + public: + static PyTypeObject* type_object() + { + return behaviors().type_object(); + } + + static int check( PyObject *p ) + { + // is p like me? + return p->ob_type == type_object(); + } + + static int check( const Object& ob ) + { + return check( ob.ptr()); + } + + + // + // every object needs getattr implemented + // to support methods + // + virtual Object getattr( const char *name ) + { + return getattr_methods( name ); + } + + protected: + explicit PythonExtension() + : PythonExtensionBase() + { + #ifdef PyObject_INIT + (void)PyObject_INIT( this, type_object() ); + #else + ob_refcnt = 1; + ob_type = type_object(); + #endif + + // every object must support getattr + behaviors().supportGetattr(); + } + + virtual ~PythonExtension() + {} + + static PythonType &behaviors() + { + static PythonType* p; + if( p == NULL ) + { +#if defined( _CPPRTTI ) + const char *default_name = (typeid ( T )).name(); +#else + const char *default_name = "unknown"; +#endif + p = new PythonType( sizeof( T ), 0, default_name ); + p->dealloc( extension_object_deallocator ); + } + + return *p; + } + + + typedef Object (T::*method_varargs_function_t)( const Tuple &args ); + typedef Object (T::*method_keyword_function_t)( const Tuple &args, const Dict &kws ); + typedef std::map *> method_map_t; + + // support the default attributes, __name__, __doc__ and methods + virtual Object getattr_default( const char *_name ) + { + std::string name( _name ); + + if( name == "__name__" && type_object()->tp_name != NULL ) + { + return Py::String( type_object()->tp_name ); + } + else if( name == "__doc__" && type_object()->tp_doc != NULL ) + { + return Py::String( type_object()->tp_doc ); + } + +// trying to fake out being a class for help() +// else if( name == "__bases__" ) +// { +// return Py::Tuple(0); +// } +// else if( name == "__module__" ) +// { +// return Py::Nothing(); +// } +// else if( name == "__dict__" ) +// { +// return Py::Dict(); +// } + else + { + return getattr_methods( _name ); + } + } + + // turn a name into function object + virtual Object getattr_methods( const char *_name ) + { + std::string name( _name ); + + method_map_t &mm = methods(); + + if( name == "__methods__" ) + { + List methods; + + for( EXPLICIT_TYPENAME method_map_t::iterator i = mm.begin(); i != mm.end(); ++i ) + methods.append( String( (*i).first ) ); + + return methods; + } + + // see if name exists + if( mm.find( name ) == mm.end() ) + throw AttributeError( "method '" + name + "' does not exist." ); + + Tuple self( 2 ); + + self[0] = Object( this ); + self[1] = String( name ); + + MethodDefExt *method_definition = mm[ name ]; + + PyObject *func = PyCFunction_New( &method_definition->ext_meth_def, self.ptr() ); + + return Object(func, true); + } + + static void add_varargs_method( const char *name, method_varargs_function_t function, const char *doc="" ) + { + method_map_t &mm = methods(); + + MethodDefExt *method_definition = new MethodDefExt + ( + name, + function, + method_varargs_call_handler, + doc + ); + + mm[std::string( name )] = method_definition; + } + + static void add_keyword_method( const char *name, method_keyword_function_t function, const char *doc="" ) + { + method_map_t &mm = methods(); + + MethodDefExt *method_definition = new MethodDefExt + ( + name, + function, + method_keyword_call_handler, + doc + ); + + mm[std::string( name )] = method_definition; + } + + private: + static method_map_t &methods(void) + { + static method_map_t *map_of_methods = NULL; + if( map_of_methods == NULL ) + map_of_methods = new method_map_t; + + return *map_of_methods; + } + + static PyObject *method_keyword_call_handler( PyObject *_self_and_name_tuple, PyObject *_args, PyObject *_keywords ) + { + try + { + Tuple self_and_name_tuple( _self_and_name_tuple ); + + PyObject *self_in_cobject = self_and_name_tuple[0].ptr(); + T *self = static_cast( self_in_cobject ); + + String name( self_and_name_tuple[1] ); + + method_map_t &mm = methods(); + MethodDefExt *meth_def = mm[ name ]; + if( meth_def == NULL ) + return 0; + + Tuple args( _args ); + + // _keywords may be NULL so be careful about the way the dict is created + Dict keywords; + if( _keywords != NULL ) + keywords = Dict( _keywords ); + + Object result( (self->*meth_def->ext_keyword_function)( args, keywords ) ); + + return new_reference_to( result.ptr() ); + } + catch( Exception & ) + { + return 0; + } + } + + static PyObject *method_varargs_call_handler( PyObject *_self_and_name_tuple, PyObject *_args ) + { + try + { + Tuple self_and_name_tuple( _self_and_name_tuple ); + + PyObject *self_in_cobject = self_and_name_tuple[0].ptr(); + T *self = static_cast( self_in_cobject ); + + String name( self_and_name_tuple[1] ); + + method_map_t &mm = methods(); + MethodDefExt *meth_def = mm[ name ]; + if( meth_def == NULL ) + return 0; + + Tuple args( _args ); + + Object result; + + // TMM: 7Jun'01 - Adding try & catch in case of STL debug-mode exceptions. + #ifdef _STLP_DEBUG + try + { + result = (self->*meth_def->ext_varargs_function)( args ); + } + catch (std::__stl_debug_exception) + { + // throw cxx::RuntimeError( sErrMsg ); + throw cxx::RuntimeError( "Error message not set yet." ); + } + #else + result = (self->*meth_def->ext_varargs_function)( args ); + #endif // _STLP_DEBUG + + return new_reference_to( result.ptr() ); + } + catch( Exception & ) + { + return 0; + } + } + + static void extension_object_deallocator ( PyObject* t ) + { + delete (T *)( t ); + } + + // + // prevent the compiler generating these unwanted functions + // + explicit PythonExtension( const PythonExtension& other ); + void operator=( const PythonExtension& rhs ); + }; + + // + // ExtensionObject is an Object that will accept only T's. + // + template + class ExtensionObject: public Object + { + public: + + explicit ExtensionObject ( PyObject *pyob ) + : Object( pyob ) + { + validate(); + } + + ExtensionObject( const ExtensionObject& other ) + : Object( *other ) + { + validate(); + } + + ExtensionObject( const Object& other ) + : Object( *other ) + { + validate(); + } + + ExtensionObject& operator= ( const Object& rhs ) + { + return (*this = *rhs ); + } + + ExtensionObject& operator= ( PyObject* rhsp ) + { + if( ptr() == rhsp ) + return *this; + set( rhsp ); + return *this; + } + + virtual bool accepts ( PyObject *pyob ) const + { + return ( pyob && T::check( pyob )); + } + + // + // Obtain a pointer to the PythonExtension object + // + T *extensionObject(void) + { + return static_cast( ptr() ); + } + }; + + } // Namespace Py +// End of CXX_Extensions.h +#endif diff --git a/lib/kross/python/cxx/IndirectPythonInterface.cxx b/lib/kross/python/cxx/IndirectPythonInterface.cxx new file mode 100644 index 00000000..caaa0913 --- /dev/null +++ b/lib/kross/python/cxx/IndirectPythonInterface.cxx @@ -0,0 +1,550 @@ +// +// IndirectPythonInterface.cxx +// +#undef _XOPEN_SOURCE +#include "IndirectPythonInterface.hxx" + +namespace Py +{ +bool _Buffer_Check( PyObject *op ) { return (op)->ob_type == _Buffer_Type(); } +bool _CFunction_Check( PyObject *op ) { return (op)->ob_type == _CFunction_Type(); } +bool _Class_Check( PyObject *op ) { return (op)->ob_type == _Class_Type(); } +bool _CObject_Check( PyObject *op ) { return (op)->ob_type == _CObject_Type(); } +bool _Complex_Check( PyObject *op ) { return (op)->ob_type == _Complex_Type(); } +bool _Dict_Check( PyObject *op ) { return (op)->ob_type == _Dict_Type(); } +bool _File_Check( PyObject *op ) { return (op)->ob_type == _File_Type(); } +bool _Float_Check( PyObject *op ) { return (op)->ob_type == _Float_Type(); } +bool _Function_Check( PyObject *op ) { return (op)->ob_type == _Function_Type(); } +bool _Instance_Check( PyObject *op ) { return (op)->ob_type == _Instance_Type(); } +bool _Int_Check( PyObject *op ) { return (op)->ob_type == _Int_Type(); } +bool _List_Check( PyObject *o ) { return o->ob_type == _List_Type(); } +bool _Long_Check( PyObject *op ) { return (op)->ob_type == _Long_Type(); } +bool _Method_Check( PyObject *op ) { return (op)->ob_type == _Method_Type(); } +bool _Module_Check( PyObject *op ) { return (op)->ob_type == _Module_Type(); } +bool _Range_Check( PyObject *op ) { return (op)->ob_type == _Range_Type(); } +bool _Slice_Check( PyObject *op ) { return (op)->ob_type == _Slice_Type(); } +bool _String_Check( PyObject *o ) { return o->ob_type == _String_Type(); } +bool _TraceBack_Check( PyObject *v ) { return (v)->ob_type == _TraceBack_Type(); } +bool _Tuple_Check( PyObject *op ) { return (op)->ob_type == _Tuple_Type(); } +bool _Type_Check( PyObject *op ) { return (op)->ob_type == _Type_Type(); } + +#if PY_MAJOR_VERSION >= 2 +bool _Unicode_Check( PyObject *op ) { return (op)->ob_type == _Unicode_Type(); } +#endif + + + +#if defined(PY_WIN32_DELAYLOAD_PYTHON_DLL) + +#if defined(MS_WINDOWS) +#include + + +static HMODULE python_dll; + +static PyObject *ptr__Exc_ArithmeticError = NULL; +static PyObject *ptr__Exc_AssertionError = NULL; +static PyObject *ptr__Exc_AttributeError = NULL; +static PyObject *ptr__Exc_EnvironmentError = NULL; +static PyObject *ptr__Exc_EOFError = NULL; +static PyObject *ptr__Exc_Exception = NULL; +static PyObject *ptr__Exc_FloatingPointError = NULL; +static PyObject *ptr__Exc_ImportError = NULL; +static PyObject *ptr__Exc_IndexError = NULL; +static PyObject *ptr__Exc_IOError = NULL; +static PyObject *ptr__Exc_KeyboardInterrupt = NULL; +static PyObject *ptr__Exc_KeyError = NULL; +static PyObject *ptr__Exc_LookupError = NULL; +static PyObject *ptr__Exc_MemoryError = NULL; +static PyObject *ptr__Exc_MemoryErrorInst = NULL; +static PyObject *ptr__Exc_NameError = NULL; +static PyObject *ptr__Exc_NotImplementedError = NULL; +static PyObject *ptr__Exc_OSError = NULL; +static PyObject *ptr__Exc_OverflowError = NULL; +static PyObject *ptr__Exc_RuntimeError = NULL; +static PyObject *ptr__Exc_StandardError = NULL; +static PyObject *ptr__Exc_SyntaxError = NULL; +static PyObject *ptr__Exc_SystemError = NULL; +static PyObject *ptr__Exc_SystemExit = NULL; +static PyObject *ptr__Exc_TypeError = NULL; +static PyObject *ptr__Exc_ValueError = NULL; +static PyObject *ptr__Exc_ZeroDivisionError = NULL; + +#ifdef MS_WINDOWS +static PyObject *ptr__Exc_WindowsError = NULL; +#endif + +#if PY_MAJOR_VERSION >= 2 +static PyObject *ptr__Exc_IndentationError = NULL; +static PyObject *ptr__Exc_TabError = NULL; +static PyObject *ptr__Exc_UnboundLocalError = NULL; +static PyObject *ptr__Exc_UnicodeError = NULL; +#endif + +static PyObject *ptr__PyNone = NULL; + +static PyTypeObject *ptr__Buffer_Type = NULL; +static PyTypeObject *ptr__CFunction_Type = NULL; +static PyTypeObject *ptr__Class_Type = NULL; +static PyTypeObject *ptr__CObject_Type = NULL; +static PyTypeObject *ptr__Complex_Type = NULL; +static PyTypeObject *ptr__Dict_Type = NULL; +static PyTypeObject *ptr__File_Type = NULL; +static PyTypeObject *ptr__Float_Type = NULL; +static PyTypeObject *ptr__Function_Type = NULL; +static PyTypeObject *ptr__Instance_Type = NULL; +static PyTypeObject *ptr__Int_Type = NULL; +static PyTypeObject *ptr__List_Type = NULL; +static PyTypeObject *ptr__Long_Type = NULL; +static PyTypeObject *ptr__Method_Type = NULL; +static PyTypeObject *ptr__Module_Type = NULL; +static PyTypeObject *ptr__Range_Type = NULL; +static PyTypeObject *ptr__Slice_Type = NULL; +static PyTypeObject *ptr__String_Type = NULL; +static PyTypeObject *ptr__TraceBack_Type = NULL; +static PyTypeObject *ptr__Tuple_Type = NULL; +static PyTypeObject *ptr__Type_Type = NULL; + +#if PY_MAJOR_VERSION >= 2 +static PyTypeObject *ptr__Unicode_Type = NULL; +#endif + +static int *ptr_Py_DebugFlag = NULL; +static int *ptr_Py_InteractiveFlag = NULL; +static int *ptr_Py_OptimizeFlag = NULL; +static int *ptr_Py_NoSiteFlag = NULL; +static int *ptr_Py_TabcheckFlag = NULL; +static int *ptr_Py_VerboseFlag = NULL; + +#if PY_MAJOR_VERSION >= 2 +static int *ptr_Py_UnicodeFlag = NULL; +#endif + +static char **ptr__Py_PackageContext = NULL; + +#ifdef Py_REF_DEBUG +int *ptr_Py_RefTotal; +#endif + + +//-------------------------------------------------------------------------------- +class GetAddressException + { +public: + GetAddressException( const char *_name ) + : name( _name ) + {} + virtual ~GetAddressException() {} + const char *name; + }; + + +//-------------------------------------------------------------------------------- +static PyObject *GetPyObjectPointer_As_PyObjectPointer( const char *name ) + { + FARPROC addr = GetProcAddress( python_dll, name ); + if( addr == NULL ) + throw GetAddressException( name ); + + return *(PyObject **)addr; + } + +static PyObject *GetPyObject_As_PyObjectPointer( const char *name ) + { + FARPROC addr = GetProcAddress( python_dll, name ); + if( addr == NULL ) + throw GetAddressException( name ); + + return (PyObject *)addr; + } + +static PyTypeObject *GetPyTypeObjectPointer_As_PyTypeObjectPointer( const char *name ) + { + FARPROC addr = GetProcAddress( python_dll, name ); + if( addr == NULL ) + throw GetAddressException( name ); + + return *(PyTypeObject **)addr; + } + +static PyTypeObject *GetPyTypeObject_As_PyTypeObjectPointer( const char *name ) + { + FARPROC addr = GetProcAddress( python_dll, name ); + if( addr == NULL ) + throw GetAddressException( name ); + + return (PyTypeObject *)addr; + } + +static int *GetInt_as_IntPointer( const char *name ) + { + FARPROC addr = GetProcAddress( python_dll, name ); + if( addr == NULL ) + throw GetAddressException( name ); + + return (int *)addr; + } + +static char **GetCharPointer_as_CharPointerPointer( const char *name ) + { + FARPROC addr = GetProcAddress( python_dll, name ); + if( addr == NULL ) + throw GetAddressException( name ); + + return (char **)addr; + } + + +#ifdef _DEBUG +static const char python_dll_name_format[] = "PYTHON%1.1d%1.1d_D.DLL"; +#else +static const char python_dll_name_format[] = "PYTHON%1.1d%1.1d.DLL"; +#endif + +//-------------------------------------------------------------------------------- +bool InitialisePythonIndirectInterface() + { + char python_dll_name[sizeof(python_dll_name_format)]; + + sprintf( python_dll_name, python_dll_name_format, PY_MAJOR_VERSION, PY_MINOR_VERSION ); + + python_dll = LoadLibrary( python_dll_name ); + if( python_dll == NULL ) + return false; + + try + { +#ifdef Py_REF_DEBUG + ptr_Py_RefTotal = GetInt_as_IntPointer( "_Py_RefTotal" ); +#endif + ptr_Py_DebugFlag = GetInt_as_IntPointer( "Py_DebugFlag" ); + ptr_Py_InteractiveFlag = GetInt_as_IntPointer( "Py_InteractiveFlag" ); + ptr_Py_OptimizeFlag = GetInt_as_IntPointer( "Py_OptimizeFlag" ); + ptr_Py_NoSiteFlag = GetInt_as_IntPointer( "Py_NoSiteFlag" ); + ptr_Py_TabcheckFlag = GetInt_as_IntPointer( "Py_TabcheckFlag" ); + ptr_Py_VerboseFlag = GetInt_as_IntPointer( "Py_VerboseFlag" ); +#if PY_MAJOR_VERSION >= 2 + ptr_Py_UnicodeFlag = GetInt_as_IntPointer( "Py_UnicodeFlag" ); +#endif + ptr__Py_PackageContext = GetCharPointer_as_CharPointerPointer( "_Py_PackageContext" ); + + ptr__Exc_ArithmeticError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_ArithmeticError" ); + ptr__Exc_AssertionError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_AssertionError" ); + ptr__Exc_AttributeError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_AttributeError" ); + ptr__Exc_EnvironmentError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_EnvironmentError" ); + ptr__Exc_EOFError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_EOFError" ); + ptr__Exc_Exception = GetPyObjectPointer_As_PyObjectPointer( "PyExc_Exception" ); + ptr__Exc_FloatingPointError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_FloatingPointError" ); + ptr__Exc_ImportError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_ImportError" ); + ptr__Exc_IndexError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_IndexError" ); + ptr__Exc_IOError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_IOError" ); + ptr__Exc_KeyboardInterrupt = GetPyObjectPointer_As_PyObjectPointer( "PyExc_KeyboardInterrupt" ); + ptr__Exc_KeyError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_KeyError" ); + ptr__Exc_LookupError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_LookupError" ); + ptr__Exc_MemoryError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_MemoryError" ); + ptr__Exc_MemoryErrorInst = GetPyObjectPointer_As_PyObjectPointer( "PyExc_MemoryErrorInst" ); + ptr__Exc_NameError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_NameError" ); + ptr__Exc_NotImplementedError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_NotImplementedError" ); + ptr__Exc_OSError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_OSError" ); + ptr__Exc_OverflowError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_OverflowError" ); + ptr__Exc_RuntimeError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_RuntimeError" ); + ptr__Exc_StandardError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_StandardError" ); + ptr__Exc_SyntaxError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_SyntaxError" ); + ptr__Exc_SystemError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_SystemError" ); + ptr__Exc_SystemExit = GetPyObjectPointer_As_PyObjectPointer( "PyExc_SystemExit" ); + ptr__Exc_TypeError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_TypeError" ); + ptr__Exc_ValueError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_ValueError" ); +#ifdef MS_WINDOWS + ptr__Exc_WindowsError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_WindowsError" ); +#endif + ptr__Exc_ZeroDivisionError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_ZeroDivisionError" ); + +#if PY_MAJOR_VERSION >= 2 + ptr__Exc_IndentationError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_IndentationError" ); + ptr__Exc_TabError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_TabError" ); + ptr__Exc_UnboundLocalError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_UnboundLocalError" ); + ptr__Exc_UnicodeError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_UnicodeError" ); +#endif + ptr__PyNone = GetPyObject_As_PyObjectPointer( "_Py_NoneStruct" ); + + ptr__Buffer_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyBuffer_Type" ); + ptr__CFunction_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyCFunction_Type" ); + ptr__Class_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyClass_Type" ); + ptr__CObject_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyCObject_Type" ); + ptr__Complex_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyComplex_Type" ); + ptr__Dict_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyDict_Type" ); + ptr__File_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyFile_Type" ); + ptr__Float_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyFloat_Type" ); + ptr__Function_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyFunction_Type" ); + ptr__Instance_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyInstance_Type" ); + ptr__Int_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyInt_Type" ); + ptr__List_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyList_Type" ); + ptr__Long_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyLong_Type" ); + ptr__Method_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyMethod_Type" ); + ptr__Module_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyModule_Type" ); + ptr__Range_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyRange_Type" ); + ptr__Slice_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PySlice_Type" ); + ptr__String_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyString_Type" ); + ptr__TraceBack_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyTraceBack_Type" ); + ptr__Tuple_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyTuple_Type" ); + ptr__Type_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyType_Type" ); + +#if PY_MAJOR_VERSION >= 2 + ptr__Unicode_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyUnicode_Type" ); +#endif + } + catch( GetAddressException &e ) + { + OutputDebugString( python_dll_name ); + OutputDebugString( " does not contain symbol "); + OutputDebugString( e.name ); + OutputDebugString( "\n" ); + + return false; + } + + return true; + } + +// +// Wrap variables as function calls +// +PyObject * _Exc_ArithmeticError() { return ptr__Exc_ArithmeticError; } +PyObject * _Exc_AssertionError() { return ptr__Exc_AssertionError; } +PyObject * _Exc_AttributeError() { return ptr__Exc_AttributeError; } +PyObject * _Exc_EnvironmentError() { return ptr__Exc_EnvironmentError; } +PyObject * _Exc_EOFError() { return ptr__Exc_EOFError; } +PyObject * _Exc_Exception() { return ptr__Exc_Exception; } +PyObject * _Exc_FloatingPointError() { return ptr__Exc_FloatingPointError; } +PyObject * _Exc_ImportError() { return ptr__Exc_ImportError; } +PyObject * _Exc_IndexError() { return ptr__Exc_IndexError; } +PyObject * _Exc_IOError() { return ptr__Exc_IOError; } +PyObject * _Exc_KeyboardInterrupt() { return ptr__Exc_KeyboardInterrupt; } +PyObject * _Exc_KeyError() { return ptr__Exc_KeyError; } +PyObject * _Exc_LookupError() { return ptr__Exc_LookupError; } +PyObject * _Exc_MemoryError() { return ptr__Exc_MemoryError; } +PyObject * _Exc_MemoryErrorInst() { return ptr__Exc_MemoryErrorInst; } +PyObject * _Exc_NameError() { return ptr__Exc_NameError; } +PyObject * _Exc_NotImplementedError() { return ptr__Exc_NotImplementedError; } +PyObject * _Exc_OSError() { return ptr__Exc_OSError; } +PyObject * _Exc_OverflowError() { return ptr__Exc_OverflowError; } +PyObject * _Exc_RuntimeError() { return ptr__Exc_RuntimeError; } +PyObject * _Exc_StandardError() { return ptr__Exc_StandardError; } +PyObject * _Exc_SyntaxError() { return ptr__Exc_SyntaxError; } +PyObject * _Exc_SystemError() { return ptr__Exc_SystemError; } +PyObject * _Exc_SystemExit() { return ptr__Exc_SystemExit; } +PyObject * _Exc_TypeError() { return ptr__Exc_TypeError; } +PyObject * _Exc_ValueError() { return ptr__Exc_ValueError; } +#ifdef MS_WINDOWS +PyObject * _Exc_WindowsError() { return ptr__Exc_WindowsError; } +#endif +PyObject * _Exc_ZeroDivisionError() { return ptr__Exc_ZeroDivisionError; } + +#if PY_MAJOR_VERSION >= 2 +PyObject * _Exc_IndentationError() { return ptr__Exc_IndentationError; } +PyObject * _Exc_TabError() { return ptr__Exc_TabError; } +PyObject * _Exc_UnboundLocalError() { return ptr__Exc_UnboundLocalError; } +PyObject * _Exc_UnicodeError() { return ptr__Exc_UnicodeError; } +#endif + +// +// wrap items in Object.h +// +PyObject * _None() { return ptr__PyNone; } + + +PyTypeObject * _Buffer_Type() { return ptr__Buffer_Type; } +PyTypeObject * _CFunction_Type() { return ptr__CFunction_Type; } +PyTypeObject * _Class_Type() { return ptr__Class_Type; } +PyTypeObject * _CObject_Type() { return ptr__CObject_Type; } +PyTypeObject * _Complex_Type() { return ptr__Complex_Type; } +PyTypeObject * _Dict_Type() { return ptr__Dict_Type; } +PyTypeObject * _File_Type() { return ptr__File_Type; } +PyTypeObject * _Float_Type() { return ptr__Float_Type; } +PyTypeObject * _Function_Type() { return ptr__Function_Type; } +PyTypeObject * _Instance_Type() { return ptr__Instance_Type; } +PyTypeObject * _Int_Type() { return ptr__Int_Type; } +PyTypeObject * _List_Type() { return ptr__List_Type; } +PyTypeObject * _Long_Type() { return ptr__Long_Type; } +PyTypeObject * _Method_Type() { return ptr__Method_Type; } +PyTypeObject * _Module_Type() { return ptr__Module_Type; } +PyTypeObject * _Range_Type() { return ptr__Range_Type; } +PyTypeObject * _Slice_Type() { return ptr__Slice_Type; } +PyTypeObject * _String_Type() { return ptr__String_Type; } +PyTypeObject * _TraceBack_Type() { return ptr__TraceBack_Type; } +PyTypeObject * _Tuple_Type() { return ptr__Tuple_Type; } +PyTypeObject * _Type_Type() { return ptr__Type_Type; } + +#if PY_MAJOR_VERSION >= 2 +PyTypeObject * _Unicode_Type() { return ptr__Unicode_Type; } +#endif + +char *__Py_PackageContext() { return *ptr__Py_PackageContext; } + + +// +// wrap the Python Flag variables +// +int &_Py_DebugFlag() { return *ptr_Py_DebugFlag; } +int &_Py_InteractiveFlag() { return *ptr_Py_InteractiveFlag; } +int &_Py_OptimizeFlag() { return *ptr_Py_OptimizeFlag; } +int &_Py_NoSiteFlag() { return *ptr_Py_NoSiteFlag; } +int &_Py_TabcheckFlag() { return *ptr_Py_TabcheckFlag; } +int &_Py_VerboseFlag() { return *ptr_Py_VerboseFlag; } +#if PY_MAJOR_VERSION >= 2 +int &_Py_UnicodeFlag() { return *ptr_Py_UnicodeFlag; } +#endif + +void _XINCREF( PyObject *op ) + { + // This function must match the contents of Py_XINCREF(op) + if( op == NULL ) + return; + +#ifdef Py_REF_DEBUG + (*ptr_Py_RefTotal)++; +#endif + (op)->ob_refcnt++; + + } + +void _XDECREF( PyObject *op ) + { + // This function must match the contents of Py_XDECREF(op); + if( op == NULL ) + return; + +#ifdef Py_REF_DEBUG + (*ptr_Py_RefTotal)--; +#endif + + if (--(op)->ob_refcnt == 0) + _Py_Dealloc((PyObject *)(op)); + } + + +#else +#error "Can only delay load under Win32" +#endif + +#else + +// +// Duplicated these declarations from rangeobject.h which is missing the +// extern "C". This has been reported as a bug upto and include 2.1 +// +extern "C" DL_IMPORT(PyTypeObject) PyRange_Type; +extern "C" DL_IMPORT(PyObject *) PyRange_New(long, long, long, int); + + +//================================================================================ +// +// Map onto Macros +// +//================================================================================ + +// +// Wrap variables as function calls +// + +PyObject * _Exc_ArithmeticError() { return ::PyExc_ArithmeticError; } +PyObject * _Exc_AssertionError() { return ::PyExc_AssertionError; } +PyObject * _Exc_AttributeError() { return ::PyExc_AttributeError; } +PyObject * _Exc_EnvironmentError() { return ::PyExc_EnvironmentError; } +PyObject * _Exc_EOFError() { return ::PyExc_EOFError; } +PyObject * _Exc_Exception() { return ::PyExc_Exception; } +PyObject * _Exc_FloatingPointError() { return ::PyExc_FloatingPointError; } +PyObject * _Exc_ImportError() { return ::PyExc_ImportError; } +PyObject * _Exc_IndexError() { return ::PyExc_IndexError; } +PyObject * _Exc_IOError() { return ::PyExc_IOError; } +PyObject * _Exc_KeyboardInterrupt() { return ::PyExc_KeyboardInterrupt; } +PyObject * _Exc_KeyError() { return ::PyExc_KeyError; } +PyObject * _Exc_LookupError() { return ::PyExc_LookupError; } +PyObject * _Exc_MemoryError() { return ::PyExc_MemoryError; } +PyObject * _Exc_MemoryErrorInst() { return ::PyExc_MemoryErrorInst; } +PyObject * _Exc_NameError() { return ::PyExc_NameError; } +PyObject * _Exc_NotImplementedError() { return ::PyExc_NotImplementedError; } +PyObject * _Exc_OSError() { return ::PyExc_OSError; } +PyObject * _Exc_OverflowError() { return ::PyExc_OverflowError; } +PyObject * _Exc_RuntimeError() { return ::PyExc_RuntimeError; } +PyObject * _Exc_StandardError() { return ::PyExc_StandardError; } +PyObject * _Exc_SyntaxError() { return ::PyExc_SyntaxError; } +PyObject * _Exc_SystemError() { return ::PyExc_SystemError; } +PyObject * _Exc_SystemExit() { return ::PyExc_SystemExit; } +PyObject * _Exc_TypeError() { return ::PyExc_TypeError; } +PyObject * _Exc_ValueError() { return ::PyExc_ValueError; } +PyObject * _Exc_ZeroDivisionError() { return ::PyExc_ZeroDivisionError; } + +#ifdef MS_WINDOWS +PyObject * _Exc_WindowsError() { return ::PyExc_WindowsError; } +#endif + + +#if PY_MAJOR_VERSION >= 2 +PyObject * _Exc_IndentationError() { return ::PyExc_IndentationError; } +PyObject * _Exc_TabError() { return ::PyExc_TabError; } +PyObject * _Exc_UnboundLocalError() { return ::PyExc_UnboundLocalError; } +PyObject * _Exc_UnicodeError() { return ::PyExc_UnicodeError; } +#endif + + +// +// wrap items in Object.h +// +PyObject * _None() { return &::_Py_NoneStruct; } + +PyTypeObject * _Buffer_Type() { return &PyBuffer_Type; } +PyTypeObject * _CFunction_Type() { return &PyCFunction_Type; } +PyTypeObject * _Class_Type() { return &PyClass_Type; } +PyTypeObject * _CObject_Type() { return &PyCObject_Type; } +PyTypeObject * _Complex_Type() { return &PyComplex_Type; } +PyTypeObject * _Dict_Type() { return &PyDict_Type; } +PyTypeObject * _File_Type() { return &PyFile_Type; } +PyTypeObject * _Float_Type() { return &PyFloat_Type; } +PyTypeObject * _Function_Type() { return &PyFunction_Type; } +PyTypeObject * _Instance_Type() { return &PyInstance_Type; } +PyTypeObject * _Int_Type() { return &PyInt_Type; } +PyTypeObject * _List_Type() { return &PyList_Type; } +PyTypeObject * _Long_Type() { return &PyLong_Type; } +PyTypeObject * _Method_Type() { return &PyMethod_Type; } +PyTypeObject * _Module_Type() { return &PyModule_Type; } +PyTypeObject * _Range_Type() { return &PyRange_Type; } +PyTypeObject * _Slice_Type() { return &PySlice_Type; } +PyTypeObject * _String_Type() { return &PyString_Type; } +PyTypeObject * _TraceBack_Type() { return &PyTraceBack_Type; } +PyTypeObject * _Tuple_Type() { return &PyTuple_Type; } +PyTypeObject * _Type_Type() { return &PyType_Type; } + +#if PY_MAJOR_VERSION >= 2 +PyTypeObject * _Unicode_Type() { return &PyUnicode_Type; } +#endif + +// +// wrap flags +// +int &_Py_DebugFlag() { return Py_DebugFlag; } +int &_Py_InteractiveFlag() { return Py_InteractiveFlag; } +int &_Py_OptimizeFlag() { return Py_OptimizeFlag; } +int &_Py_NoSiteFlag() { return Py_NoSiteFlag; } +int &_Py_TabcheckFlag() { return Py_TabcheckFlag; } +int &_Py_VerboseFlag() { return Py_VerboseFlag; } +#if PY_MAJOR_VERSION >= 2 +int &_Py_UnicodeFlag() { return Py_UnicodeFlag; } +#endif +char *__Py_PackageContext() { return _Py_PackageContext; } + +// +// Needed to keep the abstactions for delayload interface +// +void _XINCREF( PyObject *op ) + { + Py_XINCREF(op); + } + +void _XDECREF( PyObject *op ) + { + Py_XDECREF(op); + } + +#endif +} diff --git a/lib/kross/python/cxx/IndirectPythonInterface.hxx b/lib/kross/python/cxx/IndirectPythonInterface.hxx new file mode 100644 index 00000000..8f2d275d --- /dev/null +++ b/lib/kross/python/cxx/IndirectPythonInterface.hxx @@ -0,0 +1,156 @@ +#ifndef __CXX_INDIRECT_PYTHON_INTERFACE__HXX__ +#define __CXX_INDIRECT_PYTHON_INTERFACE__HXX__ + +#include + +namespace Py +{ +bool InitialisePythonIndirectInterface(); + +// +// Wrap Exception variables as function calls +// +PyObject * _Exc_Exception(); +PyObject * _Exc_StandardError(); +PyObject * _Exc_ArithmeticError(); +PyObject * _Exc_LookupError(); + +PyObject * _Exc_AssertionError(); +PyObject * _Exc_AttributeError(); +PyObject * _Exc_EOFError(); +PyObject * _Exc_FloatingPointError(); +PyObject * _Exc_EnvironmentError(); +PyObject * _Exc_IOError(); +PyObject * _Exc_OSError(); +PyObject * _Exc_ImportError(); +PyObject * _Exc_IndexError(); +PyObject * _Exc_KeyError(); +PyObject * _Exc_KeyboardInterrupt(); +PyObject * _Exc_MemoryError(); +PyObject * _Exc_NameError(); +PyObject * _Exc_OverflowError(); +PyObject * _Exc_RuntimeError(); +PyObject * _Exc_NotImplementedError(); +PyObject * _Exc_SyntaxError(); +PyObject * _Exc_SystemError(); +PyObject * _Exc_SystemExit(); +PyObject * _Exc_TypeError(); +PyObject * _Exc_ValueError(); +PyObject * _Exc_ZeroDivisionError(); +#ifdef MS_WINDOWS +PyObject * _Exc_WindowsError(); +#endif + +PyObject * _Exc_MemoryErrorInst(); + +#if PY_MAJOR_VERSION >= 2 +PyObject * _Exc_IndentationError(); +PyObject * _Exc_TabError(); +PyObject * _Exc_UnboundLocalError(); +PyObject * _Exc_UnicodeError(); +#endif + +// +// Wrap Object variables as function calls +// +PyObject * _None(); + + +// +// Wrap Type variables as function calls +// +PyTypeObject * _List_Type(); +bool _List_Check( PyObject *o ); + +PyTypeObject * _Buffer_Type(); +bool _Buffer_Check( PyObject *op ); + +PyTypeObject * _Class_Type(); +bool _Class_Check( PyObject *op ); + +PyTypeObject * _Instance_Type(); +bool _Instance_Check( PyObject *op ); + +PyTypeObject * _Method_Type(); +bool _Method_Check( PyObject *op ); + +PyTypeObject * _CObject_Type(); +bool _CObject_Check( PyObject *op ); + +PyTypeObject * _Complex_Type(); +bool _Complex_Check( PyObject *op ); + +PyTypeObject * _Dict_Type(); +bool _Dict_Check( PyObject *op ); + +PyTypeObject * _File_Type(); +bool _File_Check( PyObject *op ); + +PyTypeObject * _Float_Type(); +bool _Float_Check( PyObject *op ); + +PyTypeObject * _Frame_Type(); +bool _Frame_Check( PyObject *op ); + +PyTypeObject * _Function_Type(); +bool _Function_Check( PyObject *op ); + +PyTypeObject * _Int_Type(); +bool _Int_Check( PyObject *op ); + +PyTypeObject * _List_Type(); +bool _List_Check( PyObject *op ); + +PyTypeObject * _Long_Type(); +bool _Long_Check( PyObject *op ); + +PyTypeObject * _CFunction_Type(); +bool _CFunction_Check( PyObject *op ); + +PyTypeObject * _Module_Type(); +bool _Module_Check( PyObject *op ); + +PyTypeObject * _Type_Type(); +bool _Type_Check( PyObject *op ); + +PyTypeObject * _Range_Type(); +bool _Range_Check( PyObject *op ); + +PyTypeObject * _Slice_Type(); +bool _Slice_Check( PyObject *op ); + +PyTypeObject * _String_Type(); +bool _String_Check( PyObject *op ); + +PyTypeObject * _Unicode_Type(); +bool _Unicode_Check( PyObject *op ); + +PyTypeObject * _TraceBack_Type(); +bool _TraceBack_Check( PyObject *v ); + +PyTypeObject * _Tuple_Type(); +bool _Tuple_Check( PyObject *op ); + +#if PY_MAJOR_VERSION >= 2 +PyTypeObject * _Unicode_Type(); +bool _Unicode_Check( PyObject *op ); +#endif + +int &_Py_DebugFlag(); +int &_Py_InteractiveFlag(); +int &_Py_OptimizeFlag(); +int &_Py_NoSiteFlag(); +int &_Py_TabcheckFlag(); +int &_Py_VerboseFlag(); + +#if PY_MAJOR_VERSION >= 2 +int &_Py_UnicodeFlag(); +#endif + +void _XINCREF( PyObject *op ); +void _XDECREF( PyObject *op ); + +char *__Py_PackageContext(); +} + +#endif // __CXX_INDIRECT_PYTHON_INTERFACE__HXX__ diff --git a/lib/kross/python/cxx/Legal.html b/lib/kross/python/cxx/Legal.html new file mode 100755 index 00000000..cf6a530f --- /dev/null +++ b/lib/kross/python/cxx/Legal.html @@ -0,0 +1,40 @@ + + + +Legal Notice + + + + + +

Legal Notice

+ +

*** Legal Notice for all LLNL-contributed files ***

+ +

Copyright (c) 1996. The Regents of the University of California. All rights reserved.

+ +

Permission to use, copy, modify, and distribute this software for any purpose without +fee is hereby granted, provided that this entire notice is included in all copies of any +software which is or includes a copy or modification of this software and in all copies of +the supporting documentation for such software.

+ +

This work was produced at the University of California, Lawrence Livermore National +Laboratory under contract no. W-7405-ENG-48 between the U.S. Department of Energy and The +Regents of the University of California for the operation of UC LLNL.

+ +

DISCLAIMER

+ +

This software was prepared as an account of work sponsored by an agency of the United +States Government. Neither the United States Government nor the University of California +nor any of their employees, makes any warranty, express or implied, or assumes any +liability or responsibility for the accuracy, completeness, or usefulness of any +information, apparatus, product, or process disclosed, or represents that its use would +not infringe privately-owned rights. Reference herein to any specific commercial products, +process, or service by trade name, trademark, manufacturer, or otherwise, does not +necessarily constitute or imply its endorsement, recommendation, or favoring by the United +States Government or the University of California. The views and opinions of authors +expressed herein do not necessarily state or reflect those of the United States Government +or the University of California, and shall not be used for advertising or product +endorsement purposes.

+ + diff --git a/lib/kross/python/cxx/Makefile.am b/lib/kross/python/cxx/Makefile.am new file mode 100644 index 00000000..d1c72c87 --- /dev/null +++ b/lib/kross/python/cxx/Makefile.am @@ -0,0 +1,19 @@ +include $(top_srcdir)/lib/kross/Makefile.global + +noinst_LTLIBRARIES = libkrosspythoncxx.la + +libkrosspythoncxx_la_SOURCES = \ + cxxsupport.cxx \ + cxx_extensions.cxx \ + cxxextensions.c \ + IndirectPythonInterface.cxx + +libkrosspythoncxx_la_LDFLAGS = $(LIBPYTHON) $(all_libraries) -Wnounresolved + +METASOURCES = AUTO +INCLUDES = $(KROSS_INCLUDES) $(PYTHONINC) $(all_includes) +SUBDIRS = . + +clean: + @rm -f *.o 2> /dev/null + @rm -f $(BIN) 2> /dev/null diff --git a/lib/kross/python/cxx/Objects.hxx b/lib/kross/python/cxx/Objects.hxx new file mode 100644 index 00000000..41648320 --- /dev/null +++ b/lib/kross/python/cxx/Objects.hxx @@ -0,0 +1,2804 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 1998 The Regents of the University of California. +// All rights reserved. See LEGAL.LLNL for full text and disclaimer. +//---------------------------------------------------------------------------// + +#ifndef __CXX_Objects__h +#define __CXX_Objects__h + +// Prevent warnings +#if defined(_XOPEN_SOURCE) +#undef _XOPEN_SOURCE +#endif + +#include "Python.h" +#include "Config.hxx" +#include "Exception.hxx" + + +#include +#include STR_STREAM +#include +#include +#include +#include + +namespace Py + { + typedef int sequence_index_type; // type of an index into a sequence + + // Forward declarations + class Object; + class Type; + template class SeqBase; + class String; + class List; + template class MapBase; + + // new_reference_to also overloaded below on Object + inline PyObject* new_reference_to(PyObject* p) + { + Py::_XINCREF(p); + return p; + } + + // returning Null() from an extension method triggers a + // Python exception + inline PyObject* Null() + { + return (static_cast(0)); + } + + //===========================================================================// + // class Object + // The purpose of this class is to serve as the most general kind of + // Python object, for the purpose of writing C++ extensions in Python + // Objects hold a PyObject* which they own. This pointer is always a + // valid pointer to a Python object. In children we must maintain this behavior. + // + // Instructions on how to make your own class MyType descended from Object: + // (0) Pick a base class, either Object or perhaps SeqBase or MapBase. + // This example assumes Object. + + // (1) Write a routine int MyType_Check (PyObject *) modeled after PyInt_Check, + // PyFloat_Check, etc. + + // (2) Add method accepts: + // virtual bool accepts (PyObject *pyob) const { + // return pyob && MyType_Check (pyob); + // } + + // (3) Include the following constructor and copy constructor + // + /* + explicit MyType (PyObject *pyob): Object(pyob) { + validate(); + } + + MyType(const Object& other): Object(other.ptr()) { + validate(); + } + */ + + // Alernate version for the constructor to allow for construction from owned pointers: + /* + explicit MyType (PyObject *pyob): Object(pyob) { + validate(); + } + */ + + // You may wish to add other constructors; see the classes below for examples. + // Each constructor must use "set" to set the pointer + // and end by validating the pointer you have created. + + // (4) Each class needs at least these two assignment operators: + /* + MyType& operator= (const Object& rhs) { + return (*this = *rhs); + } + + Mytype& operator= (PyObject* rhsp) { + if(ptr() == rhsp) return *this; + set(rhsp); + return *this; + } + */ + // Note on accepts: constructors call the base class + // version of a virtual when calling the base class constructor, + // so the test has to be done explicitly in a descendent. + + // If you are inheriting from PythonExtension to define an object + // note that it contains PythonExtension::check + // which you can use in accepts when writing a wrapper class. + // See Demo/range.h and Demo/range.cxx for an example. + + class Object + { + private: + // the pointer to the Python object + // Only Object sets this directly. + // The default constructor for Object sets it to Py_None and + // child classes must use "set" to set it + // + PyObject* p; + + protected: + + void set (PyObject* pyob, bool owned = false) + { + release(); + p = pyob; + if (!owned) + { + Py::_XINCREF (p); + } + validate(); + } + + void release () + { + Py::_XDECREF (p); + p = 0; + } + + void validate() + { + // release pointer if not the right type + if (! accepts (p)) + { + release (); + if(PyErr_Occurred()) + { // Error message already set + throw Exception(); + } + // Better error message if RTTI available +#if defined( _CPPRTTI ) + std::string s("Error creating object of type "); + s += (typeid (*this)).name(); + throw TypeError (s); +#else + throw TypeError ("CXX: type error."); +#endif + } + } + + public: + // Constructor acquires new ownership of pointer unless explicitly told not to. + explicit Object (PyObject* pyob=Py::_None(), bool owned = false): p (pyob) + { + if(!owned) + { + Py::_XINCREF (p); + } + validate(); + } + + // Copy constructor acquires new ownership of pointer + Object (const Object& ob): p(ob.p) + { + Py::_XINCREF (p); + validate(); + } + + // Assignment acquires new ownership of pointer + Object& operator= (const Object& rhs) + { + set(rhs.p); + return *this; + } + + Object& operator= (PyObject* rhsp) + { + if(ptr() == rhsp) return *this; + set (rhsp); + return *this; + } + + // Destructor + virtual ~Object () + { + release (); + } + + // Loaning the pointer to others, retain ownership + PyObject* operator* () const + { + return p; + } + + // Explicit reference_counting changes + void increment_reference_count() + { + Py::_XINCREF(p); + } + + void decrement_reference_count() + { + // not allowed to commit suicide, however + if(reference_count() == 1) + throw RuntimeError("Object::decrement_reference_count error."); + Py::_XDECREF(p); + } + // Would like to call this pointer() but messes up STL in SeqBase + PyObject* ptr () const + { + return p; + } + + // + // Queries + // + + // Can pyob be used in this object's constructor? + virtual bool accepts (PyObject *pyob) const + { + return (pyob != 0); + } + + int reference_count () const + { // the reference count + return p ? p->ob_refcnt : 0; + } + + Type type () const; // the type object associated with this one + + String str () const; // the str() representation + + std::string as_string() const; + + String repr () const; // the repr () representation + + List dir () const; // the dir() list + + bool hasAttr (const std::string& s) const + { + return PyObject_HasAttrString (p, const_cast(s.c_str())) ? true: false; + } + + Object getAttr (const std::string& s) const + { + return Object (PyObject_GetAttrString (p, const_cast(s.c_str())), true); + } + + Object getItem (const Object& key) const + { + return Object (PyObject_GetItem(p, *key), true); + } + + long hashValue () const + { + return PyObject_Hash (p); + } + + // + // int print (FILE* fp, int flags=Py_Print_RAW) + // { + // return PyObject_Print (p, fp, flags); + // } + // + bool is(PyObject *pother) const + { // identity test + return p == pother; + } + + bool is(const Object& other) const + { // identity test + return p == other.p; + } + + bool isCallable () const + { + return PyCallable_Check (p) != 0; + } + + bool isInstance () const + { + return PyInstance_Check (p) != 0; + } + + bool isDict () const + { + return Py::_Dict_Check (p); + } + + bool isList () const + { + return Py::_List_Check (p); + } + + bool isMapping () const + { + return PyMapping_Check (p) != 0; + } + + bool isNumeric () const + { + return PyNumber_Check (p) != 0; + } + + bool isSequence () const + { + return PySequence_Check (p) != 0; + } + + bool isTrue () const + { + return PyObject_IsTrue (p) != 0; + } + + bool isType (const Type& t) const; + + bool isTuple() const + { + return Py::_Tuple_Check(p); + } + + bool isString() const + { + return Py::_String_Check(p) || Py::_Unicode_Check(p); + } + + bool isUnicode() const + { + return Py::_Unicode_Check(p); + } + + // Commands + void setAttr (const std::string& s, const Object& value) + { + if(PyObject_SetAttrString (p, const_cast(s.c_str()), *value) == -1) + throw AttributeError ("getAttr failed."); + } + + void delAttr (const std::string& s) + { + if(PyObject_DelAttrString (p, const_cast(s.c_str())) == -1) + throw AttributeError ("delAttr failed."); + } + + // PyObject_SetItem is too weird to be using from C++ + // so it is intentionally omitted. + + void delItem (const Object& /*key*/) + { + //if(PyObject_DelItem(p, *key) == -1) + // failed to link on Windows? + throw KeyError("delItem failed."); + } + // Equality and comparison use PyObject_Compare + + bool operator==(const Object& o2) const + { + int k = PyObject_Compare (p, *o2); + if (PyErr_Occurred()) throw Exception(); + return k == 0; + } + + bool operator!=(const Object& o2) const + { + int k = PyObject_Compare (p, *o2); + if (PyErr_Occurred()) throw Exception(); + return k != 0; + + } + + bool operator>=(const Object& o2) const + { + int k = PyObject_Compare (p, *o2); + if (PyErr_Occurred()) throw Exception(); + return k >= 0; + } + + bool operator<=(const Object& o2) const + { + int k = PyObject_Compare (p, *o2); + if (PyErr_Occurred()) throw Exception(); + return k <= 0; + } + + bool operator<(const Object& o2) const + { + int k = PyObject_Compare (p, *o2); + if (PyErr_Occurred()) throw Exception(); + return k < 0; + } + + bool operator>(const Object& o2) const + { + int k = PyObject_Compare (p, *o2); + if (PyErr_Occurred()) throw Exception(); + return k > 0; + } + }; + // End of class Object + inline PyObject* new_reference_to(const Object& g) + { + PyObject* p = g.ptr(); + Py::_XINCREF(p); + return p; + } + + // Nothing() is what an extension method returns if + // there is no other return value. + inline Object Nothing() + { + return Object(Py::_None()); + } + + // Python special None value + inline Object None() + { + return Object(Py::_None()); + } + + // TMM: 31May'01 - Added the #ifndef so I can exlude iostreams. +#ifndef CXX_NO_IOSTREAMS + std::ostream& operator<< (std::ostream& os, const Object& ob); +#endif + + // Class Type + class Type: public Object + { + public: + explicit Type (PyObject* pyob, bool owned = false): Object(pyob, owned) + { + validate(); + } + + Type (const Object& ob): Object(*ob) + { + validate(); + } + + Type(const Type& t): Object(t) + { + validate(); + } + + Type& operator= (const Object& rhs) + { + return (*this = *rhs); + } + + Type& operator= (PyObject* rhsp) + { + if(ptr() == rhsp) return *this; + set (rhsp); + return *this; + } + virtual bool accepts (PyObject *pyob) const + { + return pyob && Py::_Type_Check (pyob); + } + }; + + + // + // Convert an owned Python pointer into a CXX Object + // + inline Object asObject (PyObject *p) + { + return Object(p, true); + } + + + + + // =============================================== + // class Int + class Int: public Object + { + public: + // Constructor + explicit Int (PyObject *pyob, bool owned = false): Object (pyob, owned) + { + validate(); + } + + Int (const Int& ob): Object(*ob) + { + validate(); + } + + // create from long + explicit Int (long v = 0L): Object(PyInt_FromLong(v), true) + { + validate(); + } + + // create from int + explicit Int (int v) + { + long w = v; + set(PyInt_FromLong(w), true); + validate(); + } + + Int (const Object& ob) + { + set(PyNumber_Int(*ob), true); + validate(); + } + + // Assignment acquires new ownership of pointer + + Int& operator= (const Object& rhs) + { + return (*this = *rhs); + } + + Int& operator= (PyObject* rhsp) + { + if(ptr() == rhsp) return *this; + set (PyNumber_Int(rhsp), true); + return *this; + } + // Membership + virtual bool accepts (PyObject *pyob) const + { + return pyob && Py::_Int_Check (pyob); + } + // convert to long + operator long() const + { + return PyInt_AsLong (ptr()); + } + // assign from an int + Int& operator= (int v) + { + set (PyInt_FromLong (long(v)), true); + return *this; + } + // assign from long + Int& operator= (long v) + { + set (PyInt_FromLong (v), true); + return *this; + } + }; + + // =============================================== + // class Long + class Long: public Object + { + public: + // Constructor + explicit Long (PyObject *pyob, bool owned = false): Object (pyob, owned) + { + validate(); + } + + Long (const Long& ob): Object(ob.ptr()) + { + validate(); + } + + // create from long + explicit Long (long v = 0L) + : Object(PyLong_FromLong(v), true) + { + validate(); + } + // create from int + explicit Long (int v) + : Object(PyLong_FromLong(static_cast(v)), true) + { + validate(); + } + + // create from unsigned long + explicit Long (unsigned long v) + : Object(PyLong_FromUnsignedLong(v), true) + { + validate(); + } + + // try to create from any object + Long (const Object& ob) + : Object(PyNumber_Long(*ob), true) + { + validate(); + } + + // Assignment acquires new ownership of pointer + + Long& operator= (const Object& rhs) + { + return (*this = *rhs); + } + + Long& operator= (PyObject* rhsp) + { + if(ptr() == rhsp) return *this; + set (PyNumber_Long(rhsp), true); + return *this; + } + // Membership + virtual bool accepts (PyObject *pyob) const + { + return pyob && Py::_Long_Check (pyob); + } + // convert to long + operator long() const + { + return PyLong_AsLong (ptr()); + } + operator double() const + { + return PyLong_AsDouble (ptr()); + } + operator unsigned long() const + { + return PyLong_AsUnsignedLong (ptr()); + } + // assign from an int + Long& operator= (int v) + { + set(PyLong_FromLong (long(v)), true); + return *this; + } + // assign from long + Long& operator= (long v) + { + set(PyLong_FromLong (v), true); + return *this; + } + // assign from unsigned long + Long& operator= (unsigned long v) + { + set(PyLong_FromUnsignedLong (v), true); + return *this; + } + }; + + // =============================================== + // class Float + // + class Float: public Object + { + public: + // Constructor + explicit Float (PyObject *pyob, bool owned = false): Object(pyob, owned) + { + validate(); + } + + Float (const Float& f): Object(f) + { + validate(); + } + + // make from double + explicit Float (double v=0.0) + : Object(PyFloat_FromDouble (v), true) + { + validate(); + } + + // try to make from any object + Float (const Object& ob) + : Object(PyNumber_Float(*ob), true) + { + validate(); + } + + Float& operator= (const Object& rhs) + { + return (*this = *rhs); + } + + Float& operator= (PyObject* rhsp) + { + if(ptr() == rhsp) return *this; + set (PyNumber_Float(rhsp), true); + return *this; + } + // Membership + virtual bool accepts (PyObject *pyob) const + { + return pyob && Py::_Float_Check (pyob); + } + // convert to double + operator double () const + { + return PyFloat_AsDouble (ptr()); + } + // assign from a double + Float& operator= (double v) + { + set(PyFloat_FromDouble (v), true); + return *this; + } + // assign from an int + Float& operator= (int v) + { + set(PyFloat_FromDouble (double(v)), true); + return *this; + } + // assign from long + Float& operator= (long v) + { + set(PyFloat_FromDouble (double(v)), true); + return *this; + } + // assign from an Int + Float& operator= (const Int& iob) + { + set(PyFloat_FromDouble (double(long(iob))), true); + return *this; + } + }; + + // =============================================== + // class Complex + class Complex: public Object + { + public: + // Constructor + explicit Complex (PyObject *pyob, bool owned = false): Object(pyob, owned) + { + validate(); + } + + Complex (const Complex& f): Object(f) + { + validate(); + } + + // make from double + explicit Complex (double v=0.0, double w=0.0) + :Object(PyComplex_FromDoubles (v, w), true) + { + validate(); + } + + Complex& operator= (const Object& rhs) + { + return (*this = *rhs); + } + + Complex& operator= (PyObject* rhsp) + { + if(ptr() == rhsp) return *this; + set (rhsp); + return *this; + } + // Membership + virtual bool accepts (PyObject *pyob) const + { + return pyob && Py::_Complex_Check (pyob); + } + // convert to Py_complex + operator Py_complex () const + { + return PyComplex_AsCComplex (ptr()); + } + // assign from a Py_complex + Complex& operator= (const Py_complex& v) + { + set(PyComplex_FromCComplex (v), true); + return *this; + } + // assign from a double + Complex& operator= (double v) + { + set(PyComplex_FromDoubles (v, 0.0), true); + return *this; + } + // assign from an int + Complex& operator= (int v) + { + set(PyComplex_FromDoubles (double(v), 0.0), true); + return *this; + } + // assign from long + Complex& operator= (long v) + { + set(PyComplex_FromDoubles (double(v), 0.0), true); + return *this; + } + // assign from an Int + Complex& operator= (const Int& iob) + { + set(PyComplex_FromDoubles (double(long(iob)), 0.0), true); + return *this; + } + + double real() const + { + return PyComplex_RealAsDouble(ptr()); + } + + double imag() const + { + return PyComplex_ImagAsDouble(ptr()); + } + }; + // Sequences + // Sequences are here represented as sequences of items of type T. + // The base class SeqBase represents that. + // In basic Python T is always "Object". + + // seqref is what you get if you get elements from a non-const SeqBase. + // Note: seqref could probably be a nested class in SeqBase but that might stress + // some compilers needlessly. Simlarly for mapref later. + + // While this class is not intended for enduser use, it needs some public + // constructors for the benefit of the STL. + + // See Scott Meyer's More Essential C++ for a description of proxies. + // This application is even more complicated. We are doing an unusual thing + // in having a double proxy. If we want the STL to work + // properly we have to compromise by storing the rvalue inside. The + // entire Object API is repeated so that things like s[i].isList() will + // work properly. + + // Still, once in a while a weird compiler message may occur using expressions like x[i] + // Changing them to Object(x[i]) helps the compiler to understand that the + // conversion of a seqref to an Object is wanted. + + template + class seqref + { + protected: + SeqBase& s; // the sequence + int offset; // item number + T the_item; // lvalue + public: + + seqref (SeqBase& seq, sequence_index_type j) + : s(seq), offset(j), the_item (s.getItem(j)) + {} + + seqref (const seqref& range) + : s(range.s), offset(range.offset), the_item(range.the_item) + {} + + // TMM: added this seqref ctor for use with STL algorithms + seqref (Object& obj) + : s(dynamic_cast< SeqBase&>(obj)) + , offset( NULL ) + , the_item(s.getItem(offset)) + {} + ~seqref() + {} + + operator T() const + { // rvalue + return the_item; + } + + seqref& operator=(const seqref& rhs) + { //used as lvalue + the_item = rhs.the_item; + s.setItem(offset, the_item); + return *this; + } + + seqref& operator=(const T& ob) + { // used as lvalue + the_item = ob; + s.setItem(offset, ob); + return *this; + } + + // forward everything else to the item + PyObject* ptr () const + { + return the_item.ptr(); + } + + int reference_count () const + { // the reference count + return the_item.reference_count(); + } + + Type type () const + { + return the_item.type(); + } + + String str () const; + + String repr () const; + + bool hasAttr (const std::string& attr_name) const + { + return the_item.hasAttr(attr_name); + } + + Object getAttr (const std::string& attr_name) const + { + return the_item.getAttr(attr_name); + } + + Object getItem (const Object& key) const + { + return the_item.getItem(key); + } + + long hashValue () const + { + return the_item.hashValue(); + } + + bool isCallable () const + { + return the_item.isCallable(); + } + + bool isInstance () const + { + return the_item.isInstance(); + } + + bool isDict () const + { + return the_item.isDict(); + } + + bool isList () const + { + return the_item.isList(); + } + + bool isMapping () const + { + return the_item.isMapping(); + } + + bool isNumeric () const + { + return the_item.isNumeric(); + } + + bool isSequence () const + { + return the_item.isSequence(); + } + + bool isTrue () const + { + return the_item.isTrue(); + } + + bool isType (const Type& t) const + { + return the_item.isType (t); + } + + bool isTuple() const + { + return the_item.isTuple(); + } + + bool isString() const + { + return the_item.isString(); + } + // Commands + void setAttr (const std::string& attr_name, const Object& value) + { + the_item.setAttr(attr_name, value); + } + + void delAttr (const std::string& attr_name) + { + the_item.delAttr(attr_name); + } + + void delItem (const Object& key) + { + the_item.delItem(key); + } + + bool operator==(const Object& o2) const + { + return the_item == o2; + } + + bool operator!=(const Object& o2) const + { + return the_item != o2; + } + + bool operator>=(const Object& o2) const + { + return the_item >= o2; + } + + bool operator<=(const Object& o2) const + { + return the_item <= o2; + } + + bool operator<(const Object& o2) const + { + return the_item < o2; + } + + bool operator>(const Object& o2) const + { + return the_item > o2; + } + }; // end of seqref + + + // class SeqBase + // ...the base class for all sequence types + + template + class SeqBase: public Object + { + public: + // STL definitions + typedef size_t size_type; + typedef seqref reference; + typedef T const_reference; + typedef seqref* pointer; + typedef int difference_type; + typedef T value_type; // TMM: 26Jun'01 + + virtual size_type max_size() const + { + return std::string::npos; // ? + } + + virtual size_type capacity() const + { + return size(); + } + + virtual void swap(SeqBase& c) + { + SeqBase temp = c; + c = ptr(); + set(temp.ptr()); + } + + virtual size_type size () const + { + return PySequence_Length (ptr()); + } + + explicit SeqBase () + :Object(PyTuple_New(0), true) + { + validate(); + } + + explicit SeqBase (PyObject* pyob, bool owned=false) + : Object(pyob, owned) + { + validate(); + } + + SeqBase (const Object& ob): Object(ob) + { + validate(); + } + + // Assignment acquires new ownership of pointer + + SeqBase& operator= (const Object& rhs) + { + return (*this = *rhs); + } + + SeqBase& operator= (PyObject* rhsp) + { + if(ptr() == rhsp) return *this; + set (rhsp); + return *this; + } + + virtual bool accepts (PyObject *pyob) const + { + return pyob && PySequence_Check (pyob); + } + + size_type length () const + { + return PySequence_Length (ptr()); + } + + // Element access + const T operator[](sequence_index_type index) const + { + return getItem(index); + } + + seqref operator[](sequence_index_type index) + { + return seqref(*this, index); + } + + virtual T getItem (sequence_index_type i) const + { + return T(asObject(PySequence_GetItem (ptr(), i))); + } + + virtual void setItem (sequence_index_type i, const T& ob) + { + if (PySequence_SetItem (ptr(), i, *ob) == -1) + { + throw Exception(); + } + } + + SeqBase repeat (int count) const + { + return SeqBase (PySequence_Repeat (ptr(), count), true); + } + + SeqBase concat (const SeqBase& other) const + { + return SeqBase (PySequence_Concat(ptr(), *other), true); + } + + // more STL compatability + const T front () const + { + return getItem(0); + } + + seqref front() + { + return seqref(this, 0); + } + + const T back () const + { + return getItem(size()-1); + } + + seqref back() + { + return seqref(this, size()-1); + } + + void verify_length(size_type required_size) const + { + if (size() != required_size) + throw IndexError ("Unexpected SeqBase length."); + } + + void verify_length(size_type min_size, size_type max_size) const + { + size_type n = size(); + if (n < min_size || n > max_size) + throw IndexError ("Unexpected SeqBase length."); + } + + class iterator + : public random_access_iterator_parent(seqref) + { + protected: + friend class SeqBase; + SeqBase* seq; + int count; + + public: + ~iterator () + {} + + iterator () + : seq( 0 ) + , count( 0 ) + {} + + iterator (SeqBase* s, int where) + : seq( s ) + , count( where ) + {} + + iterator (const iterator& other) + : seq( other.seq ) + , count( other.count ) + {} + + bool eql (const iterator& other) const + { + return (*seq == *other.seq) && (count == other.count); + } + + bool neq (const iterator& other) const + { + return (*seq != *other.seq) || (count != other.count); + } + + bool lss (const iterator& other) const + { + return (count < other.count); + } + + bool gtr (const iterator& other) const + { + return (count > other.count); + } + + bool leq (const iterator& other) const + { + return (count <= other.count); + } + + bool geq (const iterator& other) const + { + return (count >= other.count); + } + + seqref operator*() + { + return seqref(*seq, count); + } + + seqref operator[] (sequence_index_type i) + { + return seqref(*seq, count + i); + } + + iterator& operator=(const iterator& other) + { + if (this == &other) return *this; + seq = other.seq; + count = other.count; + return *this; + } + + iterator operator+(int n) const + { + return iterator(seq, count + n); + } + + iterator operator-(int n) const + { + return iterator(seq, count - n); + } + + iterator& operator+=(int n) + { + count = count + n; + return *this; + } + + iterator& operator-=(int n) + { + count = count - n; + return *this; + } + + int operator-(const iterator& other) const + { + if (*seq != *other.seq) + throw RuntimeError ("SeqBase::iterator comparison error"); + return count - other.count; + } + + // prefix ++ + iterator& operator++ () + { count++; return *this;} + // postfix ++ + iterator operator++ (int) + { return iterator(seq, count++);} + // prefix -- + iterator& operator-- () + { count--; return *this;} + // postfix -- + iterator operator-- (int) + { return iterator(seq, count--);} + + std::string diagnose() const + { + std::OSTRSTREAM oss; + oss << "iterator diagnosis " << seq << ", " << count << std::ends; + return std::string(oss.str()); + } + }; // end of class SeqBase::iterator + + iterator begin () + { + return iterator(this, 0); + } + + iterator end () + { + return iterator(this, length()); + } + + class const_iterator + : public random_access_iterator_parent(const Object) + { + protected: + friend class SeqBase; + const SeqBase* seq; + sequence_index_type count; + + public: + ~const_iterator () + {} + + const_iterator () + : seq( 0 ) + , count( 0 ) + {} + + const_iterator (const SeqBase* s, int where) + : seq( s ) + , count( where ) + {} + + const_iterator(const const_iterator& other) + : seq( other.seq ) + , count( other.count ) + {} + + const T operator*() const + { + return seq->getItem(count); + } + + const T operator[] (sequence_index_type i) const + { + return seq->getItem(count + i); + } + + const_iterator& operator=(const const_iterator& other) + { + if (this == &other) return *this; + seq = other.seq; + count = other.count; + return *this; + } + + const_iterator operator+(int n) const + { + return const_iterator(seq, count + n); + } + + bool eql (const const_iterator& other) const + { + return (*seq == *other.seq) && (count == other.count); + } + + bool neq (const const_iterator& other) const + { + return (*seq != *other.seq) || (count != other.count); + } + + bool lss (const const_iterator& other) const + { + return (count < other.count); + } + + bool gtr (const const_iterator& other) const + { + return (count > other.count); + } + + bool leq (const const_iterator& other) const + { + return (count <= other.count); + } + + bool geq (const const_iterator& other) const + { + return (count >= other.count); + } + + const_iterator operator-(int n) + { + return const_iterator(seq, count - n); + } + + const_iterator& operator+=(int n) + { + count = count + n; + return *this; + } + + const_iterator& operator-=(int n) + { + count = count - n; + return *this; + } + + int operator-(const const_iterator& other) const + { + if (*seq != *other.seq) + throw RuntimeError ("SeqBase::const_iterator::- error"); + return count - other.count; + } + // prefix ++ + const_iterator& operator++ () + { count++; return *this;} + // postfix ++ + const_iterator operator++ (int) + { return const_iterator(seq, count++);} + // prefix -- + const_iterator& operator-- () + { count--; return *this;} + // postfix -- + const_iterator operator-- (int) + { return const_iterator(seq, count--);} + }; // end of class SeqBase::const_iterator + + const_iterator begin () const + { + return const_iterator(this, 0); + } + + const_iterator end () const + { + return const_iterator(this, length()); + } + }; + + // Here's an important typedef you might miss if reading too fast... + typedef SeqBase Sequence; + + template bool operator==(const EXPLICIT_TYPENAME SeqBase::iterator& left, const EXPLICIT_TYPENAME SeqBase::iterator& right); + template bool operator!=(const EXPLICIT_TYPENAME SeqBase::iterator& left, const EXPLICIT_TYPENAME SeqBase::iterator& right); + template bool operator< (const EXPLICIT_TYPENAME SeqBase::iterator& left, const EXPLICIT_TYPENAME SeqBase::iterator& right); + template bool operator> (const EXPLICIT_TYPENAME SeqBase::iterator& left, const EXPLICIT_TYPENAME SeqBase::iterator& right); + template bool operator<=(const EXPLICIT_TYPENAME SeqBase::iterator& left, const EXPLICIT_TYPENAME SeqBase::iterator& right); + template bool operator>=(const EXPLICIT_TYPENAME SeqBase::iterator& left, const EXPLICIT_TYPENAME SeqBase::iterator& right); + + template bool operator==(const EXPLICIT_TYPENAME SeqBase::const_iterator& left, const EXPLICIT_TYPENAME SeqBase::const_iterator& right); + template bool operator!=(const EXPLICIT_TYPENAME SeqBase::const_iterator& left, const EXPLICIT_TYPENAME SeqBase::const_iterator& right); + template bool operator< (const EXPLICIT_TYPENAME SeqBase::const_iterator& left, const EXPLICIT_TYPENAME SeqBase::const_iterator& right); + template bool operator> (const EXPLICIT_TYPENAME SeqBase::const_iterator& left, const EXPLICIT_TYPENAME SeqBase::const_iterator& right); + template bool operator<=(const EXPLICIT_TYPENAME SeqBase::const_iterator& left, const EXPLICIT_TYPENAME SeqBase::const_iterator& right); + template bool operator>=(const EXPLICIT_TYPENAME SeqBase::const_iterator& left, const EXPLICIT_TYPENAME SeqBase::const_iterator& right); + + + extern bool operator==(const Sequence::iterator& left, const Sequence::iterator& right); + extern bool operator!=(const Sequence::iterator& left, const Sequence::iterator& right); + extern bool operator< (const Sequence::iterator& left, const Sequence::iterator& right); + extern bool operator> (const Sequence::iterator& left, const Sequence::iterator& right); + extern bool operator<=(const Sequence::iterator& left, const Sequence::iterator& right); + extern bool operator>=(const Sequence::iterator& left, const Sequence::iterator& right); + + extern bool operator==(const Sequence::const_iterator& left, const Sequence::const_iterator& right); + extern bool operator!=(const Sequence::const_iterator& left, const Sequence::const_iterator& right); + extern bool operator< (const Sequence::const_iterator& left, const Sequence::const_iterator& right); + extern bool operator> (const Sequence::const_iterator& left, const Sequence::const_iterator& right); + extern bool operator<=(const Sequence::const_iterator& left, const Sequence::const_iterator& right); + extern bool operator>=(const Sequence::const_iterator& left, const Sequence::const_iterator& right); + + // ================================================== + // class Char + // Python strings return strings as individual elements. + // I'll try having a class Char which is a String of length 1 + // + typedef std::basic_string unicodestring; + extern Py_UNICODE unicode_null_string[1]; + + class Char: public Object + { + public: + explicit Char (PyObject *pyob, bool owned = false): Object(pyob, owned) + { + validate(); + } + + Char (const Object& ob): Object(ob) + { + validate(); + } + + Char (const std::string& v = "") + :Object(PyString_FromStringAndSize (const_cast(v.c_str()),1), true) + { + validate(); + } + + Char (char v) + : Object(PyString_FromStringAndSize (&v, 1), true) + { + validate(); + } + + Char (Py_UNICODE v) + : Object(PyUnicode_FromUnicode (&v, 1), true) + { + validate(); + } + // Assignment acquires new ownership of pointer + Char& operator= (const Object& rhs) + { + return (*this = *rhs); + } + + Char& operator= (PyObject* rhsp) + { + if(ptr() == rhsp) return *this; + set (rhsp); + return *this; + } + + // Membership + virtual bool accepts (PyObject *pyob) const + { + return pyob && (Py::_String_Check(pyob) || Py::_Unicode_Check(pyob)) && PySequence_Length (pyob) == 1; + } + + // Assignment from C string + Char& operator= (const std::string& v) + { + set(PyString_FromStringAndSize (const_cast(v.c_str()),1), true); + return *this; + } + + Char& operator= (char v) + { + set(PyString_FromStringAndSize (&v, 1), true); + return *this; + } + + Char& operator= (const unicodestring& v) + { + set(PyUnicode_FromUnicode (const_cast(v.data()),1), true); + return *this; + } + + Char& operator= (Py_UNICODE v) + { + set(PyUnicode_FromUnicode (&v, 1), true); + return *this; + } + + // Conversion + operator String() const; + + operator std::string () const + { + return std::string(PyString_AsString (ptr())); + } + }; + + class String: public SeqBase + { + public: + virtual size_type capacity() const + { + return max_size(); + } + + explicit String (PyObject *pyob, bool owned = false): SeqBase(pyob, owned) + { + validate(); + } + + String (const Object& ob): SeqBase(ob) + { + validate(); + } + + String() + : SeqBase( PyString_FromStringAndSize( "", 0 ), true ) + { + validate(); + } + + String( const std::string& v ) + : SeqBase( PyString_FromStringAndSize( const_cast(v.data()), + static_cast( v.length() ) ), true ) + { + validate(); + } + + String( const char *s, const char *encoding, const char *error="strict" ) + : SeqBase( PyUnicode_Decode( s, strlen( s ), encoding, error ), true ) + { + validate(); + } + + String( const char *s, int len, const char *encoding, const char *error="strict" ) + : SeqBase( PyUnicode_Decode( s, len, encoding, error ), true ) + { + validate(); + } + + String( const std::string &s, const char *encoding, const char *error="strict" ) + : SeqBase( PyUnicode_Decode( s.c_str(), s.length(), encoding, error ), true ) + { + validate(); + } + + String( const std::string& v, std::string::size_type vsize ) + : SeqBase(PyString_FromStringAndSize( const_cast(v.data()), + static_cast( vsize ) ), true) + { + validate(); + } + + String( const char *v, int vsize ) + : SeqBase(PyString_FromStringAndSize( const_cast(v), vsize ), true ) + { + validate(); + } + + String( const char* v ) + : SeqBase( PyString_FromString( v ), true ) + { + validate(); + } + + // Assignment acquires new ownership of pointer + String& operator= ( const Object& rhs ) + { + return *this = *rhs; + } + + String& operator= (PyObject* rhsp) + { + if( ptr() == rhsp ) + return *this; + set (rhsp); + return *this; + } + // Membership + virtual bool accepts (PyObject *pyob) const + { + return pyob && (Py::_String_Check(pyob) || Py::_Unicode_Check(pyob)); + } + + // Assignment from C string + String& operator= (const std::string& v) + { + set( PyString_FromStringAndSize( const_cast( v.data() ), + static_cast( v.length() ) ), true ); + return *this; + } + String& operator= (const unicodestring& v) + { + set( PyUnicode_FromUnicode( const_cast( v.data() ), + static_cast( v.length() ) ), true ); + return *this; + } + + + // Encode + String encode( const char *encoding, const char *error="strict" ) + { + if( isUnicode() ) + { + return String( PyUnicode_AsEncodedString( ptr(), encoding, error ) ); + } + else + { + return String( PyString_AsEncodedObject( ptr(), encoding, error ) ); + } + } + + String decode( const char *encoding, const char *error="strict" ) + { + return Object( PyString_AsDecodedObject( ptr(), encoding, error ) ); + } + + // Queries + virtual size_type size () const + { + if( isUnicode() ) + { + return static_cast( PyUnicode_GET_SIZE (ptr()) ); + } + else + { + return static_cast( PyString_Size (ptr()) ); + } + } + + operator std::string () const + { + return as_std_string(); + } + + std::string as_std_string() const + { + if( isUnicode() ) + { + throw TypeError("cannot return std::string from Unicode object"); + } + else + { + return std::string( PyString_AsString( ptr() ), static_cast( PyString_Size( ptr() ) ) ); + } + } + + unicodestring as_unicodestring() const + { + if( isUnicode() ) + { + return unicodestring( PyUnicode_AS_UNICODE( ptr() ), + static_cast( PyUnicode_GET_SIZE( ptr() ) ) ); + } + else + { + throw TypeError("can only return unicodestring from Unicode object"); + } + } + }; + + // ================================================== + // class Tuple + class Tuple: public Sequence + { + public: + virtual void setItem (sequence_index_type offset, const Object&ob) + { + // note PyTuple_SetItem is a thief... + if(PyTuple_SetItem (ptr(), offset, new_reference_to(ob)) == -1) + { + throw Exception(); + } + } + + // Constructor + explicit Tuple (PyObject *pyob, bool owned = false): Sequence (pyob, owned) + { + validate(); + } + + Tuple (const Object& ob): Sequence(ob) + { + validate(); + } + + // New tuple of a given size + explicit Tuple (int size = 0) + { + set(PyTuple_New (size), true); + validate (); + for (sequence_index_type i=0; i < size; i++) + { + if(PyTuple_SetItem (ptr(), i, new_reference_to(Py::_None())) == -1) + { + throw Exception(); + } + } + } + // Tuple from any sequence + explicit Tuple (const Sequence& s) + { + sequence_index_type limit( sequence_index_type( s.length() ) ); + + set(PyTuple_New (limit), true); + validate(); + + for(sequence_index_type i=0; i < limit; i++) + { + if(PyTuple_SetItem (ptr(), i, new_reference_to(s[i])) == -1) + { + throw Exception(); + } + } + } + // Assignment acquires new ownership of pointer + + Tuple& operator= (const Object& rhs) + { + return (*this = *rhs); + } + + Tuple& operator= (PyObject* rhsp) + { + if(ptr() == rhsp) return *this; + set (rhsp); + return *this; + } + // Membership + virtual bool accepts (PyObject *pyob) const + { + return pyob && Py::_Tuple_Check (pyob); + } + + Tuple getSlice (int i, int j) const + { + return Tuple (PySequence_GetSlice (ptr(), i, j), true); + } + + }; + + // ================================================== + // class List + + class List: public Sequence + { + public: + // Constructor + explicit List (PyObject *pyob, bool owned = false): Sequence(pyob, owned) + { + validate(); + } + List (const Object& ob): Sequence(ob) + { + validate(); + } + // Creation at a fixed size + List (int size = 0) + { + set(PyList_New (size), true); + validate(); + for (sequence_index_type i=0; i < size; i++) + { + if(PyList_SetItem (ptr(), i, new_reference_to(Py::_None())) == -1) + { + throw Exception(); + } + } + } + + // List from a sequence + List (const Sequence& s): Sequence() + { + int n = s.length(); + set(PyList_New (n), true); + validate(); + for (sequence_index_type i=0; i < n; i++) + { + if(PyList_SetItem (ptr(), i, new_reference_to(s[i])) == -1) + { + throw Exception(); + } + } + } + + virtual size_type capacity() const + { + return max_size(); + } + // Assignment acquires new ownership of pointer + + List& operator= (const Object& rhs) + { + return (*this = *rhs); + } + + List& operator= (PyObject* rhsp) + { + if(ptr() == rhsp) return *this; + set (rhsp); + return *this; + } + // Membership + virtual bool accepts (PyObject *pyob) const + { + return pyob && Py::_List_Check (pyob); + } + + List getSlice (int i, int j) const + { + return List (PyList_GetSlice (ptr(), i, j), true); + } + + void setSlice (int i, int j, const Object& v) + { + if(PyList_SetSlice (ptr(), i, j, *v) == -1) + { + throw Exception(); + } + } + + void append (const Object& ob) + { + if(PyList_Append (ptr(), *ob) == -1) + { + throw Exception(); + } + } + + void insert (int i, const Object& ob) + { + if(PyList_Insert (ptr(), i, *ob) == -1) + { + throw Exception(); + } + } + + void sort () + { + if(PyList_Sort(ptr()) == -1) + { + throw Exception(); + } + } + + void reverse () + { + if(PyList_Reverse(ptr()) == -1) + { + throw Exception(); + } + } + }; + + + // Mappings + // ================================================== + template + class mapref + { + protected: + MapBase& s; // the map + Object key; // item key + T the_item; + + public: + mapref (MapBase& map, const std::string& k) + : s(map), the_item() + { + key = String(k); + if(map.hasKey(key)) the_item = map.getItem(key); + }; + + mapref (MapBase& map, const Object& k) + : s(map), key(k), the_item() + { + if(map.hasKey(key)) the_item = map.getItem(key); + }; + + ~mapref() + {} + + // MapBase stuff + // lvalue + mapref& operator=(const mapref& other) + { + if(this == &other) return *this; + the_item = other.the_item; + s.setItem(key, other.the_item); + return *this; + }; + + mapref& operator= (const T& ob) + { + the_item = ob; + s.setItem (key, ob); + return *this; + } + + // rvalue + operator T() const + { + return the_item; + } + + // forward everything else to the_item + PyObject* ptr () const + { + return the_item.ptr(); + } + + int reference_count () const + { // the mapref count + return the_item.reference_count(); + } + + Type type () const + { + return the_item.type(); + } + + String str () const + { + return the_item.str(); + } + + String repr () const + { + return the_item.repr(); + } + + bool hasAttr (const std::string& attr_name) const + { + return the_item.hasAttr(attr_name); + } + + Object getAttr (const std::string& attr_name) const + { + return the_item.getAttr(attr_name); + } + + Object getItem (const Object& k) const + { + return the_item.getItem(k); + } + + long hashValue () const + { + return the_item.hashValue(); + } + + bool isCallable () const + { + return the_item.isCallable(); + } + + bool isList () const + { + return the_item.isList(); + } + + bool isMapping () const + { + return the_item.isMapping(); + } + + bool isNumeric () const + { + return the_item.isNumeric(); + } + + bool isSequence () const + { + return the_item.isSequence(); + } + + bool isTrue () const + { + return the_item.isTrue(); + } + + bool isType (const Type& t) const + { + return the_item.isType (t); + } + + bool isTuple() const + { + return the_item.isTuple(); + } + + bool isString() const + { + return the_item.isString(); + } + + // Commands + void setAttr (const std::string& attr_name, const Object& value) + { + the_item.setAttr(attr_name, value); + } + + void delAttr (const std::string& attr_name) + { + the_item.delAttr(attr_name); + } + + void delItem (const Object& k) + { + the_item.delItem(k); + } + }; // end of mapref + + // TMM: now for mapref + template< class T > + bool operator==(const mapref& left, const mapref& right) + { + return true; // NOT completed. + } + + template< class T > + bool operator!=(const mapref& left, const mapref& right) + { + return true; // not completed. + } + + template + class MapBase: public Object + { + protected: + explicit MapBase() + {} + public: + // reference: proxy class for implementing [] + // TMM: 26Jun'01 - the types + // If you assume that Python mapping is a hash_map... + // hash_map::value_type is not assignable, but + // (*it).second = data must be a valid expression + typedef size_t size_type; + typedef Object key_type; + typedef mapref data_type; + typedef std::pair< const T, T > value_type; + typedef std::pair< const T, mapref > reference; + typedef const std::pair< const T, const T > const_reference; + typedef std::pair< const T, mapref > pointer; + + // Constructor + explicit MapBase (PyObject *pyob, bool owned = false): Object(pyob, owned) + { + validate(); + } + + // TMM: 02Jul'01 - changed MapBase to Object in next line + MapBase (const Object& ob): Object(ob) + { + validate(); + } + + // Assignment acquires new ownership of pointer + MapBase& operator= (const Object& rhs) + { + return (*this = *rhs); + } + + MapBase& operator= (PyObject* rhsp) + { + if(ptr() == rhsp) return *this; + set (rhsp); + return *this; + } + // Membership + virtual bool accepts (PyObject *pyob) const + { + return pyob && PyMapping_Check(pyob); + } + + // Clear -- PyMapping Clear is missing + // + + void clear () + { + List k = keys(); + for(List::iterator i = k.begin(); i != k.end(); i++) + { + delItem(*i); + } + } + + virtual size_type size() const + { + return PyMapping_Length (ptr()); + } + + // Element Access + T operator[](const std::string& key) const + { + return getItem(key); + } + + T operator[](const Object& key) const + { + return getItem(key); + } + + mapref operator[](const std::string& key) + { + return mapref(*this, key); + } + + mapref operator[](const Object& key) + { + return mapref(*this, key); + } + + int length () const + { + return PyMapping_Length (ptr()); + } + + bool hasKey (const std::string& s) const + { + return PyMapping_HasKeyString (ptr(),const_cast(s.c_str())) != 0; + } + + bool hasKey (const Object& s) const + { + return PyMapping_HasKey (ptr(), s.ptr()) != 0; + } + + T getItem (const std::string& s) const + { + return T( + asObject(PyMapping_GetItemString (ptr(),const_cast(s.c_str()))) + ); + } + + T getItem (const Object& s) const + { + return T( + asObject(PyObject_GetItem (ptr(), s.ptr())) + ); + } + + virtual void setItem (const char *s, const Object& ob) + { + if (PyMapping_SetItemString (ptr(), const_cast(s), *ob) == -1) + { + throw Exception(); + } + } + + virtual void setItem (const std::string& s, const Object& ob) + { + if (PyMapping_SetItemString (ptr(), const_cast(s.c_str()), *ob) == -1) + { + throw Exception(); + } + } + + virtual void setItem (const Object& s, const Object& ob) + { + if (PyObject_SetItem (ptr(), s.ptr(), ob.ptr()) == -1) + { + throw Exception(); + } + } + + void delItem (const std::string& s) + { + if (PyMapping_DelItemString (ptr(), const_cast(s.c_str())) == -1) + { + throw Exception(); + } + } + + void delItem (const Object& s) + { + if (PyMapping_DelItem (ptr(), *s) == -1) + { + throw Exception(); + } + } + // Queries + List keys () const + { + return List(PyMapping_Keys(ptr()), true); + } + + List values () const + { // each returned item is a (key, value) pair + return List(PyMapping_Values(ptr()), true); + } + + List items () const + { + return List(PyMapping_Items(ptr()), true); + } + + // iterators for MapBase + // Added by TMM: 2Jul'01 - NOT COMPLETED + // There is still a bug. I decided to stop, before fixing the bug, because + // this can't be halfway efficient until Python gets built-in iterators. + // My current soln is to iterate over the map by getting a copy of its keys + // and iterating over that. Not a good solution. + + // The iterator holds a MapBase* rather than a MapBase because that's + // how the sequence iterator is implemented and it works. But it does seem + // odd to me - we are iterating over the map object, not the reference. + +#if 0 // here is the test code with which I found the (still existing) bug + typedef cxx::Dict d_t; + d_t d; + cxx::String s1("blah"); + cxx::String s2("gorf"); + d[ "one" ] = s1; + d[ "two" ] = s1; + d[ "three" ] = s2; + d[ "four" ] = s2; + + d_t::iterator it; + it = d.begin(); // this (using the assignment operator) is causing + // a problem; if I just use the copy ctor it works fine. + for( ; it != d.end(); ++it ) + { + d_t::value_type vt( *it ); + cxx::String rs = vt.second.repr(); + std::string ls = rs.operator std::string(); + fprintf( stderr, "%s\n", ls ); + } +#endif // 0 + + class iterator + { + // : public forward_iterator_parent( std::pair ) { + protected: + typedef std::forward_iterator_tag iterator_category; + typedef std::pair< const T, T > value_type; + typedef int difference_type; + typedef std::pair< const T, mapref > pointer; + typedef std::pair< const T, mapref > reference; + + friend class MapBase; + // + MapBase* map; + List keys; // for iterating over the map + List::iterator pos; // index into the keys + + public: + ~iterator () + {} + + iterator () + : map( 0 ) + , keys() + , pos() + {} + + iterator (MapBase* m, bool end = false ) + : map( m ) + , keys( m->keys() ) + , pos( end ? keys.end() : keys.begin() ) + {} + + iterator (const iterator& other) + : map( other.map ) + , keys( other.keys ) + , pos( other.pos ) + {} + + reference operator*() + { + Object key = *pos; + return std::make_pair(key, mapref(*map,key)); + } + + iterator& operator=(const iterator& other) + { + if (this == &other) + return *this; + map = other.map; + keys = other.keys; + pos = other.pos; + return *this; + } + + bool eql(const iterator& right) const + { + return *map == *right.map && pos == right.pos; + } + bool neq( const iterator& right ) const + { + return *map != *right.map || pos != right.pos; + } + + // pointer operator->() { + // return ; + // } + + // prefix ++ + iterator& operator++ () + { pos++; return *this;} + // postfix ++ + iterator operator++ (int) + { return iterator(map, keys, pos++);} + // prefix -- + iterator& operator-- () + { pos--; return *this;} + // postfix -- + iterator operator-- (int) + { return iterator(map, keys, pos--);} + + std::string diagnose() const + { + std::OSTRSTREAM oss; + oss << "iterator diagnosis " << map << ", " << pos << std::ends; + return std::string(oss.str()); + } + }; // end of class MapBase::iterator + + iterator begin () + { + return iterator(this); + } + + iterator end () + { + return iterator(this, true); + } + + class const_iterator + { + protected: + typedef std::forward_iterator_tag iterator_category; + typedef const std::pair< const T, T > value_type; + typedef int difference_type; + typedef const std::pair< const T, T > pointer; + typedef const std::pair< const T, T > reference; + + friend class MapBase; + const MapBase* map; + List keys; // for iterating over the map + List::iterator pos; // index into the keys + + public: + ~const_iterator () + {} + + const_iterator () + : map( 0 ) + , keys() + , pos() + {} + + const_iterator (const MapBase* m, List k, List::iterator p ) + : map( m ) + , keys( k ) + , pos( p ) + {} + + const_iterator(const const_iterator& other) + : map( other.map ) + , keys( other.keys ) + , pos( other.pos ) + {} + + bool eql(const const_iterator& right) const + { + return *map == *right.map && pos == right.pos; + } + bool neq( const const_iterator& right ) const + { + return *map != *right.map || pos != right.pos; + } + + + // const_reference operator*() { + // Object key = *pos; + // return std::make_pair( key, map->[key] ); + // GCC < 3 barfes on this line at the '['. + // } + + const_iterator& operator=(const const_iterator& other) + { + if (this == &other) return *this; + map = other.map; + keys = other.keys; + pos = other.pos; + return *this; + } + + // prefix ++ + const_iterator& operator++ () + { pos++; return *this;} + // postfix ++ + const_iterator operator++ (int) + { return const_iterator(map, keys, pos++);} + // prefix -- + const_iterator& operator-- () + { pos--; return *this;} + // postfix -- + const_iterator operator-- (int) + { return const_iterator(map, keys, pos--);} + }; // end of class MapBase::const_iterator + + const_iterator begin () const + { + return const_iterator(this, 0); + } + + const_iterator end () const + { + return const_iterator(this, length()); + } + + }; // end of MapBase + + typedef MapBase Mapping; + + template bool operator==(const EXPLICIT_TYPENAME MapBase::iterator& left, const EXPLICIT_TYPENAME MapBase::iterator& right); + template bool operator!=(const EXPLICIT_TYPENAME MapBase::iterator& left, const EXPLICIT_TYPENAME MapBase::iterator& right); + template bool operator==(const EXPLICIT_TYPENAME MapBase::const_iterator& left, const EXPLICIT_TYPENAME MapBase::const_iterator& right); + template bool operator!=(const EXPLICIT_TYPENAME MapBase::const_iterator& left, const EXPLICIT_TYPENAME MapBase::const_iterator& right); + + extern bool operator==(const Mapping::iterator& left, const Mapping::iterator& right); + extern bool operator!=(const Mapping::iterator& left, const Mapping::iterator& right); + extern bool operator==(const Mapping::const_iterator& left, const Mapping::const_iterator& right); + extern bool operator!=(const Mapping::const_iterator& left, const Mapping::const_iterator& right); + + + // ================================================== + // class Dict + class Dict: public Mapping + { + public: + // Constructor + explicit Dict (PyObject *pyob, bool owned=false): Mapping (pyob, owned) + { + validate(); + } + Dict (const Dict& ob): Mapping(ob) + { + validate(); + } + // Creation + Dict () + { + set(PyDict_New (), true); + validate(); + } + // Assignment acquires new ownership of pointer + + Dict& operator= (const Object& rhs) + { + return (*this = *rhs); + } + + Dict& operator= (PyObject* rhsp) + { + if(ptr() == rhsp) return *this; + set(rhsp); + return *this; + } + // Membership + virtual bool accepts (PyObject *pyob) const + { + return pyob && Py::_Dict_Check (pyob); + } + }; + + class Callable: public Object + { + protected: + explicit Callable (): Object() + {} + public: + // Constructor + explicit Callable (PyObject *pyob, bool owned = false): Object (pyob, owned) + { + validate(); + } + + Callable (const Object& ob): Object(ob) + { + validate(); + } + + // Assignment acquires new ownership of pointer + + Callable& operator= (const Object& rhs) + { + return (*this = *rhs); + } + + Callable& operator= (PyObject* rhsp) + { + if(ptr() == rhsp) return *this; + set (rhsp); + return *this; + } + + // Membership + virtual bool accepts (PyObject *pyob) const + { + return pyob && PyCallable_Check (pyob); + } + + // Call + Object apply(const Tuple& args) const + { + return asObject(PyObject_CallObject(ptr(), args.ptr())); + } + + // Call with keywords + Object apply(const Tuple& args, const Dict& kw) const + { + return asObject( PyEval_CallObjectWithKeywords( ptr(), args.ptr(), kw.ptr() ) ); + } + + Object apply(PyObject* pargs = 0) const + { + return apply (Tuple(pargs)); + } + }; + + class Module: public Object + { + public: + explicit Module (PyObject* pyob, bool owned = false): Object (pyob, owned) + { + validate(); + } + + // Construct from module name + explicit Module (const std::string&s): Object() + { + PyObject *m = PyImport_AddModule( const_cast(s.c_str()) ); + set( m, false ); + validate (); + } + + // Copy constructor acquires new ownership of pointer + Module (const Module& ob): Object(*ob) + { + validate(); + } + + Module& operator= (const Object& rhs) + { + return (*this = *rhs); + } + + Module& operator= (PyObject* rhsp) + { + if(ptr() == rhsp) return *this; + set(rhsp); + return *this; + } + + Dict getDict() + { + return Dict(PyModule_GetDict(ptr())); + // Caution -- PyModule_GetDict returns borrowed reference! + } + }; + + // Numeric interface + inline Object operator+ (const Object& a) + { + return asObject(PyNumber_Positive(*a)); + } + inline Object operator- (const Object& a) + { + return asObject(PyNumber_Negative(*a)); + } + + inline Object abs(const Object& a) + { + return asObject(PyNumber_Absolute(*a)); + } + + inline std::pair coerce(const Object& a, const Object& b) + { + PyObject *p1, *p2; + p1 = *a; + p2 = *b; + if(PyNumber_Coerce(&p1,&p2) == -1) + { + throw Exception(); + } + return std::pair(asObject(p1), asObject(p2)); + } + + inline Object operator+ (const Object& a, const Object& b) + { + return asObject(PyNumber_Add(*a, *b)); + } + inline Object operator+ (const Object& a, int j) + { + return asObject(PyNumber_Add(*a, *Int(j))); + } + inline Object operator+ (const Object& a, double v) + { + return asObject(PyNumber_Add(*a, *Float(v))); + } + inline Object operator+ (int j, const Object& b) + { + return asObject(PyNumber_Add(*Int(j), *b)); + } + inline Object operator+ (double v, const Object& b) + { + return asObject(PyNumber_Add(*Float(v), *b)); + } + + inline Object operator- (const Object& a, const Object& b) + { + return asObject(PyNumber_Subtract(*a, *b)); + } + inline Object operator- (const Object& a, int j) + { + return asObject(PyNumber_Subtract(*a, *Int(j))); + } + inline Object operator- (const Object& a, double v) + { + return asObject(PyNumber_Subtract(*a, *Float(v))); + } + inline Object operator- (int j, const Object& b) + { + return asObject(PyNumber_Subtract(*Int(j), *b)); + } + inline Object operator- (double v, const Object& b) + { + return asObject(PyNumber_Subtract(*Float(v), *b)); + } + + inline Object operator* (const Object& a, const Object& b) + { + return asObject(PyNumber_Multiply(*a, *b)); + } + inline Object operator* (const Object& a, int j) + { + return asObject(PyNumber_Multiply(*a, *Int(j))); + } + inline Object operator* (const Object& a, double v) + { + return asObject(PyNumber_Multiply(*a, *Float(v))); + } + inline Object operator* (int j, const Object& b) + { + return asObject(PyNumber_Multiply(*Int(j), *b)); + } + inline Object operator* (double v, const Object& b) + { + return asObject(PyNumber_Multiply(*Float(v), *b)); + } + + inline Object operator/ (const Object& a, const Object& b) + { + return asObject(PyNumber_Divide(*a, *b)); + } + inline Object operator/ (const Object& a, int j) + { + return asObject(PyNumber_Divide(*a, *Int(j))); + } + inline Object operator/ (const Object& a, double v) + { + return asObject(PyNumber_Divide(*a, *Float(v))); + } + inline Object operator/ (int j, const Object& b) + { + return asObject(PyNumber_Divide(*Int(j), *b)); + } + inline Object operator/ (double v, const Object& b) + { + return asObject(PyNumber_Divide(*Float(v), *b)); + } + + inline Object operator% (const Object& a, const Object& b) + { + return asObject(PyNumber_Remainder(*a, *b)); + } + inline Object operator% (const Object& a, int j) + { + return asObject(PyNumber_Remainder(*a, *Int(j))); + } + inline Object operator% (const Object& a, double v) + { + return asObject(PyNumber_Remainder(*a, *Float(v))); + } + inline Object operator% (int j, const Object& b) + { + return asObject(PyNumber_Remainder(*Int(j), *b)); + } + inline Object operator% (double v, const Object& b) + { + return asObject(PyNumber_Remainder(*Float(v), *b)); + } + + inline Object type(const Exception&) // return the type of the error + { + PyObject *ptype, *pvalue, *ptrace; + PyErr_Fetch(&ptype, &pvalue, &ptrace); + Object result(ptype); + PyErr_Restore(ptype, pvalue, ptrace); + return result; + } + + inline Object value(const Exception&) // return the value of the error + { + PyObject *ptype, *pvalue, *ptrace; + PyErr_Fetch(&ptype, &pvalue, &ptrace); + Object result; + if(pvalue) result = pvalue; + PyErr_Restore(ptype, pvalue, ptrace); + return result; + } + + inline Object trace(const Exception&) // return the traceback of the error + { + PyObject *ptype, *pvalue, *ptrace; + PyErr_Fetch(&ptype, &pvalue, &ptrace); + Object result; + if(ptrace) result = ptrace; + PyErr_Restore(ptype, pvalue, ptrace); + return result; + } + + + +template +String seqref::str () const + { + return the_item.str(); + } + +template +String seqref::repr () const + { + return the_item.repr(); + } + + + } // namespace Py +#endif // __CXX_Objects__h diff --git a/lib/kross/python/cxx/PyCXX.html b/lib/kross/python/cxx/PyCXX.html new file mode 100644 index 00000000..566974c1 --- /dev/null +++ b/lib/kross/python/cxx/PyCXX.html @@ -0,0 +1,2131 @@ + + + + +Writing Python Extensions in C++ + + + + + + +

Writing Python Extensions in C++

+ +

Barry Scott
+Reading, Berkshire, England
+barry@barrys-emacs.org
+

+ +

Paul F. Dubois, dubois1@llnl.gov
+Lawrence Livermore National Laboratory
+Livermore, California, U.S.A.

+ + +

PyCXX is designed to make it easier to extend Python with C++

+ + +

PyCXX is a set of C++ facilities to make it easier to write Python extensions. +The chief way in which PyCXX makes it easier to write Python extensions is that it greatly +increases the probability that your program will not make a reference-counting error and +will not have to continually check error returns from the Python C API. PyCXX +integrates Python with C++ in these ways:

+ +
    +
  • C++ exception handling is relied on to detect errors and clean up. In a complicated + function this is often a tremendous problem when writing in C. With PyCXX, we let the + compiler keep track of what objects need to be dereferenced when an error occurs. +
  • The Standard Template Library (STL) and its many algorithms plug and play with Python + containers such as lists and tuples. +
  • The optional CXX_Extensions facility allows you to replace the clumsy C tables with + objects and method calls that define your modules and extension objects. +
+ +

Download and Installation

+ +

Download PyCXX from http://sourceforge.net/projects/cxx/.

+ +

The distribution layout is:

+ + + + + + + +
DirectoryDescription
.Makefile for Unix and Windows, Release documentation
./CXXHeader files
./SrcSource files
./DocDocumentation
./DemoTesting and Demonstartion files
+ +

To use PyCXX you use its include files and add its source routines to your module.

+ +

Installation:

+
    +
  • Install the PyCXX files into a directory of your choice. For example:
    +Windows: C:\PyCXX
    +Unix: /usr/local/PyCXX +
  • Tell your compiler where the PyCXX header files are:
    +Windows: cl /I=C:\PyCXX ...
    +Unix: g++ -I/usr/local/PyCXX ... +
  • Include PyCXX headers files in your code using the CXX prefix:
    +#include "CXX/Object.hxx" +
+ +

The header file CXX/config.h may need to be adjusted for the +compiler you use. As of this writing, only a fairly obscure reference to part of the +standard library needs this adjustment. Unlike prior releases, PyCXX now assumes namespace +support and a standard C++ library.

+ +

Use of namespaces

+ +

All PyCXX assets are in namespace "Py". You need to include +the Py:: prefix when referring to them, or include the statement:

+ +

using namespace Py;

+ +

Wrappers for standard objects: CXX_Objects.h

+ +

Header file CXX_Objects.h requires adding file Src/cxxsupport.cxx to +your module sources. CXX_Objects provides a set of wrapper classes that allow you access +to most of the Python C API using a C++ notation that closely resembles Python. For +example, this Python:

+ +
d = {}
+d["a"] = 1
+d["b"] = 2
+alist = d.keys()
+print alist
+ +

Can be written in C++:

+ +
Dict d;
+List alist;
+d["a"] = Int(1);
+d["b"] = Int(2);
+alist = d.keys();
+std::cout << alist << std::endl;
+
+ +

You can optionally use the CXX/Extensions.hxx facility described later +to define Python extension modules and extension objects.

+ +

We avoid programming with Python object pointers

+ +

The essential idea is that we avoid, as much as possible, programming with pointers to +Python objects, that is, variables of type PyObject*. Instead, +we use instances of a family of C++ classes that represent the +usual Python objects. This family is easily extendible to include new kinds of Python +objects.

+ +

For example, consider the case in which we wish to write a method, taking a single +integer argument, that will create a Python dict + and insert into it that the integer plus one under the key value. + In C we might do that as follows:

+ +
static PyObject* mymodule_addvalue (PyObject* self, PyObject* args)
+        {
+	PyObject *d;
+	PyObject* f;
+	int k;
+	PyArgs_ParseTuple(args, "i", &k);
+	d = PyDict_New();
+	if (!d)
+		return NULL;
+
+	f = PyInt_NEW(k+1);
+	if(!f)
+                {
+		Py_DECREF(d); /* have to get rid of d first */
+		return NULL;
+	        }
+	if(PyDict_SetItemString(d, "value", f) == -1)
+                {
+		Py_DECREF(f);
+		Py_DECREF(d);
+		return NULL;
+                }
+
+	return d;
+        }
+ +

If you have written a significant Python extension, this tedium looks all too familiar. +The vast bulk of the coding is error checking and cleanup. Now compare the same thing +written in C++ using CXX/Objects.hxx. The things with Python-like names (Int, Dict, Tuple) are +from CXX/Objects.hxx.

+ +
static PyObject* mymodule_addvalue (PyObject* self, PyObject* pargs)
+        { 
+	try     { 
+		Tuple args(pargs); 
+		args.verify_length(1); 
+
+		Dict d; 
+		Int k = args[0]; 
+		d["value"] = k + 1;
+
+		return new_reference_to(d); 
+	        } 
+	catch (const PyException&)
+                { 
+		return NULL;
+	        }
+        }
+ +

If there are not the right number of arguments or the argument is not an +integer, an exception is thrown. In this case we choose to catch it and convert it into a +Python exception. The C++ exception handling mechanism takes care all the cleanup.

+ +

Note that the creation of the Int k got the first argument and verified +that it is an Int.

+ +

Just to peek ahead, if you wrote this method in an +ExtensionModule-derived module of your own, it would be a method and it could be written +even more simply:

+ +
+Object addvalue (Object & self, const Tuple & args)
+      {
+      args.verify_length(1);
+      Dict d;
+      Int k = args[0];
+      d["value"] = k + 1;
+      return d;
+      }
+
+ +

The basic concept is to wrap Python pointers

+ + +

The basic concept of CXX/Objects.hxx is to create a wrapper around +each PyObject * so that the reference counting can be +done automatically, thus eliminating the most frequent source of errors. In addition, we +can then add methods and operators so that Python objects can be manipulated in C++ +much like you would in Python.

+ +

Each Object contains a PyObject * +to which it owns a reference. When an Object is destroyed, it releases its ownership on +the pointer. Since C++ calls the destructors on objects that are about to go out of scope, +we are guaranteed that we will keep the reference counts right even if we unexpectedly +leave a routine with an exception.

+ +

As a matter of philosophy, CXX/Objects.hxx prevents the creation of instances of its +classes unless the instance will be a valid instance of its class. When an attempt is made +to create an object that will not be valid, an exception is thrown.

+ +

Class Object represents the most general kind of Python object. The rest of the classes +that represent Python objects inherit from it.

+ +
Object
+    Type
+    Int
+    Float
+    Long
+    Complex
+    Char
+    Sequence -> SeqBase<T>
+        String
+        Tuple
+        List
+    Mapping -> MapBase<T>
+        Dict
+    Callable
+    Module
+ +

There are several constructors for each of these classes. For example, you can create +an Int from an integer as in

+ +
Int s(3)
+ +

However, you can also create an instance of one of these classes using any PyObject* or +another Object. If the corresponding Python object does not actually have the type +desired, an exception is thrown. This is accomplished as follows. Class Object defines a +virtual function accepts:

+ +
virtual bool accepts(PyObject* p)
+ +

The base class version of accepts returns true for any pointer p except 0. This means +we can create an Object using any PyObject *, or from any other +Object. However, if we attempt to create an Int from a PyObject *, +the overridding version +of accepts in class Int will only accept pointers that correspond to Python ints. +Therefore if we have a Tuple t and we wish to get the first element and be sure it is an +Int, we do

+ +
Int first_element = t[0]
+ +

This will not only accomplish the goal of extracting the first element of the Tuple t, +but it will ensure that the result is an Int. If not, an exception is thrown. The +exception mechanism is discussed later.

+ +

Class Object

+ +

Class Object serves as the base class for the other classes. Its default constructor +constructs a Py_None, the unique object of Python type None. The interface to Object +consists of a large number of methods corresponding to the operations that are defined for +every Python object. In each case, the methods throw an exception if anything goes +wrong.

+ +

There is no method corresponding to PyObject_SetItem with an arbitrary Python object +as a key. Instead, create an instance of a more specific child of Object and use the +appropriate facilities.

+ +

The comparison operators use the Python comparison function to compare values. The +method is is available to test for absolute identity.

+ +

A conversion to standard library string type std::string is supplied using method +as_string. Stream output of PyCXX Object instances uses this conversion, +which in turn uses the Python object's str() representation.

+ +

All the numeric operators are defined on all possible combinations of Object, +long, and double. These use the corresponding Python operators, +and should the operation fail for some reason, an exception is thrown.

+ +

Dealing with pointers returned by the Python C API

+ +

Often, PyObject * pointers are acquired from some function, +particularly functions in the Python C API. If you wish to make an object from the pointer +returned by such a function, you need to know if the function returns you an owned +or unowned reference. Unowned references are unusual but there are some cases where +unowned references are returned.

+ +

Usually, Object and its children acquire a new reference when constructed from a +PyObject *. This is usually not the right behavior if the reference comes from one +of the Python C API calls.

+ +

If p is an owned reference, you can add the boolean true as an extra +argument in the creation routine, Object(p, true), or use the function asObject(p) which +returns an Object created using the owned reference. For example, the routine +PyString_FromString returns an owned reference to a Python string object. You could write:

+ +
Object w = asObject( PyString_FromString("my string") );
+ +

or using the constructor,

+ +
Object w( PyString_FromString("my string"), true );
+ +

In fact, you would never do this, since PyCXX has a class String and you can just say:

+ +
String w( "my string" );
+ +

Indeed, since most of the Python C API is similarly embodied in Object +and its descendents, you probably will not use asObject all that often.

+

Table 1: Class Object

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ReturnsName(signature)Comment

Basic Methods

explicit Object (PyObject* pyob=Py_None, bool owned=false) Construct from pointer.
explicitObject (const Object& ob)Copycons; acquires an owned reference.
Object&operator= (const Object& rhs) Acquires an owned reference.
Object&operator= (PyObject* rhsp) Acquires an owned reference.
virtual~Object () Releases the reference.
voidincrement_reference_count() Explicitly increment the count
voiddecrement_reference_count()Explicitly decrement count but not to zero
PyObject*operator* () constLends the pointer
PyObject*ptr () constLends the pointer
virtual boolaccepts (PyObject *pyob) constWould assignment of pyob to this object succeed?
std::stringas_string() conststr() representation
Python API Interface
intreference_count () const reference count
Typetype () constassociated type object
Stringstr () conststr() representation
Stringrepr () constrepr () representation
boolhasAttr (const std::string& s) consthasattr(this, s)
ObjectgetAttr (const std::string& s) constgetattr(this, s)
ObjectgetItem (const Object& key) constgetitem(this, key)
longhashValue () consthash(this)
voidsetAttr (const std::string& s,
const Object& value)
this.s = value
voiddelAttr (const std::string& s) del this.s
voiddelItem (const Object& key) del this[key]
boolisCallable () constdoes this have callable behavior?
boolisList () constis this a Python list?
boolisMapping () constdoes this have mapping behaviors?
boolisNumeric () constdoes this have numeric behaviors?
boolisSequence () const does this have sequence behaviors?
boolisTrue () constis this true in the Python sense?
boolisType (const Type& t) constis type(this) == t?
boolisTuple() constis this a Python tuple?
boolisString() constis this a Python string?
boolisUnicode() constis this a Python Unicode string?
boolisDict() constis this a Python dictionary?
Comparison Operators
boolis(PyObject* pother) consttest for identity
boolis(const Object& other) consttest for identity
bool operator==(const Object& o2) constComparisons use Python cmp
booloperator!=(const Object& o2) constComparisons use Python cmp
booloperator>=(const Object& o2) constComparisons use Python cmp
booloperator<=(const Object& o2) const Comparisons use Python cmp
booloperator<(const Object& o2) constComparisons use Python cmp
booloperator>(const Object& o2) constComparisons use Python cmp
+ +

The Basic Types

+ +

Corresponding to each of the basic Python types is a class that inherits from Object. +Here are the interfaces for those types. Each of them inherits from Object and therefore +has all of the inherited methods listed for Object. Where a virtual function is overridden +in a class, the name is underlined.

+ +

Class Type

+ +

Class Type corresponds to Python type objects. There is no default constructor.

+ +

Table 2: class Type

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ReturnsName and SignatureComments
explicitType (PyObject* pyob, bool owned = false)Constructor
explicitType (const Object& ob)Constructor
explicitType(const Type& t)Copycons
Type&operator= (const Object& rhs) Assignment
Type&operator= (PyObject* rhsp) Assignment
virtual boolaccepts (PyObject *pyob) constUses PyType_Check
+ +

Class Int

+ +

Class Int, derived publically from Object, corresponds to Python ints. Note that the +latter correspond to C long ints. Class Int has an implicit user-defined conversion to +long int. All constructors, on the other hand, are explicit. The default constructor +creates a Python int zero.

+ +

Table 3: class Int

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Returns + Name and Signature + Comments +
explicitInt (PyObject *pyob, bool owned= false, bool owned = false)Constructor
explicitInt (const Int& ob)Constructor
explicitInt (long v = 0L)Construct from long
explicitInt (int v)Contruct from int
explicitInt (const Object& ob)Copycons
Int&operator= (const Object& rhs)Assignment
Int&operator= (PyObject* rhsp)Assignment
virtual bool   (PyObject *pyob) const Based on PyInt_Check
longoperator long() const Implicit conversion to long int
Int&operator= (int v)Assign from int
Int&operator= (long v) Assign from long
+ +
+ +

Class Long

+ +

Class Long, derived publically from Object, corresponds to Python type long. In Python, +a long is an integer type of unlimited size, and is usually used for applications such as +cryptography, not as a normal integer. Implicit conversions to both double and long are +provided, although the latter may of course fail if the number is actually too big. All +constructors are explicit. The default constructor produces a Python long zero.

+ +

Table 4: Class Long

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Returns + Name and Signature + Comments +
explicitLong (PyObject *pyob, bool owned = false)Constructor
explicitLong (const Int& ob)Constructor
explicitLong (long v = 0L)Construct from long
explicitLong (int v)Contruct from int
explicitLong (const Object& ob)Copycons
Long&operator= (const Object& rhs)Assignment
Long&operator= (PyObject* rhsp)Assignment
virtual bool(PyObject *pyob) const Based on PyLong_Check
doubleoperator double() const Implicit conversion to double
longoperator long() constImplicit conversion to long
Long&operator= (int v)Assign from int
Long&operator= (long v) Assign from long
+ +

Class Float

+ +

Class Float corresponds to Python floats, which in turn correspond to C double. The +default constructor produces the Python float 0.0.

+ +

Table 5: Class Float

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Returns + Name and Signature + Comments +
explicitFloat (PyObject *pyob, bool owned = false) + Constructor
Float (const Float& f)   Construct from float
explicitFloat (double v=0.0)Construct from double
explicitFloat (const Object& ob)Copycons
Float&operator= (const Object& rhs)Assignment
Float&operator= (PyObject* rhsp)Assignment
virtual bool accepts (PyObject *pyob) constBased on PyFloat_Check
double operator double () constImplicit conversion to double
Float& operator= (double v)Assign from double
Float& operator= (int v)Assign from int
Float& operator= (long v)Assign from long
Float& operator= (const Int& iob)Assign from Int
+ +

Sequences

+ +

PyCXX implements a quite sophisticated wrapper class for Python sequences. While every +effort has been made to disguise the sophistication, it may pop up in the form of obscure +compiler error messages, so in this documentation we will first detail normal usage and +then discuss what is under the hood.

+ +

The basic idea is that we would like the subscript operator [] to work properly, and to +be able to use STL-style iterators and STL algorithms across the elements of the sequence.

+ +

Sequences are implemented in terms of a templated base class, SeqBase<T>. The +parameter T is the answer to the question, sequence of what? For Lists, for example, T is +Object, because the most specific thing we know about an element of a List is simply that +it is an Object. (Class List is defined below; it is a descendent of Object that holds a +pointer to a Python list). For strings, T is Char, which is a wrapper in turn of Python +strings whose length is one.

+ +

For convenience, the word Sequence is a typedef of SeqBase<Object>.

+ +

General sequences

+ +

Suppose you are writing an extension module method that expects the first argument to +be any kind of Python sequence, and you wish to return the length of that sequence. You +might write:

+ +
static PyObject*
+my_module_seqlen (PyObject *self, PyObject* args) {
+    try
+        {
+        Tuple t(args);       // set up a Tuple pointing to the arguments.
+        if(t.length() != 1) 
+             throw PyException("Incorrect number of arguments to seqlen.");
+        Sequence s = t[0];   // get argument and be sure it is a sequence
+        return new_reference_to(Int(s.length()));
+        }
+    catch(const PyException&)
+        {
+        return Py_Null;
+        }
+}
+ +

As we will explain later, the try/catch structure converts any errors, such as the +first argument not being a sequence, into a Python exception.

+ +

Subscripting

+ +

When a sequence is subscripted, the value returned is a special kind of object which +serves as a proxy object. The general idea of proxy objects is discussed in Scott Meyers' +book, "More Effective C++". Proxy objects are necessary because when one +subscripts a sequence it is not clear whether the value is to be used or the location +assigned to. Our proxy object is even more complicated than normal because a sequence +reference such as s[i] is not a direct reference to the i'th object of s.

+ +

In normal use, you are not supposed to notice this magic going on behind your back. You +write:

+ +
Object t;
+Sequence s;
+s[2] = t + s[1]
+ +

and here is what happens: s[1] returns a proxy object. Since there is no addition +operator in Object that takes a proxy as an argument, the compiler decides to invoke an +automatic conversion of the proxy to an Object, which returns the desired component of s. +The addition takes place, and then there is an assignment operator in the proxy class +created by the s[2], and that assignment operator stuffs the result into the 2 component +of s.

+ +

It is possible to fool this mechanism and end up with a compiler failing to admit that +a s[i] is an Object. If that happens, you can work around it by writing Object(s[i]), +which makes the desired implicit conversion, explicit.

+ +

Iterators

+ +

Each sequence class provides the following interface. The class seqref<T> is the +proxy class. We omit the details of the iterator, const_iterator, and seqref<T> +here. See CXX_Objects.h if necessary. The purpose of most of this interface is to satisfy +requirements of the STL.

+ +

The SeqBase<T> Interface

+ +

SeqBase<T> inherits from Object.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type + Name +
typedef int size_type
typedef seqref<T>reference
typedef T const_reference
typedef seqref<T>*pointer
typedef int difference_type
virtual size_typemax_size() const
virtual size_type capacity() const;
virtual void swap(SeqBase<T>& c);
virtual size_type size () const;
explicit SeqBase<T> ();
explicit SeqBase<T> (PyObject* pyob, bool owned = false);
explicit SeqBase<T> (const Object& ob);
SeqBase<T>& operator= (const Object& rhs);
SeqBase<T>& operator= (PyObject* rhsp);
virtual bool accepts (PyObject *pyob) const;
size_type length () const ;
const T operator[](size_type index) const;
seqref<T> operator[](size_type index);
virtual T getItem (size_type i) const;
virtual void setItem (size_type i, const T& ob);
SeqBase<T> repeat (int count) const;
SeqBase<T> concat (const SeqBase<T>& other) const ;
const T front () const;
seqref<T> front();
const T back () const;
seqref<T> back();
void verify_length(size_type required_size);
void verify_length(size_type min_size, size_type max_size);
classiterator;
iterator begin ();
iterator end ();
class const_iterator;
const_iterator begin () const;
const_iterator end () const;
+ +

Any heir of class Object that has a sequence behavior should inherit from class +SeqBase<T>, where T is specified as the type of object that represents the +individual elements of the sequence. The requirements on T are that it has a constructor +that takes a PyObject* as an argument, that it has a default constructor, a copy +constructor, and an assignment operator. In short, any properly defined heir of Object +will work.

+ +

Classes Char and String

+ +

Python strings are unusual in that they are immutable sequences of characters. However, +there is no character type per se; rather, when subscripted strings return a string of +length one. To simulate this, we define two classes Char and String. The Char class +represents a Python string object of length one. The String class represents a Python +string, and its elements make up a sequence of Char's.

+ +

The user interface for Char is limited. Unlike String, for example, it is not a +sequence.

+ +

The Char interface

+ +

Char inherits from Object.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type + Name +
explicitChar (PyObject *pyob, bool owned = false)
Char (const Object& ob)
Char (const std::string& v = "")
Char (char v)
Char (Py_UNICODE v)
Char&operator= (const std::string& v)
Char&operator= (char v)
Char&operator= (Py_UNICODE v)
Char&operator= (std::basic_string v)
operator String() const
operator std::string () const
+ +

The String Interface

+ +

String inherits from SeqBase<Char>.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type + Name +
explicit String (PyObject *pyob, bool owned = false)
String (const Object& ob)
String (const std::string& v = "")
String (const std::string& v, const char *encoding, const char *error="strict")
String (const char *s, const char *encoding, const char *error="strict")
String (const char *s, int len, const char *encoding, const char *error="strict")
String (const std::string& v, std::string::size_type vsize)
String (const char* v)
String&operator= (const std::string& v)
std::stringoperator std::string () const
Stringencode( const char *encoding, const char *error="strict" )
Stringdecode( const char *encoding, const char *error="strict" )
std::stringas_std_string() const
unicodestringas_unicodestring() const
+ +

Class Tuple

+ +

Class Tuple represents Python tuples. A Tuple is a Sequence. There are two kinds of +constructors: one takes a PyObject* as usual, the other takes an integer number as an +argument and returns a Tuple of that length, each component initialized to Py_None. The +default constructor produces an empty Tuple.

+ +

Tuples are not immutable, but attempts to assign to their components will fail if the +reference count is not 1. That is, it is safe to set the elements of a Tuple you have just +made, but not thereafter.

+ +

Example: create a Tuple containing (1, 2, 4)

+ +
Tuple t(3)
+t[0] = Int(1)
+t[1] = Int(2)
+t[2] = Int(4)
+ +

Example: create a Tuple from a list:

+ +
Dict d
+...
+Tuple t(d.keys())
+ +

The Tuple Interface

+ +

Tuple inherits from Sequence.. Special run-time checks prevent modification if the +reference count is greater than one.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type + Name + Comment +
virtual voidsetItem (int offset, const Object&ob) setItem is overriden to handle tuples properly.
explicitTuple (PyObject *pyob, bool owned = false)
Tuple (const Object& ob)
explicitTuple (int size = 0)Create a tuple of the given size. Items initialize to Py_None. Default is an empty + tuple.
explicitTuple (const Sequence& s)Create a tuple from any sequence.
Tuple&operator= (const Object& rhs)
Tuple&operator= (PyObject* rhsp)
TuplegetSlice (int i, int j) const Equivalent to python's t[i:j]
+ +

Class List

+ +

Class List represents a Python list, and the methods available faithfully reproduce the +Python API for lists. A List is a Sequence.

+ +

The List Interface

+ +

List inherits from Sequence.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type + Name + Comment +
explicitList (PyObject *pyob, bool owned = false)
List (const Object& ob)
List (int size = 0)Create a list of the given size. Items initialized to Py_None. Default is an empty list.
List (const Sequence& s)Create a list from any sequence.
List&operator= (const Object& rhs)
List&operator= (PyObject* rhsp)
ListgetSlice (int i, int j) const
voidsetSlice (int i, int j, const Object& v)
voidappend (const Object& ob)
voidinsert (int i, const Object& ob)
voidsort ()Sorts the list in place, using Python's member function. You can also use + the STL sort function on any List instance.
voidreverse ()Reverses the list in place, using Python's member function.
+ +

Mappings

+ +

A class MapBase<T> is used as the base class for Python objects with a mapping +behavior. The key behavior of this class is the ability to set and use items by +subscripting with strings. A proxy class mapref<T> is defined to produce the correct +behavior for both use and assignment.

+ +

For convenience, Mapping is a typedef for MapBase<Object>.

+ +

The MapBase<T> interface

+ +

MapBase<T> inherits from Object. T should be chosen to reflect the kind of +element returned by the mapping.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type + Name + Comment +
Toperator[](const std::string& key) const
mapref<T> operator[](const std::string& key)
intlength () constNumber of entries.
inthasKey (const std::string& s) const Is m[s] defined?
TgetItem (const std::string& s) constm[s]
virtual voidsetItem (const std::string& s, const Object& ob)m[s] = ob
voiddelItem (const std::string& s)del m[s]
voiddelItem (const Object& s)
Listkeys () constA list of the keys.
Listvalues () constA list of the values.
Listitems () constEach item is a key-value pair.
+ +

Class Dict

+ +

Class Dict represents Python dictionarys. A Dict is a Mapping. Assignment to +subscripts can be used to set the components.

+ +
Dict d
+d["Paul Dubois"] = "(925)-422-5426"
+ +

Interface for Class Dict

+ +

Dict inherits from MapBase<Object>.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type + Name + Comment +
explicitDict (PyObject *pyob, bool owned = false)
Dict (const Dict& ob)
Dict () Creates an empty dictionary
Dict&operator= (const Object& rhs)
Dict&operator= (PyObject* rhsp)
+ +

Other classes and facilities.

+ +

Class Callable provides an interface to those Python objects that support a call +method. Class Module holds a pointer to a module. If you want to create an extension +module, however, see the extension facility. There is a large set of numeric operators.

+ +

Interface to class Callable

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type + Name + Comment +
explicitCallable (PyObject *pyob, bool owned = false)
Callable& operator= (const Object& rhs)
Callable& operator= (PyObject* rhsp)
Objectapply(const Tuple& args) constCall the object with the given arguments
Objectapply(PyObject* pargs = 0) const Call the object with args as the arguments. Checks that pargs is a tuple.
+ +

Interface to class Module

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type + Name + Comment +
explicitModule (PyObject* pyob, bool owned = false)
explicitModule (const std::string name)Construct from name of module; does the import if needed.
Module (const Module& ob) Copy constructor
Module&operator= (const Object& rhs) Assignment
Module&operator= (PyObject* rhsp) Assignment
+ +

Numeric interface

+ +

Unary operators for plus and minus, and binary operators +, -, *, /, and % are defined +for pairs of objects and for objects with scalar integers or doubles (in either +order). Functions abs(ob) and coerce(o1, o2) are also defined.

+ +

The signature for coerce is:

+ +
inline std::pair<Object,Object> coerce(const Object& a, const Object& b)
+ +

Unlike the C API function, this simply returns the pair after coercion.

+ +

Stream I/O

+ +

Any object can be printed using stream I/O, using std::ostream& operator<< +(std::ostream& os, const Object& ob). The object's str() representation is +converted to a standard string which is passed to std::ostream& operator<< +(std::ostream& os, const std::string&).

+ +

Exceptions

+ +

The Python exception facility and the C++ exception facility can be merged via the use +of try/catch blocks in the bodies of extension objects and module functions.

+ +

Class Exception and its children

+ +

A set of classes is provided. Each is derived from class Exception, and represents a +particular sort of Python exception, such as IndexError, RuntimeError, ValueError. Each of +them (other than Exception) has a constructor which takes an explanatory string as an +argument, and is used in a throw statement such as:

+ +
throw IndexError("Index too large in MyObject access.");
+ +

If in using a routine from the Python API, you discover that it has returned a NULL +indicating an error, then Python has already set the error message. In that case you +merely throw Exception.

+ +

List of Exceptions

+ +

The exception hierarchy mirrors the Python exception hierarchy. The concrete exception +classes are shown here.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeInterface for class Exception
explicit Exception()
Exception (const std::string& reason)
Exception (PyObject* exception, const std::string& reason)
void clear()
Constructors for other children of class Exception
TypeError (const std::string& reason)
IndexError (const std::string& reason)
AttributeError (const std::string& reason)
NameError (const std::string& reason)
RuntimeError (const std::string& reason)
SystemError (const std::string& reason)
KeyError (const std::string& reason)
ValueError (const std::string& reason)
OverflowError (const std::string& reason)
ZeroDivisionError (const std::string& reason)
MemoryError (const std::string& reason)
SystemExit (const std::string& reason)
+ +

Using Exceptions in extension methods

+ +

The exception facility allows you to integrate the C++ and Python exception mechanisms. +To do this, you must use the style described below when writing module methods in the old +C style.

+ +

Note: If using the ExtensionModule or PythonExtension mechanisms described below, the +method handlers include exception handling so that you only need to use exceptions +explicitly in unusual cases.

+ +

Catching Exceptions from the Python API or PyCXX.

+ +

When writing an extension module method, you can use the following boilerplate. Any +exceptions caused by the Python API or PyCXX itself will be converted into a Python +exception. Note that Exception is the most general of the exceptions listed above, and +therefore this one catch clause will serve to catch all of them. You may wish to catch +other exceptions, not in the Exception family, in the same way. If so, you need to make +sure you set the error in Python before returning.

+ +
static PyObject *
+some_module_method(PyObject* self, PyObject* args)
+{
+    Tuple a(args); // we know args is a Tuple
+    try {
+        ...calculate something from a...
+        return ...something, usually of the form new_reference_to(some Object);
+    }
+    catch(const Exception&) {
+        //Exception caught, passing it on to Python
+        return Null ();
+    }
+}
+
+ +

How to clear an Exception

+ +

If you anticipate that an Exception may be thrown and wish to recover from it, change +the catch phrase to set a reference to an Exception, and use the method clear() from class +Exception to clear it.:

+ +
catch(Exception& e)
+    {
+    e.clear();
+    ...now decide what to do about it...
+    }
+
+ +

Extension Facilities

+ +

CXX/Extensions.hxx provides facilities for: + +

    +
  • Creating a Python extension module
  • +
  • Creating new Python extension types
  • +
+ +

These facilities use CXX/Objects.hxx and its support file cxxsupport.cxx.

+ +

If you use CXX/Extensions.hxx you must also include source files cxxextensions.c and +cxx_extensions.cxx

+ +

Creating an Python extension module

+ +

The usual method of creating a Python extension module is to declare and initialize its +method table in C. This requires knowledge of the correct form for the table and the order +in which entries are to be made into it, and requires casts to get everything to compile +without warning. The PyCXX header file CXX/Extensions.h offers a simpler method. Here is a +sample usage, in which a module named "example" is created. Note that two +details are necessary: + +

    +
  • The initialization function must be declared to have external C linkage and to have the + expected name. This is a requirement imposed by Python
  • +
  • The ExtensionModule object must have a storage class that survives the call to the + initialization function. This is most easily accomplished by using a static local inside + the initialization function, as in initexample below.
  • +
+ +

To create an extension module, you inherit from class ExtensionModule templated on +yourself: In the constructor, you make calls to register methods of this class with Python +as extension module methods. In this example, two methods are added (this is a simplified +form of the example in Demo/example.cxx):

+ +
class example_module : public ExtensionModule<example_module>
+{
+public:
+    example_module()
+        : ExtensionModule<example_module>( "example" )
+        {
+        add_varargs_method("sum", &example_module::ex_sum, "sum(arglist) = sum of arguments");
+        add_varargs_method("test", &example_module::ex_test, "test(arglist) runs a test suite");
+
+        initialize( "documentation for the example module" );
+        }
+
+     virtual ~example_module() {}
+
+private:
+     Object ex_sum(const Tuple &a) { ... }
+     Object ex_test(const Tuple &a) { ... }
+};
+
+ +

To initialize the extension, you just instantiate one static instance (static so it +does not destroy itself!):

+ +
void initexample()
+{
+static example_module* example = new example_module;
+}
+ +

The methods can be written to take Tuples as arguments and return Objects. If +exceptions occur they are trapped for you and a Python exception is generated. So, for +example, the implementation of ex_sum might be:

+ +
Object ex_sum (const Tuple &a)
+    {
+        Float f(0.0);
+        for( int i = 0; i < a.length(); i++ )
+        {
+            Float g(a[i]);
+            f = f + g;
+        }
+        return f;
+    }
+ +

class ExtensionModule contains methods to return itself as a Module object, or to +return its dictionary.

+ +

Interface to class ExtensionModule

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type + Name + Comment +
explicitExtensionModule (char* name) Create an extension module named "name"
virtual ~ExtensionModule () Destructor
DictmoduleDictionary() constReturns the module dictionary; module must be initialized.
Modulemodule() constThis module as a Module.
void add_varargs_method (char *name, method_varargs_function_t method, char *documentation="")Add a method to the module.
void add_keyword_method (char *name, method_keyword_function_t method, char *documentation=""Add a method that takes keywords
voidinitialize() (protected, call from constructor)Initialize the module once all methods have been added.
+ +

The signatures above are:

+ +
typedef Object (T::*method_varargs_function_t)( const Tuple &args );
+typedef Object (T::*method_keyword_function_t)( const Tuple &args, const Dict &kws
+);
+ +

That is, the methods take a Tuple or a Tuple and a Dict, and return an Object. The +example below has an & in front of the name of the method; we found one compiler that +needed this.

+ +

Creating a Python extension type

+ +

One of the great things about Python is the way you can create your own object types +and have Python welcome them as first-class citizens. Unfortunately, part of the way you +have to do this is not great. Key to the process is the creation of a Python "type +object". All instances of this type must share a reference to this one unique type +object. The type object itself has a multitude of "slots" into which the +addresses of functions can be added in order to give the object the desired behavior.

+ +

Creating extension objects is of course harder since you must specify +how the object behaves and give it methods. This is shown in some detail in the example +range.h and range.cxx, with the test routine rangetest.cxx, in directory Demo. If you have never +created a Python extension before, you should read the Extension manual first and be very +familiar with Python's "special class methods". Then what follows will make more +sense.

+ +

The basic idea is to inherit from PythonExtension templated on your self

+ +
class MyObject: public PythonExtension<MyObject> {...}
+ +

As a consequence: + +

    +
  • MyObject is a child of PyObject, so that a MyObject* is-a PyObject*.
  • +
  • A static method check(PyObject*) is created in class MyObject. This function + returns a boolean, testing whether or not the argument is in fact a pointer to an instance + of MyObject.
  • +
  • The user can connect methods of MyObject to Python so that they are methods on MyObject + objects. Each such method has the signature:
    + Object method_name (const Tuple& args).
  • +
  • The user can override virtual methods of PythonExtension in order to set behaviors.
  • +
  • A method is created to handle the deletion of an instance if and when its reference + count goes to zero. This method ensures the calling of the class destructor ~MyObject(), + if any, and releases the memory (see below).
  • +
  • Both automatic and heap-based instances of MyObject can be created.
  • +
+ +

Sample usage of PythonExtension

+ +

Here is a brief overview. You create a class that inherits from PythonExtension +templated upon itself. You override various methods from PythonExtension to implement +behaviors, such as getattr, sequence_item, etc. You can also add methods to the object +that are usable from Python using a similar scheme as for module methods above.

+ +

One of the consequences of inheriting from PythonExtension is that you are inheriting +from PyObject itself. So your class is-a PyObject and instances of it can be passed to the +Python C API. Note: this example uses the namespace feature of PyCXX.

+ +

Hint: You can avoid needing to specify the Py:: prefix if you include the C++ statement +using Py; at the top of your files.

+ +
class range: public Py::PythonExtension<range> {
+public:
+    ... constructors, data, etc.
+    ... methods not callable from Python
+    // initializer, see below
+    static void init_type();
+    // override functions from PythonExtension
+    virtual Py::Object repr();
+    virtual Py::Object getattr( const char *name );
+
+    virtual int sequence_length();
+    virtual Py::Object sequence_item( int i );
+    virtual Py::Object sequence_concat( const Py::Object &j );
+    virtual Py::Object sequence_slice( int i, int j );
+
+    // define python methods of this object
+    Py::Object amethod (const Py::Tuple& args);
+    Py::Object value (const Py::Tuple& args);
+    Py::Object assign (const Py::Tuple& args); 
+};
+ +

+To initialize the type we provide a static method that we can call from some module's +initializer. We set the name, doc string, and indicate which behaviors range objects +support. Then we adds the methods.

+ +
void range::init_type()
+{
+    behaviors().name("range");
+    behaviors().doc("range objects: start, stop, step");
+    behaviors().supportRepr();
+    behaviors().supportGetattr();
+    behaviors().supportSequenceType();
+
+    add_varargs_method("amethod", &range::amethod,
+        "demonstrate how to document amethod");
+    add_varargs_method("assign", &range::assign);
+    add_varargs_method("value", &range::value);
+}
+ + +

Do not forget to add the call range::init_type() to some module's init function. You will want +a method in some module that can create range objects, too.

+ +

Interface to PythonExtension <T>

+ +

Your extension class T inherits PythonExtension<T>.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type + Name + Comment +
virtual ~PythonExtension<T>() Destructor
PyTypeObject* type_object() constReturns the object type object.
intcheck (PyObject* p)Is p a T?
Protected
void add_varargs_method (char *name, method_keyword_function_t method, char *documentation=""Add a method that takes arguments
void add_keyword_method (char *name, method_keyword_function_t method, char *documentation=""Add a method that takes keywords
static PythonType&behaviors()The type object
voidinitialize() (protected, call from constructor)Initialize the module once all methods have been added.
+ +

As before the signatures for the methods are Object mymethod(const Tuple& +args) and Object mykeywordmethod (const Tuple& args, const Dict& keys). In this +case, the methods must be methods of T.

+ +

To set the behaviors of the object you override some or all of these methods from +PythonExtension<T>:

+ +
    virtual int print( FILE *, int );
+    virtual Object getattr( const char * );
+    virtual int setattr( const char *, const Object & );
+    virtual Object getattro( const Object & );
+    virtual int setattro( const Object &, const Object & );
+    virtual int compare( const Object & );
+    virtual Object repr();
+    virtual Object str();
+    virtual long hash();
+    virtual Object call( const Object &, const Object & );
+
+    // Sequence methods
+    virtual int sequence_length();
+    virtual Object sequence_concat( const Object & );
+    virtual Object sequence_repeat( int );
+    virtual Object sequence_item( int );
+    virtual Object sequence_slice( int, int );
+    virtual int sequence_ass_item( int, const Object & );
+    virtual int sequence_ass_slice( int, int, const Object & );
+
+    // Mapping
+    virtual int mapping_length();
+    virtual Object mapping_subscript( const Object & );
+    virtual int mapping_ass_subscript( const Object &, const Object & );
+
+    // Number
+    virtual int number_nonzero();
+    virtual Object number_negative();
+    virtual Object number_positive();
+    virtual Object number_absolute();
+    virtual Object number_invert();
+    virtual Object number_int();
+    virtual Object number_float();
+    virtual Object number_long();
+    virtual Object number_oct();
+    virtual Object number_hex();
+    virtual Object number_add( const Object & );
+    virtual Object number_subtract( const Object & );
+    virtual Object number_multiply( const Object & );
+    virtual Object number_divide( const Object & );
+    virtual Object number_remainder( const Object & );
+    virtual Object number_divmod( const Object & );
+    virtual Object number_lshift( const Object & );
+    virtual Object number_rshift( const Object & );
+    virtual Object number_and( const Object & );
+    virtual Object number_xor( const Object & );
+    virtual Object number_or( const Object & );
+    virtual Object number_power( const Object &, const Object & );
+
+    // Buffer
+    virtual int buffer_getreadbuffer( int, void** );
+    virtual int buffer_getwritebuffer( int, void** );
+    virtual int buffer_getsegcount( int* );
+ +

Note that dealloc is not one of the functions you can override. That is what your +destructor is for. As noted below, dealloc behavior is provided for you by +PythonExtension.

+ +

Type initialization

+ +

To initialize your type, supply a static public member function that can be called +from the extension module. In that function, obtain the PythonType object by calling +behaviors() and apply appropriate "support" methods from PythonType to turn on +the support for that behavior or set of behaviors.

+ +
    void supportPrint(void);
+    void supportGetattr(void);
+    void supportSetattr(void);
+    void supportGetattro(void);
+    void supportSetattro(void);
+    void supportCompare(void);
+    void supportRepr(void);
+    void supportStr(void);
+    void supportHash(void);
+    void supportCall(void);
+
+    void supportSequenceType(void);
+    void supportMappingType(void);
+    void supportNumberType(void);
+    void supportBufferType(void);
+ +

Then call add_varargs_method or add_keyword_method to add any methods desired to the +object.

+ +

Notes on memory management and extension objects

+ +

Normal Python objects exist only on the heap. That is unfortunate, as object creation +and destruction can be relatively expensive. Class PythonExtension allows creation of both +local and heap-based objects.

+ +

If an extension object is created using operator new, as in:

+ +
range* my_r_ref = new range(1, 20, 3)
+ +

then the entity my_r_ref can be thought of as "owning" the reference created +in the new object. Thus, the object will never have a reference count of zero. If the +creator wishes to delete this object, they should either make sure the reference count is +1 and then do delete my_r_ref, or decrement the reference with Py_DECREF(my_r_ref).

+ +

Should my_r_ref give up ownership by being used in an Object constructor, all will +still be well. When the Object goes out of scope its destructor will be called, and that +will decrement the reference count, which in turn will trigger the special dealloc routine +that calls the destructor and deletes the pointer.

+ +

If the object is created with automatic scope, as in:

+ +
range my_r(1, 20, 3)
+ +

then my_r can be thought of as owning the reference, and when my_r goes out of scope +the object will be destroyed. Of course, care must be taken not to have kept any permanent +reference to this object. Fortunately, in the case of an exception, the C++ exception +facility will call the destructor of my_r. Naturally, care must be taken not to end up +with a dangling reference, but such objects can be created and destroyed more efficiently +than heap-based PyObjects.

+ +

Putting it all together

+ +

The Demo directory of the distribution contains an extensive example of how to use many +of the facilities in PyCXX. It also serves as a test routine. This test is not completely +exhaustive but does excercise much of the facility.

+ +

Acknowledgment

+ +

Thank you to Geoffrey Furnish for patiently teaching me the finer points of C++ and its +template facility, and his critique of PyCXX in particular. With version 4 I welcome Barry +Scott as co-author. -- Paul Dubois

+ + + diff --git a/lib/kross/python/cxx/README.html b/lib/kross/python/cxx/README.html new file mode 100644 index 00000000..d698725a --- /dev/null +++ b/lib/kross/python/cxx/README.html @@ -0,0 +1,436 @@ + + + +PyCXX README + + + + + + +

PyCXX -- Python C++ Extensions Support

+ +

Installation using distutils

+ +

Windows Installation and Demo

+
    +
  1. Fetch +http://prdownloads.sourceforge.net/cxx/pycxx_5_3_1.tar.gz +
  2. Expand the archive into a directory of your choosing C:\ for example. Note: WinZip can expand .tar.gz files. +
  3. Install the PyCXX files: +
      +
    1. C:> cd \pycxx_5_3_1
      +
    2. C:\pycxx_5_3_1> python setup.py install
      +
    +
  4. Install the PyCXX Demo: +
      +
    1. C:> cd \PYCXX_5_3_1\Demo
      +
    2. C:\PYCXX_5_3_1\Demo> python setup.py install
      +
    +
  5. Run the demo: +
      +
    1. C:> python
      +
    2. >>> import CXX.example
      +
    3. >>> CXX.example.test()
      +
    4. >>> r = CXX.example.range( 11, 100, 13 )
      +
    5. >>> for i in r: print i
      +
    6. ...
      +
    + +
+ + +

Unix Installation and Demo

+

Note: distutils is not available for Python 1.5.2

+ +
    +
  1. Fetch +http://prdownloads.sourceforge.net/cxx/PyCXX-V5.3.0.tar.gz +
  2. Login as root. root access is typically needed on Unix systems to install the PyCXX files into the Python directories. +
  3. Expand the archive into a directory of your choosing ~\ for example. +
  4. Install the PyCXX files: +
      +
    1. # cd ~\PYCXX_5_3_1
      +
    2. # python setup.py install
      +
    +
  5. Install the PyCXX Demo: +
      +
    1. # cd ~\PYCXX_5_3_1\Demo
      +
    2. # python setup.py install
      +
    +
  6. Run the demo: +
      +
    1. ~ python
      +
    2. >>> import CXX.example
      +
    3. >>> CXX.example.test()
      +
    4. >>> r = CXX.example.range( 11, 100, 13 )
      +
    5. >>> for i in r: print i
      +
    6. ...
      +
    + +
+ +

Installation using Project and Makefile

+ +

If you cannot or do not wish to use the distutils methods to work with PyCXX a set +of Makefiles and Project files are provided.

+ +

Windows Installation and Demo

+

+

    +
  1. Fetch +http://prdownloads.sourceforge.net/cxx/pycxx_5_3_1.tar.gz +
  2. Expand the archive into a directory of your choosing C:\ for example. WinZip can expand .tar.gz files. +
  3. Build the example. Using Microsoft Visual C++ 6.0 load the workspace corresponsing to the version of +Python you wish the work with. +
      +
    • example_py15.dsw - Python 1.5.2 +
    • example_py20.dsw - Python 2.0 and 2.0.1 +
    • example_py21.dsw - Python 2.1 and 2.1.1 +
    • example_py22.dsw - Python 2.2 and its maintanence release +
    • example_py23.dsw - Python 2.3 and its maintanence release +
    +
  4. Run the example. (I'll assume you are testing Python 2.3) +
      +
    • cd c:\PYCXX_5_3_1\pyds23 +
    • c:\python21\python -c "import example;example.test()" +
    +
+

+

Unix Installation and Demo

+

+

    +
  1. Fetch +http://prdownloads.sourceforge.net/cxx/PyCXX-V5.3.0.tar.gz +
  2. Expand the archive into a directory of your choosing ~/ for example. +
  3. Select to makefile for your system and python version. +
      +
    • example_freebsd_py15.mak - FreeBSD Python 1.5.2 (see note below) +
    • example_freebsd_py20.mak - FreeBSD Python 2.0, 2.0.1 +
    • example_freebsd_py21.mak - FreeBSD Python 2.1, 2.1.1 +
    • example_freebsd_py22.mak - FreeBSD Python 2.2 +
    • example_linux_py15.mak - Linux Python 1.5.2 +
    • example_linux_py20.mak - Linux Python 2.0, 2.0.1 +
    • example_linux_py21.mak - Linux Python 2.1, 2.1.1 +
    • example_linux_py22.mak - Linux Python 2.2 +
    +
  4. Build the example. Use GNU make
    +$ make -f example-makefile example.so +
  5. Run the example.
    +$ make -f example-makefile test +
+

+ +

Note: The Unix version of python 1.5.2 may need to be rebuilt so that C++ is support. +If you get reports of undefined symbols like cout or cerr then its likely that python +is not compiled and linked to support C and C++.

+ +

To create a makefile for another vendors Unix follow these steps:

+
    +
  1. copy one of the example make files above. +
  2. edit the variables to match your Python installation and C++ compile needs +
  3. Proceed to build and test as above. +
+

Note: most of the makefile rules to build PyCXX and its example are contained in example_common.mak. +

+ +

Revision History

+

Version 5.3.1 (19-Jan-2005)

+

Support GCC4 and Microsoft .NET 2003 aka MSVC 7.1 + +

Version 5.3 (21-Oct-2004)

+

String object now support python string and unicode string objects. +

Fix most bugs reported on SourceForge + +

Version 5.2 (27-Nov-2003)

+

PyCXX supports Python version 2.3, 2.2, 2.1, 2.0 and 1.5.2 on Windows and Unix.

+

Fixed problems with keyword functions.

+

Improve Extension API to give access to names and docs +

Support GCC 3.

+

Added support for custom Exceptions

+

+ +

Version 5.1 (2-Aug-2001)

+

I'm using the name PyCXX for this package, CXX is far to close to a compilers name.

+ +

PyCXX supports Python version 2.2, 2.1.1, 2.1, 2.0, 2.0.1 and 1.5.2 on Windows and Unix.

+ +

New in this release:

+
    +
  • Support for the Windows Linker /DELAYLOAD feature. Enable this feature by +defining PY_WIN32_DELAYLOAD_PYTHON_DLL when compiling IndirectPythonInterface.cxx +
  • Remove "CXX/Array.hxx" and associated code - its does not belong in PyCXX +
  • Work on the docs. Mostly to clean up the HTML to allow more extensive work. +
  • Reformated the sources to a consistent style. The mix of styles and tabs sizes +was making working on the sources error prone. +
  • Added workaround to setup.py to allow GCC to compile c++ code. +
  • Added Microsoft Visual C++ 6.0 project files for 1.5, 2.0 and 2.1 builds +
  • Added Unix make files for Linux (tested on RedHat 7.1) and FreeBSD (tested on 4.3) +
  • Merged changes from Tom Malcolmson +
+ +

(July 9, 2000)

+

Renamed all header files to reflect the CXX include name space and that they are +C++ header files. +

+ + + + + + +
OldNew
#include "CXX_Config.h"#include "CXX/Config.hxx"
#include "CXX_Exception.h"#include "CXX/Exception.hxx"
#include "CXX_Extensions.h"#include "CXX/Extensions.hxx"
#include "CXX_Objects.h"#include "CXX/Objects.hxx"
+ +

Version 5 (May 18, 2000)

+

This version adds Distutils support for installation and some code cleanup.

+ +

Version 4 (October 11, 1999)

+ +

This version contains a massive revision to the part of CXX that supports creating +extension objects and extension modules. Barry Scott contributed these changes.

+ +

CXX has always consisted of two parts: the basic CXX_Objects.h and the more +experimental CXX_Extensions.h. We will describe the changes to CXX_Objects first, and then +the changes to CXX_Extensions.h.

+ +

Changes to CXX_Objects

+ +

1. Owned option eliminates need for FromAPI in most cases

+ +

Object's constructor from PyObject* and method set have a new (backward compatible) +signature:

+ +
+Object (PyObject* pyob, bool owned = false);
+void set(PyObject* pyob, bool owned = false);
+
+ +

Users may call these with owned = true if they own the reference pyob already and want +the Object instance to take over ownership.

+ +

A new inline function Object asObject(PyObject* pyob) returns Object(pyob, true); thus, +one way to construct an object from a pointer returned by the Python API is to call +asObject on it.

+ +

Previously a class FromAPI was provided to solve the problem of taking over an owned +reference. FromAPI will be eliminated in the next release. It is no longer used by CXX +itself or its demos. The new mechanism is much cleaner and more efficient.

+ +

Other classes in CXX have been given the same "owned" option on their +constructors: Int, Float, Long, Complex, SeqBase<T>, Tuple, List, Dict, Module, +Callable.

+ +

2. Namespace support in compiler assumed

+ +

Since EGCS / GCC now supports namespaces and the standard library, the need for +CXX_config.h is almost gone. We have eliminated all the macros except for one obscure one +dealing with iterator traits in the standard library.

+ +

Changes to CXX_Extensions

+ +

The changes to CXX_Extensions.h are not backward compatible. However, they simplify +coding so much that we think it is worth the disruption.

+ +

1. Creating an extension module

+ +

To create an extension module, you inherit from class ExtensionModule templated on +yourself: In the constructor, you make calls to register methods of this class with Python +as extension module methods. In this example, two methods are added (this is a simplified +form of the example in Demo/example.cxx):

+ +
class example_module : public ExtensionModule<example_module>
+{
+public:
+    example_module()
+	: ExtensionModule<example_module>( "example" )
+	{
+	add_varargs_method("sum", &example_module::ex_sum, "sum(arglist) = sum of arguments");
+	add_varargs_method("test", &example_module::ex_test, "test(arglist) runs a test suite");
+
+	initialize( "documentation for the example module" );
+	}
+
+    virtual ~example_module() {}
+
+private:
+    Object ex_sum (const Tuple &a) { ... }
+    Object ex_test( const Tuple &a) { ... }
+};
+
+ +

To initialize the extension, you just instantiate one static instance (static so it +doesn't destroy itself!):

+ +
+void initexample()
+    {
+    static example_module* example = new example_module;
+    }
+
+ +

The methods can be written to take Tuples as arguments and return Objects. If +exceptions occur they are trapped for you and a Python exception is generated. So, for +example, the implementation of ex_sum might be:

+ +
+Object ex_sum (const Tuple &a)
+    {
+        Float f(0.0);
+        for( int i = 0; i < a.length(); i++ )
+        { 
+            Float g(a[i]);
+            f = f + g;
+        }
+        return f;
+    }
+
+ +

class ExtensionModule contains methods to return itself as a Module object, or to +return its dictionary.

+ +

Creating extension objects

+ +

Creating extension objects is of course harder since you must specify how the object +behaves and give it methods. This is shown in some detail in the example range.h and range.cxx, +with the test routine rangetest.cxx, in directory Demo.

+ +

Here is a brief overview. You create a class that inherits from PythonExtension +templated upon itself. You override various methods from PythonExtension to implement +behaviors, such as getattr, sequence_item, etc. You can also add methods to the object +that are usable from Python using a similar scheme as for module methods above.

+ +

One of the consequences of inheriting from PythonExtension is that you are inheriting +from PyObject itself. So your class is-a PyObject and instances of it can be passed to the +Python C API. Note: this example uses the namespace feature of CXX. The Py:: 's are not +required if you use the namespace instead.

+ +
+class range: public Py::PythonExtension<range> {
+public:
+    ... constructors, etc.
+
+    ... methods
+    // initializer, see below
+    static void init_type();
+    // override functions from PythonExtension
+    virtual Py::Object repr();
+    virtual Py::Object getattr( const char *name );
+
+    virtual int sequence_length();
+    virtual Py::Object sequence_item( int i );
+    virtual Py::Object sequence_concat( const Py::Object &j );
+    virtual Py::Object sequence_slice( int i, int j );
+
+    // define python methods of this object
+    Py::Object amethod (const Py::Tuple& args);
+    Py::Object value (const Py::Tuple& args);
+    Py::Object assign (const Py::Tuple& args); 
+};
+
+ +

+To initialize the type you provide a static method that you can call from some module's +initializer. This method sets the name, doc string, and indicates which behaviors it +supports. It then adds the methods.

+ +
+void range::init_type()
+{
+    behaviors().name("range");
+    behaviors().doc("range objects: start, stop, step");
+    behaviors().supportRepr();
+    behaviors().supportGetattr();
+    behaviors().supportSequenceType();
+
+    add_varargs_method("amethod", &range::amethod,
+        "demonstrate how to document amethod");
+    add_varargs_method("assign", &range::assign);
+    add_varargs_method("value", &range::value);
+}
+
+ +

Version 3 (June 18, 1999)

+ +

1. CXX compiles with EGCS snapshot 19990616. EGCS requires a standard library class +random_access_iterator that is not yet available in some other compilers (such as Windows +VC6). Therefore a new switch:

+ +

STANDARD_LIBRARY_HAS_ITERATOR_TRAITS

+ +

has been added to CXX_Config.h that you may need to toggle if you get an error on the +two lines that mention random_access_iterator. The current definition is correct for VC6 +and EGCS-19990616.

+ +

2. A new constructor was added to Module to allow construction from a string containing +the module name. A test was added for this to the demo.

+ +

Version 2 (Dec. 28, 1998)

+ +

Fixed definition of extension type to match 1.5.2. This version will presumably not +compile with older versions of Python. This can be fixed by using the previous version's +definition. I did not take the time to find out what these new "flags" are for +nor put in any methods to deal with them.

+ +

Version 1

+ +

This is an experimental set of files for supporting the creation of Python extensions +in C++.

+ +

Documentation is in progress at http://xfiles.llnl.gov. +

+ +

To use CXX you use the header files in Include, such as CXX_Objects.h or +CXX_Extensions.h. You must include the sources in Src in your sources to supply parts of +the CXX classes required.

+ +

A demo is included. The Setup file in this directory compiles this demo named +"example". To try the demo, which is also a test routine, you import example and +then execute:

+ +
+example.test()
+
+ +

You can also play with the extension object whose constructor is named "range":

+ +
+s = range(1, 100, 2)
+print s[2]  # should print 5
+
+ +

Compilation with Microsoft Visual C++ 5.0 will succeed but only if you have Service +Pack 3 installed. Compilation has been known to succeed on a Unix system using KCC by +using:

+ +
+setenv CCC "KCC -x"
+
+ +

before running makethis.py.

+ +

There is also a python.cxx file for making a stand-alone Python containing this +example, as well as a similar file arraytest.cxx for testing Array.

+ +

Comments to barry@barrys-emacs.org, please.

+ +

Barry Scott

+ + diff --git a/lib/kross/python/cxx/Readme.Kross.txt b/lib/kross/python/cxx/Readme.Kross.txt new file mode 100644 index 00000000..ec00230a --- /dev/null +++ b/lib/kross/python/cxx/Readme.Kross.txt @@ -0,0 +1,16 @@ +Kross uses PyCXX 5.3.1 (http://cxx.sourceforge.net/) +to access the Python C API. + +Following patches where applied and send back to the PyCXX team. + +- Unsigned patch + http://sourceforge.net/tracker/index.php?func=detail&aid=1085205&group_id=3180&atid=303180 +- isInstance patch + http://sourceforge.net/tracker/index.php?func=detail&aid=1178048&group_id=3180&atid=303180 +- dir patch + http://sourceforge.net/tracker/index.php?func=detail&aid=1186676&group_id=3180&atid=303180 +- fixed typos + http://sourceforge.net/tracker/index.php?func=detail&aid=1266579&group_id=3180&atid=103180 + http://sourceforge.net/tracker/index.php?func=detail&aid=1293777&group_id=3180&atid=103180 + +I also changed the includes to get PyCXX compiled the way we use it. diff --git a/lib/kross/python/cxx/Version.txt b/lib/kross/python/cxx/Version.txt new file mode 100644 index 00000000..27be42b3 --- /dev/null +++ b/lib/kross/python/cxx/Version.txt @@ -0,0 +1 @@ +PyCxx 5.3.1 diff --git a/lib/kross/python/cxx/cxx_extensions.cxx b/lib/kross/python/cxx/cxx_extensions.cxx new file mode 100644 index 00000000..f9c942ad --- /dev/null +++ b/lib/kross/python/cxx/cxx_extensions.cxx @@ -0,0 +1,1287 @@ +#include "Extensions.hxx" +#include "Exception.hxx" + +#include + +namespace Py +{ + +//================================================================================ +// +// Implementation of MethodTable +// +//================================================================================ + +PyMethodDef MethodTable::method( const char* method_name, PyCFunction f, int flags, const char* doc ) + { + PyMethodDef m; + m.ml_name = const_cast( method_name ); + m.ml_meth = f; + m.ml_flags = flags; + m.ml_doc = const_cast( doc ); + return m; + } + +MethodTable::MethodTable() + { + t.push_back( method( 0, 0, 0, 0 ) ); + mt = 0; + } + +MethodTable::~MethodTable() + { + delete [] mt; + } + +void MethodTable::add( const char* method_name, PyCFunction f, const char* doc, int flag ) + { + if( !mt ) + { + t.insert( t.end()-1, method( method_name, f, flag, doc ) ); + } + else + { + throw RuntimeError( "Too late to add a module method!" ); + } + } + +PyMethodDef* MethodTable::table() + { + if( !mt ) + { + int t1size = t.size(); + mt = new PyMethodDef[t1size]; + int j = 0; + for( std::vector::iterator i = t.begin(); i != t.end(); i++ ) + { + mt[j++] = *i; + } + } + return mt; + } + +//================================================================================ +// +// Implementation of ExtensionModule +// +//================================================================================ +ExtensionModuleBase::ExtensionModuleBase( const char *name ) + : module_name( name ) + , full_module_name( __Py_PackageContext() != NULL ? std::string( __Py_PackageContext() ) : module_name ) + , method_table() + {} + +ExtensionModuleBase::~ExtensionModuleBase() + {} + +const std::string &ExtensionModuleBase::name() const + { + return module_name; + } + +const std::string &ExtensionModuleBase::fullName() const + { + return full_module_name; + } + +class ExtensionModuleBasePtr : public PythonExtension + { +public: + ExtensionModuleBasePtr( ExtensionModuleBase *_module ) + : module( _module ) + {} + virtual ~ExtensionModuleBasePtr() + {} + + ExtensionModuleBase *module; + }; + + +void ExtensionModuleBase::initialize( const char *module_doc ) + { + PyObject *module_ptr = new ExtensionModuleBasePtr( this ); + + Py_InitModule4 + ( + const_cast( module_name.c_str() ), // name + method_table.table(), // methods + const_cast( module_doc ), // docs + module_ptr, // pass to functions as "self" + PYTHON_API_VERSION // API version + ); + } + +Py::Module ExtensionModuleBase::module(void) const + { + return Module( full_module_name ); + } + +Py::Dict ExtensionModuleBase::moduleDictionary(void) const + { + return module().getDict(); + } + +//-------------------------------------------------------------------------------- + +//================================================================================ +// +// Implementation of PythonType +// +//================================================================================ + +extern "C" + { + static void standard_dealloc(PyObject* p); + // + // All the following functions redirect the call from Python + // onto the matching virtual function in PythonExtensionBase + // + static int print_handler (PyObject*, FILE *, int); + static PyObject* getattr_handler (PyObject*, char*); + static int setattr_handler (PyObject*, char*, PyObject*); + static PyObject* getattro_handler (PyObject*, PyObject*); + static int setattro_handler (PyObject*, PyObject*, PyObject*); + static int compare_handler (PyObject*, PyObject*); + static PyObject* repr_handler (PyObject*); + static PyObject* str_handler (PyObject*); + static long hash_handler (PyObject*); + static PyObject* call_handler (PyObject*, PyObject*, PyObject*); + +#if PY_VERSION_HEX < 0x02050000 + typedef int Py_ssize_t; +#endif + // Sequence methods + static Py_ssize_t sequence_length_handler(PyObject*); + static PyObject* sequence_concat_handler(PyObject*,PyObject*); + static PyObject* sequence_repeat_handler(PyObject*, Py_ssize_t); + static PyObject* sequence_item_handler(PyObject*, Py_ssize_t); + static PyObject* sequence_slice_handler(PyObject*, Py_ssize_t, Py_ssize_t); + static int sequence_ass_item_handler(PyObject*, Py_ssize_t, PyObject*); + static int sequence_ass_slice_handler(PyObject*, Py_ssize_t, Py_ssize_t, PyObject*); + // Mapping + static Py_ssize_t mapping_length_handler(PyObject*); + static PyObject* mapping_subscript_handler(PyObject*, PyObject*); + static int mapping_ass_subscript_handler(PyObject*, PyObject*, PyObject*); + + // Numeric methods + static int number_nonzero_handler (PyObject*); + static PyObject* number_negative_handler (PyObject*); + static PyObject* number_positive_handler (PyObject*); + static PyObject* number_absolute_handler (PyObject*); + static PyObject* number_invert_handler (PyObject*); + static PyObject* number_int_handler (PyObject*); + static PyObject* number_float_handler (PyObject*); + static PyObject* number_long_handler (PyObject*); + static PyObject* number_oct_handler (PyObject*); + static PyObject* number_hex_handler (PyObject*); + static PyObject* number_add_handler (PyObject*, PyObject*); + static PyObject* number_subtract_handler (PyObject*, PyObject*); + static PyObject* number_multiply_handler (PyObject*, PyObject*); + static PyObject* number_divide_handler (PyObject*, PyObject*); + static PyObject* number_remainder_handler (PyObject*, PyObject*); + static PyObject* number_divmod_handler (PyObject*, PyObject*); + static PyObject* number_lshift_handler (PyObject*, PyObject*); + static PyObject* number_rshift_handler (PyObject*, PyObject*); + static PyObject* number_and_handler (PyObject*, PyObject*); + static PyObject* number_xor_handler (PyObject*, PyObject*); + static PyObject* number_or_handler (PyObject*, PyObject*); + static PyObject* number_power_handler(PyObject*, PyObject*, PyObject*); + + // Buffer + static Py_ssize_t buffer_getreadbuffer_handler (PyObject*, Py_ssize_t, void**); + static Py_ssize_t buffer_getwritebuffer_handler (PyObject*, Py_ssize_t, void**); + static Py_ssize_t buffer_getsegcount_handler (PyObject*, Py_ssize_t*); + } + + +extern "C" void standard_dealloc( PyObject* p ) + { + PyMem_DEL( p ); + } + +void PythonType::supportSequenceType() + { + if( !sequence_table ) + { + sequence_table = new PySequenceMethods; + table->tp_as_sequence = sequence_table; + sequence_table->sq_length = sequence_length_handler; + sequence_table->sq_concat = sequence_concat_handler; + sequence_table->sq_repeat = sequence_repeat_handler; + sequence_table->sq_item = sequence_item_handler; + sequence_table->sq_slice = sequence_slice_handler; + + sequence_table->sq_ass_item = sequence_ass_item_handler; // BAS setup seperately? + sequence_table->sq_ass_slice = sequence_ass_slice_handler; // BAS setup seperately? + } + } + +void PythonType::supportMappingType() + { + if( !mapping_table ) + { + mapping_table = new PyMappingMethods; + table->tp_as_mapping = mapping_table; + mapping_table->mp_length = mapping_length_handler; + mapping_table->mp_subscript = mapping_subscript_handler; + mapping_table->mp_ass_subscript = mapping_ass_subscript_handler; // BAS setup seperately? + } + } + +void PythonType::supportNumberType() + { + if( !number_table ) + { + number_table = new PyNumberMethods; + table->tp_as_number = number_table; + number_table->nb_add = number_add_handler; + number_table->nb_subtract = number_subtract_handler; + number_table->nb_multiply = number_multiply_handler; + number_table->nb_divide = number_divide_handler; + number_table->nb_remainder = number_remainder_handler; + number_table->nb_divmod = number_divmod_handler; + number_table->nb_power = number_power_handler; + number_table->nb_negative = number_negative_handler; + number_table->nb_positive = number_positive_handler; + number_table->nb_absolute = number_absolute_handler; + number_table->nb_nonzero = number_nonzero_handler; + number_table->nb_invert = number_invert_handler; + number_table->nb_lshift = number_lshift_handler; + number_table->nb_rshift = number_rshift_handler; + number_table->nb_and = number_and_handler; + number_table->nb_xor = number_xor_handler; + number_table->nb_or = number_or_handler; + number_table->nb_coerce = 0; + number_table->nb_int = number_int_handler; + number_table->nb_long = number_long_handler; + number_table->nb_float = number_float_handler; + number_table->nb_oct = number_oct_handler; + number_table->nb_hex = number_hex_handler; + } + } + +void PythonType::supportBufferType() + { + if( !buffer_table ) + { + buffer_table = new PyBufferProcs; + table->tp_as_buffer = buffer_table; + buffer_table->bf_getreadbuffer = buffer_getreadbuffer_handler; + buffer_table->bf_getwritebuffer = buffer_getwritebuffer_handler; + buffer_table->bf_getsegcount = buffer_getsegcount_handler; + } + } + +// if you define one sequence method you must define +// all of them except the assigns + +PythonType::PythonType( size_t basic_size, int itemsize, const char *default_name ) + : table( new PyTypeObject ) + , sequence_table( NULL ) + , mapping_table( NULL ) + , number_table( NULL ) + , buffer_table( NULL ) + { + *reinterpret_cast( table ) = py_object_initializer; + table->ob_type = _Type_Type(); + table->ob_size = 0; + table->tp_name = const_cast( default_name ); + table->tp_basicsize = basic_size; + table->tp_itemsize = itemsize; + table->tp_dealloc = ( destructor ) standard_dealloc; + table->tp_print = 0; + table->tp_getattr = 0; + table->tp_setattr = 0; + table->tp_compare = 0; + table->tp_repr = 0; + table->tp_as_number = 0; + table->tp_as_sequence = 0; + table->tp_as_mapping = 0; + table->tp_hash = 0; + table->tp_call = 0; + table->tp_str = 0; + table->tp_getattro = 0; + table->tp_setattro = 0; + table->tp_as_buffer = 0; + table->tp_flags = 0L; + table->tp_doc = 0; +#if PY_MAJOR_VERSION > 2 || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 0) + // first use in 2.0 + table->tp_traverse = 0L; + table->tp_clear = 0L; +#else + table->tp_xxx5 = 0L; + table->tp_xxx6 = 0L; +#endif +#if PY_MAJOR_VERSION > 2 || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 1) + // first defined in 2.1 + table->tp_richcompare = 0L; + table->tp_weaklistoffset = 0L; +#else + table->tp_xxx7 = 0L; + table->tp_xxx8 = 0L; +#endif + +#ifdef COUNT_ALLOCS + table->tp_alloc = 0; + table->tp_free = 0; + table->tp_maxalloc = 0; + table->tp_next = 0; +#endif + } + +PythonType::~PythonType( ) + { + delete table; + delete sequence_table; + delete mapping_table; + delete number_table; + delete buffer_table; + } + +PyTypeObject* PythonType::type_object( ) const + {return table;} + +void PythonType::name( const char* nam ) + { + table->tp_name = const_cast( nam ); + } + +const char *PythonType::getName() const + { + return table->tp_name; + } + +void PythonType::doc( const char* d ) + { + table->tp_doc = const_cast( d ); + } + +const char *PythonType::getDoc() const + { + return table->tp_doc; + } + +void PythonType::dealloc( void( *f )( PyObject* )) + { + table->tp_dealloc = f; + } + +void PythonType::supportPrint() + { + table->tp_print = print_handler; + } + +void PythonType::supportGetattr() + { + table->tp_getattr = getattr_handler; + } + +void PythonType::supportSetattr() + { + table->tp_setattr = setattr_handler; + } + +void PythonType::supportGetattro() + { + table->tp_getattro = getattro_handler; + } + +void PythonType::supportSetattro() + { + table->tp_setattro = setattro_handler; + } + +void PythonType::supportCompare() + { + table->tp_compare = compare_handler; + } + +void PythonType::supportRepr() + { + table->tp_repr = repr_handler; + } + +void PythonType::supportStr() + { + table->tp_str = str_handler; + } + +void PythonType::supportHash() + { + table->tp_hash = hash_handler; + } + +void PythonType::supportCall() + { + table->tp_call = call_handler; + } + +//-------------------------------------------------------------------------------- +// +// Handlers +// +//-------------------------------------------------------------------------------- +extern "C" int print_handler( PyObject *self, FILE *fp, int flags ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return p->print( fp, flags ); + } + catch( Py::Exception & ) + { + return -1; // indicate error + } + } + +extern "C" PyObject* getattr_handler( PyObject *self, char *name ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->getattr( name ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" int setattr_handler( PyObject *self, char *name, PyObject *value ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return p->setattr( name, Py::Object( value ) ); + } + catch( Py::Exception & ) + { + return -1; // indicate error + } + } + +extern "C" PyObject* getattro_handler( PyObject *self, PyObject *name ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->getattro( Py::Object( name ) ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" int setattro_handler( PyObject *self, PyObject *name, PyObject *value ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return p->setattro( Py::Object( name ), Py::Object( value ) ); + } + catch( Py::Exception & ) + { + return -1; // indicate error + } + } + +extern "C" int compare_handler( PyObject *self, PyObject *other ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return p->compare( Py::Object( other ) ); + } + catch( Py::Exception & ) + { + return -1; // indicate error + } + } + +extern "C" PyObject* repr_handler( PyObject *self ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->repr() ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* str_handler( PyObject *self ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->str() ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" long hash_handler( PyObject *self ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return p->hash(); + } + catch( Py::Exception & ) + { + return -1; // indicate error + } + } + +extern "C" PyObject* call_handler( PyObject *self, PyObject *args, PyObject *kw ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->call( Py::Object( args ), Py::Object( kw ) ) ); + if( kw != NULL ) + return new_reference_to( p->call( Py::Object( args ), Py::Object( kw ) ) ); + else + return new_reference_to( p->call( Py::Object( args ), Py::Object() ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + + +// Sequence methods +extern "C" Py_ssize_t sequence_length_handler( PyObject *self ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return p->sequence_length(); + } + catch( Py::Exception & ) + { + return -1; // indicate error + } + } + +extern "C" PyObject* sequence_concat_handler( PyObject *self, PyObject *other ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->sequence_concat( Py::Object( other ) ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* sequence_repeat_handler( PyObject *self, Py_ssize_t count ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->sequence_repeat( count ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* sequence_item_handler( PyObject *self, Py_ssize_t index ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->sequence_item( index ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* sequence_slice_handler( PyObject *self, Py_ssize_t first, Py_ssize_t last ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->sequence_slice( first, last ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" int sequence_ass_item_handler( PyObject *self, Py_ssize_t index, PyObject *value ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return p->sequence_ass_item( index, Py::Object( value ) ); + } + catch( Py::Exception & ) + { + return -1; // indicate error + } + } + +extern "C" int sequence_ass_slice_handler( PyObject *self, Py_ssize_t first, Py_ssize_t last, PyObject *value ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return p->sequence_ass_slice( first, last, Py::Object( value ) ); + } + catch( Py::Exception & ) + { + return -1; // indicate error + } + } + +// Mapping +extern "C" Py_ssize_t mapping_length_handler( PyObject *self ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return p->mapping_length(); + } + catch( Py::Exception & ) + { + return -1; // indicate error + } + } + +extern "C" PyObject* mapping_subscript_handler( PyObject *self, PyObject *key ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->mapping_subscript( Py::Object( key ) ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" int mapping_ass_subscript_handler( PyObject *self, PyObject *key, PyObject *value ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return p->mapping_ass_subscript( Py::Object( key ), Py::Object( value ) ); + } + catch( Py::Exception & ) + { + return -1; // indicate error + } + } + +// Number +extern "C" int number_nonzero_handler( PyObject *self ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return p->number_nonzero(); + } + catch( Py::Exception & ) + { + return -1; // indicate error + } + } + +extern "C" PyObject* number_negative_handler( PyObject *self ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_negative() ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_positive_handler( PyObject *self ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_positive() ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_absolute_handler( PyObject *self ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_absolute() ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_invert_handler( PyObject *self ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_invert() ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_int_handler( PyObject *self ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_int() ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_float_handler( PyObject *self ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_float() ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_long_handler( PyObject *self ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_long() ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_oct_handler( PyObject *self ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_oct() ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_hex_handler( PyObject *self ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_hex() ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_add_handler( PyObject *self, PyObject *other ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_add( Py::Object( other ) ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_subtract_handler( PyObject *self, PyObject *other ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_subtract( Py::Object( other ) ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_multiply_handler( PyObject *self, PyObject *other ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_multiply( Py::Object( other ) ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_divide_handler( PyObject *self, PyObject *other ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_divide( Py::Object( other ) ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_remainder_handler( PyObject *self, PyObject *other ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_remainder( Py::Object( other ) ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_divmod_handler( PyObject *self, PyObject *other ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_divmod( Py::Object( other ) ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_lshift_handler( PyObject *self, PyObject *other ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_lshift( Py::Object( other ) ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_rshift_handler( PyObject *self, PyObject *other ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_rshift( Py::Object( other ) ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_and_handler( PyObject *self, PyObject *other ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_and( Py::Object( other ) ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_xor_handler( PyObject *self, PyObject *other ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_xor( Py::Object( other ) ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_or_handler( PyObject *self, PyObject *other ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_or( Py::Object( other ) ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +extern "C" PyObject* number_power_handler( PyObject *self, PyObject *x1, PyObject *x2 ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return new_reference_to( p->number_power( Py::Object( x1 ), Py::Object( x2 ) ) ); + } + catch( Py::Exception & ) + { + return NULL; // indicate error + } + } + +// Buffer +extern "C" Py_ssize_t buffer_getreadbuffer_handler( PyObject *self, Py_ssize_t index, void **pp ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return p->buffer_getreadbuffer( index, pp ); + } + catch( Py::Exception & ) + { + return -1; // indicate error + } + } + +extern "C" Py_ssize_t buffer_getwritebuffer_handler( PyObject *self, Py_ssize_t index, void **pp ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + return p->buffer_getwritebuffer( index, pp ); + } + catch( Py::Exception & ) + { + return -1; // indicate error + } + } + +extern "C" Py_ssize_t buffer_getsegcount_handler( PyObject *self, Py_ssize_t *count ) + { + try + { + PythonExtensionBase *p = static_cast( self ); + int i_count = *count; + Py_ssize_t r = p->buffer_getsegcount( &i_count ); + *count = i_count; + return r; + } + catch( Py::Exception & ) + { + return -1; // indicate error + } + } + + +//================================================================================ +// +// Implementation of PythonExtensionBase +// +//================================================================================ +#define missing_method( method ) \ +throw RuntimeError( "Extension object does not support method " #method ); + +PythonExtensionBase::PythonExtensionBase() + { + } + +PythonExtensionBase::~PythonExtensionBase() + { + assert( ob_refcnt == 0 ); + } + +int PythonExtensionBase::print( FILE *, int ) + { missing_method( print ); return -1; } + +int PythonExtensionBase::setattr( const char*, const Py::Object & ) + { missing_method( setattr ); return -1; } + +Py::Object PythonExtensionBase::getattro( const Py::Object & ) + { missing_method( getattro ); return Py::Nothing(); } + +int PythonExtensionBase::setattro( const Py::Object &, const Py::Object & ) + { missing_method( setattro ); return -1; } + +int PythonExtensionBase::compare( const Py::Object & ) + { missing_method( compare ); return -1; } + +Py::Object PythonExtensionBase::repr() + { missing_method( repr ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::str() + { missing_method( str ); return Py::Nothing(); } + +long PythonExtensionBase::hash() + { missing_method( hash ); return -1; } + +Py::Object PythonExtensionBase::call( const Py::Object &, const Py::Object & ) + { missing_method( call ); return Py::Nothing(); } + + +// Sequence methods +int PythonExtensionBase::sequence_length() + { missing_method( sequence_length ); return -1; } + +Py::Object PythonExtensionBase::sequence_concat( const Py::Object & ) + { missing_method( sequence_concat ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::sequence_repeat( int ) + { missing_method( sequence_repeat ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::sequence_item( int ) + { missing_method( sequence_item ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::sequence_slice( int, int ) + { missing_method( sequence_slice ); return Py::Nothing(); } + +int PythonExtensionBase::sequence_ass_item( int, const Py::Object & ) + { missing_method( sequence_ass_item ); return -1; } + +int PythonExtensionBase::sequence_ass_slice( int, int, const Py::Object & ) + { missing_method( sequence_ass_slice ); return -1; } + + +// Mapping +int PythonExtensionBase::mapping_length() + { missing_method( mapping_length ); return -1; } + +Py::Object PythonExtensionBase::mapping_subscript( const Py::Object & ) + { missing_method( mapping_subscript ); return Py::Nothing(); } + +int PythonExtensionBase::mapping_ass_subscript( const Py::Object &, const Py::Object & ) + { missing_method( mapping_ass_subscript ); return -1; } + + +// Number +int PythonExtensionBase::number_nonzero() + { missing_method( number_nonzero ); return -1; } + +Py::Object PythonExtensionBase::number_negative() + { missing_method( number_negative ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_positive() + { missing_method( number_positive ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_absolute() + { missing_method( number_absolute ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_invert() + { missing_method( number_invert ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_int() + { missing_method( number_int ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_float() + { missing_method( number_float ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_long() + { missing_method( number_long ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_oct() + { missing_method( number_oct ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_hex() + { missing_method( number_hex ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_add( const Py::Object & ) + { missing_method( number_add ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_subtract( const Py::Object & ) + { missing_method( number_subtract ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_multiply( const Py::Object & ) + { missing_method( number_multiply ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_divide( const Py::Object & ) + { missing_method( number_divide ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_remainder( const Py::Object & ) + { missing_method( number_remainder ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_divmod( const Py::Object & ) + { missing_method( number_divmod ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_lshift( const Py::Object & ) + { missing_method( number_lshift ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_rshift( const Py::Object & ) + { missing_method( number_rshift ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_and( const Py::Object & ) + { missing_method( number_and ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_xor( const Py::Object & ) + { missing_method( number_xor ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_or( const Py::Object & ) + { missing_method( number_or ); return Py::Nothing(); } + +Py::Object PythonExtensionBase::number_power( const Py::Object &, const Py::Object & ) + { missing_method( number_power ); return Py::Nothing(); } + + +// Buffer +int PythonExtensionBase::buffer_getreadbuffer( int, void** ) + { missing_method( buffer_getreadbuffer ); return -1; } + +int PythonExtensionBase::buffer_getwritebuffer( int, void** ) + { missing_method( buffer_getwritebuffer ); return -1; } + +int PythonExtensionBase::buffer_getsegcount( int* ) + { missing_method( buffer_getsegcount ); return -1; } + +//-------------------------------------------------------------------------------- +// +// Method call handlers for +// PythonExtensionBase +// ExtensionModuleBase +// +//-------------------------------------------------------------------------------- + +extern "C" PyObject *method_keyword_call_handler( PyObject *_self_and_name_tuple, PyObject *_args, PyObject *_keywords ) + { + try + { + Tuple self_and_name_tuple( _self_and_name_tuple ); + + PyObject *self_in_cobject = self_and_name_tuple[0].ptr(); + void *self_as_void = PyCObject_AsVoidPtr( self_in_cobject ); + if( self_as_void == NULL ) + return NULL; + + ExtensionModuleBase *self = static_cast( self_as_void ); + + String py_name( self_and_name_tuple[1] ); + std::string name( py_name.as_std_string() ); + + Tuple args( _args ); + if( _keywords == NULL ) + { + Dict keywords; // pass an empty dict + + Object result( self->invoke_method_keyword( name, args, keywords ) ); + return new_reference_to( result.ptr() ); + } + else + { + Dict keywords( _keywords ); + + Object result( self->invoke_method_keyword( name, args, keywords ) ); + return new_reference_to( result.ptr() ); + } + } + catch( Exception & ) + { + return 0; + } + } + +extern "C" PyObject *method_varargs_call_handler( PyObject *_self_and_name_tuple, PyObject *_args ) + { + try + { + Tuple self_and_name_tuple( _self_and_name_tuple ); + + PyObject *self_in_cobject = self_and_name_tuple[0].ptr(); + void *self_as_void = PyCObject_AsVoidPtr( self_in_cobject ); + if( self_as_void == NULL ) + return NULL; + + ExtensionModuleBase *self = static_cast( self_as_void ); + + String py_name( self_and_name_tuple[1] ); + std::string name( py_name.as_std_string() ); + + Tuple args( _args ); + + Object result( self->invoke_method_varargs( name, args ) ); + + return new_reference_to( result.ptr() ); + } + catch( Exception & ) + { + return 0; + } + } + +extern "C" void do_not_dealloc( void * ) + {} + + +//-------------------------------------------------------------------------------- +// +// ExtensionExceptionType +// +//-------------------------------------------------------------------------------- +ExtensionExceptionType::ExtensionExceptionType() + : Py::Object() + { + } + +void ExtensionExceptionType::init( ExtensionModuleBase &module, const std::string& name ) + { + std::string module_name( module.fullName() ); + module_name += "."; + module_name += name; + + set( PyErr_NewException( const_cast( module_name.c_str() ), NULL, NULL ), true ); + } + +ExtensionExceptionType::~ExtensionExceptionType() + { + } + +Exception::Exception( ExtensionExceptionType &exception, const std::string& reason ) + { + PyErr_SetString (exception.ptr(), reason.c_str()); + } + + +} // end of namespace Py diff --git a/lib/kross/python/cxx/cxxextensions.c b/lib/kross/python/cxx/cxxextensions.c new file mode 100644 index 00000000..6f369b2d --- /dev/null +++ b/lib/kross/python/cxx/cxxextensions.c @@ -0,0 +1,19 @@ +/* + Copyright 1998 The Regents of the University of California. + All rights reserved. See Legal.htm for full text and disclaimer. +*/ + +#include "Python.h" +#ifdef __cplusplus +extern "C" { +#endif +PyObject py_object_initializer = {PyObject_HEAD_INIT(0)}; +#ifdef __cplusplus +} +#endif + + + + + + diff --git a/lib/kross/python/cxx/cxxsupport.cxx b/lib/kross/python/cxx/cxxsupport.cxx new file mode 100644 index 00000000..61329b60 --- /dev/null +++ b/lib/kross/python/cxx/cxxsupport.cxx @@ -0,0 +1,142 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 1998 The Regents of the University of California. +// All rights reserved. See Legal.htm for full text and disclaimer. +//---------------------------------------------------------------------------// + +#include "Objects.hxx" +namespace Py { + +Py_UNICODE unicode_null_string[1] = { 0 }; + +Type Object::type () const + { + return Type (PyObject_Type (p), true); + } + +String Object::str () const + { + return String (PyObject_Str (p), true); + } + +String Object::repr () const + { + return String (PyObject_Repr (p), true); + } + +std::string Object::as_string() const + { + return static_cast(str()); + } + +List Object::dir () const + { + return List (PyObject_Dir (p), true); + } + +bool Object::isType (const Type& t) const + { + return type ().ptr() == t.ptr(); + } + +Char::operator String() const + { + return String(ptr()); + } + +// TMM: non-member operaters for iterators - see above +// I've also made a bug fix in respect to the cxx code +// (dereffed the left.seq and right.seq comparison) +bool operator==(const Sequence::iterator& left, const Sequence::iterator& right) + { + return left.eql( right ); + } + +bool operator!=(const Sequence::iterator& left, const Sequence::iterator& right) + { + return left.neq( right ); + } + +bool operator< (const Sequence::iterator& left, const Sequence::iterator& right) + { + return left.lss( right ); + } + +bool operator> (const Sequence::iterator& left, const Sequence::iterator& right) + { + return left.gtr( right ); + } + +bool operator<=(const Sequence::iterator& left, const Sequence::iterator& right) + { + return left.leq( right ); + } + +bool operator>=(const Sequence::iterator& left, const Sequence::iterator& right) + { + return left.geq( right ); + } + +// now for const_iterator +bool operator==(const Sequence::const_iterator& left, const Sequence::const_iterator& right) + { + return left.eql( right ); + } + +bool operator!=(const Sequence::const_iterator& left, const Sequence::const_iterator& right) + { + return left.neq( right ); + } + +bool operator< (const Sequence::const_iterator& left, const Sequence::const_iterator& right) + { + return left.lss( right ); + } + +bool operator> (const Sequence::const_iterator& left, const Sequence::const_iterator& right) + { + return left.gtr( right ); + } + +bool operator<=(const Sequence::const_iterator& left, const Sequence::const_iterator& right) + { + return left.leq( right ); + } + +bool operator>=(const Sequence::const_iterator& left, const Sequence::const_iterator& right) + { + return left.geq( right ); + } + +// For mappings: +bool operator==(const Mapping::iterator& left, const Mapping::iterator& right) + { + return left.eql( right ); + } + +bool operator!=(const Mapping::iterator& left, const Mapping::iterator& right) + { + return left.neq( right ); + } + +// now for const_iterator +bool operator==(const Mapping::const_iterator& left, const Mapping::const_iterator& right) + { + return left.eql( right ); + } + +bool operator!=(const Mapping::const_iterator& left, const Mapping::const_iterator& right) + { + return left.neq( right ); + } + +// TMM: 31May'01 - Added the #ifndef so I can exclude iostreams. +#ifndef CXX_NO_IOSTREAMS +// output + +std::ostream& operator<< (std::ostream& os, const Object& ob) + { + return (os << static_cast(ob.str())); + } +#endif + +} // Py diff --git a/lib/kross/python/pythonconfig.h b/lib/kross/python/pythonconfig.h new file mode 100644 index 00000000..64b4eefb --- /dev/null +++ b/lib/kross/python/pythonconfig.h @@ -0,0 +1,105 @@ +/*************************************************************************** + * pythonconfig.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_PYTHON_CONFIG_H +#define KROSS_PYTHON_CONFIG_H + +#include "../main/krossconfig.h" + +// Prevent warnings +#if defined(_XOPEN_SOURCE) + #undef _XOPEN_SOURCE +#endif +#if defined(_POSIX_C_SOURCE) + #undef _POSIX_C_SOURCE +#endif + +// The Python.h needs to be included first. +#include +#include +#include +#include +#include + +// Include the PyCXX stuff. +#include "cxx/Config.hxx" +#include "cxx/Objects.hxx" +#include "cxx/Extensions.hxx" + +namespace Kross { + +/** + * The Python plugin for the \a Kross scripting framework. + * + * The code in this namespace manage the embedded python + * interpreter and python-scripts. + * + * There is no dependency to e.g. the \a Kross::KexiDB + * wrapper. Everything is handled through the common + * \a Kross::Api bridge. Therefore this interpreter- + * implementation should be able to make all defined + * wrappers accessible by the python scripting + * language. + * + * Internaly we use PyCXX - a set of classes to help + * create extensions of python in the C++ language - to + * access the python c api. Any python version since + * 2.0 is supported. + * + * \author Sebastian Sauer + * \sa http://www.python.org + * \sa http://cxx.sourceforge.net + */ +namespace Python { + + // The version of this python plugin. This will be exported + // to the scripting code. That way we're able to write + // scripting code for different incompatible Kross python + // bindings by checking the version. You should increment + // this number only if you really know what you're doing. + #define KROSS_PYTHON_VERSION 1 + + // Enable debugging for Kross::Python::PythonScript + //#define KROSS_PYTHON_SCRIPT_CTOR_DEBUG + //#define KROSS_PYTHON_SCRIPT_DTOR_DEBUG + //#define KROSS_PYTHON_SCRIPT_INIT_DEBUG + //#define KROSS_PYTHON_SCRIPT_FINALIZE_DEBUG + //#define KROSS_PYTHON_SCRIPT_EXEC_DEBUG + //#define KROSS_PYTHON_SCRIPT_CALLFUNC_DEBUG + //#define KROSS_PYTHON_SCRIPT_CLASSINSTANCE_DEBUG + + // Enable debugging for Kross::Python::PythonModule + //#define KROSS_PYTHON_MODULE_DEBUG + + // Enable debugging for Kross::Python::PythonExtension + //#define KROSS_PYTHON_EXTENSION_CTOR_DEBUG + //#define KROSS_PYTHON_EXTENSION_DTOR_DEBUG + + //#define KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG + //#define KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG + + //#define KROSS_PYTHON_EXTENSION_GETATTR_DEBUG + //#define KROSS_PYTHON_EXTENSION_GETATTRMETHOD_DEBUG + //#define KROSS_PYTHON_EXTENSION_SETATTR_DEBUG + + //#define KROSS_PYTHON_EXTENSION_CALL_DEBUG + +}} + +#endif diff --git a/lib/kross/python/pythonextension.cpp b/lib/kross/python/pythonextension.cpp new file mode 100644 index 00000000..59d9aaed --- /dev/null +++ b/lib/kross/python/pythonextension.cpp @@ -0,0 +1,445 @@ +/*************************************************************************** + * pythonextension.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "pythonextension.h" +#include "pythonobject.h" + +#include "../api/variant.h" +#include "../api/dict.h" +#include "../api/exception.h" + +using namespace Kross::Python; + +PythonExtension::PythonExtension(Kross::Api::Object::Ptr object) + : Py::PythonExtension() + , m_object(object) +{ +#ifdef KROSS_PYTHON_EXTENSION_CTOR_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::Constructor objectname='%1' objectclass='%2'").arg(m_object->getName()).arg(m_object->getClassName()) ); +#endif + + behaviors().name("KrossPythonExtension"); + /* + behaviors().doc( + "The common KrossPythonExtension object enables passing " + "of Kross::Api::Object's from C/C++ to Python and " + "backwards in a transparent way." + ); + */ + behaviors().supportGetattr(); + + m_proxymethod = new Py::MethodDefExt( + "", // methodname, not needed cause we use the method only internaly. + 0, // method that should handle the callback, not needed cause proxyhandler will handle it. + Py::method_varargs_call_handler_t( proxyhandler ), // callback handler + "" // documentation + ); +} + +PythonExtension::~PythonExtension() +{ +#ifdef KROSS_PYTHON_EXTENSION_DTOR_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::Destructor objectname='%1' objectclass='%2'").arg(m_object->getName()).arg(m_object->getClassName()) ); +#endif + delete m_proxymethod; +} + +#if 0 +Py::Object PythonExtension::str() +{ + Kross::Api::Callable* callable = dynamic_cast< Kross::Api::Callable* >(m_object); + QString s = callable ? callable->getName() : m_object->getClassName(); + return toPyObject(s.isEmpty() ? : s); +} + +Py::Object PythonExtension::repr() +{ + return toPyObject( m_object->toString() ); +} +#endif + +Py::Object PythonExtension::getattr(const char* n) +{ +#ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::getattr name='%1'").arg(n) ); +#endif + + if(n[0] == '_') { + if(!strcmp(n, "__methods__")) { + Py::List methods; + QStringList calls = m_object->getCalls(); + for(QStringList::Iterator it = calls.begin(); it != calls.end(); ++it) { +#ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::getattr name='%1' callable='%2'").arg(n).arg(*it) ); +#endif + methods.append(Py::String( (*it).latin1() )); + } + return methods; + } + + if(!strcmp(n, "__members__")) { + Py::List members; + Kross::Api::Callable* callable = dynamic_cast(m_object.data()); + if(callable) { + QMap children = callable->getChildren(); + QMap::Iterator it( children.begin() ); + for(; it != children.end(); ++it) { +#ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::getattr n='%1' child='%2'").arg(n).arg(it.key()) ); +#endif + members.append(Py::String( it.key().latin1() )); + } + } + return members; + } + + //if(n == "__dict__") { krosswarning( QString("PythonExtension::getattr(%1) __dict__").arg(n) ); return Py::None(); } + //if(n == "__class__") { krosswarning( QString("PythonExtension::getattr(%1) __class__").arg(n) ); return Py::None(); } + +#ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::getattr name='%1' is a internal name.").arg(n) ); +#endif + return Py::PythonExtension::getattr_methods(n); + } + + // Redirect the call to our static proxy method which will take care + // of handling the call. + Py::Tuple self(2); + self[0] = Py::Object(this); + self[1] = Py::String(n); + return Py::Object(PyCFunction_New( &m_proxymethod->ext_meth_def, self.ptr() ), true); +} + +/* +Py::Object PythonExtension::getattr_methods(const char* n) +{ +#ifdef KROSS_PYTHON_EXTENSION_GETATTRMETHOD_DEBUG + krossdebug( QString("PythonExtension::getattr_methods name=%1").arg(n) ); +#endif + return Py::PythonExtension::getattr_methods(n); +} + +int PythonExtension::setattr(const char* name, const Py::Object& value) +{ +#ifdef KROSS_PYTHON_EXTENSION_SETATTR_DEBUG + krossdebug( QString("PythonExtension::setattr name=%1 value=%2").arg(name).arg(value.as_string().c_str()) ); +#endif + return Py::PythonExtension::setattr(name, value); +} +*/ + +Kross::Api::List::Ptr PythonExtension::toObject(const Py::Tuple& tuple) +{ +#ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::toObject(Py::Tuple)") ); +#endif + + QValueList l; + uint size = tuple.size(); + for(uint i = 0; i < size; i++) + l.append( toObject( tuple[i] ) ); + return new Kross::Api::List(l); +} + +Kross::Api::List::Ptr PythonExtension::toObject(const Py::List& list) +{ +#ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::toObject(Py::List)") ); +#endif + + QValueList l; + uint length = list.length(); + for(uint i = 0; i < length; i++) + l.append( toObject( list[i] ) ); + return new Kross::Api::List(l); +} + +Kross::Api::Dict::Ptr PythonExtension::toObject(const Py::Dict& dict) +{ + QMap map; + Py::List l = dict.keys(); + uint length = l.length(); + for(Py::List::size_type i = 0; i < length; ++i) { + const char* n = l[i].str().as_string().c_str(); + map.replace(n, toObject( dict[n] )); + } + return new Kross::Api::Dict(map); +} + +Kross::Api::Object::Ptr PythonExtension::toObject(const Py::Object& object) +{ +#ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::toObject(Py::Object) object='%1'").arg(object.as_string().c_str()) ); +#endif + if(object == Py::None()) + return 0; + PyTypeObject *type = (PyTypeObject*) object.type().ptr(); +#ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::toObject(Py::Object) type='%1'").arg(type->tp_name) ); +#endif + if(type == &PyInt_Type) + return new Kross::Api::Variant(int(Py::Int(object))); + if(type == &PyBool_Type) + return new Kross::Api::Variant(QVariant(object.isTrue(),0)); + if(type == &PyLong_Type) + return new Kross::Api::Variant(Q_LLONG(long(Py::Long(object)))); + if(type == &PyFloat_Type) + return new Kross::Api::Variant(double(Py::Float(object))); + + if( PyType_IsSubtype(type,&PyString_Type) ) { +#ifdef Py_USING_UNICODE + /* TODO + if(type == &PyUnicode_Type) { + Py::unicodestring u = Py::String(object).as_unicodestring(); + std::string s; + std::copy(u.begin(), u.end(), std::back_inserter(s)); + return new Kross::Api::Variant(s.c_str()); + } + */ +#endif + return new Kross::Api::Variant(object.as_string().c_str()); + } + + if(type == &PyTuple_Type) + return toObject(Py::Tuple(object)).data(); + if(type == &PyList_Type) + return toObject(Py::List(object)).data(); + if(type == &PyDict_Type) + return toObject(Py::Dict(object.ptr())).data(); + + if(object.isInstance()) + return new PythonObject(object); + + Py::ExtensionObject extobj(object); + PythonExtension* extension = extobj.extensionObject(); + if(! extension) { + krosswarning("EXCEPTION in PythonExtension::toObject(): Failed to determinate PythonExtension object."); + throw Py::Exception("Failed to determinate PythonExtension object."); + } + if(! extension->m_object) { + krosswarning("EXCEPTION in PythonExtension::toObject(): Failed to convert the PythonExtension object into a Kross::Api::Object."); + throw Py::Exception("Failed to convert the PythonExtension object into a Kross::Api::Object."); + } + +#ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG + krossdebug( "Kross::Python::PythonExtension::toObject(Py::Object) successfully converted into Kross::Api::Object." ); +#endif + return extension->m_object; +} + +const Py::Object PythonExtension::toPyObject(const QString& s) +{ +#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::toPyObject(QString)") ); +#endif + return s.isNull() ? Py::String() : Py::String(s.latin1()); +} + +const Py::List PythonExtension::toPyObject(const QStringList& list) +{ +#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::toPyObject(QStringList)") ); +#endif + Py::List l; + for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) + l.append(toPyObject(*it)); + return l; +} + +const Py::Dict PythonExtension::toPyObject(const QMap& map) +{ +#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::toPyObject(QMap)") ); +#endif + Py::Dict d; + for(QMap::ConstIterator it = map.constBegin(); it != map.constEnd(); ++it) + d.setItem(it.key().latin1(), toPyObject(it.data())); + return d; +} + +const Py::List PythonExtension::toPyObject(const QValueList& list) +{ +#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::toPyObject(QValueList)") ); +#endif + Py::List l; + for(QValueList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) + l.append(toPyObject(*it)); + return l; +} + +const Py::Object PythonExtension::toPyObject(const QVariant& variant) +{ +#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::toPyObject(QVariant) typename='%1'").arg(variant.typeName()) ); +#endif + + switch(variant.type()) { + case QVariant::Invalid: + return Py::None(); + case QVariant::Bool: + return Py::Int(variant.toBool()); + case QVariant::Int: + return Py::Int(variant.toInt()); + case QVariant::UInt: + return Py::Long((unsigned long)variant.toUInt()); + case QVariant::Double: + return Py::Float(variant.toDouble()); + case QVariant::Date: + case QVariant::Time: + case QVariant::DateTime: + case QVariant::ByteArray: + case QVariant::BitArray: + case QVariant::CString: + case QVariant::String: + return toPyObject(variant.toString()); + case QVariant::StringList: + return toPyObject(variant.toStringList()); + case QVariant::Map: + return toPyObject(variant.toMap()); + case QVariant::List: + return toPyObject(variant.toList()); + + // To handle following both cases is a bit difficult + // cause Python doesn't spend an easy possibility + // for such large numbers (TODO maybe BigInt?). So, + // we risk overflows here, but well... + case QVariant::LongLong: { + Q_LLONG l = variant.toLongLong(); + //return (l < 0) ? Py::Long((long)l) : Py::Long((unsigned long)l); + return Py::Long((long)l); + //return Py::Long(PyLong_FromLong( (long)l ), true); + } break; + case QVariant::ULongLong: { + return Py::Long((unsigned long)variant.toULongLong()); + } break; + + default: { + krosswarning( QString("Kross::Python::PythonExtension::toPyObject(QVariant) Not possible to convert the QVariant type '%1' to a Py::Object.").arg(variant.typeName()) ); + return Py::None(); + } + } +} + +const Py::Object PythonExtension::toPyObject(Kross::Api::Object::Ptr object) +{ + if(! object) { +#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG + krossdebug("Kross::Python::PythonExtension::toPyObject(Kross::Api::Object) is NULL => Py::None"); +#endif + return Py::None(); + } + + const QString classname = object->getClassName(); + if(classname == "Kross::Api::Variant") { + QVariant v = static_cast( object.data() )->getValue(); +#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::toPyObject(Kross::Api::Object) is Kross::Api::Variant %1").arg(v.toString()) ); +#endif + return toPyObject(v); + } + + if(classname == "Kross::Api::List") { +#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG + krossdebug("Kross::Python::PythonExtension::toPyObject(Kross::Api::Object) is Kross::Api::List"); +#endif + Py::List pylist; + Kross::Api::List* list = static_cast( object.data() ); + QValueList valuelist = list->getValue(); + for(QValueList::Iterator it = valuelist.begin(); it != valuelist.end(); ++it) + pylist.append( toPyObject(*it) ); // recursive + return pylist; + } + + if(classname == "Kross::Api::Dict") { +#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG + krossdebug("Kross::Python::PythonExtension::toPyObject(Kross::Api::Object) is Kross::Api::Dict"); +#endif + Py::Dict pydict; + Kross::Api::Dict* dict = static_cast( object.data() ); + QMap valuedict = dict->getValue(); + for(QMap::Iterator it = valuedict.begin(); it != valuedict.end(); ++it) { + const char* n = it.key().latin1(); + pydict[ n ] = toPyObject( it.data() ); // recursive + } + return pydict; + } + +#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG + krossdebug( QString("Trying to handle PythonExtension::toPyObject(%1) as PythonExtension").arg(object->getClassName()) ); +#endif + return Py::asObject( new PythonExtension(object) ); +} + +const Py::Tuple PythonExtension::toPyTuple(Kross::Api::List::Ptr list) +{ +#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::toPyTuple(Kross::Api::List) name='%1'").arg(list ? list->getName() : "NULL") ); +#endif + uint count = list ? list->count() : 0; + Py::Tuple tuple(count); + for(uint i = 0; i < count; i++) + tuple.setItem(i, toPyObject(list->item(i))); + return tuple; +} + +PyObject* PythonExtension::proxyhandler(PyObject *_self_and_name_tuple, PyObject *args) +{ + Py::Tuple tuple(_self_and_name_tuple); + PythonExtension *self = static_cast( tuple[0].ptr() ); + QString methodname = Py::String(tuple[1]).as_string().c_str(); + + try { + Kross::Api::List::Ptr arguments = toObject( Py::Tuple(args) ); + +#ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::proxyhandler methodname='%1' arguments='%2'").arg(methodname).arg(arguments->toString()) ); +#endif + + Kross::Api::Callable* callable = dynamic_cast(self->m_object.data()); + if(callable && callable->hasChild(methodname)) { +#ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::proxyhandler methodname='%1' is a child object of '%2'.").arg(methodname).arg(self->m_object->getName()) ); +#endif + Py::Object result = toPyObject( callable->getChild(methodname)->call(QString::null, arguments) ); + result.increment_reference_count(); + return result.ptr(); + } +#ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::proxyhandler try to call function with methodname '%1' in object '%2'.").arg(methodname).arg(self->m_object->getName()) ); +#endif + Py::Object result = toPyObject( self->m_object->call(methodname, arguments) ); + result.increment_reference_count(); + return result.ptr(); + } + catch(Py::Exception& e) { + const QString err = Py::value(e).as_string().c_str(); + krosswarning( QString("Py::Exception in Kross::Python::PythonExtension::proxyhandler %1").arg(err) ); + //throw e; + } + catch(Kross::Api::Exception::Ptr e) { + const QString err = e->toString(); + krosswarning( QString("Kross::Api::Exception in Kross::Python::PythonExtension::proxyhandler %1").arg(err) ); + // Don't throw here cause it will end in a crash deep in python. The + // error is already handled anyway. + //throw Py::Exception( (char*) e->toString().latin1() ); + } + + return Py_None; +} diff --git a/lib/kross/python/pythonextension.h b/lib/kross/python/pythonextension.h new file mode 100644 index 00000000..02e42587 --- /dev/null +++ b/lib/kross/python/pythonextension.h @@ -0,0 +1,221 @@ +/*************************************************************************** + * pythonextension.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_PYTHON_EXTENSION_H +#define KROSS_PYTHON_EXTENSION_H + +#include "pythonconfig.h" + +#include "../api/object.h" +#include "../api/list.h" +#include "../api/dict.h" +#include "../api/class.h" + +#include +#include +#include +#include +#include +#include + +namespace Kross { namespace Python { + + // Forward declaration. + class PythonScript; + + /** + * The PythonExtension is a wrapper-object to let C++ and + * Python interact together. + * Instances of this class are used everytime if we send + * or got something to/from python. + */ + class PythonExtension : public Py::PythonExtension + { + friend class PythonScript; + friend class PythonObject; + friend class PythonModule; + + public: + + /** + * Constructor. + * + * \param object The \a Kross::Api::Object object + * this instance is the wrapper for. + */ + explicit PythonExtension(Kross::Api::Object::Ptr object); + + /** + * Destructor. + */ + virtual ~PythonExtension(); + +#if 0 + /** + * Overloaded method to return the string-representation + * of this object. + * + * \return The string representation. + */ + virtual Py::Object str(); + + /** + * Overloaded method to return the string-representation + * of the value this object has. + * + * \return A string representation of the value. + */ + virtual Py::Object repr(); +#endif + + /** + * Overloaded method to handle attribute calls + * from within python. + * + * \param name The name of the attribute that + * should be handled. + * \return An \a Py::Object that could be + * a value or a callable object. Python + * will decide what to do with the + * returnvalue. + */ + virtual Py::Object getattr(const char* name); + + //virtual Py::Object getattr_methods(const char* name); + //virtual int setattr(const char* name, const Py::Object& value); + + private: + + /** + * Converts a \a Py::Tuple into a \a Kross::Api::List . + * + * \param tuple The Py::Tuple to convert. + * \return The to a Kross::Api::List converted Py::Tuple . + */ + static Kross::Api::List::Ptr toObject(const Py::Tuple& tuple); + + /** + * Converts a \a Py::List into a \a Kross::Api::List . + * + * \param list The Py::List to convert. + * \return The to a Kross::Api::List converted Py::List . + */ + static Kross::Api::List::Ptr toObject(const Py::List& list); + + /** + * Converts a \a Py::Dict into a \a Kross::Api::Dict . + * + * \param dict The Py::Dict to convert. + * \return The to a Kross::Api::Dict converted Py::Dict . + */ + static Kross::Api::Dict::Ptr toObject(const Py::Dict& dict); + + /** + * Converts a \a Py::Object into a \a Kross::Api::Object. + * + * \param object The Py::Object to convert. + * \return The to a Kross::Api::Object converted Py::Object. + */ + static Kross::Api::Object::Ptr toObject(const Py::Object& object); + + /** + * Converts a QString to a Py::Object. If + * the QString isNull() then Py::None() will + * be returned. + * + * \param s The QString to convert. + * \return The to a Py::String converted QString. + */ + static const Py::Object toPyObject(const QString& s); + + /** + * Converts a QStringList to a Py::List. + * + * \param list The QStringList to convert. + * \return The to a Py::List converted QStringList. + */ + static const Py::List toPyObject(const QStringList& list); + + /** + * Converts a QMap to a Py::Dict. + * + * \param map The QMap to convert. + * \return The to a Py::Dict converted QMap. + */ + static const Py::Dict toPyObject(const QMap& map); + + /** + * Converts a QValueList to a Py::List. + * + * \param list The QValueList to convert. + * \return The to a Py::List converted QValueList. + */ + static const Py::List toPyObject(const QValueList& list); + + /** + * Converts a QVariant to a Py::Object. + * + * \param variant The QVariant to convert. + * \return The to a Py::Object converted QVariant. + */ + static const Py::Object toPyObject(const QVariant& variant); + + /** + * Converts a \a Kross::Api::Object to a Py::Object. + * + * \param object The Kross::Api::Object to convert. + * \return The to a Py::Object converted Kross::Api::Object. + */ + static const Py::Object toPyObject(Kross::Api::Object::Ptr object); + + /** + * Converts a \a Kross::Api::List into a Py::Tuple. + * + * \param list The Kross::Api::List to convert. + * \return The to a Py::Tuple converted Kross::Api::List. + */ + static const Py::Tuple toPyTuple(Kross::Api::List::Ptr list); + + /// The \a Kross::Api::Object this PythonExtension wraps. + Kross::Api::Object::Ptr m_object; + + /** + * The proxymethod which will handle all calls to our + * \a PythonExtension instance. + */ + Py::MethodDefExt* m_proxymethod; + + /** + * The static proxy-handler which will be used to dispatch + * a call to our \a PythonExtension instance and redirect + * the call to the matching method. + * + * \param _self_and_name_tuple A tuple containing as first + * argument a reference to our \a PythonExtension + * instance. + * \param _args The optional passed arguments for the method + * which should be called. + * \return The returnvalue of the methodcall. + */ + static PyObject* proxyhandler(PyObject* _self_and_name_tuple, PyObject* _args); + }; + +}} + +#endif diff --git a/lib/kross/python/pythoninterpreter.cpp b/lib/kross/python/pythoninterpreter.cpp new file mode 100644 index 00000000..92f627dd --- /dev/null +++ b/lib/kross/python/pythoninterpreter.cpp @@ -0,0 +1,255 @@ +/*************************************************************************** + * pythoninterpreter.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "pythoninterpreter.h" +#include "pythonscript.h" +#include "pythonmodule.h" +#include "pythonsecurity.h" +//#include "pythonextension.h" +#include "../api/variant.h" + +#include +#include + +#if defined(Q_WS_WIN) + #define PYPATHDELIMITER ";" +#else + #define PYPATHDELIMITER ":" +#endif + +extern "C" +{ + /** + * Exported and loadable function as entry point to use + * the \a PythonInterpreter. + * The krosspython library the \a PythonInterpreter is part + * will be loaded dynamicly at runtime from e.g. + * \a Kross::Api::Manager::getInterpreter and this exported + * function will be used to return an instance of the + * \a PythonInterpreter implementation. + */ + void* krossinterpreter(Kross::Api::InterpreterInfo* info) + { + try { + return new Kross::Python::PythonInterpreter(info); + } + catch(Kross::Api::Exception::Ptr e) { + Kross::krosswarning("krossinterpreter(Kross::Api::InterpreterInfo* info): Unhandled exception."); + } + return 0; + } +} + +using namespace Kross::Python; + +namespace Kross { namespace Python { + + /// \internal + class PythonInterpreterPrivate + { + public: + + /// The __main__ python module. + PythonModule* mainmodule; + + /// The \a PythonSecurity python module to wrap the RestrictedPython functionality. + PythonSecurity* security; + }; + +}} + +PythonInterpreter::PythonInterpreter(Kross::Api::InterpreterInfo* info) + : Kross::Api::Interpreter(info) + , d(new PythonInterpreterPrivate()) +{ + // Initialize the python interpreter. + initialize(); + + // Set name of the program. + Py_SetProgramName(const_cast("Kross")); + + /* + // Set arguments. + //char* comm[0]; + const char* comm = const_cast("kross"); // name. + PySys_SetArgv(1, comm); + */ + + // In the python sys.path are all module-directories are + // listed in. + QString path; + + // First import the sys-module to remember it's sys.path + // list in our path QString. + Py::Module sysmod( PyImport_ImportModule("sys"), true ); + Py::Dict sysmoddict = sysmod.getDict(); + Py::Object syspath = sysmoddict.getItem("path"); + if(syspath.isList()) { + Py::List syspathlist = syspath; + for(Py::List::iterator it = syspathlist.begin(); it != syspathlist.end(); ++it) + if( (*it).isString() ) + path.append( QString(Py::String(*it).as_string().c_str()) + PYPATHDELIMITER ); + } + else + path = Py_GetPath(); + + // Determinate additional module-paths we like to add. + // First add the global Kross modules-path. + QStringList krossdirs = KGlobal::dirs()->findDirs("data", "kross/python"); + for(QStringList::Iterator krossit = krossdirs.begin(); krossit != krossdirs.end(); ++krossit) + path.append(*krossit + PYPATHDELIMITER); + // Then add the application modules-path. + QStringList appdirs = KGlobal::dirs()->findDirs("appdata", "kross/python"); + for(QStringList::Iterator appit = appdirs.begin(); appit != appdirs.end(); ++appit) + path.append(*appit + PYPATHDELIMITER); + + // Set the extended sys.path. + PySys_SetPath( (char*) path.latin1() ); + + krossdebug(QString("Python ProgramName: %1").arg(Py_GetProgramName())); + krossdebug(QString("Python ProgramFullPath: %1").arg(Py_GetProgramFullPath())); + krossdebug(QString("Python Version: %1").arg(Py_GetVersion())); + krossdebug(QString("Python Platform: %1").arg(Py_GetPlatform())); + krossdebug(QString("Python Prefix: %1").arg(Py_GetPrefix())); + krossdebug(QString("Python ExecPrefix: %1").arg(Py_GetExecPrefix())); + krossdebug(QString("Python Path: %1").arg(Py_GetPath())); + krossdebug(QString("Python System Path: %1").arg(path)); + + // Initialize the main module. + d->mainmodule = new PythonModule(this); + + // The main dictonary. + Py::Dict moduledict = d->mainmodule->getDict(); + //TODO moduledict["KrossPythonVersion"] = Py::Int(KROSS_PYTHON_VERSION); + + // Prepare the interpreter. + QString s = + "import sys\n" + //"sys.setdefaultencoding('latin-1')\n" + + // Dirty hack to get sys.argv defined. Needed for e.g. TKinter. + "sys.argv = ['']\n" + + // On the try to read something from stdin always return an empty + // string. That way such reads don't block our script. + "import cStringIO\n" + "sys.stdin = cStringIO.StringIO()\n" + + // Class to redirect something. We use this class e.g. to redirect + // and to a c++ event. + "class Redirect:\n" + " def __init__(self, target):\n" + " self.target = target\n" + " def write(self, s):\n" + " self.target.call(s)\n" + + // Wrap builtin __import__ method. All import requests are + // first redirected to our PythonModule.import method and + // if the call returns None, then we call the original + // python import mechanism. + "import __builtin__\n" + "import __main__\n" + "class Importer:\n" + " def __init__(self):\n" + " self.realImporter = __builtin__.__import__\n" + " __builtin__.__import__ = self._import\n" + " def _import(self, name, globals=None, locals=None, fromlist=[]):\n" + " mod = __main__._import(name, globals, locals, fromlist)\n" + " if mod != None: return mod\n" + " return self.realImporter(name, globals, locals, fromlist)\n" + "Importer()\n" + ; + + PyObject* pyrun = PyRun_String(s.latin1(), Py_file_input, moduledict.ptr(), moduledict.ptr()); + if(! pyrun) { + Py::Object errobj = Py::value(Py::Exception()); // get last error + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Failed to prepare the __main__ module: %1").arg(errobj.as_string().c_str())) ); + } + Py_XDECREF(pyrun); // free the reference. + + // Initialize the RestrictedPython module. + d->security = new PythonSecurity(this); +} + +PythonInterpreter::~PythonInterpreter() +{ + // Free the zope security module. + delete d->security; d->security = 0; + // Free the main module. + delete d->mainmodule; d->mainmodule = 0; + // Finalize the python interpreter. + finalize(); + // Delete the private d-pointer. + delete d; +} + +void PythonInterpreter::initialize() +{ + // Initialize python. + Py_Initialize(); + + /* Not needed cause we use the >= Python 2.3 GIL-mechanism. + PyThreadState* d->globalthreadstate, d->threadstate; + // First we have to initialize threading if python supports it. + PyEval_InitThreads(); + // The main thread. We don't use it later. + d->globalthreadstate = PyThreadState_Swap(NULL); + d->globalthreadstate = PyEval_SaveThread(); + // We use an own sub-interpreter for each thread. + d->threadstate = Py_NewInterpreter(); + // Note that this application has multiple threads. + // It maintains a separate interp (sub-interpreter) for each thread. + PyThreadState_Swap(d->threadstate); + // Work done, release the lock. + PyEval_ReleaseLock(); + */ +} + +void PythonInterpreter::finalize() +{ + /* Not needed cause we use the >= Python 2.3 GIL-mechanism. + // Lock threads. + PyEval_AcquireLock(); + // Free the used thread. + PyEval_ReleaseThread(d->threadstate); + // Set back to rememberd main thread. + PyThreadState_Swap(d->globalthreadstate); + // Work done, unlock. + PyEval_ReleaseLock(); + */ + + // Finalize python. + Py_Finalize(); +} + +Kross::Api::Script* PythonInterpreter::createScript(Kross::Api::ScriptContainer* scriptcontainer) +{ + return new PythonScript(this, scriptcontainer); +} + +PythonModule* PythonInterpreter::mainModule() +{ + return d->mainmodule; +} + +PythonSecurity* PythonInterpreter::securityModule() +{ + return d->security; +} + diff --git a/lib/kross/python/pythoninterpreter.h b/lib/kross/python/pythoninterpreter.h new file mode 100644 index 00000000..7c4088fa --- /dev/null +++ b/lib/kross/python/pythoninterpreter.h @@ -0,0 +1,90 @@ +/*************************************************************************** + * pythoninterpreter.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_PYTHON_INTERPRETER_H +#define KROSS_PYTHON_INTERPRETER_H + +#include "pythonconfig.h" +#include "../api/object.h" +#include "../api/interpreter.h" +#include "../main/manager.h" +//#include "../api/script.h" +#include "../main/scriptcontainer.h" + +#include + +namespace Kross { namespace Python { + + // Forward declarations. + class PythonSecurity; + class PythonModule; + class PythonInterpreterPrivate; + + /** + * Python interpreter bridge. + * + * Implements an \a Kross::Api::Interpreter for the python + * interpreter. + */ + class PythonInterpreter : public Kross::Api::Interpreter + { + public: + + /** + * Constructor. + * + * \param info The \a Kross::Api::InterpreterInfo instance + * which describes the \a PythonInterpreter for + * applications using Kross. + */ + PythonInterpreter(Kross::Api::InterpreterInfo* info); + + /** + * Destructor. + */ + virtual ~PythonInterpreter(); + + /** + * \return a \a PythonScript instance. + */ + virtual Kross::Api::Script* createScript(Kross::Api::ScriptContainer* scriptcontainer); + + /** + * \return the \a MainModule instance. + */ + PythonModule* mainModule(); + + /** + * \return the \a PythonSecurity instance. + */ + PythonSecurity* securityModule(); + + private: + /// Internal d-pointer class. + PythonInterpreterPrivate* d; + + /// Initialize the python interpreter. + inline void initialize(); + /// Finalize the python interpreter. + inline void finalize(); + }; + +}} + +#endif diff --git a/lib/kross/python/pythonmodule.cpp b/lib/kross/python/pythonmodule.cpp new file mode 100644 index 00000000..b54eb73f --- /dev/null +++ b/lib/kross/python/pythonmodule.cpp @@ -0,0 +1,100 @@ +/*************************************************************************** + * pythonmodule.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "pythonmodule.h" +#include "pythoninterpreter.h" + +#include + +using namespace Kross::Python; + +namespace Kross { namespace Python { + + /// @internal + class PythonModulePrivate + { + public: + + /** + * The \a PythonInterpreter instance this module is + * part of. + */ + PythonInterpreter* m_interpreter; + + /** + * List of \a PythonExtension instances accessible + * via this \a PythonModule instance. + */ + QMap m_modules; + + }; + +}} + +PythonModule::PythonModule(PythonInterpreter* interpreter) + : Py::ExtensionModule("__main__") + , d(new PythonModulePrivate()) +{ +#ifdef KROSS_PYTHON_MODULE_DEBUG + krossdebug( QString("Kross::Python::PythonModule::Constructor") ); +#endif + + d->m_interpreter = interpreter; + + add_varargs_method("_import", &PythonModule::import, "FIXME: Documentation"); + + initialize("The PythonModule is the __main__ python environment used as global object namespace."); +} + +PythonModule::~PythonModule() +{ +#ifdef KROSS_PYTHON_MODULE_DEBUG + krossdebug( QString("Kross::Python::PythonModule::Destructor name='%1'").arg(name().c_str()) ); +#endif + + delete d; +} + +Py::Dict PythonModule::getDict() +{ + return moduleDictionary(); +} + +Py::Object PythonModule::import(const Py::Tuple& args) +{ + if(args.size() > 0) { + QString modname = args[0].as_string().c_str(); + if(modname.startsWith("kross")) { +#ifdef KROSS_PYTHON_MODULE_DEBUG + krossdebug( QString("Kross::Python::PythonModule::import() module=%1").arg(modname) ); +#endif + if( modname.find( QRegExp("[^a-zA-Z0-9\\_\\-]") ) >= 0 ) { + krosswarning( QString("Denied import of Kross module '%1' cause of untrusted chars.").arg(modname) ); + } + else { + Kross::Api::Module::Ptr module = Kross::Api::Manager::scriptManager()->loadModule(modname); + if(module) + return PythonExtension::toPyObject( Kross::Api::Object::Ptr(module) ); + krosswarning( QString("Loading of Kross module '%1' failed.").arg(modname) ); + } + + } + } + return Py::None(); +} diff --git a/lib/kross/python/pythonmodule.h b/lib/kross/python/pythonmodule.h new file mode 100644 index 00000000..1775ae68 --- /dev/null +++ b/lib/kross/python/pythonmodule.h @@ -0,0 +1,76 @@ +/*************************************************************************** + * pythonmodule.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_PYTHON_MODULE_H +#define KROSS_PYTHON_MODULE_H + +#include "pythonconfig.h" +#include "../api/object.h" +#include "../api/script.h" +#include "pythonextension.h" + +#include + +namespace Kross { namespace Python { + + // Forward declaration. + class PythonInterpreter; + class PythonModulePrivate; + + /** + * The PythonModule is the __main__ python environment + * used as global object namespace. + * + * The module also spends access to the whole Kross + * functionality and manages all the PythonExtension + * modules. + */ + class PythonModule : public Py::ExtensionModule + { + public: + + /** + * Constructor. + * + * \param interpreter The \a PythonInterpreter instance + * used to create this PythonModule. + */ + PythonModule(PythonInterpreter* interpreter); + + /** + * Destructor. + */ + virtual ~PythonModule(); + + /** + * \return the dictonary this PythonModule has. + */ + Py::Dict getDict(); + + private: + /// Internal d-pointer class. + PythonModulePrivate* d; + + /// Import hook used to load external kross libs on demand. + Py::Object import(const Py::Tuple&); + }; + +}} + +#endif diff --git a/lib/kross/python/pythonobject.cpp b/lib/kross/python/pythonobject.cpp new file mode 100644 index 00000000..d59087b3 --- /dev/null +++ b/lib/kross/python/pythonobject.cpp @@ -0,0 +1,94 @@ +/*************************************************************************** + * pythonobject.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "pythonobject.h" +#include "pythonextension.h" + +using namespace Kross::Python; + +PythonObject::PythonObject(const Py::Object& object) + : Kross::Api::Object() + , m_pyobject(object) +{ + krossdebug( QString("PythonObject::PythonObject() constructor") ); + + Py::List x( object.dir() ); + for(Py::Sequence::iterator i= x.begin(); i != x.end(); ++i) { + std::string s = (*i).str(); + if(s == "__init__") + continue; + + //if(! m_pyobject.hasAttr( (*i).str() )) continue; + Py::Object o = m_pyobject.getAttr(s); + + QString t; + if(o.isCallable()) t += "isCallable "; + if(o.isDict()) t += "isDict "; + if(o.isList()) t += "isList "; + if(o.isMapping()) t += "isMapping "; + if(o.isNumeric()) t += "isNumeric "; + if(o.isSequence()) t += "isSequence "; + if(o.isTrue()) t += "isTrue "; + if(o.isInstance()) t += "isInstance "; + krossdebug( QString("PythonObject::PythonObject() method '%1' (%2)").arg( (*i).str().as_string().c_str() ).arg(t) ); + + if(o.isCallable()) + m_calls.append( (*i).str().as_string().c_str() ); + } +} + +PythonObject::~PythonObject() +{ +} + +const QString PythonObject::getClassName() const +{ + return "Kross::Python::PythonObject"; +} + +Kross::Api::Object::Ptr PythonObject::call(const QString& name, Kross::Api::List::Ptr arguments) +{ + krossdebug( QString("PythonObject::call(%1)").arg(name) ); + + if(m_pyobject.isInstance()) { + //if(! m_calls.contains(n)) throw ... + + PyObject* r = PyObject_CallMethod(m_pyobject.ptr(), (char*) name.latin1(), 0); + if(! r) { //FIXME happens too if e.g. number of arguments doesn't match !!! + Py::Object errobj = Py::value(Py::Exception()); // get last error + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Failed to call method '%1': %2").arg(name).arg(errobj.as_string().c_str())) ); + } + Py::Object result(r, true); + + //krossdebug( QString("PythonObject::call(%1) call return value = '%2'").arg(name).arg(result.as_string().c_str()) ); + return PythonExtension::toObject(result); + } + /*TODO??? ELSE create class instance for class-definitions??? + Kross::Api::ClassBase* clazz = new Kross::Api::ClassBase("", this); + return new PythonExtension(clazz); + */ + + return Kross::Api::Object::call(name, arguments); +} + +QStringList PythonObject::getCalls() +{ + return m_calls; +} + diff --git a/lib/kross/python/pythonobject.h b/lib/kross/python/pythonobject.h new file mode 100644 index 00000000..6d33da26 --- /dev/null +++ b/lib/kross/python/pythonobject.h @@ -0,0 +1,95 @@ +/*************************************************************************** + * pythonobject.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_PYTHON_OBJECT_H +#define KROSS_PYTHON_OBJECT_H + +#include "pythonconfig.h" +#include "../api/object.h" +#include "../api/list.h" +#include "pythonextension.h" + +#include +#include + +namespace Kross { namespace Python { + + /** + * The PythonObject class is used for Instances of Python + * Classes by the \a PythonExtension class. + */ + class PythonObject : public Kross::Api::Object + { + public: + + /** + * Constructor. + * + * \param object The Py::Object this \a PythonObject + * provides access to. + */ + explicit PythonObject(const Py::Object& object); + + /** + * Destructor. + */ + virtual ~PythonObject(); + + /** + * Return the class name. This could be something + * like "Kross::Python::PythonObject" for this + * object. The value is mainly used for display + * purposes. + * + * \return The name of this class. + */ + virtual const QString getClassName() const; + + /** + * Pass a call to the object. Objects like \a Class + * are able to handle call's by just implementating + * this function. + * + * \throws TypeException if the object or the name + * is not callable. + * \param name Each call has a name that says what + * should be called. In the case of a \a Class + * the name is the functionname. + * \param arguments The list of arguments passed to + * the call. + * \return The call-result as Object* instance or + * NULL if the call has no result. + */ + virtual Kross::Api::Object::Ptr call(const QString& name, Kross::Api::List::Ptr arguments); + + /** + * Return a list of supported callable objects. + * + * \return List of supported calls. + */ + virtual QStringList getCalls(); + + private: + const Py::Object m_pyobject; + QStringList m_calls; + }; + +}} + +#endif diff --git a/lib/kross/python/pythonscript.cpp b/lib/kross/python/pythonscript.cpp new file mode 100644 index 00000000..082b5440 --- /dev/null +++ b/lib/kross/python/pythonscript.cpp @@ -0,0 +1,460 @@ +/*************************************************************************** + * pythonscript.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "pythonscript.h" +#include "pythonmodule.h" +#include "pythoninterpreter.h" +#include "pythonsecurity.h" +#include "../main/scriptcontainer.h" + +//#include + +using namespace Kross::Python; + +namespace Kross { namespace Python { + + /// @internal + class PythonScriptPrivate + { + public: + + /** + * The \a Py::Module instance this \a PythonScript + * has as local context. + */ + Py::Module* m_module; + + /** + * The PyCodeObject object representing the + * compiled python code. Internaly we first + * compile the python code and later execute + * it. + */ + Py::Object* m_code; + + /** + * A list of functionnames. + */ + QStringList m_functions; + + /** + * A list of classnames. + */ + QStringList m_classes; + }; + +}} + +PythonScript::PythonScript(Kross::Api::Interpreter* interpreter, Kross::Api::ScriptContainer* scriptcontainer) + : Kross::Api::Script(interpreter, scriptcontainer) + , d(new PythonScriptPrivate()) +{ +#ifdef KROSS_PYTHON_SCRIPT_CTOR_DEBUG + krossdebug("PythonScript::PythonScript() Constructor."); +#endif + d->m_module = 0; + d->m_code = 0; +} + +PythonScript::~PythonScript() +{ +#ifdef KROSS_PYTHON_SCRIPT_DTOR_DEBUG + krossdebug("PythonScript::~PythonScript() Destructor."); +#endif + finalize(); + delete d; +} + +void PythonScript::initialize() +{ + finalize(); + clearException(); // clear previously thrown exceptions. + + try { + if(m_scriptcontainer->getCode().isNull()) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Invalid scripting code for script '%1'").arg( m_scriptcontainer->getName() )) ); + + if(m_scriptcontainer->getName().isNull()) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Name for the script is invalid!")) ); + + PyObject* pymod = PyModule_New( (char*) m_scriptcontainer->getName().latin1() ); + d->m_module = new Py::Module(pymod, true); + if(! d->m_module) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Failed to initialize local module context for script '%1'").arg( m_scriptcontainer->getName() )) ); + +#ifdef KROSS_PYTHON_SCRIPT_INIT_DEBUG + krossdebug( QString("PythonScript::initialize() module='%1' refcount='%2'").arg(d->m_module->as_string().c_str()).arg(d->m_module->reference_count()) ); +#endif + + // Set the "self" variable to point to the ScriptContainer + // we are using for the script. That way we are able to + // simply access the ScriptContainer itself from within + // python scripting code. + Py::Dict moduledict = d->m_module->getDict(); + moduledict["self"] = PythonExtension::toPyObject( m_scriptcontainer ); + //moduledict["parent"] = PythonExtension::toPyObject( m_manager ); + +/* + // Prepare the local context. + QString s = + //"import sys\n" + "if self.has(\"stdout\"):\n" + " self.stdout = Redirect( self.get(\"stdout\") )\n" + "if self.has(\"stderr\"):\n" + " self.stderr = Redirect( self.get(\"stderr\") )\n" + ; + Py::Dict mainmoduledict = ((PythonInterpreter*)m_interpreter)->mainModule()->getDict(); + PyObject* pyrun = PyRun_StringFlags((char*)s.latin1(), Py_file_input, mainmoduledict.ptr(), moduledict.ptr()); + if(! pyrun) + throw Py::Exception(); // throw exception + Py_XDECREF(pyrun); // free the reference. +*/ + + // Compile the python script code. It will be later on request + // executed. That way we cache the compiled code. + PyObject* code = 0; + bool restricted = m_scriptcontainer->getOption("restricted", QVariant(false,0), true).toBool(); + + krossdebug( QString("PythonScript::initialize() name=%1 restricted=%2").arg(m_scriptcontainer->getName()).arg(restricted) ); + if(restricted) { + + // Use the RestrictedPython module wrapped by the PythonSecurity class. + code = dynamic_cast(m_interpreter)->securityModule()->compile_restricted( + m_scriptcontainer->getCode(), + m_scriptcontainer->getName(), + "exec" + ); + + } + else { + //PyCompilerFlags* cf = new PyCompilerFlags; + //cf->cf_flags |= PyCF_SOURCE_IS_UTF8; + + // Just compile the code without any restrictions. + code = Py_CompileString( + (char*) m_scriptcontainer->getCode().latin1(), + (char*) m_scriptcontainer->getName().latin1(), + Py_file_input + ); + } + + if(! code) + throw Py::Exception(); + d->m_code = new Py::Object(code, true); + } + catch(Py::Exception& e) { + QString err = Py::value(e).as_string().c_str(); + Kross::Api::Exception::Ptr exception = toException( QString("Failed to compile python code: %1").arg(err) ); + e.clear(); // exception is handled. clear it now. + throw exception; + } +} + +void PythonScript::finalize() +{ +#ifdef KROSS_PYTHON_SCRIPT_FINALIZE_DEBUG + if(d->m_module) + krossdebug( QString("PythonScript::finalize() module='%1' refcount='%2'").arg(d->m_module->as_string().c_str()).arg(d->m_module->reference_count()) ); +#endif + + delete d->m_module; d->m_module = 0; + delete d->m_code; d->m_code = 0; + d->m_functions.clear(); + d->m_classes.clear(); +} + +Kross::Api::Exception::Ptr PythonScript::toException(const QString& error) +{ + long lineno = -1; + QStringList errorlist; + + PyObject *type, *value, *traceback; + PyErr_Fetch(&type, &value, &traceback); + Py_FlushLine(); + PyErr_NormalizeException(&type, &value, &traceback); + + if(traceback) { + Py::List tblist; + try { + Py::Module tbmodule( PyImport_Import(Py::String("traceback").ptr()), true ); + Py::Dict tbdict = tbmodule.getDict(); + Py::Callable tbfunc(tbdict.getItem("format_tb")); + Py::Tuple args(1); + args.setItem(0, Py::Object(traceback)); + tblist = tbfunc.apply(args); + uint length = tblist.length(); + for(Py::List::size_type i = 0; i < length; ++i) + errorlist.append( Py::Object(tblist[i]).as_string().c_str() ); + } + catch(Py::Exception& e) { + QString err = Py::value(e).as_string().c_str(); + e.clear(); // exception is handled. clear it now. + krosswarning( QString("Kross::Python::PythonScript::toException() Failed to fetch a traceback: %1").arg(err) ); + } + + PyObject *next; + while (traceback && traceback != Py_None) { + PyFrameObject *frame = (PyFrameObject*)PyObject_GetAttrString(traceback, "tb_frame"); + Py_DECREF(frame); + { + PyObject *getobj = PyObject_GetAttrString(traceback, "tb_lineno"); + lineno = PyInt_AsLong(getobj); + Py_DECREF(getobj); + } + if(Py_OptimizeFlag) { + PyObject *getobj = PyObject_GetAttrString(traceback, "tb_lasti"); + int lasti = PyInt_AsLong(getobj); + Py_DECREF(getobj); + lineno = PyCode_Addr2Line(frame->f_code, lasti); + } + + //const char* filename = PyString_AsString(frame->f_code->co_filename); + //const char* name = PyString_AsString(frame->f_code->co_name); + //errorlist.append( QString("%1#%2: \"%3\"").arg(filename).arg(lineno).arg(name) ); + + next = PyObject_GetAttrString(traceback, "tb_next"); + Py_DECREF(traceback); + traceback = next; + } + } + + if(lineno < 0) { + if(value) { + PyObject *getobj = PyObject_GetAttrString(value, "lineno"); + if(getobj) { + lineno = PyInt_AsLong(getobj); + Py_DECREF(getobj); + } + } + if(lineno < 0) + lineno = 0; + } + + //PyErr_Restore(type, value, traceback); + + Kross::Api::Exception::Ptr exception = new Kross::Api::Exception(error, lineno - 1); + if(errorlist.count() > 0) + exception->setTrace( errorlist.join("\n") ); + return exception; +} + +const QStringList& PythonScript::getFunctionNames() +{ + if(! d->m_module) + initialize(); //TODO catch exception + return d->m_functions; + /* + QStringList list; + Py::List l = d->m_module->getDict().keys(); + int length = l.length(); + for(Py::List::size_type i = 0; i < length; ++i) + list.append( l[i].str().as_string().c_str() ); + return list; + */ +} + +Kross::Api::Object::Ptr PythonScript::execute() +{ +#ifdef KROSS_PYTHON_SCRIPT_EXEC_DEBUG + krossdebug( QString("PythonScript::execute()") ); +#endif + + try { + if(! d->m_module) + initialize(); + + // the main module dictonary. + Py::Dict mainmoduledict = ((PythonInterpreter*)m_interpreter)->mainModule()->getDict(); + // the local context dictonary. + Py::Dict moduledict( d->m_module->getDict().ptr() ); + + // Initialize context before execution. + QString s = + "import sys\n" + //"if self.has(\"stdout\"):\n" + //" sys.stdout = Redirect( self.get(\"stdout\") )\n" + //"if self.has(\"stderr\"):\n" + //" sys.stderr = Redirect( self.get(\"stderr\") )\n" + ; + + PyObject* pyrun = PyRun_String(s.latin1(), Py_file_input, mainmoduledict.ptr(), moduledict.ptr()); + if(! pyrun) + throw Py::Exception(); // throw exception + Py_XDECREF(pyrun); // free the reference. + + // Acquire interpreter lock*/ + PyGILState_STATE gilstate = PyGILState_Ensure(); + + // Evaluate the already compiled code. + PyObject* pyresult = PyEval_EvalCode( + (PyCodeObject*)d->m_code->ptr(), + mainmoduledict.ptr(), + moduledict.ptr() + ); + + // Free interpreter lock + PyGILState_Release(gilstate); + + if(! pyresult || PyErr_Occurred()) { + krosswarning("Kross::Python::PythonScript::execute(): Failed to PyEval_EvalCode"); + throw Py::Exception(); + } + Py::Object result(pyresult, true); + +#ifdef KROSS_PYTHON_SCRIPT_EXEC_DEBUG + krossdebug( QString("PythonScript::execute() result=%1").arg(result.as_string().c_str()) ); +#endif + + for(Py::Dict::iterator it = moduledict.begin(); it != moduledict.end(); ++it) { + Py::Dict::value_type vt(*it); + if(PyClass_Check( vt.second.ptr() )) { +#ifdef KROSS_PYTHON_SCRIPT_EXEC_DEBUG + krossdebug( QString("PythonScript::execute() class '%1' added.").arg(vt.first.as_string().c_str()) ); +#endif + d->m_classes.append( vt.first.as_string().c_str() ); + } + else if(vt.second.isCallable()) { +#ifdef KROSS_PYTHON_SCRIPT_EXEC_DEBUG + krossdebug( QString("PythonScript::execute() function '%1' added.").arg(vt.first.as_string().c_str()) ); +#endif + d->m_functions.append( vt.first.as_string().c_str() ); + } + } + + Kross::Api::Object::Ptr r = PythonExtension::toObject(result); + return r; + } + catch(Py::Exception& e) { + try { + Py::Object errobj = Py::value(e); + if(errobj.ptr() == Py_None) // at least string-exceptions have there errormessage in the type-object + errobj = Py::type(e); + QString err = errobj.as_string().c_str(); + + Kross::Api::Exception::Ptr exception = toException( QString("Failed to execute python code: %1").arg(err) ); + e.clear(); // exception is handled. clear it now. + setException( exception ); + } + catch(Py::Exception& e) { + QString err = Py::value(e).as_string().c_str(); + Kross::Api::Exception::Ptr exception = toException( QString("Failed to execute python code: %1").arg(err) ); + e.clear(); // exception is handled. clear it now. + setException( exception ); + } + } + catch(Kross::Api::Exception::Ptr e) { + setException(e); + } + + return 0; // return nothing if exception got thrown. +} + +Kross::Api::Object::Ptr PythonScript::callFunction(const QString& name, Kross::Api::List::Ptr args) +{ +#ifdef KROSS_PYTHON_SCRIPT_CALLFUNC_DEBUG + krossdebug( QString("PythonScript::callFunction(%1, %2)") + .arg(name) + .arg(args ? QString::number(args->count()) : QString("NULL")) ); +#endif + + if(hadException()) return 0; // abort if we had an unresolved exception. + + if(! d->m_module) { + setException( new Kross::Api::Exception(QString("Script not initialized.")) ); + return 0; + } + + try { + Py::Dict moduledict = d->m_module->getDict(); + + // Try to determinate the function we like to execute. + PyObject* func = PyDict_GetItemString(moduledict.ptr(), name.latin1()); + + if( (! d->m_functions.contains(name)) || (! func) ) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("No such function '%1'.").arg(name)) ); + + Py::Callable funcobject(func, true); // the funcobject takes care of freeing our func pyobject. + + // Check if the object is really a function and therefore callable. + if(! funcobject.isCallable()) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Function is not callable.")) ); + + // Call the function. + Py::Object result = funcobject.apply(PythonExtension::toPyTuple(args)); + return PythonExtension::toObject(result); + } + catch(Py::Exception& e) { + QString err = Py::value(e).as_string().c_str(); + e.clear(); // exception is handled. clear it now. + setException( new Kross::Api::Exception(QString("Python Exception: %1").arg(err)) ); + } + catch(Kross::Api::Exception::Ptr e) { + setException(e); + } + + return 0; // return nothing if exception got thrown. +} + +const QStringList& PythonScript::getClassNames() +{ + if(! d->m_module) + initialize(); //TODO catch exception + return d->m_classes; +} + +Kross::Api::Object::Ptr PythonScript::classInstance(const QString& name) +{ + if(hadException()) return 0; // abort if we had an unresolved exception. + + if(! d->m_module) { + setException( new Kross::Api::Exception(QString("Script not initialized.")) ); + return 0; + } + + try { + Py::Dict moduledict = d->m_module->getDict(); + + // Try to determinate the class. + PyObject* pyclass = PyDict_GetItemString(moduledict.ptr(), name.latin1()); + if( (! d->m_classes.contains(name)) || (! pyclass) ) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("No such class '%1'.").arg(name)) ); + + PyObject *pyobj = PyInstance_New(pyclass, 0, 0);//aclarg, 0); + if(! pyobj) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Failed to create instance of class '%1'.").arg(name)) ); + + Py::Object classobject(pyobj, true); + +#ifdef KROSS_PYTHON_SCRIPT_CLASSINSTANCE_DEBUG + krossdebug( QString("PythonScript::classInstance() inst='%1'").arg(classobject.as_string().c_str()) ); +#endif + return PythonExtension::toObject(classobject); + } + catch(Py::Exception& e) { + QString err = Py::value(e).as_string().c_str(); + e.clear(); // exception is handled. clear it now. + setException( Kross::Api::Exception::Ptr( new Kross::Api::Exception(err) ) ); + } + catch(Kross::Api::Exception::Ptr e) { + setException(e); + } + + return 0; // return nothing if exception got thrown. +} + diff --git a/lib/kross/python/pythonscript.h b/lib/kross/python/pythonscript.h new file mode 100644 index 00000000..6cbef625 --- /dev/null +++ b/lib/kross/python/pythonscript.h @@ -0,0 +1,98 @@ +/*************************************************************************** + * pythonscript.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_PYTHON_PYTHONSCRIPT_H +#define KROSS_PYTHON_PYTHONSCRIPT_H + +#include "pythonconfig.h" +#include "../api/script.h" + +namespace Kross { namespace Python { + + // Forward declarations. + class PythonScriptPrivate; + class PythonModuleManager; + + /** + * Handle python scripts. This class implements + * \a Kross::Api::Script for python. + */ + class PythonScript : public Kross::Api::Script + { + public: + + /** + * Constructor. + * + * \param interpreter The \a Kross::Python::PythonInterpreter used + * to create this PythonScript instance. + * \param scriptcontainer The with this PythonScript associated + * \a Kross::Api::ScriptContainer instance that spends us + * e.g. the python scripting code. + */ + explicit PythonScript(Kross::Api::Interpreter* interpreter, Kross::Api::ScriptContainer* scriptcontainer); + + /** + * Destructor. + */ + virtual ~PythonScript(); + + /** + * Return a list of callable functionnames this + * script spends. + */ + virtual const QStringList& getFunctionNames(); + + /** + * Execute the script. + */ + virtual Kross::Api::Object::Ptr execute(); + + /** + * Call a function. + */ + virtual Kross::Api::Object::Ptr callFunction(const QString& name, Kross::Api::List::Ptr args); + + /** + * Return a list of class types this script supports. + */ + virtual const QStringList& getClassNames(); + + /** + * Create and return a new class instance. + */ + virtual Kross::Api::Object::Ptr classInstance(const QString& name); + + private: + /// Private d-pointer class. + PythonScriptPrivate* d; + + /// Initialize the script. + void initialize(); + /// Finalize and cleanup the script. + void finalize(); + + /// \return a \a Kross::Api::Exception instance. + Kross::Api::Exception::Ptr toException(const QString& error); + }; + +}} + +#endif + diff --git a/lib/kross/python/pythonsecurity.cpp b/lib/kross/python/pythonsecurity.cpp new file mode 100644 index 00000000..86be0092 --- /dev/null +++ b/lib/kross/python/pythonsecurity.cpp @@ -0,0 +1,181 @@ +/*************************************************************************** + * pythonsecurity.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "pythonsecurity.h" +#include "pythoninterpreter.h" +#include "pythonmodule.h" + +using namespace Kross::Python; + +PythonSecurity::PythonSecurity(PythonInterpreter* interpreter) + : Py::ExtensionModule("PythonSecurity") + , m_interpreter(interpreter) + , m_pymodule(0) +{ + add_varargs_method("_getattr_", &PythonSecurity::_getattr_, "Secure wapper around the getattr method."); + initialize("The PythonSecurity module used to wrap the RestrictedPython functionality."); + + /* TESTCASE + initRestrictedPython(); + compile_restricted( + "a = 2 + 5\n" + "import os\n" + "import sys\n" + "b = sys.path\n" + "print \"######### >>>testcase<<< #########\" \n" + , + "mytestcase", // filename + "exec" // 'exec' or 'eval' or 'single' + ); + */ + +} + +PythonSecurity::~PythonSecurity() +{ + delete m_pymodule; +} + +void PythonSecurity::initRestrictedPython() +{ + try { + Py::Dict mainmoduledict = ((PythonInterpreter*)m_interpreter)->mainModule()->getDict(); + PyObject* pymodule = PyImport_ImportModuleEx( + "RestrictedPython", // name of the module being imported (may be a dotted name) + mainmoduledict.ptr(), // reference to the current global namespace + mainmoduledict.ptr(), // reference to the local namespace + 0 // PyObject *fromlist + ); + if(! pymodule) + throw Py::Exception(); + m_pymodule = new Py::Module(pymodule, true); + + PyObject* pyrun = PyRun_String( + //"import os\n" + //"import sys\n" + "import __main__\n" + "import PythonSecurity\n" + "from RestrictedPython import compile_restricted, PrintCollector\n" + "from RestrictedPython.Eval import RestrictionCapableEval\n" + "from RestrictedPython.RCompile import RModule\n" + + "setattr(__main__, '_getattr_', PythonSecurity._getattr_)\n" + "setattr(__main__, '_print_', PrintCollector)\n" + , + Py_file_input, + m_pymodule->getDict().ptr(), + m_pymodule->getDict().ptr() + ); + if(! pyrun) + throw Py::Exception(); + + krossdebug("PythonSecurity::PythonSecurity SUCCESSFULLY LOADED"); + } + catch(Py::Exception& e) { + QString err = Py::value(e).as_string().c_str(); + e.clear(); + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Failed to initialize PythonSecurity module: %1").arg(err) ) ); + } +} + +Py::Object PythonSecurity::_getattr_(const Py::Tuple& args) +{ + krossdebug("PythonSecurity::_getattr_"); + for(uint i = 0; i < args.size(); i++) { + Py::Object o = args[i]; + krossdebug( o.as_string().c_str() ); + } + return Py::None(); +} + +PyObject* PythonSecurity::compile_restricted(const QString& source, const QString& filename, const QString& mode) +{ + krossdebug("PythonSecurity::compile_restricted"); + if(! m_pymodule) + initRestrictedPython(); // throws exception if failed + + try { + Py::Dict mainmoduledict = ((PythonInterpreter*)m_interpreter)->mainModule()->getDict(); + + PyObject* func = PyDict_GetItemString(m_pymodule->getDict().ptr(), "compile_restricted"); + if(! func) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("No such function '%1'.").arg("compile_restricted")) ); + + Py::Callable funcobject(func, true); // the funcobject takes care of freeing our func pyobject. + + if(! funcobject.isCallable()) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Function '%1' is not callable.").arg("compile_restricted")) ); + + Py::Tuple args(3); + args[0] = Py::String(source.utf8()); + args[1] = Py::String(filename.utf8()); + args[2] = Py::String(mode.utf8()); + + Py::Object result = funcobject.apply(args); + + PyObject* pycode = PyEval_EvalCode( + (PyCodeObject*)result.ptr(), + mainmoduledict.ptr(), + mainmoduledict.ptr() + ); + if(! pycode) + throw Py::Exception(); + + /* + Py::List ml = mainmoduledict; + for(Py::List::size_type mi = 0; mi < ml.length(); ++mi) { + krossdebug( QString("dir() = %1").arg( ml[mi].str().as_string().c_str() ) ); + //krossdebug( QString("dir().dir() = %1").arg( Py::Object(ml[mi]).dir().as_string().c_str() ) ); + } + */ + + Py::Object code(pycode); + krossdebug( QString("%1 callable=%2").arg(code.as_string().c_str()).arg(PyCallable_Check(code.ptr())) ); + Py::List l = code.dir(); + for(Py::List::size_type i = 0; i < l.length(); ++i) { + krossdebug( QString("dir() = %1").arg( l[i].str().as_string().c_str() ) ); + //krossdebug( QString("dir().dir() = %1").arg( Py::Object(l[i]).dir().as_string().c_str() ) ); + } + + return pycode; + } + catch(Py::Exception& e) { + QString err = Py::value(e).as_string().c_str(); + e.clear(); + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Function '%1' failed with python exception: %2").arg("compile_restricted").arg(err) ) ); + } +} + +#if 0 +void PythonSecurity::compile_restricted_function(const Py::Tuple& /*args*/, const QString& /*body*/, const QString& /*name*/, const QString& /*filename*/, const Py::Object& /*globalize*/) +{ + //TODO +} + +void PythonSecurity::compile_restricted_exec(const QString& /*source*/, const QString& /*filename*/) +{ + //TODO +} + +void PythonSecurity::compile_restricted_eval(const QString& /*source*/, const QString& /*filename*/) +{ + //TODO +} +#endif + diff --git a/lib/kross/python/pythonsecurity.h b/lib/kross/python/pythonsecurity.h new file mode 100644 index 00000000..7ffbcfff --- /dev/null +++ b/lib/kross/python/pythonsecurity.h @@ -0,0 +1,109 @@ +/*************************************************************************** + * pythonsecurity.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_PYTHON_SECURITY_H +#define KROSS_PYTHON_SECURITY_H + +#include "pythonconfig.h" + +#include + +namespace Kross { namespace Python { + + // Forward declaration. + class PythonInterpreter; + + /** + * This class handles the used Zope3 RestrictedPython + * package to spend a restricted sandbox for scripting + * code. + * + * The RestrictedPython code is avaible as Python files. + * So, this class takes care of loading them and spending + * the functions we need to access the functionality + * from within Kross. That way it's easy to update the + * module with a newer version if some security issues + * show up. + * + * What the RestrictedPython code does is to compile + * the plain python code (py) into compiled python code (pyc) + * and manipulate those compiled code by replacing unsafe + * code with own wrapped code. + * As example a simple "x = y.z" would be transfered to + * "x = _getattr_(y, 'z')". The _getattr_ is defined in + * the RestrictedPython module and will take care of + * applied restrictions. + * + * \see http://www.zope.org + * \see http://svn.zope.org/Zope3/trunk/src/RestrictedPython/ + */ + class PythonSecurity : public Py::ExtensionModule + { + public: + + /** + * Constructor. + * + * \param interpreter The \a PythonInterpreter instance + * used to create this Module. + */ + explicit PythonSecurity(PythonInterpreter* interpreter); + + /** + * Destructor. + */ + virtual ~PythonSecurity(); + + /** + * Compile python scripting code and return a restricted + * code object. + * + * \param source The python scripting code. + * \param filename The filename used on errormessages. + * \param mode Compilemode, could be 'exec' or 'eval' or 'single'. + * \return The compiled python code object on success else + * NULL. The caller owns the resulting object and needs + * to take care to decrease the ref-counter it not needed + * any longer. + */ + PyObject* compile_restricted(const QString& source, const QString& filename, const QString& mode); + +#if 0 + //TODO + void compile_restricted_function(const Py::Tuple& args, const QString& body, const QString& name, const QString& filename, const Py::Object& globalize = Py::None()); + void compile_restricted_exec(const QString& source, const QString& filename = ""); + void compile_restricted_eval(const QString& source, const QString& filename = ""); +#endif + + private: + /// We keep a pointer to the used \a PythonInterpreter. + PythonInterpreter* m_interpreter; + /// The imported external RestrictedPython module. + Py::Module* m_pymodule; + + /// Initialize the restricted python module. + inline void initRestrictedPython(); + + /// Secure wrapper around the getattr method. + Py::Object _getattr_(const Py::Tuple&); + }; + +}} + +#endif diff --git a/lib/kross/python/scripts/Makefile.am b/lib/kross/python/scripts/Makefile.am new file mode 100644 index 00000000..c3b720ce --- /dev/null +++ b/lib/kross/python/scripts/Makefile.am @@ -0,0 +1,4 @@ +modulesdir = $(kde_datadir)/kross/python +modules_SCRIPTS = gui.py + +SUBDIRS = RestrictedPython diff --git a/lib/kross/python/scripts/RestrictedPython/Eval.py b/lib/kross/python/scripts/RestrictedPython/Eval.py new file mode 100644 index 00000000..841067a1 --- /dev/null +++ b/lib/kross/python/scripts/RestrictedPython/Eval.py @@ -0,0 +1,118 @@ +############################################################################## +# +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE +# +############################################################################## +"""Restricted Python Expressions + +$Id: Eval.py 24763 2004-05-17 05:59:28Z philikon $ +""" + +__version__='$Revision: 1.6 $'[11:-2] + +from RestrictedPython import compile_restricted_eval + +from string import translate, strip +import string + +nltosp = string.maketrans('\r\n',' ') + +default_guarded_getattr = getattr # No restrictions. + +def default_guarded_getitem(ob, index): + # No restrictions. + return ob[index] + +PROFILE = 0 + +class RestrictionCapableEval: + """A base class for restricted code.""" + + globals = {'__builtins__': None} + rcode = None # restricted + ucode = None # unrestricted + used = None + + def __init__(self, expr): + """Create a restricted expression + + where: + + expr -- a string containing the expression to be evaluated. + """ + expr = strip(expr) + self.__name__ = expr + expr = translate(expr, nltosp) + self.expr = expr + self.prepUnrestrictedCode() # Catch syntax errors. + + def prepRestrictedCode(self): + if self.rcode is None: + if PROFILE: + from time import clock + start = clock() + co, err, warn, used = compile_restricted_eval( + self.expr, '') + if PROFILE: + end = clock() + print 'prepRestrictedCode: %d ms for %s' % ( + (end - start) * 1000, `self.expr`) + if err: + raise SyntaxError, err[0] + self.used = tuple(used.keys()) + self.rcode = co + + def prepUnrestrictedCode(self): + if self.ucode is None: + # Use the standard compiler. + co = compile(self.expr, '', 'eval') + if self.used is None: + # Examine the code object, discovering which names + # the expression needs. + names=list(co.co_names) + used={} + i=0 + code=co.co_code + l=len(code) + LOAD_NAME=101 + HAVE_ARGUMENT=90 + while(i < l): + c=ord(code[i]) + if c==LOAD_NAME: + name=names[ord(code[i+1])+256*ord(code[i+2])] + used[name]=1 + i=i+3 + elif c >= HAVE_ARGUMENT: i=i+3 + else: i=i+1 + self.used=tuple(used.keys()) + self.ucode=co + + def eval(self, mapping): + # This default implementation is probably not very useful. :-( + # This is meant to be overridden. + self.prepRestrictedCode() + code = self.rcode + d = {'_getattr_': default_guarded_getattr, + '_getitem_': default_guarded_getitem} + d.update(self.globals) + has_key = d.has_key + for name in self.used: + try: + if not has_key(name): + d[name] = mapping[name] + except KeyError: + # Swallow KeyErrors since the expression + # might not actually need the name. If it + # does need the name, a NameError will occur. + pass + return eval(code, d) + + def __call__(self, **kw): + return self.eval(kw) diff --git a/lib/kross/python/scripts/RestrictedPython/Guards.py b/lib/kross/python/scripts/RestrictedPython/Guards.py new file mode 100644 index 00000000..4fbdcad1 --- /dev/null +++ b/lib/kross/python/scripts/RestrictedPython/Guards.py @@ -0,0 +1,136 @@ +############################################################################## +# +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE +# +############################################################################## +__version__ = '$Revision: 1.14 $'[11:-2] + +import exceptions + +# This tiny set of safe builtins is extended by users of the module. +# AccessControl.ZopeGuards contains a large set of wrappers for builtins. +# DocumentTemplate.DT_UTil contains a few. + +safe_builtins = {} + +for name in ['False', 'None', 'True', 'abs', 'basestring', 'bool', 'callable', + 'chr', 'cmp', 'complex', 'divmod', 'float', 'hash', + 'hex', 'id', 'int', 'isinstance', 'issubclass', 'len', + 'long', 'oct', 'ord', 'pow', 'range', 'repr', 'round', + 'str', 'tuple', 'unichr', 'unicode', 'xrange', 'zip']: + + safe_builtins[name] = __builtins__[name] + +# Wrappers provided by this module: +# delattr +# setattr + +# Wrappers provided by ZopeGuards: +# __import__ +# apply +# dict +# enumerate +# filter +# getattr +# hasattr +# iter +# list +# map +# max +# min +# sum + +# Builtins that are intentionally disabled +# compile - don't let them produce new code +# dir - a general purpose introspector, probably hard to wrap +# execfile - no direct I/O +# file - no direct I/O +# globals - uncontrolled namespace access +# input - no direct I/O +# locals - uncontrolled namespace access +# open - no direct I/O +# raw_input - no direct I/O +# vars - uncontrolled namespace access + +# There are several strings that describe Python. I think there's no +# point to including these, although they are obviously safe: +# copyright, credits, exit, help, license, quit + +# Not provided anywhere. Do something about these? Several are +# related to new-style classes, which we are too scared of to support +# <0.3 wink>. coerce, buffer, and reload are esoteric enough that no +# one should care. + +# buffer +# classmethod +# coerce +# eval +# intern +# object +# property +# reload +# slice +# staticmethod +# super +# type + +for name in dir(exceptions): + if name[0] != "_": + safe_builtins[name] = getattr(exceptions, name) + +def _write_wrapper(): + # Construct the write wrapper class + def _handler(secattr, error_msg): + # Make a class method. + def handler(self, *args): + try: + f = getattr(self.ob, secattr) + except AttributeError: + raise TypeError, error_msg + f(*args) + return handler + class Wrapper: + def __len__(self): + # Required for slices with negative bounds. + return len(self.ob) + def __init__(self, ob): + self.__dict__['ob'] = ob + __setitem__ = _handler('__guarded_setitem__', + 'object does not support item or slice assignment') + __delitem__ = _handler('__guarded_delitem__', + 'object does not support item or slice assignment') + __setattr__ = _handler('__guarded_setattr__', + 'attribute-less object (assign or del)') + __delattr__ = _handler('__guarded_delattr__', + 'attribute-less object (assign or del)') + return Wrapper + +def _full_write_guard(): + # Nested scope abuse! + # safetype and Wrapper variables are used by guard() + safetype = {dict: True, list: True}.has_key + Wrapper = _write_wrapper() + def guard(ob): + # Don't bother wrapping simple types, or objects that claim to + # handle their own write security. + if safetype(type(ob)) or hasattr(ob, '_guarded_writes'): + return ob + # Hand the object to the Wrapper instance, then return the instance. + return Wrapper(ob) + return guard +full_write_guard = _full_write_guard() + +def guarded_setattr(object, name, value): + setattr(full_write_guard(object), name, value) +safe_builtins['setattr'] = guarded_setattr + +def guarded_delattr(object, name): + delattr(full_write_guard(object), name) +safe_builtins['delattr'] = guarded_delattr diff --git a/lib/kross/python/scripts/RestrictedPython/Limits.py b/lib/kross/python/scripts/RestrictedPython/Limits.py new file mode 100644 index 00000000..3b782e65 --- /dev/null +++ b/lib/kross/python/scripts/RestrictedPython/Limits.py @@ -0,0 +1,46 @@ +############################################################################## +# +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE +# +############################################################################## + +__version__='$Revision: 1.5 $'[11:-2] + +limited_builtins = {} + +def limited_range(iFirst, *args): + # limited range function from Martijn Pieters + RANGELIMIT = 1000 + if not len(args): + iStart, iEnd, iStep = 0, iFirst, 1 + elif len(args) == 1: + iStart, iEnd, iStep = iFirst, args[0], 1 + elif len(args) == 2: + iStart, iEnd, iStep = iFirst, args[0], args[1] + else: + raise AttributeError, 'range() requires 1-3 int arguments' + if iStep == 0: raise ValueError, 'zero step for range()' + iLen = int((iEnd - iStart) / iStep) + if iLen < 0: iLen = 0 + if iLen >= RANGELIMIT: raise ValueError, 'range() too large' + return range(iStart, iEnd, iStep) +limited_builtins['range'] = limited_range + +def limited_list(seq): + if isinstance(seq, str): + raise TypeError, 'cannot convert string to list' + return list(seq) +limited_builtins['list'] = limited_list + +def limited_tuple(seq): + if isinstance(seq, str): + raise TypeError, 'cannot convert string to tuple' + return tuple(seq) +limited_builtins['tuple'] = limited_tuple diff --git a/lib/kross/python/scripts/RestrictedPython/Makefile.am b/lib/kross/python/scripts/RestrictedPython/Makefile.am new file mode 100644 index 00000000..3d5b3c73 --- /dev/null +++ b/lib/kross/python/scripts/RestrictedPython/Makefile.am @@ -0,0 +1,4 @@ +restrictedpythondir = $(kde_datadir)/kross/python/RestrictedPython +restrictedpython_SCRIPTS = __init__.py Eval.py Guards.py Limits.py \ + MutatingWalker.py PrintCollector.py RCompile.py RestrictionMutator.py \ + SelectCompiler.py Utilities.py diff --git a/lib/kross/python/scripts/RestrictedPython/MutatingWalker.py b/lib/kross/python/scripts/RestrictedPython/MutatingWalker.py new file mode 100644 index 00000000..b0b8c9ce --- /dev/null +++ b/lib/kross/python/scripts/RestrictedPython/MutatingWalker.py @@ -0,0 +1,74 @@ +############################################################################## +# +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE +# +############################################################################## + +__version__='$Revision: 1.6 $'[11:-2] + +from SelectCompiler import ast + +ListType = type([]) +TupleType = type(()) +SequenceTypes = (ListType, TupleType) + +class MutatingWalker: + + def __init__(self, visitor): + self.visitor = visitor + self._cache = {} + + def defaultVisitNode(self, node, walker=None, exclude=None): + for name, child in node.__dict__.items(): + if exclude is not None and name in exclude: + continue + v = self.dispatchObject(child) + if v is not child: + # Replace the node. + node.__dict__[name] = v + return node + + def visitSequence(self, seq): + res = seq + for idx in range(len(seq)): + child = seq[idx] + v = self.dispatchObject(child) + if v is not child: + # Change the sequence. + if type(res) is ListType: + res[idx : idx + 1] = [v] + else: + res = res[:idx] + (v,) + res[idx + 1:] + return res + + def dispatchObject(self, ob): + ''' + Expected to return either ob or something that will take + its place. + ''' + if isinstance(ob, ast.Node): + return self.dispatchNode(ob) + elif type(ob) in SequenceTypes: + return self.visitSequence(ob) + else: + return ob + + def dispatchNode(self, node): + klass = node.__class__ + meth = self._cache.get(klass, None) + if meth is None: + className = klass.__name__ + meth = getattr(self.visitor, 'visit' + className, + self.defaultVisitNode) + self._cache[klass] = meth + return meth(node, self) + +def walk(tree, visitor): + return MutatingWalker(visitor).dispatchNode(tree) diff --git a/lib/kross/python/scripts/RestrictedPython/PrintCollector.py b/lib/kross/python/scripts/RestrictedPython/PrintCollector.py new file mode 100644 index 00000000..15a1c180 --- /dev/null +++ b/lib/kross/python/scripts/RestrictedPython/PrintCollector.py @@ -0,0 +1,23 @@ +############################################################################## +# +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE +# +############################################################################## + +__version__='$Revision: 1.4 $'[11:-2] + +class PrintCollector: + '''Collect written text, and return it when called.''' + def __init__(self): + self.txt = [] + def write(self, text): + self.txt.append(text) + def __call__(self): + return ''.join(self.txt) diff --git a/lib/kross/python/scripts/RestrictedPython/RCompile.py b/lib/kross/python/scripts/RestrictedPython/RCompile.py new file mode 100644 index 00000000..0a538657 --- /dev/null +++ b/lib/kross/python/scripts/RestrictedPython/RCompile.py @@ -0,0 +1,235 @@ +############################################################################## +# +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE +# +############################################################################## +"""Compiles restricted code using the compiler module from the +Python standard library. +""" + +__version__='$Revision: 1.6 $'[11:-2] + +from compiler import ast, parse, misc, syntax, pycodegen +from compiler.pycodegen import AbstractCompileMode, Expression, \ + Interactive, Module, ModuleCodeGenerator, FunctionCodeGenerator, findOp + +import MutatingWalker +from RestrictionMutator import RestrictionMutator + + +def niceParse(source, filename, mode): + try: + return parse(source, mode) + except: + # Try to make a clean error message using + # the builtin Python compiler. + try: + compile(source, filename, mode) + except SyntaxError: + raise + # Some other error occurred. + raise + +class RestrictedCompileMode(AbstractCompileMode): + """Abstract base class for hooking up custom CodeGenerator.""" + # See concrete subclasses below. + + def __init__(self, source, filename): + self.rm = RestrictionMutator() + AbstractCompileMode.__init__(self, source, filename) + + def parse(self): + return niceParse(self.source, self.filename, self.mode) + + def _get_tree(self): + tree = self.parse() + MutatingWalker.walk(tree, self.rm) + if self.rm.errors: + raise SyntaxError, self.rm.errors[0] + misc.set_filename(self.filename, tree) + syntax.check(tree) + return tree + + def compile(self): + tree = self._get_tree() + gen = self.CodeGeneratorClass(tree) + self.code = gen.getCode() + + +def compileAndTuplize(gen): + try: + gen.compile() + except SyntaxError, v: + return None, (str(v),), gen.rm.warnings, gen.rm.used_names + return gen.getCode(), (), gen.rm.warnings, gen.rm.used_names + +def compile_restricted_function(p, body, name, filename, globalize=None): + """Compiles a restricted code object for a function. + + The function can be reconstituted using the 'new' module: + + new.function(, ) + + The globalize argument, if specified, is a list of variable names to be + treated as globals (code is generated as if each name in the list + appeared in a global statement at the top of the function). + """ + gen = RFunction(p, body, name, filename, globalize) + return compileAndTuplize(gen) + +def compile_restricted_exec(s, filename=''): + """Compiles a restricted code suite.""" + gen = RModule(s, filename) + return compileAndTuplize(gen) + +def compile_restricted_eval(s, filename=''): + """Compiles a restricted expression.""" + gen = RExpression(s, filename) + return compileAndTuplize(gen) + +def compile_restricted(source, filename, mode): + """Replacement for the builtin compile() function.""" + if mode == "single": + gen = RInteractive(source, filename) + elif mode == "exec": + gen = RModule(source, filename) + elif mode == "eval": + gen = RExpression(source, filename) + else: + raise ValueError("compile_restricted() 3rd arg must be 'exec' or " + "'eval' or 'single'") + gen.compile() + return gen.getCode() + +class RestrictedCodeGenerator: + """Mixin for CodeGenerator to replace UNPACK_SEQUENCE bytecodes. + + The UNPACK_SEQUENCE opcode is not safe because it extracts + elements from a sequence without using a safe iterator or + making __getitem__ checks. + + This code generator replaces use of UNPACK_SEQUENCE with calls to + a function that unpacks the sequence, performes the appropriate + security checks, and returns a simple list. + """ + + # Replace the standard code generator for assignments to tuples + # and lists. + + def _gen_safe_unpack_sequence(self, num): + # We're at a place where UNPACK_SEQUENCE should be generated, to + # unpack num items. That's a security hole, since it exposes + # individual items from an arbitrary iterable. We don't remove + # the UNPACK_SEQUENCE, but instead insert a call to our _getiter_() + # wrapper first. That applies security checks to each item as + # it's delivered. codegen is (just) a bit messy because the + # iterable is already on the stack, so we have to do a stack swap + # to get things in the right order. + self.emit('LOAD_GLOBAL', '_getiter_') + self.emit('ROT_TWO') + self.emit('CALL_FUNCTION', 1) + self.emit('UNPACK_SEQUENCE', num) + + def _visitAssSequence(self, node): + if findOp(node) != 'OP_DELETE': + self._gen_safe_unpack_sequence(len(node.nodes)) + for child in node.nodes: + self.visit(child) + + visitAssTuple = _visitAssSequence + visitAssList = _visitAssSequence + + # Call to generate code for unpacking nested tuple arguments + # in function calls. + + def unpackSequence(self, tup): + self._gen_safe_unpack_sequence(len(tup)) + for elt in tup: + if isinstance(elt, tuple): + self.unpackSequence(elt) + else: + self._nameOp('STORE', elt) + +# A collection of code generators that adds the restricted mixin to +# handle unpacking for all the different compilation modes. They +# are defined here (at the end) so that can refer to RestrictedCodeGenerator. + +class RestrictedFunctionCodeGenerator(RestrictedCodeGenerator, + pycodegen.FunctionCodeGenerator): + pass + +class RestrictedExpressionCodeGenerator(RestrictedCodeGenerator, + pycodegen.ExpressionCodeGenerator): + pass + +class RestrictedInteractiveCodeGenerator(RestrictedCodeGenerator, + pycodegen.InteractiveCodeGenerator): + pass + +class RestrictedModuleCodeGenerator(RestrictedCodeGenerator, + pycodegen.ModuleCodeGenerator): + + def initClass(self): + ModuleCodeGenerator.initClass(self) + self.__class__.FunctionGen = RestrictedFunctionCodeGenerator + + +# These subclasses work around the definition of stub compile and mode +# attributes in the common base class AbstractCompileMode. If it +# didn't define new attributes, then the stub code inherited via +# RestrictedCompileMode would override the real definitions in +# Expression. + +class RExpression(RestrictedCompileMode, Expression): + mode = "eval" + CodeGeneratorClass = RestrictedExpressionCodeGenerator + +class RInteractive(RestrictedCompileMode, Interactive): + mode = "single" + CodeGeneratorClass = RestrictedInteractiveCodeGenerator + +class RModule(RestrictedCompileMode, Module): + mode = "exec" + CodeGeneratorClass = RestrictedModuleCodeGenerator + +class RFunction(RModule): + """A restricted Python function built from parts.""" + + CodeGeneratorClass = RestrictedModuleCodeGenerator + + def __init__(self, p, body, name, filename, globals): + self.params = p + self.body = body + self.name = name + self.globals = globals or [] + RModule.__init__(self, None, filename) + + def parse(self): + # Parse the parameters and body, then combine them. + firstline = 'def f(%s): pass' % self.params + tree = niceParse(firstline, '', 'exec') + f = tree.node.nodes[0] + body_code = niceParse(self.body, self.filename, 'exec') + # Stitch the body code into the function. + f.code.nodes = body_code.node.nodes + f.name = self.name + # Look for a docstring. + stmt1 = f.code.nodes[0] + if (isinstance(stmt1, ast.Discard) and + isinstance(stmt1.expr, ast.Const) and + isinstance(stmt1.expr.value, str)): + f.doc = stmt1.expr.value + # The caller may specify that certain variables are globals + # so that they can be referenced before a local assignment. + # The only known example is the variables context, container, + # script, traverse_subpath in PythonScripts. + if self.globals: + f.code.nodes.insert(0, ast.Global(self.globals)) + return tree diff --git a/lib/kross/python/scripts/RestrictedPython/RestrictionMutator.py b/lib/kross/python/scripts/RestrictedPython/RestrictionMutator.py new file mode 100644 index 00000000..a8b3850e --- /dev/null +++ b/lib/kross/python/scripts/RestrictedPython/RestrictionMutator.py @@ -0,0 +1,372 @@ +############################################################################## +# +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE +# +############################################################################## +"""Modify AST to include security checks. + +RestrictionMutator modifies a tree produced by +compiler.transformer.Transformer, restricting and enhancing the +code in various ways before sending it to pycodegen. + +$Revision: 1.13 $ +""" + +from SelectCompiler import ast, parse, OP_ASSIGN, OP_DELETE, OP_APPLY + +# These utility functions allow us to generate AST subtrees without +# line number attributes. These trees can then be inserted into other +# trees without affecting line numbers shown in tracebacks, etc. +def rmLineno(node): + """Strip lineno attributes from a code tree.""" + if node.__dict__.has_key('lineno'): + del node.lineno + for child in node.getChildren(): + if isinstance(child, ast.Node): + rmLineno(child) + +def stmtNode(txt): + """Make a "clean" statement node.""" + node = parse(txt).node.nodes[0] + rmLineno(node) + return node + +# The security checks are performed by a set of six functions that +# must be provided by the restricted environment. + +_apply_name = ast.Name("_apply_") +_getattr_name = ast.Name("_getattr_") +_getitem_name = ast.Name("_getitem_") +_getiter_name = ast.Name("_getiter_") +_print_target_name = ast.Name("_print") +_write_name = ast.Name("_write_") + +# Constants. +_None_const = ast.Const(None) +_write_const = ast.Const("write") + +_printed_expr = stmtNode("_print()").expr +_print_target_node = stmtNode("_print = _print_()") + +class FuncInfo: + print_used = False + printed_used = False + +class RestrictionMutator: + + def __init__(self): + self.warnings = [] + self.errors = [] + self.used_names = {} + self.funcinfo = FuncInfo() + + def error(self, node, info): + """Records a security error discovered during compilation.""" + lineno = getattr(node, 'lineno', None) + if lineno is not None and lineno > 0: + self.errors.append('Line %d: %s' % (lineno, info)) + else: + self.errors.append(info) + + def checkName(self, node, name): + """Verifies that a name being assigned is safe. + + This is to prevent people from doing things like: + + __metatype__ = mytype (opens up metaclasses, a big unknown + in terms of security) + __path__ = foo (could this confuse the import machinery?) + _getattr = somefunc (not very useful, but could open a hole) + + Note that assigning a variable is not the only way to assign + a name. def _badname, class _badname, import foo as _badname, + and perhaps other statements assign names. Special case: + '_' is allowed. + """ + if name.startswith("_") and name != "_": + # Note: "_" *is* allowed. + self.error(node, '"%s" is an invalid variable name because' + ' it starts with "_"' % name) + if name == "printed": + self.error(node, '"printed" is a reserved name.') + + def checkAttrName(self, node): + """Verifies that an attribute name does not start with _. + + As long as guards (security proxies) have underscored names, + this underscore protection is important regardless of the + security policy. Special case: '_' is allowed. + """ + name = node.attrname + if name.startswith("_") and name != "_": + # Note: "_" *is* allowed. + self.error(node, '"%s" is an invalid attribute name ' + 'because it starts with "_".' % name) + + def prepBody(self, body): + """Insert code for print at the beginning of the code suite.""" + + if self.funcinfo.print_used or self.funcinfo.printed_used: + # Add code at top for creating _print_target + body.insert(0, _print_target_node) + if not self.funcinfo.printed_used: + self.warnings.append( + "Prints, but never reads 'printed' variable.") + elif not self.funcinfo.print_used: + self.warnings.append( + "Doesn't print, but reads 'printed' variable.") + + def visitFunction(self, node, walker): + """Checks and mutates a function definition. + + Checks the name of the function and the argument names using + checkName(). It also calls prepBody() to prepend code to the + beginning of the code suite. + """ + self.checkName(node, node.name) + for argname in node.argnames: + if isinstance(argname, str): + self.checkName(node, argname) + else: + for name in argname: + self.checkName(node, name) + walker.visitSequence(node.defaults) + + former_funcinfo = self.funcinfo + self.funcinfo = FuncInfo() + node = walker.defaultVisitNode(node, exclude=('defaults',)) + self.prepBody(node.code.nodes) + self.funcinfo = former_funcinfo + return node + + def visitLambda(self, node, walker): + """Checks and mutates an anonymous function definition. + + Checks the argument names using checkName(). It also calls + prepBody() to prepend code to the beginning of the code suite. + """ + for argname in node.argnames: + self.checkName(node, argname) + return walker.defaultVisitNode(node) + + def visitPrint(self, node, walker): + """Checks and mutates a print statement. + + Adds a target to all print statements. 'print foo' becomes + 'print >> _print, foo', where _print is the default print + target defined for this scope. + + Alternatively, if the untrusted code provides its own target, + we have to check the 'write' method of the target. + 'print >> ob, foo' becomes + 'print >> (_getattr(ob, 'write') and ob), foo'. + Otherwise, it would be possible to call the write method of + templates and scripts; 'write' happens to be the name of the + method that changes them. + """ + node = walker.defaultVisitNode(node) + self.funcinfo.print_used = True + if node.dest is None: + node.dest = _print_target_name + else: + # Pre-validate access to the "write" attribute. + # "print >> ob, x" becomes + # "print >> (_getattr(ob, 'write') and ob), x" + node.dest = ast.And([ + ast.CallFunc(_getattr_name, [node.dest, _write_const]), + node.dest]) + return node + + visitPrintnl = visitPrint + + def visitName(self, node, walker): + """Prevents access to protected names as defined by checkName(). + + Also converts use of the name 'printed' to an expression. + """ + if node.name == 'printed': + # Replace name lookup with an expression. + self.funcinfo.printed_used = True + return _printed_expr + self.checkName(node, node.name) + self.used_names[node.name] = True + return node + + def visitCallFunc(self, node, walker): + """Checks calls with *-args and **-args. + + That's a way of spelling apply(), and needs to use our safe + _apply_ instead. + """ + walked = walker.defaultVisitNode(node) + if node.star_args is None and node.dstar_args is None: + # This is not an extended function call + return walked + # Otherwise transform foo(a, b, c, d=e, f=g, *args, **kws) into a call + # of _apply_(foo, a, b, c, d=e, f=g, *args, **kws). The interesting + # thing here is that _apply_() is defined with just *args and **kws, + # so it gets Python to collapse all the myriad ways to call functions + # into one manageable form. + # + # From there, _apply_() digs out the first argument of *args (it's the + # function to call), wraps args and kws in guarded accessors, then + # calls the function, returning the value. + # Transform foo(...) to _apply(foo, ...) + walked.args.insert(0, walked.node) + walked.node = _apply_name + return walked + + def visitAssName(self, node, walker): + """Checks a name assignment using checkName().""" + self.checkName(node, node.name) + return node + + def visitFor(self, node, walker): + # convert + # for x in expr: + # to + # for x in _getiter(expr): + # + # Note that visitListCompFor is the same thing. Exactly the same + # transformation is needed to convert + # [... for x in expr ...] + # to + # [... for x in _getiter(expr) ...] + node = walker.defaultVisitNode(node) + node.list = ast.CallFunc(_getiter_name, [node.list]) + return node + + visitListCompFor = visitFor + + def visitGetattr(self, node, walker): + """Converts attribute access to a function call. + + 'foo.bar' becomes '_getattr(foo, "bar")'. + + Also prevents augmented assignment of attributes, which would + be difficult to support correctly. + """ + self.checkAttrName(node) + node = walker.defaultVisitNode(node) + if getattr(node, 'in_aug_assign', False): + # We're in an augmented assignment + # We might support this later... + self.error(node, 'Augmented assignment of ' + 'attributes is not allowed.') + return ast.CallFunc(_getattr_name, + [node.expr, ast.Const(node.attrname)]) + + def visitSubscript(self, node, walker): + """Checks all kinds of subscripts. + + 'foo[bar] += baz' is disallowed. + 'a = foo[bar, baz]' becomes 'a = _getitem(foo, (bar, baz))'. + 'a = foo[bar]' becomes 'a = _getitem(foo, bar)'. + 'a = foo[bar:baz]' becomes 'a = _getitem(foo, slice(bar, baz))'. + 'a = foo[:baz]' becomes 'a = _getitem(foo, slice(None, baz))'. + 'a = foo[bar:]' becomes 'a = _getitem(foo, slice(bar, None))'. + 'del foo[bar]' becomes 'del _write(foo)[bar]'. + 'foo[bar] = a' becomes '_write(foo)[bar] = a'. + + The _write function returns a security proxy. + """ + node = walker.defaultVisitNode(node) + if node.flags == OP_APPLY: + # Set 'subs' to the node that represents the subscript or slice. + if getattr(node, 'in_aug_assign', False): + # We're in an augmented assignment + # We might support this later... + self.error(node, 'Augmented assignment of ' + 'object items and slices is not allowed.') + if hasattr(node, 'subs'): + # Subscript. + subs = node.subs + if len(subs) > 1: + # example: ob[1,2] + subs = ast.Tuple(subs) + else: + # example: ob[1] + subs = subs[0] + else: + # Slice. + # example: obj[0:2] + lower = node.lower + if lower is None: + lower = _None_const + upper = node.upper + if upper is None: + upper = _None_const + subs = ast.Sliceobj([lower, upper]) + return ast.CallFunc(_getitem_name, [node.expr, subs]) + elif node.flags in (OP_DELETE, OP_ASSIGN): + # set or remove subscript or slice + node.expr = ast.CallFunc(_write_name, [node.expr]) + return node + + visitSlice = visitSubscript + + def visitAssAttr(self, node, walker): + """Checks and mutates attribute assignment. + + 'a.b = c' becomes '_write(a).b = c'. + The _write function returns a security proxy. + """ + self.checkAttrName(node) + node = walker.defaultVisitNode(node) + node.expr = ast.CallFunc(_write_name, [node.expr]) + return node + + def visitExec(self, node, walker): + self.error(node, 'Exec statements are not allowed.') + + def visitYield(self, node, walker): + self.error(node, 'Yield statements are not allowed.') + + def visitClass(self, node, walker): + """Checks the name of a class using checkName(). + + Should classes be allowed at all? They don't cause security + issues, but they aren't very useful either since untrusted + code can't assign instance attributes. + """ + self.checkName(node, node.name) + return walker.defaultVisitNode(node) + + def visitModule(self, node, walker): + """Adds prep code at module scope. + + Zope doesn't make use of this. The body of Python scripts is + always at function scope. + """ + node = walker.defaultVisitNode(node) + self.prepBody(node.node.nodes) + return node + + def visitAugAssign(self, node, walker): + """Makes a note that augmented assignment is in use. + + Note that although augmented assignment of attributes and + subscripts is disallowed, augmented assignment of names (such + as 'n += 1') is allowed. + + This could be a problem if untrusted code got access to a + mutable database object that supports augmented assignment. + """ + node.node.in_aug_assign = True + return walker.defaultVisitNode(node) + + def visitImport(self, node, walker): + """Checks names imported using checkName().""" + for name, asname in node.names: + self.checkName(node, name) + if asname: + self.checkName(node, asname) + return node + diff --git a/lib/kross/python/scripts/RestrictedPython/SelectCompiler.py b/lib/kross/python/scripts/RestrictedPython/SelectCompiler.py new file mode 100644 index 00000000..5f9da698 --- /dev/null +++ b/lib/kross/python/scripts/RestrictedPython/SelectCompiler.py @@ -0,0 +1,28 @@ +############################################################################## +# +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE +# +############################################################################## +"""Compiler selector. + +$Id: SelectCompiler.py 24763 2004-05-17 05:59:28Z philikon $ +""" + +# Use the compiler from the standard library. +import compiler +from compiler import ast +from compiler.transformer import parse +from compiler.consts import OP_ASSIGN, OP_DELETE, OP_APPLY + +from RCompile import \ + compile_restricted, \ + compile_restricted_function, \ + compile_restricted_exec, \ + compile_restricted_eval diff --git a/lib/kross/python/scripts/RestrictedPython/Utilities.py b/lib/kross/python/scripts/RestrictedPython/Utilities.py new file mode 100644 index 00000000..87eef69b --- /dev/null +++ b/lib/kross/python/scripts/RestrictedPython/Utilities.py @@ -0,0 +1,77 @@ +############################################################################## +# +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE +# +############################################################################## + +__version__='$Revision: 1.7 $'[11:-2] + +import string, math, random +import DocumentTemplate.sequence + +utility_builtins = {} + +utility_builtins['string'] = string +utility_builtins['math'] = math +utility_builtins['random'] = random +utility_builtins['whrandom'] = random +utility_builtins['sequence'] = DocumentTemplate.sequence + +try: + import DateTime + utility_builtins['DateTime']= DateTime.DateTime +except: pass + +def same_type(arg1, *args): + '''Compares the class or type of two or more objects.''' + t = getattr(arg1, '__class__', type(arg1)) + for arg in args: + if getattr(arg, '__class__', type(arg)) is not t: + return 0 + return 1 +utility_builtins['same_type'] = same_type + +def test(*args): + l=len(args) + for i in range(1, l, 2): + if args[i-1]: return args[i] + + if l%2: return args[-1] +utility_builtins['test'] = test + +def reorder(s, with=None, without=()): + # s, with, and without are sequences treated as sets. + # The result is subtract(intersect(s, with), without), + # unless with is None, in which case it is subtract(s, without). + if with is None: with=s + d={} + tt=type(()) + for i in s: + if type(i) is tt and len(i)==2: k, v = i + else: k= v = i + d[k]=v + r=[] + a=r.append + h=d.has_key + + for i in without: + if type(i) is tt and len(i)==2: k, v = i + else: k= v = i + if h(k): del d[k] + + for i in with: + if type(i) is tt and len(i)==2: k, v = i + else: k= v = i + if h(k): + a((k,d[k])) + del d[k] + + return r +utility_builtins['reorder'] = reorder diff --git a/lib/kross/python/scripts/RestrictedPython/__init__.py b/lib/kross/python/scripts/RestrictedPython/__init__.py new file mode 100644 index 00000000..0c70947e --- /dev/null +++ b/lib/kross/python/scripts/RestrictedPython/__init__.py @@ -0,0 +1,19 @@ +############################################################################## +# +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE +# +############################################################################## +''' +RestrictedPython package. +$Id: __init__.py 24763 2004-05-17 05:59:28Z philikon $ +''' + +from SelectCompiler import * +from PrintCollector import PrintCollector diff --git a/lib/kross/python/scripts/gui.py b/lib/kross/python/scripts/gui.py new file mode 100755 index 00000000..8891714a --- /dev/null +++ b/lib/kross/python/scripts/gui.py @@ -0,0 +1,396 @@ +""" +Python script for a GUI-dialog. + +Description: +Python script to provide an abstract GUI for other python scripts. That +way we've all the GUI-related code within one single file and are +able to easily modify GUI-stuff in a central place. + +Author: +Sebastian Sauer + +Copyright: +Published as-is without any warranties. +""" + +def getHome(): + """ Return the homedirectory. """ + import os + try: + home = os.getenv("HOME") + if not home: + import pwd + user = os.getenv("USER") or os.getenv("LOGNAME") + if not user: + pwent = pwd.getpwuid(os.getuid()) + else: + pwent = pwd.getpwnam(user) + home = pwent[6] + return home + except (KeyError, ImportError): + return os.curdir + +class TkDialog: + """ This class is used to wrap Tkinter into a more abstract interface.""" + + def __init__(self, title): + import Tkinter + self.root = Tkinter.Tk() + self.root.title(title) + self.root.deiconify() + + mainframe = self.Frame(self, self.root) + self.widget = mainframe.widget + + class Widget: + def __init__(self, dialog, parent): + self.dialog = dialog + self.parent = parent + #def setVisible(self, visibled): pass + #def setEnabled(self, enabled): pass + + class Frame(Widget): + def __init__(self, dialog, parent): + #TkDialog.Widget.__init__(self, dialog, parent) + import Tkinter + self.widget = Tkinter.Frame(parent) + self.widget.pack() + + class Label(Widget): + def __init__(self, dialog, parent, caption): + #TkDialog.Widget.__init__(self, dialog, parent) + import Tkinter + self.widget = Tkinter.Label(parent, text=caption) + self.widget.pack(side=Tkinter.TOP) + + class CheckBox(Widget): + def __init__(self, dialog, parent, caption, checked = True): + #TkDialog.Widget.__init__(self, dialog, parent) + import Tkinter + self.checkstate = Tkinter.IntVar() + self.checkstate.set(checked) + self.widget = Tkinter.Checkbutton(parent, text=caption, variable=self.checkstate) + self.widget.pack(side=Tkinter.TOP) + def isChecked(self): + return self.checkstate.get() + + class List(Widget): + def __init__(self, dialog, parent, caption, items): + #TkDialog.Widget.__init__(self, dialog, parent) + import Tkinter + + listframe = Tkinter.Frame(parent) + listframe.pack() + + Tkinter.Label(listframe, text=caption).pack(side=Tkinter.LEFT) + + self.items = items + self.variable = Tkinter.StringVar() + itemlist = apply(Tkinter.OptionMenu, (listframe, self.variable) + tuple( items )) + itemlist.pack(side=Tkinter.LEFT) + def get(self): + return self.variable.get() + def set(self, index): + self.variable.set( self.items[index] ) + + class Button(Widget): + def __init__(self, dialog, parent, caption, commandmethod): + #TkDialog.Widget.__init__(self, dialog, parent) + import Tkinter + self.widget = Tkinter.Button(parent, text=caption, command=self.doCommand) + self.commandmethod = commandmethod + self.widget.pack(side=Tkinter.LEFT) + def doCommand(self): + try: + self.commandmethod() + except: + #TODO why the heck we arn't able to redirect exceptions? + import traceback + import StringIO + fp = StringIO.StringIO() + traceback.print_exc(file=fp) + import tkMessageBox + tkMessageBox.showerror("Exception", fp.getvalue()) + #self.dialog.root.destroy() + + class Edit(Widget): + def __init__(self, dialog, parent, caption, text): + #TkDialog.Widget.__init__(self, dialog, parent) + import Tkinter + self.widget = Tkinter.Frame(parent) + self.widget.pack() + label = Tkinter.Label(self.widget, text=caption) + label.pack(side=Tkinter.LEFT) + self.entrytext = Tkinter.StringVar() + self.entrytext.set(text) + self.entry = Tkinter.Entry(self.widget, width=36, textvariable=self.entrytext) + self.entry.pack(side=Tkinter.LEFT) + def get(self): + return self.entrytext.get() + + class FileChooser(Edit): + def __init__(self, dialog, parent, caption, initialfile = None, filetypes = None): + TkDialog.Edit.__init__(self, dialog, parent, caption, initialfile) + import Tkinter + + self.initialfile = initialfile + self.entrytext.set(initialfile) + + btn = Tkinter.Button(self.widget, text="...", command=self.browse) + btn.pack(side=Tkinter.LEFT) + + if filetypes: + self.filetypes = filetypes + else: + self.filetypes = (('All files', '*'),) + + def browse(self): + import os + text = self.entrytext.get() + d = os.path.dirname(text) or os.path.dirname(self.initialfile) + f = os.path.basename(text) or os.path.basename(self.initialfile) + + import tkFileDialog + file = tkFileDialog.asksaveasfilename( + initialdir=d, + initialfile=f, + #defaultextension='.html', + filetypes=self.filetypes + ) + if file: + self.entrytext.set( file ) + + class MessageBox: + def __init__(self, dialog, typename, caption, message): + self.widget = dialog.widget + self.typename = typename + self.caption = str(caption) + self.message = str(message) + def show(self): + import tkMessageBox + if self.typename == "okcancel": + return tkMessageBox.askokcancel(self.caption, self.message,icon=tkmessageBox.QESTION) + else: + tkMessageBox.showinfo(self.caption, self.message) + return True + + def show(self): + self.root.mainloop() + + def close(self): + self.root.destroy() + +class QtDialog: + """ This class is used to wrap pyQt/pyKDE into a more abstract interface.""" + + def __init__(self, title): + import qt + + class Dialog(qt.QDialog): + def __init__(self, parent = None, name = None, modal = 0, fl = 0): + qt.QDialog.__init__(self, parent, name, modal, fl) + qt.QDialog.accept = self.accept + self.layout = qt.QVBoxLayout(self) + self.layout.setSpacing(6) + self.layout.setMargin(11) + + class Label(qt.QLabel): + def __init__(self, dialog, parent, caption): + qt.QLabel.__init__(self, parent) + self.setText("%s" % caption.replace("\n","
")) + + class Frame(qt.QHBox): + def __init__(self, dialog, parent): + qt.QHBox.__init__(self, parent) + self.widget = self + self.setSpacing(6) + + class Edit(qt.QHBox): + def __init__(self, dialog, parent, caption, text): + qt.QHBox.__init__(self, parent) + self.setSpacing(6) + label = qt.QLabel(caption, self) + self.edit = qt.QLineEdit(self) + self.edit.setText( str(text) ) + self.setStretchFactor(self.edit, 1) + label.setBuddy(self.edit) + def get(self): + return self.edit.text() + + class Button(qt.QPushButton): + #def __init__(self, *args): + def __init__(self, dialog, parent, caption, commandmethod): + #apply(qt.QPushButton.__init__, (self,) + args) + qt.QPushButton.__init__(self, parent) + self.commandmethod = commandmethod + self.setText(caption) + qt.QObject.connect(self, qt.SIGNAL("clicked()"), self.commandmethod) + + + class CheckBox(qt.QCheckBox): + def __init__(self, dialog, parent, caption, checked = True): + #TkDialog.Widget.__init__(self, dialog, parent) + qt.QCheckBox.__init__(self, parent) + self.setText(caption) + self.setChecked(checked) + #def isChecked(self): + # return self.isChecked() + + class List(qt.QHBox): + def __init__(self, dialog, parent, caption, items): + qt.QHBox.__init__(self, parent) + self.setSpacing(6) + label = qt.QLabel(caption, self) + self.combo = qt.QComboBox(self) + self.setStretchFactor(self.combo, 1) + label.setBuddy(self.combo) + for item in items: + self.combo.insertItem( str(item) ) + def get(self): + return self.combo.currentText() + def set(self, index): + self.combo.setCurrentItem(index) + + class FileChooser(qt.QHBox): + def __init__(self, dialog, parent, caption, initialfile = None, filetypes = None): + #apply(qt.QHBox.__init__, (self,) + args) + qt.QHBox.__init__(self, parent) + self.setMinimumWidth(400) + + self.initialfile = initialfile + self.filetypes = filetypes + + self.setSpacing(6) + label = qt.QLabel(caption, self) + self.edit = qt.QLineEdit(self) + self.edit.setText(self.initialfile) + self.setStretchFactor(self.edit, 1) + label.setBuddy(self.edit) + + browsebutton = Button(dialog, self, "...", self.browseButtonClicked) + #qt.QObject.connect(browsebutton, qt.SIGNAL("clicked()"), self.browseButtonClicked) + + def get(self): + return self.edit.text() + + def browseButtonClicked(self): + filtermask = "" + import types + if isinstance(self.filetypes, types.TupleType): + for ft in self.filetypes: + if len(ft) == 1: + filtermask += "%s\n" % (ft[0]) + if len(ft) == 2: + filtermask += "%s|%s (%s)\n" % (ft[1],ft[0],ft[1]) + if filtermask == "": + filtermask = "All files (*.*)" + else: + filtermask = filtermask[:-1] + + filename = None + try: + print "QtDialog.FileChooser.browseButtonClicked() kfile.KFileDialog" + # try to use the kfile module included in pykde + import kfile + filename = kfile.KFileDialog.getOpenFileName(self.initialfile, filtermask, self, "Save to file") + except: + print "QtDialog.FileChooser.browseButtonClicked() qt.QFileDialog" + # fallback to Qt filedialog + filename = qt.QFileDialog.getOpenFileName(self.initialfile, filtermask, self, "Save to file") + if filename != None and filename != "": + self.edit.setText(filename) + + class MessageBox: + def __init__(self, dialog, typename, caption, message): + self.widget = dialog.widget + self.typename = typename + self.caption = str(caption) + self.message = str(message) + def show(self): + result = 1 + if self.typename == "okcancel": + result = qt.QMessageBox.question(self.widget, self.caption, self.message, "&Ok", "&Cancel", "", 1) + else: + qt.QMessageBox.information(self.widget, self.caption, self.message, "&Ok") + result = 0 + if result == 0: + return True + return False + + self.app = qt.qApp + self.dialog = Dialog(self.app.mainWidget(), "Dialog", 1, qt.Qt.WDestructiveClose) + self.dialog.setCaption(title) + + self.widget = qt.QVBox(self.dialog) + self.widget.setSpacing(6) + self.dialog.layout.addWidget(self.widget) + + self.Frame = Frame + self.Label = Label + self.Edit = Edit + self.Button = Button + self.CheckBox = CheckBox + self.List = List + self.FileChooser = FileChooser + self.MessageBox = MessageBox + + def show(self): + import qt + qt.QApplication.setOverrideCursor(qt.Qt.arrowCursor) + self.dialog.exec_loop() + qt.QApplication.restoreOverrideCursor() + + def close(self): + print "QtDialog.close()" + self.dialog.close() + #self.dialog.deleteLater() + +class Dialog: + """ Central class that provides abstract GUI-access to the outer world. """ + + def __init__(self, title): + self.dialog = None + + try: + print "Trying to import PyQt..." + self.dialog = QtDialog(title) + print "PyQt is our toolkit!" + except: + try: + print "Failed to import PyQt. Trying to import TkInter..." + self.dialog = TkDialog(title) + print "Falling back to TkInter as our toolkit!" + except: + raise "Failed to import GUI-toolkit. Please install the PyQt or the Tkinter python module." + + self.widget = self.dialog.widget + + def show(self): + self.dialog.show() + + def close(self): + self.dialog.close() + + def addFrame(self, parentwidget): + return self.dialog.Frame(self.dialog, parentwidget.widget) + + def addLabel(self, parentwidget, caption): + return self.dialog.Label(self.dialog, parentwidget.widget, caption) + + def addCheckBox(self, parentwidget, caption, checked = True): + return self.dialog.CheckBox(self.dialog, parentwidget.widget, caption, checked) + + def addButton(self, parentwidget, caption, commandmethod): + return self.dialog.Button(self.dialog, parentwidget.widget, caption, commandmethod) + + def addEdit(self, parentwidget, caption, text): + return self.dialog.Edit(self.dialog, parentwidget.widget, caption, text) + + def addFileChooser(self, parentwidget, caption, initialfile = None, filetypes = None): + return self.dialog.FileChooser(self.dialog, parentwidget.widget, caption, initialfile, filetypes) + + def addList(self, parentwidget, caption, items): + return self.dialog.List(self.dialog, parentwidget.widget, caption, items) + + def showMessageBox(self, typename, caption, message): + return self.dialog.MessageBox(self.dialog, typename, caption, message) diff --git a/lib/kross/readme.dox b/lib/kross/readme.dox new file mode 100644 index 00000000..4fb7c4b4 --- /dev/null +++ b/lib/kross/readme.dox @@ -0,0 +1,28 @@ +/** @mainpage Kross + * + * Kross is a scripting bridge to embed scripting functionality + * into an application. Kross provides an abstract API to access + * the scripting functionality in a interpreter-independend + * way. The application that uses Kross should not need to know + * anything about the used interpreter-language. + * + * @see http://kross.dipe.org + * @see http://www.kexi-project.org/wiki/wikiview/index.php?Scripting + * + * @section Legal + * + * @li copyright (C) 2004-2006 by Sebastian Sauer (mail AT dipe DOT org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ diff --git a/lib/kross/ruby/Makefile.am b/lib/kross/ruby/Makefile.am new file mode 100644 index 00000000..fb8ddfac --- /dev/null +++ b/lib/kross/ruby/Makefile.am @@ -0,0 +1,16 @@ +include $(top_srcdir)/lib/kross/Makefile.global + +INCLUDES = -I$(top_srcdir)/lib/kross -I$(RUBY_INCLUDEDIR) $(all_includes) +METASOURCES = AUTO +kde_module_LTLIBRARIES = krossruby.la + +krossruby_la_LDFLAGS = $(KDE_PLUGIN) $(all_libraries) $(RUBY_LIBRUBYARG) -module $(VER_INFO) + +krossruby_la_LIBADD = \ + $(LIB_QT) \ + $(LIB_KDECORE) \ + ../api/libkrossapi.la \ + ../main/libkrossmain.la + +noinst_HEADERS = rubyinterpreter.h rubyextension.h rubyscript.h rubyconfig.h +krossruby_la_SOURCES = rubyinterpreter.cpp rubyextension.cpp rubyscript.cpp rubymodule.cpp diff --git a/lib/kross/ruby/rubyconfig.h b/lib/kross/ruby/rubyconfig.h new file mode 100644 index 00000000..81a74c55 --- /dev/null +++ b/lib/kross/ruby/rubyconfig.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * pythonconfig.h + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_RUBY_CONFIG_H +#define KROSS_RUBY_CONFIG_H + +#include "../main/krossconfig.h" + +namespace Kross { + +/** + * The Ruby plugin for the \a Kross scripting framework. + * + * @author Cyrille Berger + * @sa http://www.ruby-lang.org + */ +namespace Ruby { +// #define KROSS_RUBY_SCRIPT_DEBUG +// #define KROSS_RUBY_INTERPRETER_DEBUG +// #define KROSS_RUBY_EXTENSION_DEBUG +// #define KROSS_RUBY_MODULE_DEBUG +} +} + +#endif + diff --git a/lib/kross/ruby/rubyextension.cpp b/lib/kross/ruby/rubyextension.cpp new file mode 100644 index 00000000..2c022cee --- /dev/null +++ b/lib/kross/ruby/rubyextension.cpp @@ -0,0 +1,378 @@ +/*************************************************************************** + * rubyinterpreter.cpp + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ +#include "rubyextension.h" + +#include + +#include +#include + +#include "api/list.h" + +#include "rubyconfig.h" + +namespace Kross { + +namespace Ruby { + + +class RubyExtensionPrivate { + friend class RubyExtension; + /// The \a Kross::Api::Object this RubyExtension wraps. + Kross::Api::Object::Ptr m_object; + /// + static VALUE s_krossObject; + static VALUE s_krossException; +}; + +VALUE RubyExtensionPrivate::s_krossObject = 0; +VALUE RubyExtensionPrivate::s_krossException = 0; + +VALUE RubyExtension::method_missing(int argc, VALUE *argv, VALUE self) +{ +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug("method_missing(argc, argv, self)"); +#endif + if(argc < 1) + { + return 0; + } +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug("Converting self to Kross::Api::Object"); +#endif + + Kross::Api::Object::Ptr object = toObject( self ); + return RubyExtension::call_method(object, argc, argv); +} + +VALUE RubyExtension::call_method( Kross::Api::Object::Ptr object, int argc, VALUE *argv) +{ + QString funcname = rb_id2name(SYM2ID(argv[0])); + QValueList argsList; +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug(QString("Building arguments list for function: %1 there are %2 arguments.").arg(funcname).arg(argc-1)); +#endif + for(int i = 1; i < argc; i++) + { + Kross::Api::Object::Ptr obj = toObject(argv[i]); + if(obj) argsList.append(obj); + } + Kross::Api::Object::Ptr result; + try { // We need a double try/catch because, the cleaning is only done at the end of the catch, so if we had only one try/catch, kross would crash after the call to rb_exc_raise + try { // We can't let a C++ exceptions propagate in the C mechanism + Kross::Api::Callable* callable = dynamic_cast(object.data()); + if(callable && callable->hasChild(funcname)) { +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug( QString("Kross::Ruby::RubyExtension::method_missing name='%1' is a child object of '%2'.").arg(funcname).arg(object->getName()) ); +#endif + result = callable->getChild(funcname)->call(QString::null, new Api::List(argsList)); + } + else { +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug( QString("Kross::Ruby::RubyExtension::method_missing try to call function with name '%1' in object '%2'.").arg(funcname).arg(object->getName()) ); +#endif + result = object->call(funcname, new Api::List(argsList)); + } + } catch(Kross::Api::Exception::Ptr exception) + { +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug("c++ exception catched, raise a ruby error"); +#endif + throw convertFromException(exception); + } catch(...) + { + throw convertFromException(new Kross::Api::Exception( "Unknow error" )); // TODO: fix //i18n + } + } catch(VALUE v) { + rb_exc_raise(v ); + } + return toVALUE(result); +} + +void RubyExtension::delete_object(void* object) +{ + krossdebug("delete_object"); + RubyExtension* obj = static_cast(object); + if(obj) + delete obj; +} + +void RubyExtension::delete_exception(void* object) +{ + Kross::Api::Exception* exc = static_cast(object); + exc->_KShared_unref(); +} + + +RubyExtension::RubyExtension(Kross::Api::Object::Ptr object) : d(new RubyExtensionPrivate()) +{ + d->m_object = object; +} + + +RubyExtension::~RubyExtension() +{ + krossdebug("Delete RubyExtension"); + delete d; +} + +typedef QMap mStrObj; + +int RubyExtension::convertHash_i(VALUE key, VALUE value, VALUE vmap) +{ + QMap* map; + Data_Get_Struct(vmap, mStrObj, map); + if (key != Qundef) + { + Kross::Api::Object::Ptr o = RubyExtension::toObject( value ); + if(o) map->replace(STR2CSTR(key), o); + } + return ST_CONTINUE; +} + +bool RubyExtension::isOfExceptionType(VALUE value) +{ + VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossException ); + return (TYPE(result) == T_TRUE); +} + +bool RubyExtension::isOfObjectType(VALUE value) +{ + VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossObject ); + return (TYPE(result) == T_TRUE); +} + + +Kross::Api::Exception::Ptr RubyExtension::convertToException(VALUE value) +{ + if( isOfExceptionType(value) ) + { + Kross::Api::Exception* exception; + Data_Get_Struct(value, Kross::Api::Exception, exception); + return exception; + } + return 0; +} + +VALUE RubyExtension::convertFromException(Kross::Api::Exception::Ptr exc) +{ + if(RubyExtensionPrivate::s_krossException == 0) + { + RubyExtensionPrivate::s_krossException = rb_define_class("KrossException", rb_eRuntimeError); + } + exc->_KShared_ref(); + return Data_Wrap_Struct(RubyExtensionPrivate::s_krossException, 0, RubyExtension::delete_exception, exc.data() ); +} + + +Kross::Api::Object::Ptr RubyExtension::toObject(VALUE value) +{ +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug(QString("RubyExtension::toObject of type %1").arg(TYPE(value))); +#endif + switch( TYPE( value ) ) + { + case T_DATA: + { +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug("Object is a Kross Object"); +#endif + if( isOfObjectType(value) ) + { + RubyExtension* objectExtension; + Data_Get_Struct(value, RubyExtension, objectExtension); + Kross::Api::Object::Ptr object = objectExtension->d->m_object; + return object; + } else { + krosswarning("Cannot yet convert standard ruby type to kross object"); + return 0; + } + } + case T_FLOAT: + return new Kross::Api::Variant(NUM2DBL(value)); + case T_STRING: + return new Kross::Api::Variant(QString(STR2CSTR(value))); + case T_ARRAY: + { + QValueList l; + for(int i = 0; i < RARRAY(value)->len; i++) + { + Kross::Api::Object::Ptr o = toObject( rb_ary_entry( value , i ) ); + if(o) l.append(o); + } + return new Kross::Api::List(l); + } + case T_FIXNUM: + return new Kross::Api::Variant((Q_LLONG)FIX2INT(value)); + case T_HASH: + { + QMap map; + VALUE vmap = Data_Wrap_Struct(rb_cObject, 0,0, &map); + rb_hash_foreach(value, (int (*)(...))convertHash_i, vmap); + return new Kross::Api::Dict(map); + } + case T_BIGNUM: + { + return new Kross::Api::Variant((Q_LLONG)NUM2LONG(value)); + } + case T_TRUE: + { + return new Kross::Api::Variant(true); + } + case T_FALSE: + { + return new Kross::Api::Variant(false); + } + case T_SYMBOL: + { + return new Kross::Api::Variant(QString(rb_id2name(SYM2ID(value)))); + } + case T_MATCH: + case T_OBJECT: + case T_FILE: + case T_STRUCT: + case T_REGEXP: + case T_MODULE: + case T_ICLASS: + case T_CLASS: + krosswarning(QString("This ruby type '%1' cannot be converted to a Kross::Api::Object").arg(TYPE(value))); + default: + case T_NIL: + return 0; + } +} + +VALUE RubyExtension::toVALUE(const QString& s) +{ + return s.isNull() ? rb_str_new2("") : rb_str_new2(s.latin1()); +} + +VALUE RubyExtension::toVALUE(QStringList list) +{ + VALUE l = rb_ary_new(); + for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) + rb_ary_push(l, toVALUE(*it)); + return l; +} + + +VALUE RubyExtension::toVALUE(QMap map) +{ + VALUE h = rb_hash_new(); + for(QMap::Iterator it = map.begin(); it != map.end(); ++it) + rb_hash_aset(h, toVALUE(it.key()), toVALUE(it.data()) ); + return h; + +} + +VALUE RubyExtension::toVALUE(QValueList list) +{ + VALUE l = rb_ary_new(); + for(QValueList::Iterator it = list.begin(); it != list.end(); ++it) + rb_ary_push(l, toVALUE(*it)); + return l; +} + + +VALUE RubyExtension::toVALUE(const QVariant& variant) +{ + + switch(variant.type()) { + case QVariant::Invalid: + return Qnil; + case QVariant::Bool: + return (variant.toBool()) ? Qtrue : Qfalse; + case QVariant::Int: + return INT2FIX(variant.toInt()); + case QVariant::UInt: + return UINT2NUM(variant.toUInt()); + case QVariant::Double: + return rb_float_new(variant.toDouble()); + case QVariant::Date: + case QVariant::Time: + case QVariant::DateTime: + case QVariant::ByteArray: + case QVariant::BitArray: + case QVariant::CString: + case QVariant::String: + return toVALUE(variant.toString()); + case QVariant::StringList: + return toVALUE(variant.toStringList()); + case QVariant::Map: + return toVALUE(variant.toMap()); + case QVariant::List: + return toVALUE(variant.toList()); + + // To handle following both cases is a bit difficult + // cause Python doesn't spend an easy possibility + // for such large numbers (TODO maybe BigInt?). So, + // we risk overflows here, but well... + case QVariant::LongLong: { + return INT2NUM((long)variant.toLongLong()); + } + case QVariant::ULongLong: + return UINT2NUM((unsigned long)variant.toULongLong()); + default: { + krosswarning( QString("Kross::Ruby::RubyExtension::toVALUE(QVariant) Not possible to convert the QVariant type '%1' to a VALUE.").arg(variant.typeName()) ); + return Qundef; + } + } +} + +VALUE RubyExtension::toVALUE(Kross::Api::Object::Ptr object) +{ + if(! object.data()) { + return 0; + } + if(object->getClassName() == "Kross::Api::Variant") { + QVariant v = static_cast( object.data() )->getValue(); + return toVALUE(v); + } + + if(object->getClassName() == "Kross::Api::List") { + Kross::Api::List* list = static_cast( object.data() ); + return toVALUE((Kross::Api::List::Ptr)list); + } + + if(object->getClassName() == "Kross::Api::Dict") { + Kross::Api::Dict* dict = static_cast( object.data() ); + return toVALUE((Kross::Api::Dict::Ptr)dict); + } + + if(RubyExtensionPrivate::s_krossObject == 0) + { + RubyExtensionPrivate::s_krossObject = rb_define_class("KrossObject", rb_cObject); + rb_define_method(RubyExtensionPrivate::s_krossObject, "method_missing", (VALUE (*)(...))RubyExtension::method_missing, -1); + } + return Data_Wrap_Struct(RubyExtensionPrivate::s_krossObject, 0, RubyExtension::delete_object, new RubyExtension(object) ); +} + +VALUE RubyExtension::toVALUE(Kross::Api::List::Ptr list) +{ + VALUE l = rb_ary_new(); + uint count = list ? list->count() : 0; + for(uint i = 0; i < count; i++) + rb_ary_push(l, toVALUE(list->item(i))); + return l; + +} + +} + +} diff --git a/lib/kross/ruby/rubyextension.h b/lib/kross/ruby/rubyextension.h new file mode 100644 index 00000000..74041048 --- /dev/null +++ b/lib/kross/ruby/rubyextension.h @@ -0,0 +1,162 @@ +/*************************************************************************** + * rubyinterpreter.cpp + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ +#ifndef KROSS_RUBYRUBYEXTENSION_H +#define KROSS_RUBYRUBYEXTENSION_H + +#include + +#include +#include +#include +#include + +namespace Kross { + +namespace Ruby { + +class RubyExtensionPrivate; + +/** + * This class wraps a \a Kross::Api::Object into the world of ruby. + * @author Cyrille Berger + */ +class RubyExtension{ + friend class RubyInterpreter; + friend class RubyModule; + friend class RubyScript; + public: + /** + * Constructor. + * + * @param object The \a Kross::Api::Object instance this + * extension provides access to. + */ + RubyExtension(Kross::Api::Object::Ptr object); + /** + * Destructor. + */ + ~RubyExtension(); + private: + /** + * This function will catch functions that are undefined. + */ + static VALUE method_missing(int argc, VALUE *argv, VALUE self); + /** + * This function will call a function in a Kross object + * @param obj kross object which contains the function + * @param argc the number of argument + * @param argv the lists of arguments (the first argument is the Ruby ID of the function) + */ + static VALUE call_method( Kross::Api::Object::Ptr obj, int argc, VALUE *argv); + /** + * This function is called by ruby to delete a RubyExtension object + */ + static void delete_object(void* object); + /** + * This function is called by ruby to delete a RubyExtension object + */ + static void delete_exception(void* object); + private: // Tests + /** + * Test if the ruby object is an exception. + */ + static bool isOfExceptionType(VALUE obj); + /** + * Test if the ruby object is an object + */ + static bool isOfObjectType(VALUE obj); + private: //Converting functions + /** + * Convert a ruby object to the exception type. + * @return 0 if the object wasn't an exception. + */ + static Kross::Api::Exception::Ptr convertToException(VALUE obj); + /** + * Wrap an exception in a ruby object. + */ + static VALUE convertFromException(Kross::Api::Exception::Ptr exc); + /** + * This function iterats through a ruby hash + */ + static int convertHash_i(VALUE key, VALUE value, VALUE vmap); + /** + * Converts a \a VALUE into a \a Kross::Api::Object. + * \param object The ruby VALUE to convert. + * \return The to a Kross::Api::Object converted Py::Object. + */ + static Kross::Api::Object::Ptr toObject(VALUE value); + /** + * Converts a QString to a VALUE. If + * the QString isNull() then a "" will + * be returned. + * \param s The QString to convert. + * \return The converted QString. + */ + static VALUE toVALUE(const QString& s); + + /** + * Converts a QStringList to a VALUE. + * \param list The QStringList to convert. + * \return The converted QStringList. + */ + static VALUE toVALUE(QStringList list); + + /** + * Converts a QMap to a VALUE. + * \param map The QMap to convert. + * \return The converted QMap. + */ + static VALUE toVALUE(QMap map); + + /** + * Converts a QValueList to a VALUE. + * \param list The QValueList to convert. + * \return The converted QValueList. + */ + static VALUE toVALUE(QValueList list); + /** + * Converts a QVariant to a VALUE. + * \param variant The QVariant to convert. + * \return The converted QVariant. + */ + static VALUE toVALUE(const QVariant& variant); + + /** + * Converts a \a Kross::Api::Object to a VALUE. + * \param object The Kross::Api::Object to convert. + * \return The converted Kross::Api::Object. + */ + static VALUE toVALUE(Kross::Api::Object::Ptr object); + + /** + * Converts a \a Kross::Api::List into a VALUE. + * \param list The Kross::Api::List to convert. + * \return The converted Kross::Api::List. + */ + static VALUE toVALUE(Kross::Api::List::Ptr list); + private: + /// Private d-pointer. + RubyExtensionPrivate* d; + }; + +} + +} + +#endif diff --git a/lib/kross/ruby/rubyinterpreter.cpp b/lib/kross/ruby/rubyinterpreter.cpp new file mode 100644 index 00000000..805ae722 --- /dev/null +++ b/lib/kross/ruby/rubyinterpreter.cpp @@ -0,0 +1,149 @@ +/*************************************************************************** + * rubyinterpreter.cpp + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ +#include "rubyinterpreter.h" + +#include + +#include +#include + +#include +#include +#include
+ +#include "rubyconfig.h" +#include "rubyextension.h" +#include "rubymodule.h" +#include "rubyscript.h" + +extern "C" +{ + /** + * Exported and loadable function as entry point to use + * the \a RubyInterpreter. + * The krossruby library will be loaded dynamicly at runtime from e.g. + * \a Kross::Api::Manager::getInterpreter and this exported + * function will be used to return an instance of the + * \a RubyInterpreter implementation. + */ + void* krossinterpreter(Kross::Api::InterpreterInfo* info) + { +#ifdef KROSS_RUBY_INTERPRETER_DEBUG + krossdebug("krossinterpreter(info)"); +#endif + try { + return new Kross::Ruby::RubyInterpreter(info); + } + catch(Kross::Api::Exception::Ptr e) { + Kross::krosswarning("krossinterpreter(Kross::Api::InterpreterInfo* info): Unhandled exception."); + } + return 0; + } +}; + + +namespace Kross { + +namespace Ruby { +typedef std::map mStrVALUE; +typedef mStrVALUE::iterator mStrVALUE_it; +typedef mStrVALUE::const_iterator mStrVALUE_cit; +class RubyInterpreterPrivate { + friend class RubyInterpreter; +}; + +RubyInterpreterPrivate* RubyInterpreter::d = 0; + +RubyInterpreter::RubyInterpreter(Kross::Api::InterpreterInfo* info): Kross::Api::Interpreter(info) +{ +#ifdef KROSS_RUBY_INTERPRETER_DEBUG + krossdebug("RubyInterpreter::RubyInterpreter(info)"); +#endif + if(d == 0) + { + initRuby(); + } + if(info->hasOption("safelevel") ) + { + rb_set_safe_level( info->getOption("safelevel")->value.toInt() ); + } else { + rb_set_safe_level(4); // if the safelevel option is undefined, set it to maximum level + } +} + + +RubyInterpreter::~RubyInterpreter() +{ + finalizeRuby(); +} + + +Kross::Api::Script* RubyInterpreter::createScript(Kross::Api::ScriptContainer* scriptcontainer) +{ + return new RubyScript(this, scriptcontainer); +} + +void RubyInterpreter::initRuby() +{ + d = new RubyInterpreterPrivate(); + ruby_init(); + ruby_init_loadpath(); + rb_define_global_function("require", (VALUE (*)(...))RubyInterpreter::require, 1); +} + +void RubyInterpreter::finalizeRuby() +{ + delete d; + d = 0; + ruby_finalize(); +} + +VALUE RubyInterpreter::require (VALUE obj, VALUE name) +{ +#ifdef KROSS_RUBY_INTERPRETER_DEBUG + krossdebug("RubyInterpreter::require(obj,name)"); +#endif + QString modname = StringValuePtr(name); + if(modname.startsWith("kross")) { + krossdebug( QString("RubyInterpreter::require() module=%1").arg(modname) ); + if( modname.find( QRegExp("[^a-zA-Z0-9\\_\\-]") ) >= 0 ) { + krosswarning( QString("Denied import of Kross module '%1' cause of untrusted chars.").arg(modname) ); + } + else { + Kross::Api::Module::Ptr module = Kross::Api::Manager::scriptManager()->loadModule(modname); + if(module) + { + new RubyModule(module, modname); +// VALUE rmodule = rb_define_module(modname.ascii()); +// rb_define_module_function(); +// VALUE rm = RubyExtension::toVALUE(module); +// rb_define_variable( ("$" + modname).ascii(), & RubyInterpreter::d->m_modules.insert( mStrVALUE::value_type( modname, rm) ).first->second ); + return Qtrue; + } + krosswarning( QString("Loading of Kross module '%1' failed.").arg(modname) ); + } + } else { + return rb_f_require(obj, name); + } + return Qfalse; +} + +} + +} diff --git a/lib/kross/ruby/rubyinterpreter.h b/lib/kross/ruby/rubyinterpreter.h new file mode 100644 index 00000000..f16f2b3d --- /dev/null +++ b/lib/kross/ruby/rubyinterpreter.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * rubyinterpreter.h + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ +#ifndef KROSS_RUBYRUBYINTERPRETER_H +#define KROSS_RUBYRUBYINTERPRETER_H + +#include + +#include + +namespace Kross { + +namespace Ruby { + +class RubyInterpreterPrivate; +/** + * This class is the bridget between Kross and Ruby. + * @author Cyrille Berger + */ +class RubyInterpreter : public Kross::Api::Interpreter +{ + public: + + /** + * Constructor + * + * @param info The \a Kross::Api::InterpreterInfo instance + * that describes this \a RubyInterpreter . + */ + RubyInterpreter(Kross::Api::InterpreterInfo* info); + + /** + * Destructor. + */ + virtual ~RubyInterpreter(); + + /** + * Factory method to create and return a new \a RubyScript instance. + */ + virtual Kross::Api::Script* createScript(Kross::Api::ScriptContainer* scriptcontainer); + + private: + /// Initialize the ruby interpreter. + void initRuby(); + /// Finalize the ruby interpreter. + void finalizeRuby(); + /// Load an external plugin / module. + static VALUE require (VALUE, VALUE); + private: + /// Private d-pointer. + static RubyInterpreterPrivate* d; +}; + +} + +} + +#endif diff --git a/lib/kross/ruby/rubymodule.cpp b/lib/kross/ruby/rubymodule.cpp new file mode 100644 index 00000000..b002f24c --- /dev/null +++ b/lib/kross/ruby/rubymodule.cpp @@ -0,0 +1,68 @@ +/*************************************************************************** + * rubyinterpreter.cpp + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "rubymodule.h" + +#include "rubyconfig.h" +#include "rubyextension.h" + +namespace Kross { + +namespace Ruby { + +class RubyModulePrivate { + friend class RubyModule; + /// The \a Kross::Api::Module this RubyExtension wraps. + Kross::Api::Module::Ptr m_module; + +}; + +RubyModule::RubyModule(Kross::Api::Module::Ptr mod, QString modname) : d(new RubyModulePrivate) +{ + d->m_module = mod; + modname = modname.left(1).upper() + modname.right(modname.length() - 1 ); + krossdebug(QString("Module: %1").arg(modname)); + VALUE rmodule = rb_define_module(modname.ascii()); + rb_define_module_function(rmodule,"method_missing", (VALUE (*)(...))RubyModule::method_missing, -1); + VALUE rm = RubyExtension::toVALUE( mod.data() ); + rb_define_const(rmodule, "MODULEOBJ", rm); +} + +RubyModule::~RubyModule() +{ +} + +VALUE RubyModule::method_missing(int argc, VALUE *argv, VALUE self) +{ +#ifdef KROSS_RUBY_MODULE_DEBUG + QString funcname = rb_id2name(SYM2ID(argv[0])); + krossdebug(QString("Function %1 missing in a module").arg(funcname)); +#endif + + VALUE rubyObjectModule = rb_funcall( self, rb_intern("const_get"), 1, ID2SYM(rb_intern("MODULEOBJ")) ); + RubyModule* objectModule; + Data_Get_Struct(rubyObjectModule, RubyModule, objectModule); + Kross::Api::Object::Ptr object = (Kross::Api::Object*)objectModule->d->m_module; + + return RubyExtension::call_method(object, argc, argv); +} + +} + +} diff --git a/lib/kross/ruby/rubymodule.h b/lib/kross/ruby/rubymodule.h new file mode 100644 index 00000000..0e4c278c --- /dev/null +++ b/lib/kross/ruby/rubymodule.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * rubyinterpreter.cpp + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_RUBYRUBYMODULE_H +#define KROSS_RUBYRUBYMODULE_H + +#include + +#include + +#include +#include + +namespace Kross { + +namespace Ruby { + +class RubyModulePrivate; + +/** + * A ruby module. + * @author Cyrille Berger + */ +class RubyModule { + public: + + /** + * Constructor. + * + * @param mod The \a Kross::Api::Module this RubyExtension + * wraps. + * @param modname The name the module will be published as. + */ + RubyModule(Kross::Api::Module::Ptr mod, QString modname); + + /** + * Destructor. + */ + ~RubyModule(); + + private: + + /** + * This function will catch functions that are undefined. + */ + static VALUE method_missing(int argc, VALUE *argv, VALUE self); + + private: + /// Private d-pointer. + RubyModulePrivate* d; +}; + +} + +} + +#endif diff --git a/lib/kross/ruby/rubyscript.cpp b/lib/kross/ruby/rubyscript.cpp new file mode 100644 index 00000000..fa0ee1d0 --- /dev/null +++ b/lib/kross/ruby/rubyscript.cpp @@ -0,0 +1,193 @@ +/*************************************************************************** + * rubyscript.h + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "rubyscript.h" + +#include +#include +#include +#include + +#include
+ +#include "rubyconfig.h" +#include "rubyextension.h" +#include "rubyinterpreter.h" + +extern NODE *ruby_eval_tree; + +namespace Kross { + +namespace Ruby { + +class RubyScriptPrivate { + friend class RubyScript; + RubyScriptPrivate() : m_compile(0) { } + RNode* m_compile; + /// A list of functionnames. + QStringList m_functions; + + /// A list of classnames. + QStringList m_classes; +}; + +RubyScript::RubyScript(Kross::Api::Interpreter* interpreter, Kross::Api::ScriptContainer* scriptcontainer) + : Kross::Api::Script(interpreter, scriptcontainer), d(new RubyScriptPrivate()) +{ +} + + +RubyScript::~RubyScript() +{ +} + +#define selectScript() \ + NODE* old_tree = ruby_eval_tree; \ + ruby_eval_tree = d->m_compile; +#define unselectScript() \ + d->m_compile = 0; \ + ruby_eval_tree = old_tree; + +void RubyScript::compile() +{ +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("RubyScript::compile()"); +#endif + int critical; + + ruby_nerrs = 0; + ruby_errinfo = Qnil; + VALUE src = RubyExtension::toVALUE( m_scriptcontainer->getCode() ); + StringValue(src); + critical = rb_thread_critical; + rb_thread_critical = Qtrue; + ruby_in_eval++; + d->m_compile = rb_compile_string((char*) m_scriptcontainer->getName().latin1(), src, 0); + ruby_in_eval--; + rb_thread_critical = critical; + + if (ruby_nerrs != 0) + { +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("Compilation has failed"); +#endif + setException( new Kross::Api::Exception(QString("Failed to compile ruby code: %1").arg(STR2CSTR( rb_obj_as_string(ruby_errinfo) )), 0) ); // TODO: get the error + d->m_compile = 0; + } +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("Compilation was successfull"); +#endif +} + +const QStringList& RubyScript::getFunctionNames() +{ +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("RubyScript::getFunctionNames()"); +#endif + if(d->m_compile == 0) + { + compile(); + } + return d->m_functions; +} + +Kross::Api::Object::Ptr RubyScript::execute() +{ +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("RubyScript::execute()"); +#endif + if(d->m_compile == 0) + { + compile(); + } +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("Start execution"); +#endif + selectScript(); + int result = ruby_exec(); + if (result != 0) + { +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("Execution has failed"); +#endif + if( TYPE( ruby_errinfo ) == T_DATA && RubyExtension::isOfExceptionType( ruby_errinfo ) ) + { +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("Kross exception"); +#endif + setException( RubyExtension::convertToException( ruby_errinfo ) ); + } else { + setException( new Kross::Api::Exception(QString("Failed to execute ruby code: %1").arg(STR2CSTR( rb_obj_as_string(ruby_errinfo) )), 0) ); // TODO: get the error + } + } + + unselectScript(); +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("Execution is finished"); +#endif + return 0; +} + +Kross::Api::Object::Ptr RubyScript::callFunction(const QString& name, Kross::Api::List::Ptr args) +{ + Q_UNUSED(name) + Q_UNUSED(args) +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("RubyScript::callFunction()"); +#endif + if(d->m_compile == 0) + { + compile(); + } + selectScript(); + unselectScript(); + return 0; +} + +const QStringList& RubyScript::getClassNames() +{ +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("RubyScript::getClassNames()"); +#endif + if(d->m_compile == 0) + { + compile(); + } + return d->m_classes; +} + +Kross::Api::Object::Ptr RubyScript::classInstance(const QString& name) +{ + Q_UNUSED(name) +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("RubyScript::classInstance()"); +#endif + if(d->m_compile == 0) + { + compile(); + } + selectScript(); + unselectScript(); + return 0; +} + + +} + +} diff --git a/lib/kross/ruby/rubyscript.h b/lib/kross/ruby/rubyscript.h new file mode 100644 index 00000000..cc6eda43 --- /dev/null +++ b/lib/kross/ruby/rubyscript.h @@ -0,0 +1,98 @@ +/*************************************************************************** + * rubyscript.h + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_RUBYRUBYSCRIPT_H +#define KROSS_RUBYRUBYSCRIPT_H + +#include + +namespace Kross { + +namespace Ruby { + +class RubyScriptPrivate; + +/** + * Handle ruby scripts. This class implements + * \a Kross::Api::Script for ruby. + * @author Cyrille Berger + */ +class RubyScript : public Kross::Api::Script +{ + public: + + /** + * Constructor. + * + * @param interpreter The @a RubyInterpreter instance used to + * create this script. + * @param scriptcontainer The @a Kross::Api::ScriptContainer + * instance this @a RubyScript does handle the + * backend-work for. + */ + RubyScript(Kross::Api::Interpreter* interpreter, Kross::Api::ScriptContainer* scriptcontainer); + + /** + * Destructor. + */ + ~RubyScript(); + + /** + * Return a list of callable functionnames this + * script spends. + */ + virtual const QStringList& getFunctionNames(); + + /** + * Execute the script. + */ + virtual Kross::Api::Object::Ptr execute(); + + /** + * Call a function. + */ + virtual Kross::Api::Object::Ptr callFunction(const QString& name, Kross::Api::List::Ptr args); + + /** + * Return a list of class types this script supports. + */ + virtual const QStringList& getClassNames(); + + /** + * Create and return a new class instance. + */ + virtual Kross::Api::Object::Ptr classInstance(const QString& name); + + private: + + /** + * Compile the script. + */ + void compile(); + + private: + /// Private d-pointer. + RubyScriptPrivate* d; +}; + +} + +} + +#endif diff --git a/lib/kross/runner/Makefile.am b/lib/kross/runner/Makefile.am new file mode 100644 index 00000000..8c62b884 --- /dev/null +++ b/lib/kross/runner/Makefile.am @@ -0,0 +1,11 @@ +include $(top_srcdir)/lib/kross/Makefile.global + +#noinst_PROGRAMS = krossrunner +bin_PROGRAMS = krossrunner + +krossrunner_SOURCES = main.cpp +krossrunner_LDADD = $(LIB_QT) $(LIB_KDECORE) ../api/libkrossapi.la ../main/libkrossmain.la +INCLUDES = $(KROSS_INCLUDES) $(all_includes) +krossrunner_LDFLAGS = $(all_libraries) $(KDE_RPATH) +SUBDIRS = . +METASOURCES = AUTO diff --git a/lib/kross/runner/main.cpp b/lib/kross/runner/main.cpp new file mode 100644 index 00000000..89a35278 --- /dev/null +++ b/lib/kross/runner/main.cpp @@ -0,0 +1,144 @@ +/*************************************************************************** + * main.cpp + * This file is part of the KDE project + * copyright (C)2006 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +// for std namespace +#include +#include + +// Qt +#include +#include + +// KDE +#include +#include +#include +#include +#include + +// Kross +#include "../main/manager.h" +#include "../main/scriptcontainer.h" +#include "../api/interpreter.h" + +#define ERROR_OK 0 +#define ERROR_HELP -1 +#define ERROR_NOSUCHFILE -2 +#define ERROR_OPENFAILED -3 +#define ERROR_NOINTERPRETER -4 +#define ERROR_UNHALDEDEXCEPTION -5 +#define ERROR_EXCEPTION -6 + +KApplication* app = 0; + +int runScriptFile(const QString& scriptfile) +{ + // Read the scriptfile + QFile f(QFile::encodeName(scriptfile)); + if(! f.exists()) { + std::cerr << "No such scriptfile: " << scriptfile.latin1() << std::endl; + return ERROR_NOSUCHFILE; + } + if(! f.open(IO_ReadOnly)) { + std::cerr << "Failed to open scriptfile: " << scriptfile.latin1() << std::endl; + return ERROR_OPENFAILED; + } + QString scriptcode = f.readAll(); + f.close(); + + // Determinate the matching interpreter + Kross::Api::Manager* manager = Kross::Api::Manager::scriptManager(); + Kross::Api::InterpreterInfo* interpreterinfo = manager->getInterpreterInfo( manager->getInterpreternameForFile(scriptfile) ); + if(! interpreterinfo) { + std::cerr << "No interpreter for file: " << scriptfile.latin1() << std::endl; + return ERROR_NOINTERPRETER; + } + + // Run the script. + try { + // First we need a scriptcontainer and fill it. + Kross::Api::ScriptContainer::Ptr scriptcontainer = manager->getScriptContainer(scriptfile); + scriptcontainer->setInterpreterName( interpreterinfo->getInterpretername() ); + scriptcontainer->setCode(scriptcode); + // Now execute the scriptcontainer. + scriptcontainer->execute(); + if(scriptcontainer->hadException()) { + // We had an exception. + QString errormessage = scriptcontainer->getException()->getError(); + QString tracedetails = scriptcontainer->getException()->getTrace(); + std::cerr << QString("%2\n%1").arg(tracedetails).arg(errormessage).latin1() << std::endl; + return ERROR_EXCEPTION; + } + } + catch(Kross::Api::Exception::Ptr e) { + // Normaly that shouldn't be the case... + std::cerr << QString("EXCEPTION %1").arg(e->toString()).latin1() << std::endl; + return ERROR_UNHALDEDEXCEPTION; + } + return ERROR_OK; +} + +int main(int argc, char **argv) +{ + int result = ERROR_OK; + + KAboutData about("krossrunner", + "krossrunner", + "0.1", + "KDE application to run Kross scripts.", + KAboutData::License_LGPL, + "(C) 2006 Sebastian Sauer", + "Run Kross scripts.", + "http://www.dipe.org/kross", + "kross@dipe.org"); + about.addAuthor("Sebastian Sauer", "Author", "mail@dipe.org"); + + // Initialize command line args + KCmdLineArgs::init(argc, argv, &about); + // Tell which options are supported and parse them. + static KCmdLineOptions options[] = { + { "+file", I18N_NOOP("Scriptfile"), 0 }, + KCmdLineLastOption + }; + KCmdLineArgs::addCmdLineOptions(options); + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + // If no options are defined. + if(args->count() < 1) { + std::cout << "Syntax: " << KCmdLineArgs::appName() << " scriptfile1 [scriptfile2] [scriptfile3] ..." << std::endl; + return ERROR_HELP; + } + + // Create KApplication instance. + app = new KApplication( /* allowStyles */ true, /* GUIenabled */ true ); + + //QString interpretername = args->getOption("interpreter"); + //QString scriptfilename = args->getOption("scriptfile"); + + // Each argument is a scriptfile to open + for(int i = 0; i < args->count(); i++) { + result = runScriptFile(QFile::decodeName(args->arg(i))); + if(result != ERROR_OK) + break; + } + + // Free the KApplication instance and exit the program. + delete app; + return result; +} diff --git a/lib/kross/test/Makefile.am b/lib/kross/test/Makefile.am new file mode 100644 index 00000000..ddf235c3 --- /dev/null +++ b/lib/kross/test/Makefile.am @@ -0,0 +1,17 @@ +include $(top_srcdir)/lib/kross/Makefile.global + +noinst_PROGRAMS = krosstest + +krosstest_SOURCES = testobject.cpp testaction.cpp testplugin.cpp testwindow.cpp main.cpp + +krosstest_LDADD = \ + $(LIB_QT) \ + $(LIB_KDECORE) \ + $(LIB_KDEUI) \ + ../api/libkrossapi.la \ + ../main/libkrossmain.la + +INCLUDES = $(KROSS_INCLUDES) $(all_includes) +krosstest_LDFLAGS = $(all_libraries) $(KDE_RPATH) +SUBDIRS = . +METASOURCES = AUTO diff --git a/lib/kross/test/main.cpp b/lib/kross/test/main.cpp new file mode 100644 index 00000000..31b4f579 --- /dev/null +++ b/lib/kross/test/main.cpp @@ -0,0 +1,190 @@ +/*************************************************************************** + * main.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "../main/manager.h" + +#include "../api/object.h" +#include "../api/class.h" +#include "../api/module.h" +//#include "../api/script.h" +#include "../api/interpreter.h" + +#include "../main/scriptcontainer.h" + +//#include "../kexidb/kexidbmodule.h" + +#include "testobject.h" +#include "testaction.h" +#include "testwindow.h" +#include "testplugin.h" + +// Qt +#include +#include + +// KDE +#include +#include +#include +#include +#include + +// for std namespace +#include +#include + +KApplication *app = 0; + + + +static KCmdLineOptions options[] = +{ + { "interpreter ", I18N_NOOP("Name of the interpreter being used"), "python" }, + { "scriptfile ", I18N_NOOP("Script file to execute with the defined interpreter"), "testcase.py" }, + { "gui", I18N_NOOP("Start the GUI; otherwise the command line application is used."), 0 }, + + //{ "functionname ", I18N_NOOP("Execute the function in the defined script file."), "" }, + //{ "functionargs ", I18N_NOOP("List of arguments to pass to the function on execution."), "" }, + { 0, 0, 0 } +}; + +void runInterpreter(const QString& interpretername, const QString& scriptcode) +{ + try { + + // Return the scriptingmanager instance. The manager is used as main + // entry point to work with Kross. + Kross::Api::Manager* manager = Kross::Api::Manager::scriptManager(); + + // Add modules that should be accessible by scripting. Those + // modules are wrappers around functionality you want to be + // able to access from within scripts. You don't need to take + // care of freeing them cause that will be done by Kross. + // Modules are shared between the ScriptContainer instances. + manager->addModule( new TestPluginModule("krosstestpluginmodule") ); + + // To represent a script that should be executed Kross uses + // the Script container class. You are able to fill them with + // what is needed and just execute them. + Kross::Api::ScriptContainer::Ptr scriptcontainer = manager->getScriptContainer("MyScriptName"); + + //scriptcontainer->enableModule("KexiDB"); + + scriptcontainer->setInterpreterName(interpretername); + scriptcontainer->setCode(scriptcode); + + //TESTCASE + TestObject* testobject = new TestObject(app, scriptcontainer); + manager->addQObject( testobject ); + + /*TestAction* testaction =*/ new TestAction(scriptcontainer); + //manager->addQObject( testaction ); + + /*Kross::Api::Object* o =*/ scriptcontainer->execute(); + + // Call a function. + //scriptcontainer->callFunction("testobjectCallback" /*, Kross::Api::List* functionarguments */); + + // Call a class. + /* + Kross::Api::Object* testclassinstance = scriptcontainer->classInstance("testClass"); + if(testclassinstance) { + QValueList ll; + Kross::Api::Object* instancecallresult = testclassinstance->call("testClassFunction1", Kross::Api::List::create(ll)); + //krossdebug( QString("testClass.testClassFunction1 returnvalue => '%1'").arg( instancecallresult.toString() ) ); + } + */ + + +/* + // Connect QObject signal with scriptfunction. + scriptcontainer->connect(testobject, SIGNAL(testSignal()), "testobjectCallback"); + scriptcontainer->connect(testobject, SIGNAL(testSignalString(const QString&)), "testobjectCallbackWithParams"); + // Call the testSlot to emit the testSignal. + testobject->testSlot(); +*/ + } + catch(Kross::Api::Exception::Ptr e) { + std::cout << QString("EXCEPTION %1").arg(e->toString()).latin1() << std::endl; + } + +/*TESTCASE + Kross::Api::ScriptContainer* sc2 = manager->getScriptContainer("MyScriptName222"); + sc2->setInterpreterName(interpretername); + sc2->setCode(scriptcode); + try { + sc2->execute(); + } + catch(Kross::Api::Exception& e) { + krossdebug( QString("EXCEPTION type='%1' description='%2'").arg(e.type()).arg(e.description()) ); + } + //delete sc2; +*/ + + std::string s; std::cin >> s; // just wait. +} + +int main(int argc, char **argv) +{ + int result = 0; + + KAboutData about("krosstest", + "KrossTest", + "0.1", + "KDE application to test the Kross framework.", + KAboutData::License_LGPL, + "(C) 2005 Sebastian Sauer", + "Test the Kross framework!", + "http://www.dipe.org/kross", + "kross@dipe.org"); + about.addAuthor("Sebastian Sauer", "Author", "mail@dipe.org"); + + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addCmdLineOptions(options); + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + QString interpretername = args->getOption("interpreter"); + QString scriptfilename = args->getOption("scriptfile"); + + QFile f(QFile::encodeName(scriptfilename)); + if(f.exists() && f.open(IO_ReadOnly)) { + QString scriptcode = f.readAll(); + f.close(); + + if( args->isSet("gui") ) { + app = new KApplication(); + TestWindow *mainWin = new TestWindow(interpretername, scriptcode); + app->setMainWidget(mainWin); + mainWin->show(); + args->clear(); + result = app->exec(); + } + else { + app = new KApplication(true, true); + runInterpreter(interpretername, scriptcode); + } + } + else { + Kross::krosswarning( QString("Failed to load scriptfile: %1").arg(scriptfilename) ); + result = -1; + } + + delete app; + return result; +} diff --git a/lib/kross/test/testaction.cpp b/lib/kross/test/testaction.cpp new file mode 100644 index 00000000..93c32090 --- /dev/null +++ b/lib/kross/test/testaction.cpp @@ -0,0 +1,49 @@ +/*************************************************************************** + * testaction.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "testaction.h" + +TestAction::TestAction(Kross::Api::ScriptContainer::Ptr scriptcontainer) + : QWidget() +{ + m_actioncollection = new KActionCollection(this, this); + + m_action1 = new KAction("Action1_Text", 0, this, SLOT(activatedAction1()), m_actioncollection, "Action1"); + m_actionlist.append(m_action1); + scriptcontainer->addKAction(m_action1); + + m_action2 = new KAction("Action2_Text", 0, this, SLOT(activatedAction2()), m_actioncollection, "Action2"); + m_actionlist.append(m_action2); + scriptcontainer->addKAction(m_action2); +} + +TestAction::~TestAction() +{ +} + +void TestAction::activatedAction1() +{ + Kross::krossdebug("TestAction::activatedAction1()"); +} + +void TestAction::activatedAction2() +{ + Kross::krossdebug("TestAction::activatedAction2()"); +} + diff --git a/lib/kross/test/testaction.h b/lib/kross/test/testaction.h new file mode 100644 index 00000000..03ea95fe --- /dev/null +++ b/lib/kross/test/testaction.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * testaction.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_TEST_TESTACTION_H +#define KROSS_TEST_TESTACTION_H + +#include "../main/scriptcontainer.h" + +#include +#include +#include + +#include +#include + +class TestAction : public QWidget +{ + Q_OBJECT + public: + TestAction(Kross::Api::ScriptContainer::Ptr scriptcontainer); + ~TestAction(); + + private slots: + void activatedAction1(); + void activatedAction2(); + + private: + KAction* m_action1; + KAction* m_action2; + KActionCollection* m_actioncollection; + KActionPtrList m_actionlist; +}; + +#endif diff --git a/lib/kross/test/testcase.py b/lib/kross/test/testcase.py new file mode 100644 index 00000000..d3f2ea7b --- /dev/null +++ b/lib/kross/test/testcase.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python + +""" + This Python script is used to test the Kross scripting framework. +""" + +#def testobjectCallback(): +# print "function testobjectCallback() called !" +# return "this is the __main__.testobjectCallback() returnvalue!" +#def testobjectCallbackWithParams(argument): +# print "testobjectCallbackWithParams() argument = %s" % str(argument) +# return "this is the __main__.testobjectCallbackWithParams() returnvalue!" +#def testQtObject(self): + ## Get the QtObject instance to access the QObject. + ##testobject = get("TestObject") + #testobject = self.get("TestObject") + #if testobject == None: raise "Object 'TestObject' undefined !!!" + #print "testobject = %s %s" % (str(testobject),dir(testobject)) + ##print "propertyNames = %s" % testobject.propertyNames() + ##print "slotNames = %s" % testobject.slotNames() + ##print "signalNames = %s" % testobject.signalNames() + ## We could just call a slot or a signal. + #print testobject.call("testSlot2()"); + #print testobject.call("testSignal()"); + ##print testobject.call() #KrossTest: List::item index=0 is out of bounds. Raising TypeException. + ## Each slot a QObject spends is a object itself. + #myslot = testobject.get("testSlot()") + #print "myslotevent = %s" % str(myslot) + #print myslot.call() + #print "__name__ = %s" % __name__ + #print "__dir__ = %s" % dir() + ##print "__builtin__ = %s" % __builtin__ + #print "self = %s %s" % (str(self),dir(self)) + ##print "TestCase = %s" % str(TestCase) + #print "self.list = %s" % self.list() + #print "self.dict = %s" % self.dict() + #testobject = self.get("TestObject") + #print "testobject = %s" % testobject + #if not testobject.connect("testSignal()",testobject,"testSlot2()"): + #raise "Failed to connect testSignal() with testSlot2() at object 'TestObject'." + #testobject.signal("testSignal()") + ##testobject.testSlot() + #testobject.slot("testSlot()") + #testobject.disconnect("testSignal()") +#def testActionEvent(self): + ##action1 = get("Action1") + #action1 = self.get("Action1") + #if action1 == None: + #raise "Object 'Action1' undefined !!!" + #print "action1 = %s %s" % (str(action1),dir(action1)) + ##action1.call() + #action1.activate() + +import unittest + +class TestPlugin(unittest.TestCase): + """ Testcase to test the Kross python functionality for regressions. """ + + def setUp(self): + import krosstestpluginmodule + self.pluginobject1 = krosstestpluginmodule.testpluginobject1() + self.assert_( self.pluginobject1 ) + + self.pluginobject2 = krosstestpluginmodule.testpluginobject2() + self.assert_( self.pluginobject2 ) + + self.testqobject1 = krosstestpluginmodule.testqobject1() + self.assert_( self.testqobject1 ) + + def testBasicDataTypes(self): + self.assert_( self.pluginobject1.uintfunc(177321) == 177321 ) + self.assert_( self.pluginobject1.intfunc(93675) == 93675 ) + self.assert_( self.pluginobject1.intfunc(-73673) == -73673 ) + self.assert_( self.pluginobject1.boolfunc(True) == True ) + self.assert_( self.pluginobject1.boolfunc(False) == False ) + self.assert_( self.pluginobject1.doublefunc(4265.3723) == 4265.3723 ) + self.assert_( self.pluginobject1.doublefunc(-4265.68) == -4265.68 ) + self.assert_( self.pluginobject1.cstringfunc(" This is a Test! ") == " This is a Test! " ) + self.assert_( self.pluginobject1.stringfunc(" Another \n\r Test! $%&\"") == " Another \n\r Test! $%&\"" ) + + #TODO + #self.assert_( self.pluginobject1.stringfunc( unicode(" Unicode test ") ) == " Unicode test " ) + #self.assert_( self.pluginobject1.stringfunc(unicode(" Another Test! ")) == unicode(" Another Test! ") ) + + self.assert_( self.pluginobject1.stringstringfunc("MyString1", "MyString2") == "MyString1" ) + self.assert_( self.pluginobject1.uintdoublestringfunc(8529,285.246,"String") == 8529 ) + self.assert_( self.pluginobject1.stringlistbooluintdouble(["s1","s2"],True,6,7.0,"String") == ["s1","s2"] ) + + def testStringList(self): + self.assert_( self.pluginobject1.stringlistfunc( [] ) == [] ) + self.assert_( self.pluginobject1.stringlistfunc( ["First Item"," Second Item "] ) == ["First Item"," Second Item "] ) + self.assert_( self.pluginobject1.stringlistfunc( ("Theird Item"," Forth Item ","Fifth Item") ) == ["Theird Item"," Forth Item ","Fifth Item"] ) + + def testVariant(self): + self.assert_( self.pluginobject1.variantfunc(True) == True ) + self.assert_( self.pluginobject1.variantfunc(False) == False ) + self.assert_( self.pluginobject1.variantfunc(187937) == 187937 ) + self.assert_( self.pluginobject1.variantfunc(-69825) == -69825 ) + self.assert_( self.pluginobject1.variantfunc(8632.274) == 8632.274 ) + self.assert_( self.pluginobject1.variantfunc(-8632.351) == -8632.351 ) + self.assert_( self.pluginobject1.variantfunc(" Test \n\r This String $%&\"") == " Test \n\r This String $%&\"") + + def testObjects(self): + print "-----------------1" + newobjref = self.pluginobject1.objectfunc(self.pluginobject2) + print "-----------------2" + print str(newobjref) + + #self.assert_( newobjref.myuniqueid == self.pluginobject2.myuniqueid ) + #print "===========> %s" % self.pluginobject2.myName() + + print "testqobject1 properties=%s" % self.testqobject1.propertyNames() + print "testqobject1 slots=%s" % self.testqobject1.slotNames() + print "testqobject1 signals=%s" % self.testqobject1.signalNames() + print "-----------------3" + print "DIR=>%s" % dir(self.testqobject1) + + print "===================> slotcall-result: %s" % self.testqobject1.slot("self()") + + #testobject = newobjref.get("TestObject") + #print testobject + print "-----------------9" + + def testDefaultArguments(self): + self.assert_( self.pluginobject1.uintfunc_defarg(98765) == 98765 ) + self.assert_( self.pluginobject1.uintfunc_defarg() == 12345 ) + self.assert_( self.pluginobject1.stringfunc_defarg("MyString") == "MyString" ) + self.assert_( self.pluginobject1.stringfunc_defarg() == "MyDefaultString" ) + self.assert_( self.pluginobject1.stringlistfunc_defarg(["s1","s2","s3"]) == ["s1","s2","s3"] ) + self.assert_( self.pluginobject1.stringlistfunc_defarg() == ["Default1","Default2"] ) + self.assert_( self.pluginobject1.variantfunc_defarg(822.75173) == 822.75173 ) + self.assert_( self.pluginobject1.variantfunc_defarg() == "MyDefaultVariantString" ) + + #def testExpectedFailures(self): + # to less arguments + #self.assertRaises(ValueError, self.pluginobject1.uintfunc) + #self.assert_( self.pluginobject1.uintfunc() != 8465 ) + +print "__name__ = %s" % __name__ +#print "self = %s" % self +#print self.get("TestObject") + +suite = unittest.makeSuite(TestPlugin) +unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/lib/kross/test/testcase.rb b/lib/kross/test/testcase.rb new file mode 100644 index 00000000..71354ea2 --- /dev/null +++ b/lib/kross/test/testcase.rb @@ -0,0 +1,15 @@ +require 'test/unit' + +class TestKross < Test::Unit::TestCase + def setup: + require "krosstestpluginmodule" + testpluginobject1 = Krosstestpluginmodule::get("testpluginobject1") + #Krosskritacore::get("KritaDocument") + #testpluginobject1.func1() + #def test_primitive + # print "---------------- 1\n" + + end +end + +print "3----------------\n" diff --git a/lib/kross/test/testgui.py b/lib/kross/test/testgui.py new file mode 100644 index 00000000..f252184d --- /dev/null +++ b/lib/kross/test/testgui.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python + +""" + This Python script demonstrates the usage of the Kross + python-interface to access KexiDB functionality from + within Python. +""" + +class TkTest: + def __init__(self): + import Tkinter + self.root = Tkinter.Tk() + self.root.title("TkTest") + self.root.deiconify() + + self.mainframe = Tkinter.Frame(self.root) + self.mainframe.pack() + + self.button1 = Tkinter.Button(self.mainframe, text="Button1", command=self.callback1) + self.button1.pack(side=Tkinter.LEFT) + + self.button2 = Tkinter.Button(self.mainframe, text="Button2", command=self.callback2) + self.button2.pack(side=Tkinter.LEFT) + + self.exitbutton = Tkinter.Button(self.mainframe, text="Exit", command=self.root.destroy) + self.exitbutton.pack(side=Tkinter.LEFT) + + self.root.mainloop() + + def callback1(self): + import tkMessageBox + tkMessageBox.showinfo("Callback1", "Callback1 called.") + + def callback2(self): + import tkMessageBox + tkMessageBox.showinfo("Callback2", "Callback2 called.") + +class QtTest: + def __init__(self): + import qt + + class Button(qt.QPushButton): + def __init__(self, *args): + apply(qt.QPushButton.__init__, (self,) + args) + + class ComboBox(qt.QHBox): + def __init__(self, parent, caption, items = []): + qt.QHBox.__init__(self, parent) + self.setSpacing(6) + label = qt.QLabel(str(caption), self) + self.combobox = qt.QComboBox(self) + self.setStretchFactor(self.combobox, 1) + label.setBuddy(self.combobox) + for item in items: + self.combobox.insertItem( str(item) ) + + class FileChooser(qt.QHBox): + def __init__(self, *args): + apply(qt.QHBox.__init__, (self,) + args) + self.defaultfilename = "~/output.html" + + self.setSpacing(6) + label = qt.QLabel("File:", self) + self.edit = qt.QLineEdit(self) + self.edit.setText(self.defaultfilename) + self.setStretchFactor(self.edit, 1) + label.setBuddy(self.edit) + + browsebutton = Button("...", self) + qt.QObject.connect(browsebutton, qt.SIGNAL("clicked()"), self.browseButtonClicked) + + def file(self): + return self.edit.text() + + def browseButtonClicked(self): + filename = None + try: + # try to use the kfile module included in pykde + import kfile + filename = kfile.KFileDialog.getOpenFileName(self.defaultfilename, "*.html", self, "Save to file") + except: + # fallback to Qt filedialog + filename = qt.QFileDialog.getOpenFileName(self.defaultfilename, "*.html", self, "Save to file") + if filename != None and filename != "": + self.edit.setText(filename) + + class Dialog(qt.QDialog): + def __init__(self, parent = None, name = None, modal = 0, fl = 0): + qt.QDialog.__init__(self, parent, name, modal, fl) + qt.QDialog.accept = self.accept + self.setCaption("Export to HTML") + #self.layout() + + self.layout = qt.QVBoxLayout(self) + self.layout.setSpacing(6) + self.layout.setMargin(11) + + infolabel = qt.QLabel("Export the data of a table or a query to a HTML-file.", self) + self.layout.addWidget(infolabel) + + source = ComboBox(self, "Datasource:") + self.layout.addWidget(source) + + self.exporttype = ComboBox(self, "Style:", ["Plain","Paper","Desert","Blues"]) + self.layout.addWidget(self.exporttype) + + self.filechooser = FileChooser(self) + self.layout.addWidget(self.filechooser) + + buttonbox = qt.QHBox(self) + buttonbox.setSpacing(6) + self.layout.addWidget(buttonbox) + + savebutton = Button("Save", buttonbox) + qt.QObject.connect(savebutton, qt.SIGNAL("clicked()"), self, qt.SLOT("accept()")) + #qt.QObject.connect(savebutton, qt.SIGNAL("clicked()"), self.exportButtonClicked) + + cancelbutton = Button("Cancel", buttonbox) + qt.QObject.connect(cancelbutton, qt.SIGNAL("clicked()"), self, qt.SLOT("close()")) + + def accept(self): + print "ACCEPTTTTTTTT !!!!!!!!!!!!!!!!!!!!!!!!!!!!" + + file = qt.QFile( self.filechooser.file() ) + #if not file.exists(): + # print "File '%s' does not exist." % self.filechooser.file() + #else: + # print "File '%s' does exist." % self.filechooser.file() + + def exportButtonClicked(self): + print "Export to HTML !!!!!!!!!!!!!!!!!!!!!!!!!!!!" + + def __getattr__(self, attr): + print "=> Dialog.__getattr__(self,attr)" + #def closeEvent(self, ev): pass + def event(self, e): + print "=> Dialog.event %s" % e + #self.deleteLater() + #support.swapThreadState() # calls appropriate c-function + return qt.QDialog.event(self, e) + + app = qt.qApp + dialog = Dialog(app.mainWidget(), "Dialog", 1) + dialog.show() + +print "################## BEGIN" +#TkTest() +QtTest() +print "################## END" diff --git a/lib/kross/test/testkexidb.py b/lib/kross/test/testkexidb.py new file mode 100644 index 00000000..048b7e19 --- /dev/null +++ b/lib/kross/test/testkexidb.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python + +""" + This Python script demonstrates the usage of the Kross + python-interface to access KexiDB functionality from + within Python. +""" + +# Class to test the KexiDB functionality. +class KexiDBClass: + + # Constructor. + def __init__(self): + # The KexiDB module spends us access to the KexiDB functionality. + #import KexiDB + import krosskexidb + self.kexidbmodule = krosskexidb + print "KrossKexiDB version=%s" % self.kexidbmodule.version() + + # Create and remember the drivermanager. + self.drivermanager = self.kexidbmodule.DriverManager() + + # Print informations about the KexiDB module. + def printKexiDB(self): + print "KexiDB = %s %s" % (str(self.kexidbmodule),dir(self.kexidbmodule)) + # Each object has __name__ and __doc__ + #print "KexiDB.__name__ = %s" % self.kexidbmodule.__name__ + #print "KexiDB.__doc__ = %s" % self.kexidbmodule.__doc__ + # Print some infos about the drivermanager. + print "drivermanager = %s %s" % (self.drivermanager,dir(self.drivermanager)) + # The drivermanager holds a list of drivers he supports. + print "drivermanager.driverNames() = %s" % self.driverNames() + + # Print informations about a driver. + def printDriverManger(self, driver): + print "driver = %s %s" % (driver,dir(driver)) + # Each driver has a version to be able to determinate with what release we are working. + print "driver.versionMajor() = %s" % driver.versionMajor() + print "driver.versionMinor() = %s" % driver.versionMinor() + # Show us what connections are opened right now. + print "driver.connectionsList() = %s" % str(driver.connectionsList()) + + # Print informations about a connection. + def printConnection(self, connection): + print "connection = %s %s" % (str(connection),dir(connection)) + # Print a list of all avaible databasenames this connection has. + print "connection.databaseNames() = %s" % connection.databaseNames() + + # Return a list of drivernames. + def driverNames(self): + return self.drivermanager.driverNames() + + # Return the to drivername matching KexiDBDriver object. + def driver(self, drivername): + return self.drivermanager.driver(drivername) + + # Return a new KexiDBConnectionData object. + def getConnectionData(self): + return self.drivermanager.createConnectionData() + + # Open a connection to a filebased driver. + def connectWithFile(self, driver, filename): + # First we need a new connectiondata object. + connectiondata = self.getConnectionData() + # Fill the new connectiondata object with what we need to connect. + connectiondata.setCaption("myFileConnection") + connectiondata.setFileName(filename) + print "connectiondata.serverInfoString = %s" % connectiondata.serverInfoString() + # Create the connection now. + connection = driver.createConnection(connectiondata) + # Establish the connection. + if not connection.connect(): + raise("ERROR in connectWithDatabase(): Failed to connect!") + # Open database for usage. The filebased driver uses the filename as databasename. + self.useDatabase(connection, filename) + return connection + + # Open database for usage. + def useDatabase(self, connection, dbname): + if not connection.useDatabase(dbname): + raise("ERROR in connectWithDatabase(): Failed to use database!") + + # Create a new database. + def createDatabase(self, connection, dbname): + #print "createDatabase dbname='%s' dbnames='%s'" % (dbname,connection.databaseNames()) + connection.createDatabase(dbname) + #print "createDatabase databaseExists(%s) = %s" % (dbname,connection.databaseExists(dbname)) + #print "createDatabase dbname='%s' dbnames='%s'" % (dbname,connection.databaseNames()) + + # Drop an existing database. + def dropDatabase(self, connection, dbname): + #print "dropDatabase dbname='%s' dbnames='%s'" % (dbname,connection.databaseNames()) + myfileconnection.dropDatabase(dbname) + #print "dropDatabase databaseExists(%s) = %s" % (dbname,connection.databaseExists(dbname)) + #print "dropDatabase dbname='%s' dbnames='%s'" % (dbname,connection.databaseNames()) + + # Test KexiDBParser used to parse SQL-statements. + def testParser(self, connection, sqlstatement): + parser = connection.parser() + if not parser: + raise "ERROR in testParser(): Failed to create parser!" + print "parser.parse = %s" % parser.parse(sqlstatement) + print "parser.statement = %s" % parser.statement() + print "parser.operation = %s" % parser.operation() + print "parser.table = %s" % parser.table() + print "parser.query = %s" % parser.query() + print "parser.connection = %s" % parser.connection() + + # Execute the sql query statement and print the single string result. + def printQuerySingleString(self, connection, sqlstatement): + query = myfileconnection.querySingleString("SELECT * FROM table1", 0) + print "querySingleString = %s" % query + + # Execute the sql query statement and print the single stringlist result. + def printQueryStringList(self, connection, sqlstatement): + query = myfileconnection.queryStringList("SELECT * FROM table1", 0) + print "queryStringList = %s" % query + + # Walk through the KexiDBCursor and print all item values. + def printQueryCursor(self, cursor): + if cursor == None: + raise("ERROR: executeQuery failed!") + #print "printCursor() cursor = %s %s" % (str(cursor), dir(cursor)) + + # Go to the first item of the table. + if not cursor.moveFirst(): + raise("ERROR in printCursor(): cursor.moveFirst() returned False!") + + # Walk through all items in the table. + while(not cursor.eof()): + # Print for each item some infos about the fields and there content. + for i in range( cursor.fieldCount() ): + print "Item='%s' Field='%s' Value='%s'" % (cursor.at(), i, cursor.value(i)) + # Move to the next item + cursor.moveNext() + + # Similar to printQueryCursor + def printQuerySchema(self, connection, queryschema): + return self.printQueryCursor(connection.executeQuerySchema(queryschema)) + + # Similar to printQueryCursor + def printQueryString(self, connection, sqlstring): + return self.printQueryCursor(connection.executeQueryString(sqlstring)) + + # Add a field to the tableschema. + def addField(self, tableschema, name): + field = self.drivermanager.field() + field.setType("Text") + field.setName(name) + tableschema.fieldlist().addField(field) + print "tableschema.fieldlist().fieldCount() = %s" % tableschema.fieldlist().fieldCount() + return field + + # Create a table. + def createTable(self, connection, tablename): + # First we need a new tableschema. + tableschema = self.drivermanager.tableSchema(tablename) + self.addField(tableschema, "myfield") + print "connection.createTable = %s" % connection.createTable(tableschema, True) + return tableschema + + # Drop a table. + def dropTable(self, connection, tablename): + connection.dropTable(tablename) + + # Alter the name of a table. + def alterTableName(self, connection, tablename, newtablename): + tableschema = connection.tableSchema(tablename) + print "alterTableName from=%s to=%s tableschema=%s" % (tablename, newtablename, tableschema) + connection.alterTableName(tableschema, newtablename) + +def testKexiDB(): + global KexiDBClass + mykexidbclass = KexiDBClass() + mykexidbclass.printKexiDB() + + mydriver = mykexidbclass.driver("SQLite3") + mykexidbclass.printDriverManger(mydriver) + + myfileconnection = mykexidbclass.connectWithFile(mydriver, "/home/snoopy/test.kexi") + mykexidbclass.printConnection(myfileconnection) + #mykexidbclass.testParser(myfileconnection, "SELECT * from table1") + + #mykexidbclass.printQuerySingleString(myfileconnection, "SELECT * FROM dept") + #mykexidbclass.printQueryStringList(myfileconnection, "SELECT * FROM dept") + mykexidbclass.printQueryString(myfileconnection, "SELECT * FROM dept") + + #myqueryschema = mykexidbclass.drivermanager.querySchema() + #myqueryschema.setName("myqueryname") + #myqueryschema.setCaption("myquerycaption") + #myqueryschema.setStatement("SELECT * FROM table2") + #print "myqueryschema = %s" % myqueryschema.statement() + #mykexidbclass.printQuerySchema(myfileconnection, myqueryschema) + + #mykexidbclass.createTable(myfileconnection, "mytable123") + #mykexidbclass.dropTable(myfileconnection, "mytable123") + #mykexidbclass.alterTableName(myfileconnection, "table1", "table111") + + #TODO: new table isn't usuable!!! + #ts1 = myfileconnection.tableSchema("table2") + #ts2 = mykexidbclass.drivermanager.tableSchema("table4") + #mykexidbclass.addField(ts2, "MyField 111111111") + #print "myfileconnection.alterTable = %s" % myfileconnection.alterTable(ts1, ts2) + #TEST + #bool Connection::insertRecord(TableSchema &tableSchema, QValueList& values) + #myfileconnection.insertRecord(KexiDBField, ("field1", "field2")) + #del(mycursor) + #del(myfileconnection) + #del(mydriver) + #del(mykexidbclass) + +print "########## BEGIN TEST: KexiDB ##########" +testKexiDB() +print "########## END TEST: KexiDB ##########" diff --git a/lib/kross/test/testobject.cpp b/lib/kross/test/testobject.cpp new file mode 100644 index 00000000..893cccca --- /dev/null +++ b/lib/kross/test/testobject.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** + * testobject.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "testobject.h" + +#include // for std::out + +TestObject::TestObject() + : QObject(0, "TestObject") +{ +} + +TestObject::TestObject(QObject* parent, Kross::Api::ScriptContainer::Ptr scriptcontainer) + : QObject(parent, "TestObject") +{ + connect(this, SIGNAL(testSignal()), this, SLOT(testSignalSlot())); + connect(this, SIGNAL(stdoutSignal(const QString&)), this, SLOT(stdoutSlot(const QString&))); + connect(this, SIGNAL(stderrSignal(const QString&)), this, SLOT(stderrSlot(const QString&))); + + scriptcontainer->addQObject(this); + +//scriptcontainer->addSignal("stdout", this, SIGNAL(stdoutSignal(const QString&))); +//scriptcontainer->addSlot("stderr", this, SLOT(stderrSlot(const QString&))); + + //scriptcontainer->addSignal("myTestSignal", this, SIGNAL(testSignal())); + //scriptcontainer->addSlot("myTestSlot", this, SLOT(testSlot())); +} + +TestObject::~TestObject() +{ +} + +uint TestObject::func1(uint i) +{ + Kross::krossdebug(QString("CALLED => TestObject::func1 i=%1").arg(i) ); + return i; +} + +void TestObject::func2(QString s, int i) +{ + Kross::krossdebug(QString("CALLED => TestObject::func2 s=%1 i=%2").arg(s).arg(i)); +} + +QString TestObject::func3(QString s, int i) +{ + Kross::krossdebug(QString("CALLED => TestObject::func3 s=%1 i=%2").arg(s).arg(i)); + return s; +} + +const QString& TestObject::func4(const QString& s, int i) const +{ + Kross::krossdebug(QString("CALLED => TestObject::func4 s=%1 i=%2").arg(s).arg(i)); + return s; +} + +void TestObject::testSlot() +{ + Kross::krossdebug("TestObject::testSlot called"); + emit testSignal(); + emit testSignalString("This is the emitted TestObject::testSignalString(const QString&)"); +} + +void TestObject::testSlot2() +{ + Kross::krossdebug("TestObject::testSlot2 called"); +} + +void TestObject::stdoutSlot(const QString& s) +{ + Kross::krossdebug(QString("stdout: %1").arg(s)); + //std::cout << " " << s.latin1() << std::endl; +} + +void TestObject::stderrSlot(const QString& s) +{ + Kross::krossdebug(QString("stderr: %1").arg(s)); + //std::cout << " " << s.latin1() << std::endl; +} + +//#include "testobject.moc" diff --git a/lib/kross/test/testobject.h b/lib/kross/test/testobject.h new file mode 100644 index 00000000..011b7e54 --- /dev/null +++ b/lib/kross/test/testobject.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * testobject.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_TEST_TESTOBJECT_H +#define KROSS_TEST_TESTOBJECT_H + +#include "../main/scriptcontainer.h" + +#include +#include + +class TestObject : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString testProperty READ testProperty WRITE setTestProperty) + + public: + TestObject(); + TestObject(QObject* parent, Kross::Api::ScriptContainer::Ptr scriptcontainer); + ~TestObject(); + + uint func1(uint); + void func2(QString, int); + QString func3(QString, int); + const QString& func4(const QString&, int) const; + + QString testProperty() const { return m_prop; } + void setTestProperty(QString prop) { m_prop = prop; } + + signals: + void testSignal(); + void testSignalString(const QString&); + void stdoutSignal(const QString&); + void stderrSignal(const QString&); + public slots: + void testSlot(); + void testSlot2(); + void stdoutSlot(const QString&); + void stderrSlot(const QString&); + + QObject* self() { return this; } + + private: + QString m_prop; +}; + +#endif diff --git a/lib/kross/test/testperformance.py b/lib/kross/test/testperformance.py new file mode 100755 index 00000000..a76453ed --- /dev/null +++ b/lib/kross/test/testperformance.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python + +""" + This Python script is used as performance-tester and profiler + for the Kross scripting framework. +""" + +def runner(): + import krosstestpluginmodule + testobject1 = krosstestpluginmodule.testpluginobject1() + + def testKexiDB(kexidbfile,drivername,sqlstring): + print "test kexidb" + import krosskexidb + drivermanager = krosskexidb.DriverManager() + connectiondata = drivermanager.createConnectionData() + connectiondata.setFileName(kexidbfile) + driver = drivermanager.driver(drivername) + connection = driver.createConnection(connectiondata) + if not connection.connect(): raise "Connect failed" + if not connection.useDatabase(kexidbfile): raise "Use database failed" + for i in range(20000): + cursor = connection.executeQueryString(sqlstring) + if not cursor: raise "Failed to create cursor" + cursor.moveFirst() + while(not cursor.eof()): + for i in range( cursor.fieldCount() ): + (item,field,value) = (cursor.at(), i, cursor.value(i)) + cursor.moveNext() + + def test1(): + print "test1" + for i in range(100000): + testobject1.func1() + testobject1.func1() + testobject1.func1() + + testobject1.func2("f2s1","f2s2") + testobject1.func2("f2s3","f2s4") + testobject1.func2("f2s5","f2s6") + + testobject1.func3("f3s1","f3s2") + testobject1.func3("f3s3","f3s4") + testobject1.func3("f3s5","f3s6") + + testobject1.func4("f4s1","f4s2") + testobject1.func4("f4s3","f4s4") + testobject1.func4("f4s5","f4s6") + + testobject1.func5("f5s1","f5s2") + testobject1.func5("f5s3","f5s4") + testobject1.func5("f5s5","f5s6") + + testobject1.func6( ("One1","Two2"), "haha" ) + testobject1.func6( ("One3","Two4"), 123456789 ) + testobject1.func6( ("One5","Two6"), 12345.67890 ) + + testobject1.func7("f5s1",123) + testobject1.func7("f5s3",456) + testobject1.func7("f5s5",789) + + testobject1.func8(123,456) + testobject1.func8(12,34) + testobject1.func8(1,2) + + testobject1.func9(2.0,1.0) + testobject1.func9(4.0,3.0) + testobject1.func9(6.0,5.0) + + #test1() + testKexiDB("/home/snoopy/test.kexi", "SQLite3", "SELECT * FROM dept") + +import profile +__main__.runner=runner +profile.run("runner()") diff --git a/lib/kross/test/testplugin.cpp b/lib/kross/test/testplugin.cpp new file mode 100644 index 00000000..07fffc6a --- /dev/null +++ b/lib/kross/test/testplugin.cpp @@ -0,0 +1,126 @@ +/*************************************************************************** + * testplugin.cpp + * This file is part of the KDE project + * copyright (C)2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "testplugin.h" +#include "testobject.h" + +/************************************************************************ + * TestPluginObject + */ + +TestPluginObject::TestPluginObject(const QString& name) + : Kross::Api::Class(name) +{ + // Functions to test the basic datatypes + this->addFunction1< void, Kross::Api::Variant > + ("voiduintfunc", this, &TestPluginObject::voiduintfunc); + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant > + ("uintfunc", this, &TestPluginObject::uintfunc); + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant > + ("intfunc", this, &TestPluginObject::intfunc); + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant > + ("boolfunc", this, &TestPluginObject::boolfunc); + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant > + ("doublefunc", this, &TestPluginObject::doublefunc); + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant > + ("cstringfunc", this, &TestPluginObject::cstringfunc); + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant > + ("stringfunc", this, &TestPluginObject::stringfunc); + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant > + ("stringlistfunc", this, &TestPluginObject::stringlistfunc); + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant > + ("variantfunc", this, &TestPluginObject::variantfunc); + + // With 2 arguments + this->addFunction2< Kross::Api::Variant, Kross::Api::Variant, Kross::Api::Variant > + ("stringstringfunc", this, &TestPluginObject::stringstringfunc); + // With 3 arguments + this->addFunction3< Kross::Api::Variant, Kross::Api::Variant, Kross::Api::Variant, Kross::Api::Variant > + ("uintdoublestringfunc", this, &TestPluginObject::uintdoublestringfunc); + // With 4 arguments + this->addFunction4< Kross::Api::Variant, Kross::Api::Variant, Kross::Api::Variant, Kross::Api::Variant, Kross::Api::Variant > + ("stringlistbooluintdouble", this, &TestPluginObject::stringlistbooluintdouble); + + // With default arguments + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant > + ("uintfunc_defarg", this, &TestPluginObject::uintfunc, new Kross::Api::Variant(12345) ); + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant > + ("stringfunc_defarg", this, &TestPluginObject::stringfunc, new Kross::Api::Variant("MyDefaultString") ); + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant > + ("stringlistfunc_defarg", this, &TestPluginObject::stringlistfunc, new Kross::Api::Variant(QVariant(QStringList() << "Default1" << "Default2"))); + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant > + ("variantfunc_defarg", this, &TestPluginObject::variantfunc, new Kross::Api::Variant("MyDefaultVariantString") ); + + // Test passing of objects + this->addFunction1("objectfunc", this, &TestPluginObject::objectfunc, 0); +} + +TestPluginObject::~TestPluginObject() +{ +} + +const QString TestPluginObject::getClassName() const +{ + return "TestPluginObject"; +} + +uint TestPluginObject::uintfunc(uint i) { return i; } +void TestPluginObject::voiduintfunc(uint) {} +int TestPluginObject::intfunc(int i) { return i; } +bool TestPluginObject::boolfunc(bool b) { return b; } +double TestPluginObject::doublefunc(double d) { return d; } +QCString TestPluginObject::cstringfunc(const QCString& s) { return s; } +QString TestPluginObject::stringfunc(const QString& s) { return s; } +QStringList TestPluginObject::stringlistfunc(const QStringList& sl) { return sl; } +QVariant TestPluginObject::variantfunc(const QVariant& v) { return v; } +TestPluginObject* TestPluginObject::objectfunc(TestPluginObject* obj) { return obj; } +QString TestPluginObject::stringstringfunc(const QString& s, const QString&) { return s; } +uint TestPluginObject::uintdoublestringfunc(uint i, double, const QString&) { return i; } +QStringList TestPluginObject::stringlistbooluintdouble(const QStringList& sl, bool, uint, double) { return sl; } + +/************************************************************************ + * TestPluginModule + */ + +TestPluginModule::TestPluginModule(const QString& name) + : Kross::Api::Module(name) + , m_testobject( new TestObject() ) + +{ + addChild( new TestPluginObject("testpluginobject1") ); + + // Let's wrap a whole instance and it's methodfunctions. + Kross::Api::Event *testobjectclass = + new Kross::Api::Event("testpluginobject2"); + addChild(testobjectclass); + + // Wrap a whole QObject + addChild( new Kross::Api::QtObject( new TestObject() , "testqobject1" ) ); +} + +TestPluginModule::~TestPluginModule() +{ + delete m_testobject; +} + +const QString TestPluginModule::getClassName() const +{ + return "TestPluginModule"; +} + diff --git a/lib/kross/test/testplugin.h b/lib/kross/test/testplugin.h new file mode 100644 index 00000000..5253becf --- /dev/null +++ b/lib/kross/test/testplugin.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * testplugin.h + * This file is part of the KDE project + * copyright (C)2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_TEST_TESTPLUGIN_H +#define KROSS_TEST_TESTPLUGIN_H + +#include "../api/object.h" +#include "../api/list.h" +#include "../api/class.h" +#include "../api/proxy.h" +#include "../api/module.h" +#include "../api/qtobject.h" + +#include +#include + +class TestPluginObject : public Kross::Api::Class +{ + public: + TestPluginObject(const QString& name); + virtual ~TestPluginObject(); + virtual const QString getClassName() const; + + private: + uint uintfunc(uint); + void voiduintfunc(uint); + int intfunc(int); + bool boolfunc(bool); + double doublefunc(double); + QCString cstringfunc(const QCString&); + QString stringfunc(const QString&); + QStringList stringlistfunc(const QStringList&); + QVariant variantfunc(const QVariant&); + + TestPluginObject* objectfunc(TestPluginObject* obj); + + QString stringstringfunc(const QString&, const QString&); + uint uintdoublestringfunc(uint, double, const QString&); + QStringList stringlistbooluintdouble(const QStringList&, bool, uint, double); +}; + +class TestObject; + +class TestPluginModule : public Kross::Api::Module +{ + public: + TestPluginModule(const QString& name); + virtual ~TestPluginModule(); + virtual const QString getClassName() const; + + virtual Kross::Api::Object::Ptr get(const QString& /*name*/, void* /*pointer*/ = 0) + { + return 0; + } + private: + TestObject* m_testobject; +}; + +#endif diff --git a/lib/kross/test/testscripting.rc b/lib/kross/test/testscripting.rc new file mode 100644 index 00000000..ee641d15 --- /dev/null +++ b/lib/kross/test/testscripting.rc @@ -0,0 +1,33 @@ + + +print "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +print "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + + + + + + + + + diff --git a/lib/kross/test/testwindow.cpp b/lib/kross/test/testwindow.cpp new file mode 100644 index 00000000..f60119f0 --- /dev/null +++ b/lib/kross/test/testwindow.cpp @@ -0,0 +1,110 @@ +/*************************************************************************** + * testwindow.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "testwindow.h" +#include "testplugin.h" + +#include +#include +#include +//#include +#include +#include +#include + +#include +#include +#include +#include +#include + +TestWindow::TestWindow(const QString& interpretername, const QString& scriptcode) + : KMainWindow() + , m_interpretername(interpretername) + , m_scriptcode(scriptcode) +{ + Kross::Api::Manager* manager = Kross::Api::Manager::scriptManager(); + manager->addModule( new TestPluginModule("krosstestpluginmodule") ); + m_scriptcontainer = manager->getScriptContainer("test"); + + KPopupMenu *menuFile = new KPopupMenu( this ); + menuBar()->insertItem( "&File", menuFile ); + + m_scriptextension = new Kross::Api::ScriptGUIClient(this, this); + + QString file = KGlobal::dirs()->findResource("appdata", "testscripting.rc"); + if(file.isNull()) + file = QDir(QDir::currentDirPath()).filePath("testscripting.rc"); + else Kross::krossdebug("-------------------------222222"); + + Kross::krossdebug(QString("XML-file: %1").arg(file)); + m_scriptextension->setXMLFile(file); + + //menuFile->insertSeparator(); + + KAction* execaction = m_scriptextension->action("executescriptfile"); + if(execaction) execaction->plug(menuFile); + + KAction* configaction = m_scriptextension->action("configurescripts"); + if(configaction) configaction->plug(menuFile); + + KAction* scriptsaction = m_scriptextension->action("installedscripts"); + if(scriptsaction) scriptsaction->plug(menuFile); + //menuFile->insertItem( ( (KActionMenu*)m_scriptextension->action("scripts") )->popupMenu() ); + + QVBox* mainbox = new QVBox(this); + + QVGroupBox* interpretergrpbox = new QVGroupBox("Interpreter", mainbox); + QStringList interpreters = Kross::Api::Manager::scriptManager()->getInterpreters(); + m_interpretercombo = new QComboBox(interpretergrpbox); + m_interpretercombo->insertStringList(interpreters); + m_interpretercombo->setCurrentText(interpretername); + + QVGroupBox* scriptgrpbox = new QVGroupBox("Scripting code", mainbox); + m_codeedit = new KTextEdit(m_scriptcode, QString::null, scriptgrpbox); + m_codeedit->setWordWrap(QTextEdit::NoWrap); + m_codeedit->setTextFormat(Qt::PlainText); + + QHBox* btnbox = new QHBox(mainbox); + KPushButton* execbtn = new KPushButton("Execute", btnbox); + connect(execbtn, SIGNAL(clicked()), this, SLOT(execute())); + + setCentralWidget(mainbox); + setMinimumSize(600,420); +} + +TestWindow::~TestWindow() +{ +} + +void TestWindow::execute() +{ + m_scriptcontainer->setInterpreterName( m_interpretercombo->currentText() ); + m_scriptcontainer->setCode(m_codeedit->text()); + Kross::Api::Object::Ptr result = m_scriptcontainer->execute(); + if(m_scriptcontainer->hadException()) { + Kross::krossdebug( QString("EXCEPTION => %1").arg(m_scriptcontainer->getException()->toString()) ); + } + else { + QString s = result ? result->toString() : QString::null; + Kross::krossdebug( QString("DONE => %1").arg(s) ); + } +} + +#include "testwindow.moc" diff --git a/lib/kross/test/testwindow.h b/lib/kross/test/testwindow.h new file mode 100644 index 00000000..dbb190b9 --- /dev/null +++ b/lib/kross/test/testwindow.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * testwindow.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_TEST_TESTWINDOW_H +#define KROSS_TEST_TESTWINDOW_H + +#include "../main/manager.h" +#include "../main/scriptcontainer.h" +#include "../main/scriptguiclient.h" +#include "../api/object.h" + +//#include +#include + +#include + +class QComboBox; +class KTextEdit; + +class TestWindow : public KMainWindow +{ + Q_OBJECT + public: + TestWindow(const QString& interpretername, const QString& scriptcode); + virtual ~TestWindow(); + private slots: + void execute(); + private: + QString m_interpretername; + QString m_scriptcode; + + Kross::Api::ScriptContainer::Ptr m_scriptcontainer; + Kross::Api::ScriptGUIClient* m_scriptextension; + + QComboBox* m_interpretercombo; + KTextEdit* m_codeedit; +}; + +#endif -- cgit v1.2.1