diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
commit | 8362bf63dea22bbf6736609b0f49c152f975eb63 (patch) | |
tree | 0eea3928e39e50fae91d4e68b21b1e6cbae25604 /lib/kross | |
download | koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip |
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
Diffstat (limited to 'lib/kross')
125 files changed, 22915 insertions, 0 deletions
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<QString, Object::Ptr> 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<QString, Object::Ptr> children = getChildren(); + QMap<QString, Object::Ptr>::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 <qstring.h> +#include <qvaluelist.h> +#include <ksharedptr.h> + +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<Callable> 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<QString, Object::Ptr> 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<QString, Object::Ptr> 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 <qstring.h> +//#include <qvaluelist.h> +//#include <qmap.h> +#include <qobject.h> + +//#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 T> + class Class : public Event<T> + { + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr<T> Ptr; + + /** + * Constructor. + * + * \param name The name this class has. + */ + Class(const QString& name) + : Event<T>(name) + { + } + + /** + * Destructor. + */ + virtual ~Class() + { + } + + template<typename TYPE> + 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<T>::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<QString, Object::Ptr> value) + : Value< List, QMap<QString, Object::Ptr> >(value) +{ +} + +Dict::~Dict() +{ +} + +const QString Dict::getClassName() const +{ + return "Kross::Api::Dict"; +} + +const QString Dict::toString() +{ + QString s = "["; + QMap<QString, Object::Ptr> list = getValue(); + for(QMap<QString, Object::Ptr>::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 <qstring.h> +#include <qmap.h> + +#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<QString, Object::Ptr> > + { + friend class Value< List, QMap<QString, Object::Ptr> >; + 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<QString, Object::Ptr> 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 <qstring.h> +#include <qvaluelist.h> +#include <qmap.h> + +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 T> + 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<QString, Function* > m_functions; + + public: + + /** + * Constructor. + * + * \param name The name this \a Event has. + */ + Event(const QString& name) + : Callable(name) + { + } + + /** + * Destructor. + */ + virtual ~Event() + { + QMapConstIterator<QString, Function* > endit = m_functions.constEnd(); + for(QMapConstIterator<QString, Function* > 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<T>(static_cast<T*>(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<class RETURNOBJ, class ARG1OBJ, class ARG2OBJ, class ARG3OBJ, class ARG4OBJ, class INSTANCE, typename METHOD> + 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, RETURNOBJ, ARG1OBJ, ARG2OBJ, ARG3OBJ, ARG4OBJ> + (instance, method, arg1, arg2, arg3, arg4) + ); + } + + /// Same as above with three arguments. + template<class RETURNOBJ, class ARG1OBJ, class ARG2OBJ, class ARG3OBJ, class INSTANCE, typename METHOD> + 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, RETURNOBJ, ARG1OBJ, ARG2OBJ, ARG3OBJ> + (instance, method, arg1, arg2, arg3) + ); + } + + /// Same as above with two arguments. + template<class RETURNOBJ, class ARG1OBJ, class ARG2OBJ, class INSTANCE, typename METHOD> + 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, RETURNOBJ, ARG1OBJ, ARG2OBJ> + (instance, method, arg1, arg2) + ); + } + + /// Same as above, but with one argument. + template<class RETURNOBJ, class ARG1OBJ, class INSTANCE, typename METHOD> + inline void addFunction1(const QString& name, INSTANCE* instance, METHOD method, ARG1OBJ* arg1 = 0) + { + m_functions.replace(name, + new Kross::Api::ProxyFunction<INSTANCE, METHOD, RETURNOBJ, ARG1OBJ> + (instance, method, arg1) + ); + } + + /// Same as above with no arguments. + template<class RETURNOBJ, class INSTANCE, typename METHOD> + inline void addFunction0(const QString& name, INSTANCE* instance, METHOD method) + { + m_functions.replace(name, + new Kross::Api::ProxyFunction<INSTANCE, METHOD, RETURNOBJ> + (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 <qobject.h> +//#include <kaction.h> + +using namespace Kross::Api; + +EventAction::EventAction(const QString& name, KAction* action) + : Event<EventAction>(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 <qstring.h> +//#include <qobject.h> +#include <kaction.h> +#include <ksharedptr.h> + +#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<EventAction> + { + + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr<EventAction> 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<List> 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<EventScript>(name) +{ +} + +EventScript::~EventScript() +{ +} + +const QString EventScript::getClassName() const +{ + return "Kross::Api::EventScript"; +} + +Object::Ptr EventScript::call(const QString& name, KSharedPtr<List> 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 <qstring.h> +#include <qobject.h> + +#include "event.h" + +namespace Kross { namespace Api { + + /** + * \todo implement EventScript ?! + */ + class EventScript : public Event<EventScript> + { + + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr<EventScript> Ptr; + + /** + * Constructor. + */ + EventScript(const QString& name); + + /** + * Destructor. + */ + virtual ~EventScript(); + + virtual const QString getClassName() const; + + virtual Object::Ptr call(const QString& name, KSharedPtr<List> 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 <qmetaobject.h> +#include <private/qucom_p.h> // for the Qt QUObject API. + +using namespace Kross::Api; + +EventSignal::EventSignal(const QString& name, QObject* sender, QCString signal) + : Event<EventSignal>(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<List> 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 <qstring.h> +//#include <qvaluelist.h> +//#include <qmap.h> +//#include <qvariant.h> +//#include <qsignalmapper.h> +//#include <qguardedptr.h> +#include <qobject.h> +#include <ksharedptr.h> + +#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<EventSignal> + { + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr<EventSignal> 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<List> 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 <qmetaobject.h> +#include <private/qucom_p.h> // for the Qt QUObject API. + +using namespace Kross::Api; + +EventSlot::EventSlot(const QString& name, QObject* receiver, QCString slot) + : Event<EventSlot>(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<Kross::Api::Object*> 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<QVariant>() << i1 << i2 )); } +void EventSlot::callback(int i1, int i2, int i3) { + call(QVariant( QValueList<QVariant>() << i1 << i2 << i3 )); } +void EventSlot::callback(int i1, int i2, int i3, int i4) { + call(QVariant( QValueList<QVariant>() << i1 << i2 << i3 << i4 )); } +void EventSlot::callback(int i1, int i2, int i3, int i4, int i5) { + call(QVariant( QValueList<QVariant>() << i1 << i2 << i3 << i4 << i5 )); } +void EventSlot::callback(int i1, int i2, int i3, int i4, bool b) { + call(QVariant( QValueList<QVariant>() << i1 << i2 << i3 << i4 << b )); } +void EventSlot::callback(int i1, bool b) { + call(QVariant( QValueList<QVariant>() << i1 << b )); } +void EventSlot::callback(int i1, int i2, bool b) { + call(QVariant( QValueList<QVariant>() << i1 << i2 << b )); } +void EventSlot::callback(int i1, int i2, const QString& s) { + call(QVariant( QValueList<QVariant>() << 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<QVariant>() << s << i )); } +void EventSlot::callback(const QString& s, int i1, int i2) { + call(QVariant( QValueList<QVariant>() << s << i1 << i2 )); } +void EventSlot::callback(const QString& s, uint i) { + call(QVariant( QValueList<QVariant>() << s << i )); } +void EventSlot::callback(const QString& s, bool b) { + call(QVariant( QValueList<QVariant>() << s << b )); } +void EventSlot::callback(const QString& s, bool b1, bool b2) { + call(QVariant( QValueList<QVariant>() << s << b1 << b2 )); } +void EventSlot::callback(const QString& s, bool b, int i) { + call(QVariant( QValueList<QVariant>() << s << b << i )); } +void EventSlot::callback(const QString& s1, const QString& s2) { + call(QVariant( QValueList<QVariant>() << s1 << s2 )); } +void EventSlot::callback(const QString& s1, const QString& s2, const QString& s3) { + call(QVariant( QValueList<QVariant>() << 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 <qstring.h> +#include <qobject.h> +#include <ksharedptr.h> + +#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<EventSlot> + { + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr<EventSlot> 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<List> arguments); + +/* + private: + EventManager* m_eventmanager; + QGuardedPtr<QObject> m_sender; + QCString m_signal; + QCString m_slot; + QString m_function; + QValueList<EventSlot*> 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 <qstring.h> +//#include <ksharedptr.h> + +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 <qstring.h> +#include <ksharedptr.h> + +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<Exception> 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 <qstring.h> + +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 INSTANCE> + 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 INSTANCE, typename P1> + 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 INSTANCE, typename P1, typename P2> + 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 <klibloader.h> + +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 <qstring.h> +#include <qmap.h> + +#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<QString, Option*> 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<Object::Ptr> value) + : Value< List, QValueList<Object::Ptr> >(value) +{ +} + +List::~List() +{ +} + +const QString List::getClassName() const +{ + return "Kross::Api::List"; +} + +const QString List::toString() +{ + QString s = "["; + QValueList<Object::Ptr> list = getValue(); + for(QValueList<Object::Ptr>::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<Object::Ptr>& 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 <qstring.h> +#include <qvaluelist.h> +#include <qintdict.h> + +#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<Object::Ptr> > + { + friend class Value< List, QValueList<Object::Ptr> >; + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr<List> Ptr; + +/* + operator QStringList () { + //QValueList<Object::Ptr> getValue() + krossdebug("999999999999 ..........................."); + return QStringList(); + } +*/ + + /** + * Constructor. + * + * \param value The list of \a Object instances this + * list has initialy. + */ + List(QValueList<Object::Ptr> value = QValueList<Object::Ptr>()); + + /** + * 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<typename TYPE> + 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<OBJECT> values) : List(values) {} + + template< typename TYPE > + ListT(QValueList<TYPE> values) : List() + { + QValueListIterator<TYPE> it(values.begin()), end(values.end()); + for(; it != end; ++it) + this->append( new OBJECT(*it) ); + } + + template< typename TYPE > + ListT(QIntDict<TYPE> values) : List() + { + QIntDictIterator<TYPE> it( values ); + TYPE *t; + while( (t = it.current()) != 0 ) { + this->append( new OBJECT(t) ); + ++it; + } + } + + template< typename TYPE > + ListT(const QPtrList<TYPE> values) : List() + { + QPtrListIterator<TYPE> it(values); + TYPE *t; + while( (t = it.current()) != 0 ) { + this->append( new OBJECT(t) ); + ++it; + } + } + + virtual ~ListT() {} + + template<typename TYPE> + 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 <qstring.h> + +#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<Module> + { + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr<Module> 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<Module>(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 <qstring.h> +#include <qvaluelist.h> +#include <qmap.h> +#include <qvariant.h> +//#include <qobject.h> +#include <ksharedptr.h> + +#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<Object> 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<List> 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<class T> 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<typename TYPE> + static Object::Ptr toObject(TYPE t) { return t; } + }; + +}} + +#include "exception.h" + +namespace Kross { namespace Api { + +template<class T> inline T* Object::fromObject(Object::Ptr object) +{ + T* t = (T*) object.data(); + if(! t) + throw KSharedPtr<Exception>( 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 <qstring.h> + +namespace Kross { namespace Api { + + /** + * \internal used struct to translate an argument-value dynamicly. + */ + template<class OBJ> + struct ProxyArgTranslator { + OBJ* m_object; + ProxyArgTranslator(Kross::Api::Object* obj) { + m_object = Kross::Api::Object::fromObject<OBJ>(obj); + } + template<typename T> + inline operator T () { + return m_object->operator T(); + } + }; + + /** + * \internal used struct to translate a return-value dynamicly. + */ + struct ProxyRetTranslator { + template<class RETURNOBJ, typename TYPE> + 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<MyClass> { + * public: + * MyClass(const QString& name) : Kross::Api::Class<MyClass>(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<class PROXYFUNC, typename RETURNTYPE> + 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<ARG1OBJ> m_defarg1; + /// Second default argument. + KSharedPtr<ARG2OBJ> m_defarg2; + /// Theird default argument. + KSharedPtr<ARG3OBJ> m_defarg3; + /// Forth default argument. + KSharedPtr<ARG4OBJ> m_defarg4; + + /** + * \internal used struct that does the execution of the wrapped + * method. + */ + template<class PROXYFUNC, typename RETURNTYPE> + 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<RETURNTYPE>( + ( (self->m_instance)->*(self->m_method) )( ProxyArgTranslator<ARG1OBJ>(arg1), ProxyArgTranslator<ARG2OBJ>(arg2), ProxyArgTranslator<ARG3OBJ>(arg3), ProxyArgTranslator<ARG4OBJ>(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<class PROXYFUNC> + struct ProxyFunctionCaller<PROXYFUNC, void> { + 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<ARG1OBJ>(arg1), ProxyArgTranslator<ARG1OBJ>(arg2), ProxyArgTranslator<ARG3OBJ>(arg3), ProxyArgTranslator<ARG4OBJ>(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<ProxyFunction, RETURNOBJ>::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 INSTANCE, typename METHOD, class RETURNOBJ, class ARG1OBJ, class ARG2OBJ, class ARG3OBJ> + class ProxyFunction<INSTANCE, METHOD, RETURNOBJ, ARG1OBJ, ARG2OBJ, ARG3OBJ> : public Function + { + template<class PROXYFUNC, typename RETURNTYPE> + friend struct ProxyFunctionCaller; + private: + INSTANCE* m_instance; + const METHOD m_method; + KSharedPtr<ARG1OBJ> m_defarg1; + KSharedPtr<ARG2OBJ> m_defarg2; + KSharedPtr<ARG3OBJ> m_defarg3; + + template<class PROXYFUNC, typename RETURNTYPE> + struct ProxyFunctionCaller { + inline static Object::Ptr exec(PROXYFUNC* self, Kross::Api::Object* arg1, Kross::Api::Object* arg2, Kross::Api::Object* arg3) { + return ProxyRetTranslator::cast<RETURNTYPE>( + ( (self->m_instance)->*(self->m_method) )( ProxyArgTranslator<ARG1OBJ>(arg1), ProxyArgTranslator<ARG2OBJ>(arg2), ProxyArgTranslator<ARG3OBJ>(arg3) ) + ); + } + }; + + template<class PROXYFUNC> + struct ProxyFunctionCaller<PROXYFUNC, void> { + 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<ARG1OBJ>(arg1), ProxyArgTranslator<ARG2OBJ>(arg2), ProxyArgTranslator<ARG3OBJ>(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<ProxyFunction, RETURNOBJ>::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 INSTANCE, typename METHOD, class RETURNOBJ, class ARG1OBJ, class ARG2OBJ> + class ProxyFunction<INSTANCE, METHOD, RETURNOBJ, ARG1OBJ, ARG2OBJ> : public Function + { + template<class PROXYFUNC, typename RETURNTYPE> + friend struct ProxyFunctionCaller; + private: + INSTANCE* m_instance; + const METHOD m_method; + KSharedPtr<ARG1OBJ> m_defarg1; + KSharedPtr<ARG2OBJ> m_defarg2; + + template<class PROXYFUNC, typename RETURNTYPE> + struct ProxyFunctionCaller { + inline static Object::Ptr exec(PROXYFUNC* self, Kross::Api::Object* arg1, Kross::Api::Object* arg2) { + return ProxyRetTranslator::cast<RETURNTYPE>( + ( (self->m_instance)->*(self->m_method) )( ProxyArgTranslator<ARG1OBJ>(arg1), ProxyArgTranslator<ARG2OBJ>(arg2) ) + ); + } + }; + + template<class PROXYFUNC> + struct ProxyFunctionCaller<PROXYFUNC, void> { + inline static Object::Ptr exec(PROXYFUNC* self, Kross::Api::Object* arg1, Kross::Api::Object* arg2) { + ( (self->m_instance)->*(self->m_method) )( ProxyArgTranslator<ARG1OBJ>(arg1), ProxyArgTranslator<ARG2OBJ>(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<ProxyFunction, RETURNOBJ>::exec(this, + args->item(0, m_defarg1), args->item(1, m_defarg2) + ); + } + }; + + /** + * Template-specialization of the \a ProxyFunction above with one argument. + */ + template<class INSTANCE, typename METHOD, class RETURNOBJ, class ARG1OBJ> + class ProxyFunction<INSTANCE, METHOD, RETURNOBJ, ARG1OBJ> : public Function + { + template<class PROXYFUNC, typename RETURNTYPE> + friend struct ProxyFunctionCaller; + private: + INSTANCE* m_instance; + const METHOD m_method; + KSharedPtr<ARG1OBJ> m_defarg1; + + template<class PROXYFUNC, typename RETURNTYPE> + struct ProxyFunctionCaller { + inline static Object::Ptr exec(PROXYFUNC* self, Kross::Api::Object* arg1) { + return ProxyRetTranslator::cast<RETURNTYPE>( + ( (self->m_instance)->*(self->m_method) )( ProxyArgTranslator<ARG1OBJ>(arg1) ) + ); + } + }; + + template<class PROXYFUNC> + struct ProxyFunctionCaller<PROXYFUNC, void> { + inline static Object::Ptr exec(PROXYFUNC* self, Kross::Api::Object* arg1) { + ( (self->m_instance)->*(self->m_method) )( ProxyArgTranslator<ARG1OBJ>(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<ProxyFunction, RETURNOBJ>::exec(this, + args->item(0, m_defarg1) + ); + } + }; + + /** + * Template-specialization of the \a ProxyFunction above with no arguments. + */ + template<class INSTANCE, typename METHOD, class RETURNOBJ> + class ProxyFunction<INSTANCE, METHOD, RETURNOBJ> : public Function + { + template<class PROXYFUNC, typename RETURNTYPE> + friend struct ProxyFunctionCaller; + private: + INSTANCE* m_instance; + const METHOD m_method; + + template<class PROXYFUNC, typename RETURNTYPE> + struct ProxyFunctionCaller { + inline static Object::Ptr exec(PROXYFUNC* self) { + return ProxyRetTranslator::cast<RETURNTYPE>( + ( (self->m_instance)->*(self->m_method) )() + ); + } + }; + + template<class PROXYFUNC> + struct ProxyFunctionCaller<PROXYFUNC, void> { + 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<ProxyFunction, RETURNOBJ>::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 <qobject.h> +#include <qsignal.h> +//#include <qglobal.h> +//#include <qobjectdefs.h> +#include <qmetaobject.h> +#include <private/qucom_p.h> // for the Qt QUObject API. + +using namespace Kross::Api; + +QtObject::QtObject(QObject* object, const QString& name) + : Kross::Api::Class<QtObject>(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<QString,QString> 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; j<argcount; j++) t += "'" + Variant::toString(arguments->item(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<Kross::Api::QtObject>(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 <qstring.h> +#include <qobject.h> + +// 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<QtObject> + { + public: + + /** + * Shared pointer to implement reference-counting. + */ + typedef KSharedPtr<QtObject> 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 <qstring.h> +#include <qstringlist.h> + +#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 <qstring.h> +#include <qvariant.h> + +#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 T, class V> + 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<typename TYPE> + 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 <klocale.h> + +using namespace Kross::Api; + +Variant::Variant(const QVariant& value) + : Value<Variant, QVariant>(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<Variant>( 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<Object::Ptr> valuelist = list->getValue(); + QValueList<Object::Ptr>::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<QVariant> Variant::toList(Object::Ptr object) +{ + List* list = dynamic_cast< List* >( object.data() ); + if(list) { + QValueList<QVariant> l; + QValueList<Object::Ptr> valuelist = list->getValue(); + QValueList<Object::Ptr>::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 <qstring.h> +#include <qvariant.h> + +#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<Variant, QVariant> + { + friend class Value<Variant, QVariant>; + 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<QVariant> () { + 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<QVariant> 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 <ruby.h> + #include <version.h> + ],[ + +#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 <kdebug.h> + +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 <qstring.h> + +/** + * 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 <qstring.h> +#include <qvariant.h> +#include <qobject.h> + +#include <ksharedptr.h> +#include <kaction.h> + +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<MainModule> 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<Callable::Ptr> 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 <qobject.h> +#include <qfile.h> +#include <qregexp.h> + +#include <klibloader.h> +#include <klocale.h> +#include <kstaticdeleter.h> + +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<QString, InterpreterInfo*> interpreterinfos; + + /// Loaded modules. + QMap<QString, Module::Ptr> modules; + }; + + /** + * The Manager-singleton instance is NULL by default till the + * Manager::scriptManager() method got called first time. + */ + static KSharedPtr<Manager> m_manager = KSharedPtr<Manager>(0); + +}} + +Manager* Manager::scriptManager() +{ + if(! m_manager.data()) { + // Create the Manager-singleton on demand. + m_manager = KSharedPtr<Manager>( 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<QString, InterpreterInfo*>::Iterator it = d->interpreterinfos.begin(); it != d->interpreterinfos.end(); ++it) + delete it.data(); + delete d; +} + +QMap<QString, InterpreterInfo*> 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<QString, InterpreterInfo*>::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<QString, InterpreterInfo*>::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 <qstring.h> +#include <qstringlist.h> +#include <qmap.h> +//#include <qvariant.h> +#include <ksharedptr.h> + +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<QString, InterpreterInfo*> 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<ScriptContainer> 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 <qstylesheet.h> +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <kurl.h> +#include <kstandarddirs.h> +#include <kmimetype.h> + +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<ScriptActionCollection*> 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<br>%2").arg(text.isEmpty() ? name : text).arg(file); + else + description += QString("<br>%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<ScriptActionCollection*>::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("<b>%1</b><br>%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 <qdom.h> +#include <kaction.h> + +#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<ScriptAction> Ptr; + + /// A list of \a ScriptAction instances. + //typedef QValueList<ScriptAction::Ptr> 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<ScriptAction::Ptr> m_list; + + /** + * A map of \a ScriptAction shared pointers used to access + * the actions with there name. + */ + QMap<QCString, ScriptAction::Ptr> 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<ScriptAction::Ptr>::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<ScriptAction::Ptr> 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<ScriptAction::Ptr>::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 <qfile.h> + +#include <klocale.h> + +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<QString, QVariant> 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<QString, QVariant>& 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 <qstring.h> +#include <qvariant.h> +#include <qobject.h> +#include <ksharedptr.h> + +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<ScriptContainer> 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<QString, QVariant>& 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<Object> callFunction(const QString& functionname, KSharedPtr<List> arguments = 0); + + /** + * Return a list of classes. + */ + QStringList getClassNames(); + + /** + * Create and return a new class instance. + */ + KSharedPtr<Object> 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 <kapplication.h> +#include <kpopupmenu.h> +#include <kstandarddirs.h> +#include <kmimetype.h> +#include <kmessagebox.h> +#include <kfiledialog.h> +#include <klocale.h> +#include <kurl.h> +#include <ktar.h> +#include <kstandarddirs.h> + +#include <kio/netaccess.h> + +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<QString, ScriptActionCollection*> 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<QString, ScriptActionCollection*>::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<QString, ScriptActionCollection*> 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<QString, InterpreterInfo*> infos = Manager::scriptManager()->getInterpreterInfos(); + for(QMap<QString, InterpreterInfo*>::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 <qobject.h> +#include <qdom.h> +#include <kurl.h> +#include <kxmlguiclient.h> + +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<KAction> 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<QString, ScriptActionCollection*> 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 <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 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 <qfile.h> +#include <qfileinfo.h> +#include <qheader.h> +#include <qobjectlist.h> +#include <qtooltip.h> + +#include <kapplication.h> +#include <kdeversion.h> +#include <kfiledialog.h> +#include <kiconloader.h> +#include <klistview.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kpushbutton.h> +#include <kstandarddirs.h> +#include <ktoolbar.h> + +#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 <knewstuff/provider.h> + #include <knewstuff/engine.h> + #include <knewstuff/downloaddialog.h> + #include <knewstuff/knewstuffsecure.h> +#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<ListItem*>( m_parent->itemAt(p) ); + if(item) { + QRect r( m_parent->itemRect(item) ); + if(r.isValid() && item->action()) { + tip(r, QString("<qt>%1</qt>").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<ScriptAction::Ptr> list = collection->actions(); + QListViewItem* lastitem = 0; + for(QValueList<ScriptAction::Ptr>::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<ListItem*>(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<ListItem*>(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<ListItem*>( 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<ListItem*>( scriptsList->currentItem() ); + if(item && item->action()) + item->action()->activate(); +} + +void WdgScriptsManager::slotUnloadScript() +{ + ListItem* item = dynamic_cast<ListItem*>( 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 <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 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 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>WdgScriptsManagerBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>WdgScriptsManagerBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>181</width> + <height>467</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="caption"> + <string>Scripts Manager</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="KListView"> + <property name="name"> + <cstring>scriptsList</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>2</hsizetype> + <vsizetype>2</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="KToolBar"> + <property name="name"> + <cstring>toolBar</cstring> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KPushButton"> + <property name="name"> + <cstring>btnExec</cstring> + </property> + <property name="minimumSize"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Execute</string> + </property> + </widget> + <widget class="Line"> + <property name="name"> + <cstring>line2</cstring> + </property> + <property name="frameShape"> + <enum>VLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>btnLoad</cstring> + </property> + <property name="minimumSize"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Load</string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>btnUnload</cstring> + </property> + <property name="minimumSize"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Unload</string> + </property> + </widget> + <widget class="Line"> + <property name="name"> + <cstring>line3</cstring> + </property> + <property name="frameShape"> + <enum>VLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>btnInstall</cstring> + </property> + <property name="minimumSize"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Install</string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>btnUninstall</cstring> + </property> + <property name="minimumSize"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Uninstall</string> + </property> + </widget> + <widget class="Line"> + <property name="name"> + <cstring>line4</cstring> + </property> + <property name="frameShape"> + <enum>VLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>btnNewStuff</cstring> + </property> + <property name="minimumSize"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Get More Scripts</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>KToolBar</class> + <header location="local">ktoolbar.h</header> + <sizehint> + <width>20</width> + <height>100</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>5</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="XBM.GZ" length="79">789c534e494dcbcc4b554829cdcdad8c2fcf4c29c95030e0524611cd48cd4ccf28010a1797249664262b2467241641a592324b8aa363156c15aab914146aadb90067111b1f</data> + </image> +</images> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klistview.h</includehint> + <includehint>ktoolbar.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> 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<std::random_access_iterator_tag,itemtype,int> +#else +# define random_access_iterator_parent(itemtype) std::random_access_iterator<itemtype, int> +#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 <strstream> +# define TEMPLATE_TYPENAME class +# else +# define PYCXX_ISO_CPP_LIB 1 +# define STR_STREAM <sstream> +# define TEMPLATE_TYPENAME typename +# endif +#elif defined( __GNUC__ ) +# if __GNUC__ >= 3 +# define PYCXX_ISO_CPP_LIB 1 +# define STR_STREAM <sstream> +# define TEMPLATE_TYPENAME typename +# else +# define PYCXX_ISO_CPP_LIB 0 +# define STR_STREAM <strstream> +# define TEMPLATE_TYPENAME class +# endif +#endif + +#if PYCXX_ISO_CPP_LIB +# define STR_STREAM <sstream> +# define OSTRSTREAM ostringstream +# define EXPLICIT_TYPENAME typename +# define EXPLICIT_CLASS class +# define TEMPLATE_TYPENAME typename +#else +# define STR_STREAM <strstream> +# 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 <string> +#include <iostream> + +// 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 <vector> +#include <map> + +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<PyMethodDef> 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 T> + 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<char *>(_name); + ext_meth_def.ml_meth = _handler; + ext_meth_def.ml_flags = METH_VARARGS; + ext_meth_def.ml_doc = const_cast<char *>(_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<char *>(_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<char *>(_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<TEMPLATE_TYPENAME T> + 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<std::string,MethodDefExt<T> *> 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<T> *method_definition = new MethodDefExt<T> + ( + 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<T> *method_definition = new MethodDefExt<T> + ( + 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<T> *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<T> *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<T *>(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<T> *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<T *>(this); + + return (self->*meth_def->ext_varargs_function)( args ); + } + + private: + // + // prevent the compiler generating these unwanted functions + // + ExtensionModule( const ExtensionModule<T> & ); //unimplemented + void operator=( const ExtensionModule<T> & ); //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<TEMPLATE_TYPENAME T> + 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<std::string,MethodDefExt<T> *> 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<T> *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<T> *method_definition = new MethodDefExt<T> + ( + 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<T> *method_definition = new MethodDefExt<T> + ( + 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<T *>( self_in_cobject ); + + String name( self_and_name_tuple[1] ); + + method_map_t &mm = methods(); + MethodDefExt<T> *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<T *>( self_in_cobject ); + + String name( self_and_name_tuple[1] ); + + method_map_t &mm = methods(); + MethodDefExt<T> *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<T>& other ); + void operator=( const PythonExtension<T>& rhs ); + }; + + // + // ExtensionObject<T> is an Object that will accept only T's. + // + template<TEMPLATE_TYPENAME T> + class ExtensionObject: public Object + { + public: + + explicit ExtensionObject ( PyObject *pyob ) + : Object( pyob ) + { + validate(); + } + + ExtensionObject( const ExtensionObject<T>& 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<T *>( 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 <windows.h> + + +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 <Python.h> + +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 @@ +<html> + +<head> +<title>Legal Notice</title> +<meta name="GENERATOR" content="Microsoft FrontPage 3.0"> +</head> + +<body> + +<h1>Legal Notice</h1> + +<p>*** Legal Notice for all LLNL-contributed files *** </p> + +<p>Copyright (c) 1996. The Regents of the University of California. All rights reserved. </p> + +<p>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. </p> + +<p>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. </p> + +<h2>DISCLAIMER </h2> + +<p>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.</p> +</body> +</html> 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 <iostream> +#include STR_STREAM +#include <string> +#include <iterator> +#include <utility> +#include <typeinfo> + +namespace Py + { + typedef int sequence_index_type; // type of an index into a sequence + + // Forward declarations + class Object; + class Type; + template<TEMPLATE_TYPENAME T> class SeqBase; + class String; + class List; + template<TEMPLATE_TYPENAME T> 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<PyObject*>(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<T> or MapBase<T>. + // 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<T> to define an object + // note that it contains PythonExtension<T>::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<T> + 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<char*>(s.c_str())) ? true: false; + } + + Object getAttr (const std::string& s) const + { + return Object (PyObject_GetAttrString (p, const_cast<char*>(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<char*>(s.c_str()), *value) == -1) + throw AttributeError ("getAttr failed."); + } + + void delAttr (const std::string& s) + { + if(PyObject_DelAttrString (p, const_cast<char*>(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<long>(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<T> represents that. + // In basic Python T is always "Object". + + // seqref<T> is what you get if you get elements from a non-const SeqBase<T>. + // Note: seqref<T> could probably be a nested class in SeqBase<T> 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<TEMPLATE_TYPENAME T> + class seqref + { + protected: + SeqBase<T>& s; // the sequence + int offset; // item number + T the_item; // lvalue + public: + + seqref (SeqBase<T>& seq, sequence_index_type j) + : s(seq), offset(j), the_item (s.getItem(j)) + {} + + seqref (const seqref<T>& 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<T>&>(obj)) + , offset( NULL ) + , the_item(s.getItem(offset)) + {} + ~seqref() + {} + + operator T() const + { // rvalue + return the_item; + } + + seqref<T>& operator=(const seqref<T>& rhs) + { //used as lvalue + the_item = rhs.the_item; + s.setItem(offset, the_item); + return *this; + } + + seqref<T>& 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<T> + // ...the base class for all sequence types + + template<TEMPLATE_TYPENAME T> + class SeqBase: public Object + { + public: + // STL definitions + typedef size_t size_type; + typedef seqref<T> reference; + typedef T const_reference; + typedef seqref<T>* 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<T>& c) + { + SeqBase<T> temp = c; + c = ptr(); + set(temp.ptr()); + } + + virtual size_type size () const + { + return PySequence_Length (ptr()); + } + + explicit SeqBase<T> () + :Object(PyTuple_New(0), true) + { + validate(); + } + + explicit SeqBase<T> (PyObject* pyob, bool owned=false) + : Object(pyob, owned) + { + validate(); + } + + SeqBase<T> (const Object& ob): Object(ob) + { + validate(); + } + + // Assignment acquires new ownership of pointer + + SeqBase<T>& operator= (const Object& rhs) + { + return (*this = *rhs); + } + + SeqBase<T>& 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<T> operator[](sequence_index_type index) + { + return seqref<T>(*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<T> repeat (int count) const + { + return SeqBase<T> (PySequence_Repeat (ptr(), count), true); + } + + SeqBase<T> concat (const SeqBase<T>& other) const + { + return SeqBase<T> (PySequence_Concat(ptr(), *other), true); + } + + // more STL compatability + const T front () const + { + return getItem(0); + } + + seqref<T> front() + { + return seqref<T>(this, 0); + } + + const T back () const + { + return getItem(size()-1); + } + + seqref<T> back() + { + return seqref<T>(this, size()-1); + } + + void verify_length(size_type required_size) const + { + if (size() != required_size) + throw IndexError ("Unexpected SeqBase<T> 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<T> length."); + } + + class iterator + : public random_access_iterator_parent(seqref<T>) + { + protected: + friend class SeqBase<T>; + SeqBase<T>* seq; + int count; + + public: + ~iterator () + {} + + iterator () + : seq( 0 ) + , count( 0 ) + {} + + iterator (SeqBase<T>* 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<T> operator*() + { + return seqref<T>(*seq, count); + } + + seqref<T> operator[] (sequence_index_type i) + { + return seqref<T>(*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<T>::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<T>::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<T>; + const SeqBase<T>* seq; + sequence_index_type count; + + public: + ~const_iterator () + {} + + const_iterator () + : seq( 0 ) + , count( 0 ) + {} + + const_iterator (const SeqBase<T>* 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<T>::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<T>::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<Object> Sequence; + + template <TEMPLATE_TYPENAME T> bool operator==(const EXPLICIT_TYPENAME SeqBase<T>::iterator& left, const EXPLICIT_TYPENAME SeqBase<T>::iterator& right); + template <TEMPLATE_TYPENAME T> bool operator!=(const EXPLICIT_TYPENAME SeqBase<T>::iterator& left, const EXPLICIT_TYPENAME SeqBase<T>::iterator& right); + template <TEMPLATE_TYPENAME T> bool operator< (const EXPLICIT_TYPENAME SeqBase<T>::iterator& left, const EXPLICIT_TYPENAME SeqBase<T>::iterator& right); + template <TEMPLATE_TYPENAME T> bool operator> (const EXPLICIT_TYPENAME SeqBase<T>::iterator& left, const EXPLICIT_TYPENAME SeqBase<T>::iterator& right); + template <TEMPLATE_TYPENAME T> bool operator<=(const EXPLICIT_TYPENAME SeqBase<T>::iterator& left, const EXPLICIT_TYPENAME SeqBase<T>::iterator& right); + template <TEMPLATE_TYPENAME T> bool operator>=(const EXPLICIT_TYPENAME SeqBase<T>::iterator& left, const EXPLICIT_TYPENAME SeqBase<T>::iterator& right); + + template <TEMPLATE_TYPENAME T> bool operator==(const EXPLICIT_TYPENAME SeqBase<T>::const_iterator& left, const EXPLICIT_TYPENAME SeqBase<T>::const_iterator& right); + template <TEMPLATE_TYPENAME T> bool operator!=(const EXPLICIT_TYPENAME SeqBase<T>::const_iterator& left, const EXPLICIT_TYPENAME SeqBase<T>::const_iterator& right); + template <TEMPLATE_TYPENAME T> bool operator< (const EXPLICIT_TYPENAME SeqBase<T>::const_iterator& left, const EXPLICIT_TYPENAME SeqBase<T>::const_iterator& right); + template <TEMPLATE_TYPENAME T> bool operator> (const EXPLICIT_TYPENAME SeqBase<T>::const_iterator& left, const EXPLICIT_TYPENAME SeqBase<T>::const_iterator& right); + template <TEMPLATE_TYPENAME T> bool operator<=(const EXPLICIT_TYPENAME SeqBase<T>::const_iterator& left, const EXPLICIT_TYPENAME SeqBase<T>::const_iterator& right); + template <TEMPLATE_TYPENAME T> bool operator>=(const EXPLICIT_TYPENAME SeqBase<T>::const_iterator& left, const EXPLICIT_TYPENAME SeqBase<T>::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<Py_UNICODE> 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<char*>(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<char*>(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<Py_UNICODE*>(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<Char> + { + public: + virtual size_type capacity() const + { + return max_size(); + } + + explicit String (PyObject *pyob, bool owned = false): SeqBase<Char>(pyob, owned) + { + validate(); + } + + String (const Object& ob): SeqBase<Char>(ob) + { + validate(); + } + + String() + : SeqBase<Char>( PyString_FromStringAndSize( "", 0 ), true ) + { + validate(); + } + + String( const std::string& v ) + : SeqBase<Char>( PyString_FromStringAndSize( const_cast<char*>(v.data()), + static_cast<int>( v.length() ) ), true ) + { + validate(); + } + + String( const char *s, const char *encoding, const char *error="strict" ) + : SeqBase<Char>( PyUnicode_Decode( s, strlen( s ), encoding, error ), true ) + { + validate(); + } + + String( const char *s, int len, const char *encoding, const char *error="strict" ) + : SeqBase<Char>( PyUnicode_Decode( s, len, encoding, error ), true ) + { + validate(); + } + + String( const std::string &s, const char *encoding, const char *error="strict" ) + : SeqBase<Char>( PyUnicode_Decode( s.c_str(), s.length(), encoding, error ), true ) + { + validate(); + } + + String( const std::string& v, std::string::size_type vsize ) + : SeqBase<Char>(PyString_FromStringAndSize( const_cast<char*>(v.data()), + static_cast<int>( vsize ) ), true) + { + validate(); + } + + String( const char *v, int vsize ) + : SeqBase<Char>(PyString_FromStringAndSize( const_cast<char*>(v), vsize ), true ) + { + validate(); + } + + String( const char* v ) + : SeqBase<Char>( 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<char*>( v.data() ), + static_cast<int>( v.length() ) ), true ); + return *this; + } + String& operator= (const unicodestring& v) + { + set( PyUnicode_FromUnicode( const_cast<Py_UNICODE*>( v.data() ), + static_cast<int>( 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<size_type>( PyUnicode_GET_SIZE (ptr()) ); + } + else + { + return static_cast<size_type>( 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<size_type>( PyString_Size( ptr() ) ) ); + } + } + + unicodestring as_unicodestring() const + { + if( isUnicode() ) + { + return unicodestring( PyUnicode_AS_UNICODE( ptr() ), + static_cast<size_type>( 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<TEMPLATE_TYPENAME T> + class mapref + { + protected: + MapBase<T>& s; // the map + Object key; // item key + T the_item; + + public: + mapref<T> (MapBase<T>& map, const std::string& k) + : s(map), the_item() + { + key = String(k); + if(map.hasKey(key)) the_item = map.getItem(key); + }; + + mapref<T> (MapBase<T>& map, const Object& k) + : s(map), key(k), the_item() + { + if(map.hasKey(key)) the_item = map.getItem(key); + }; + + ~mapref() + {} + + // MapBase<T> stuff + // lvalue + mapref<T>& operator=(const mapref<T>& other) + { + if(this == &other) return *this; + the_item = other.the_item; + s.setItem(key, other.the_item); + return *this; + }; + + mapref<T>& 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<T> + template< class T > + bool operator==(const mapref<T>& left, const mapref<T>& right) + { + return true; // NOT completed. + } + + template< class T > + bool operator!=(const mapref<T>& left, const mapref<T>& right) + { + return true; // not completed. + } + + template<TEMPLATE_TYPENAME T> + class MapBase: public Object + { + protected: + explicit MapBase<T>() + {} + 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<T> data_type; + typedef std::pair< const T, T > value_type; + typedef std::pair< const T, mapref<T> > reference; + typedef const std::pair< const T, const T > const_reference; + typedef std::pair< const T, mapref<T> > pointer; + + // Constructor + explicit MapBase<T> (PyObject *pyob, bool owned = false): Object(pyob, owned) + { + validate(); + } + + // TMM: 02Jul'01 - changed MapBase<T> to Object in next line + MapBase<T> (const Object& ob): Object(ob) + { + validate(); + } + + // Assignment acquires new ownership of pointer + MapBase<T>& operator= (const Object& rhs) + { + return (*this = *rhs); + } + + MapBase<T>& 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<T> operator[](const std::string& key) + { + return mapref<T>(*this, key); + } + + mapref<T> operator[](const Object& key) + { + return mapref<T>(*this, key); + } + + int length () const + { + return PyMapping_Length (ptr()); + } + + bool hasKey (const std::string& s) const + { + return PyMapping_HasKeyString (ptr(),const_cast<char*>(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<char*>(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<char*>(s), *ob) == -1) + { + throw Exception(); + } + } + + virtual void setItem (const std::string& s, const Object& ob) + { + if (PyMapping_SetItemString (ptr(), const_cast<char*>(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<char*>(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<T> + // 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<T>* rather than a MapBase<T> 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<const T,T> ) { + 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<T> > pointer; + typedef std::pair< const T, mapref<T> > reference; + + friend class MapBase<T>; + // + MapBase<T>* map; + List keys; // for iterating over the map + List::iterator pos; // index into the keys + + public: + ~iterator () + {} + + iterator () + : map( 0 ) + , keys() + , pos() + {} + + iterator (MapBase<T>* 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<T>(*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<T>::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<T>; + const MapBase<T>* 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<T>* 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<T>::const_iterator + + const_iterator begin () const + { + return const_iterator(this, 0); + } + + const_iterator end () const + { + return const_iterator(this, length()); + } + + }; // end of MapBase<T> + + typedef MapBase<Object> Mapping; + + template <TEMPLATE_TYPENAME T> bool operator==(const EXPLICIT_TYPENAME MapBase<T>::iterator& left, const EXPLICIT_TYPENAME MapBase<T>::iterator& right); + template <TEMPLATE_TYPENAME T> bool operator!=(const EXPLICIT_TYPENAME MapBase<T>::iterator& left, const EXPLICIT_TYPENAME MapBase<T>::iterator& right); + template <TEMPLATE_TYPENAME T> bool operator==(const EXPLICIT_TYPENAME MapBase<T>::const_iterator& left, const EXPLICIT_TYPENAME MapBase<T>::const_iterator& right); + template <TEMPLATE_TYPENAME T> bool operator!=(const EXPLICIT_TYPENAME MapBase<T>::const_iterator& left, const EXPLICIT_TYPENAME MapBase<T>::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<char *>(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<Object,Object> 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<Object,Object>(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<TEMPLATE_TYPENAME T> +String seqref<T>::str () const + { + return the_item.str(); + } + +template<TEMPLATE_TYPENAME T> +String seqref<T>::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 @@ +<html> + +<head> +<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> +<title>Writing Python Extensions in C++</title> +<style> +H1, H2, H3, H4 {color: #000099; + background-color: lightskyblue} +h3 {position: relative; left: 20px} + +p {position: relative; left: 20px; margin-right: 20px} +pre {color: #0000cc; background-color: #eeeeee; position: relative; left: 40px; margin-right: 80px; + border-style: solid; border-color: black; border-width: thin} +kbd {color: #990000} +p cite, ol cite, ul cite {font-family: monospace; font-style: normal; font-size: normal} +li var, pre var, p var, kbd var {color: #009900; font-style: italic} +li samp, pre samp, p samp, kbd samp {color: #009900; font-weight: bold} +li p {position: relative; left: 0} +table { position: relative; left: 20px; border: solid #888888 1px; background-color: #eeeeee} +table th {border: solid #888888 1px; background-color: #88dd88; color: black} +table td {border: solid #888888 1px} +table td.code {border: solid #888888 1px;font-family: monospace; font-style: normal; font-size: normal} +p.param {background-color: #eeeeee; border-top: lightskyblue solid 4} +</style> + +</head> + +<body bgcolor="#FFFFFF"> + +<h1 ALIGN="center">Writing Python Extensions in C++</h1> + +<p ALIGN="CENTER">Barry Scott<br> +Reading, Berkshire, England<br> +<a href="mailto:barry@barrys-emacs.org">barry@barrys-emacs.org</a><br> +</p> + +<p ALIGN="CENTER">Paul F. Dubois, <a href="mailto:dubois1@llnl.gov">dubois1@llnl.gov</a><br> +Lawrence Livermore National Laboratory<br> +Livermore, California, U.S.A.</p> + + +<p>PyCXX is designed to make it easier to extend Python with C++</p> + + +<p>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: </p> + +<ul> + <li>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. + <li>The Standard Template Library (STL) and its many algorithms plug and play with Python + containers such as lists and tuples. + <li>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. +</ul> + +<h3>Download and Installation</h3> + +<p>Download PyCXX from <a href="http://sourceforge.net/projects/cxx/">http://sourceforge.net/projects/cxx/</a>.</p> + +<p>The distribution layout is:</p> +<table> +<tr><th>Directory</th><th>Description</th></tr> +<tr><td class=code>.</td><td>Makefile for Unix and Windows, Release documentation</td> +<tr><td class=code>./CXX</td><td>Header files</td> +<tr><td class=code>./Src</td><td>Source files</td> +<tr><td class=code>./Doc</td><td>Documentation</td> +<tr><td class=code>./Demo</td><td>Testing and Demonstartion files</td> +</table> + +<p>To use PyCXX you use its include files and add its source routines to your module.</p> + +<p>Installation:</p> +<ul> +<li>Install the PyCXX files into a directory of your choice. For example:<br> +Windows: <cite>C:\PyCXX</cite><br> +Unix: <cite>/usr/local/PyCXX</cite> +<li>Tell your compiler where the PyCXX header files are:<br> +Windows: <cite>cl /I=C:\PyCXX ...</cite><br> +Unix: <cite>g++ -I/usr/local/PyCXX ...</cite> +<li>Include PyCXX headers files in your code using the CXX prefix:<br> +<cite>#include "CXX/Object.hxx"</cite> +</ul> + +<p>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. </p> + +<h3>Use of namespaces</h3> + +<p>All PyCXX assets are in namespace "Py". You need to include +the Py:: prefix when referring to them, or include the statement:</p> + +<p>using namespace Py;</p> + +<h2>Wrappers for standard objects: CXX_Objects.h</h2> + +<p>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:</p> + +<pre>d = {} +d["a"] = 1 +d["b"] = 2 +alist = d.keys() +print alist</pre> + +<p>Can be written in C++:</p> + +<pre>Dict d; +List alist; +d["a"] = Int(1); +d["b"] = Int(2); +alist = d.keys(); +std::cout << alist << std::endl; +</pre> + +<p>You can optionally use the CXX/Extensions.hxx facility described later +to define Python extension modules and extension objects.</p> + +<h3>We avoid programming with Python object pointers</h3> + +<p>The essential idea is that we avoid, as much as possible, programming with pointers to +Python objects, that is, variables of type <cite>PyObject*</cite>. 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.</p> + +<p>For example, consider the case in which we wish to write a method, taking a single +integer argument, that will create a Python <cite>dict</cite> + and insert into it that the integer plus one under the key <cite>value</cite>. + In C we might do that as follows:</p> + +<pre>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; + }</pre> + +<p>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.</p> + +<pre>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; + } + }</pre> + +<p>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.</p> + +<p>Note that the creation of the <cite>Int k</cite> got the first argument <em>and</em> verified +that it is an <cite>Int</cite>.</p> + +<p>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:</p> + +<pre> +Object addvalue (Object & self, const Tuple & args) + { + args.verify_length(1); + Dict d; + Int k = args[0]; + d["value"] = k + 1; + return d; + } +</pre> + +<h2>The basic concept is to wrap Python pointers</h2> + + +<p>The basic concept of CXX/Objects.hxx is to create a wrapper around +each <cite>PyObject *</cite> 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.</p> + +<p>Each <cite>Object</cite> contains a <cite>PyObject *</cite> +to which it owns a reference. When an <cite>Object</cite> 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.</p> + +<p>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.</p> + +<p>Class <cite>Object</cite> represents the most general kind of Python object. The rest of the classes +that represent Python objects inherit from it.</p> + +<pre>Object + Type + Int + Float + Long + Complex + Char + Sequence -> SeqBase<T> + String + Tuple + List + Mapping -> MapBase<T> + Dict + Callable + Module</pre> + +<p>There are several constructors for each of these classes. For example, you can create +an <cite>Int</cite> from an integer as in</p> + +<pre>Int s(3)</pre> + +<p>However, you can also create an instance of one of these classes using any <cite>PyObject*</cite> or +another <cite>Object</cite>. If the corresponding Python object does not actually have the type +desired, an exception is thrown. This is accomplished as follows. Class <cite>Object</cite> defines a +virtual function <cite>accepts</cite>:</p> + +<pre>virtual bool accepts(PyObject* p)</pre> + +<p>The base class version of <cite>accepts</cite> returns true for any pointer p except 0. This means +we can create an Object using any <cite>PyObject *</cite>, or from any other +<cite>Object</cite>. However, if we attempt to create an <cite>Int</cite> from a <cite>PyObject *</cite>, +the overridding version +of <cite>accepts</cite> in class <cite>Int</cite> will only accept pointers that correspond to Python ints. +Therefore if we have a <cite>Tuple t</cite> and we wish to get the first element and be sure it is an +<cite>Int</cite>, we do</p> + +<pre>Int first_element = t[0]</pre> + +<p>This will not only accomplish the goal of extracting the first element of the <cite>Tuple t</cite>, +but it will ensure that the result is an <cite>Int</cite>. If not, an exception is thrown. The +exception mechanism is discussed later.</p> + +<h2>Class Object</h2> + +<p>Class <cite>Object</cite> serves as the base class for the other classes. Its default constructor +constructs a <cite>Py_None</cite>, the unique object of Python type <cite>None</cite>. The interface to <cite>Object</cite> +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.</p> + +<p>There is no method corresponding to <cite>PyObject_SetItem</cite> with an arbitrary Python object +as a key. Instead, create an instance of a more specific child of <cite>Object</cite> and use the +appropriate facilities.</p> + +<p>The comparison operators use the Python comparison function to compare values. The +method <cite>is</cite> is available to test for absolute identity.</p> + +<p>A conversion to standard library string type <cite>std::string</cite> is supplied using method +<cite>as_string</cite>. Stream output of PyCXX <cite>Object</cite> instances uses this conversion, +which in turn uses the Python object's str() representation.</p> + +<p>All the numeric operators are defined on all possible combinations of <cite>Object</cite>, +<cite>long</cite>, and <cite>double</cite>. These use the corresponding Python operators, +and should the operation fail for some reason, an exception is thrown.</p> + +<h3>Dealing with pointers returned by the Python C API</h3> + +<p>Often, <cite>PyObject *</cite> 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 <i>owned</i> +or <i>unowned</i> reference. Unowned references are unusual but there are some cases where +unowned references are returned.</p> + +<p>Usually, <cite>Object</cite> and its children acquire a new reference when constructed from a +<cite>PyObject *</cite>. This is usually not the right behavior if the reference comes from one +of the Python C API calls.</p> + +<p>If p is an owned reference, you can add the boolean <cite>true</cite> as an extra +argument in the creation routine, <cite>Object(p, true)</cite>, or use the function <cite>asObject(p)</cite> which +returns an <cite>Object</cite> created using the owned reference. For example, the routine +<cite>PyString_FromString</cite> returns an owned reference to a Python string object. You could write:</p> + +<pre>Object w = asObject( PyString_FromString("my string") );</pre> + +<p>or using the constructor,</p> + +<pre>Object w( PyString_FromString("my string"), true );</pre> + +<p>In fact, you would never do this, since PyCXX has a class String and you can just say: </p> + +<pre>String w( "my string" );</pre> + +<p>Indeed, since most of the Python C API is similarly embodied in <cite>Object</cite> +and its descendents, you probably will not use asObject all that often.</p> +<h3>Table 1: Class Object</h3> + +<table cellspacing=0 cellpadding=3px width="95%"> + <tr> + <th>Returns</th> + <th>Name(signature)</th> + <th>Comment</th> + </tr> + <tr> + <td colspan="3"><p align="center"><strong>Basic Methods</strong></td> + </tr> + <tr> + <td class=code>explicit </td> + <td class=code>Object (PyObject* pyob=Py_None, bool owned=false) </td> + <td>Construct from pointer. </td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Object (const Object& ob)</td> + <td>Copycons; acquires an owned reference.</td> + </tr> + <tr> + <td class=code>Object&</td> + <td class=code>operator= (const Object& rhs) </td> + <td>Acquires an owned reference.</td> + </tr> + <tr> + <td class=code>Object&</td> + <td class=code>operator= (PyObject* rhsp) </td> + <td>Acquires an owned reference.</td> + </tr> + <tr> + <td class=code>virtual</td> + <td class=code>~Object () </td> + <td>Releases the reference.</td> + </tr> + <tr> + <td class=code>void</td> + <td class=code>increment_reference_count() </td> + <td>Explicitly increment the count</td> + </tr> + <tr> + <td class=code>void</td> + <td class=code>decrement_reference_count()</td> + <td>Explicitly decrement count but not to zero</td> + </tr> + <tr> + <td class=code>PyObject*</td> + <td class=code>operator* () const</td> + <td>Lends the pointer</td> + </tr> + <tr> + <td class=code>PyObject*</td> + <td class=code>ptr () const</td> + <td>Lends the pointer</td> + </tr> + <tr> + <td class=code>virtual bool</td> + <td class=code>accepts (PyObject *pyob) const</td> + <td>Would assignment of pyob to this object succeed?</td> + </tr> + <tr> + <td class=code>std::string</td> + <td class=code>as_string() const</td> + <td>str() representation</td> + </tr> + <tr> + <td colspan="3" align="center"><strong>Python API Interface</strong></td> + </tr> + <tr> + <td class=code>int</td> + <td class=code>reference_count () const </td> + <td>reference count</td> + </tr> + <tr> + <td class=code>Type</td> + <td class=code>type () const</td> + <td>associated type object</td> + </tr> + <tr> + <td class=code>String</td> + <td class=code>str () const</td> + <td>str() representation</td> + </tr> + <tr> + <td class=code>String</td> + <td class=code>repr () const</td> + <td>repr () representation</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>hasAttr (const std::string& s) const</td> + <td>hasattr(this, s)</td> + </tr> + <tr> + <td class=code>Object</td> + <td class=code>getAttr (const std::string& s) const</td> + <td>getattr(this, s)</td> + </tr> + <tr> + <td class=code>Object</td> + <td class=code>getItem (const Object& key) const</td> + <td>getitem(this, key)</td> + </tr> + <tr> + <td class=code>long</td> + <td class=code>hashValue () const</td> + <td>hash(this)</td> + </tr> + <tr> + <td class=code>void</td> + <td class=code>setAttr (const std::string& s,<br>const Object& value)</td> + <td>this.s = value</td> + </tr> + <tr> + <td class=code>void</td> + <td class=code>delAttr (const std::string& s) </td> + <td>del this.s</td> + </tr> + <tr> + <td class=code>void</td> + <td class=code>delItem (const Object& key) </td> + <td>del this[key]</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>isCallable () const</td> + <td>does this have callable behavior?</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>isList () const</td> + <td>is this a Python list?</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>isMapping () const</td> + <td>does this have mapping behaviors?</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>isNumeric () const</td> + <td>does this have numeric behaviors?</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>isSequence () const </td> + <td>does this have sequence behaviors?</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>isTrue () const</td> + <td>is this true in the Python sense?</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>isType (const Type& t) const</td> + <td>is type(this) == t?</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>isTuple() const</td> + <td>is this a Python tuple?</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>isString() const</td> + <td>is this a Python string?</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>isUnicode() const</td> + <td>is this a Python Unicode string?</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>isDict() const</td> + <td>is this a Python dictionary?</td> + </tr> + <tr> + <td colspan="3" align="center"><strong>Comparison Operators</strong></td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>is(PyObject* pother) const</td> + <td>test for identity</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>is(const Object& other) const</td> + <td>test for identity</td> + </tr> + <tr> + <td class=code>bool </td> + <td class=code>operator==(const Object& o2) const</td> + <td>Comparisons use Python cmp</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>operator!=(const Object& o2) const</td> + <td>Comparisons use Python cmp</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>operator>=(const Object& o2) const</td> + <td>Comparisons use Python cmp</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>operator<=(const Object& o2) const </td> + <td>Comparisons use Python cmp</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>operator<(const Object& o2) const</td> + <td>Comparisons use Python cmp</td> + </tr> + <tr> + <td class=code>bool</td> + <td class=code>operator>(const Object& o2) const</td> + <td>Comparisons use Python cmp</td> + </tr> +</table> + +<h1>The Basic Types</h1> + +<p>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. </p> + +<h2>Class Type</h2> + +<p>Class Type corresponds to Python type objects. There is no default constructor.</p> + +<h3>Table 2: class Type</h3> + +<table cellspacing=0 cellpadding=3px width="95%"> + <tr> + <th>Returns</th> + <th>Name and Signature</th> + <th>Comments</th> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Type (PyObject* pyob, bool owned = false)</td> + <td>Constructor</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Type (const Object& ob)</td> + <td>Constructor</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Type(const Type& t)</td> + <td>Copycons</td> + </tr> + <tr> + <td class=code>Type&</td> + <td class=code>operator= (const Object& rhs) </td> + <td>Assignment</td> + </tr> + <tr> + <td class=code>Type&</td> + <td class=code>operator= (PyObject* rhsp) </td> + <td>Assignment</td> + </tr> + <tr> + <td class=code>virtual bool</td> + <td class=code><u>accepts</u> (PyObject *pyob) const</td> + <td>Uses PyType_Check</td> + </tr> +</table> + +<h2>Class Int</h2> + +<p>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.</p> + +<h3>Table 3: class Int</h3> + + +<table cellspacing=0 cellpadding=3px width="95%"> + <tr> + <th>Returns</td> + <th>Name and Signature</td> + <th>Comments</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Int (PyObject *pyob, bool owned= false, bool owned = false)</td> + <td>Constructor</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Int (const Int& ob)</td> + <td>Constructor</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Int (long v = 0L)</td> + <td>Construct from long</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Int (int v)</td> + <td>Contruct from int</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Int (const Object& ob)</td> + <td>Copycons</td> + </tr> + <tr> + <td class=code>Int&</td> + <td class=code>operator= (const Object& rhs)</td> + <td>Assignment</td> + </tr> + <tr> + <td class=code>Int&</td> + <td class=code>operator= (PyObject* rhsp)</td> + <td>Assignment</td> + </tr> + <tr> + <td class=code>virtual bool </td> + <td class=code> (PyObject *pyob) const </td> + <td>Based on PyInt_Check</td> + </tr> + <tr> + <td class=code>long</td> + <td class=code>operator long() const </td> + <td><em>Implicit</em> conversion to long int</td> + </tr> + <tr> + <td class=code>Int&</td> + <td class=code>operator= (int v)</td> + <td>Assign from int</td> + </tr> + <tr> + <td class=code>Int&</td> + <td class=code>operator= (long v) </td> + <td>Assign from long</td> + </tr> +</table> + +<hr> + +<h2>Class Long</h2> + +<p>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.</p> + +<h3>Table 4: Class Long</h3> + +<table cellspacing=0 cellpadding=3px width="95%"> + <tr> + <th>Returns</td> + <th>Name and Signature</td> + <th>Comments</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Long (PyObject *pyob</a>, bool owned = false)</td> + <td>Constructor</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Long (const Int& ob)</td> + <td>Constructor</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Long (long v = 0L)</td> + <td>Construct from long</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Long (int v)</td> + <td>Contruct from int</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Long (const Object& ob)</td> + <td>Copycons</td> + </tr> + <tr> + <td class=code>Long&</td> + <td class=code>operator= (const Object& rhs)</td> + <td>Assignment</td> + </tr> + <tr> + <td class=code>Long&</td> + <td class=code>operator= (PyObject* rhsp)</td> + <td>Assignment</td> + </tr> + <tr> + <td class=code>virtual bool</td> + <td class=code>(PyObject *pyob) const </td> + <td>Based on PyLong_Check</td> + </tr> + <tr> + <td class=code>double</td> + <td class=code>operator double() const </td> + <td><em>Implicit</em> conversion to double</td> + </tr> + <tr> + <td class=code>long</td> + <td class=code>operator long() const</td> + <td><em>Implicit</em> conversion to long</td> + </tr> + <tr> + <td class=code>Long&</td> + <td class=code>operator= (int v)</td> + <td>Assign from int</td> + </tr> + <tr> + <td class=code>Long&</td> + <td class=code>operator= (long v) </td> + <td>Assign from long</td> + </tr> +</table> + +<h2>Class Float</h2> + +<p>Class Float corresponds to Python floats, which in turn correspond to C double. The +default constructor produces the Python float 0.0. </p> + +<h3>Table 5: Class Float</h3> + +<table cellspacing=0 cellpadding=3px width="95%"> + <tr> + <th>Returns</td> + <th>Name and Signature</td> + <th>Comments</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Float (PyObject *pyob</a>, bool owned = false) + </td> + <td>Constructor</td> + </tr> + <tr> + <td class=code></td> + <td class=code>Float (const Float& f) </td> + <td>Construct from float</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Float (double v=0.0)</td> + <td>Construct from double</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Float (const Object& ob)</td> + <td>Copycons</td> + </tr> + <tr> + <td class=code>Float&</td> + <td class=code>operator= (const Object& rhs)</td> + <td>Assignment</td> + </tr> + <tr> + <td class=code>Float&</td> + <td class=code>operator= (PyObject* rhsp)</td> + <td>Assignment</td> + </tr> + <tr> + <td class=code>virtual bool </td> + <td class=code>accepts (PyObject *pyob) const</td> + <td>Based on PyFloat_Check</td> + </tr> + <tr> + <td class=code>double </td> + <td class=code>operator double () const</td> + <td><em>Implicit</em> conversion to double</td> + </tr> + <tr> + <td class=code>Float& </td> + <td class=code>operator= (double v)</td> + <td>Assign from double</td> + </tr> + <tr> + <td class=code>Float& </td> + <td class=code>operator= (int v)</td> + <td>Assign from int</td> + </tr> + <tr> + <td class=code>Float& </td> + <td class=code>operator= (long v)</td> + <td>Assign from long</td> + </tr> + <tr> + <td class=code>Float& </td> + <td class=code>operator= (const Int& iob)</td> + <td>Assign from Int</td> + </tr> +</table> + +<h1>Sequences</h1> + +<p>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.</p> + +<p>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.</p> + +<p>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.</p> + +<p>For convenience, the word <strong>Sequence</strong> is a typedef of SeqBase<Object>.</p> + +<h2>General sequences</h2> + +<p>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:</p> + +<pre>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; + } +}</pre> + +<p>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.</p> + +<h3>Subscripting</h3> + +<p>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. </p> + +<p>In normal use, you are not supposed to notice this magic going on behind your back. You +write:</p> + +<pre>Object t; +Sequence s; +s[2] = t + s[1]</pre> + +<p>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.</p> + +<p>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.</p> + +<h3>Iterators</h3> + +<p>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.</p> + +<h3>The SeqBase<T> Interface</h3> + +<p>SeqBase<T> inherits from Object.</p> + +<table cellspacing=0 cellpadding=3px width="95%"> + <tr> + <th>Type</td> + <th>Name</td> + </tr> + <tr> + <td class=code>typedef int </td> + <td class=code>size_type</td> + </tr> + <tr> + <td class=code>typedef seqref<T></td> + <td class=code>reference</td> + </tr> + <tr> + <td class=code>typedef T </td> + <td class=code>const_reference</td> + </tr> + <tr> + <td class=code>typedef seqref<T>*</td> + <td class=code>pointer</td> + </tr> + <tr> + <td class=code>typedef int </td> + <td class=code>difference_type</td> + </tr> + <tr> + <td class=code>virtual size_type</td> + <td class=code>max_size() const</td> + </tr> + <tr> + <td class=code>virtual size_type </td> + <td class=code>capacity() const;</td> + </tr> + <tr> + <td class=code>virtual void </td> + <td class=code>swap(SeqBase<T>& c);</td> + </tr> + <tr> + <td class=code>virtual size_type </td> + <td class=code>size () const;</td> + </tr> + <tr> + <td class=code>explicit </td> + <td class=code>SeqBase<T> ();</td> + </tr> + <tr> + <td class=code>explicit </td> + <td class=code>SeqBase<T> (PyObject* pyob, bool owned = false);</td> + </tr> + <tr> + <td class=code>explicit </td> + <td class=code>SeqBase<T> (const Object& ob);</td> + </tr> + <tr> + <td class=code>SeqBase<T>& </td> + <td class=code>operator= (const Object& rhs);</td> + </tr> + <tr> + <td class=code>SeqBase<T>& </td> + <td class=code>operator= (PyObject* rhsp);</td> + </tr> + <tr> + <td class=code>virtual bool </td> + <td class=code>accepts (PyObject *pyob) const;</td> + </tr> + <tr> + <td class=code>size_type </td> + <td class=code>length () const ;</td> + </tr> + <tr> + <td class=code>const T </td> + <td class=code>operator[](size_type index) const; </td> + </tr> + <tr> + <td class=code>seqref<T> </td> + <td class=code>operator[](size_type index); </td> + </tr> + <tr> + <td class=code>virtual T </td> + <td class=code>getItem (size_type i) const;</td> + </tr> + <tr> + <td class=code>virtual void </td> + <td class=code>setItem (size_type i, const T& ob);</td> + </tr> + <tr> + <td class=code>SeqBase<T> </td> + <td class=code>repeat (int count) const;</td> + </tr> + <tr> + <td class=code>SeqBase<T> </td> + <td class=code>concat (const SeqBase<T>& other) const ;</td> + </tr> + <tr> + <td class=code>const T </td> + <td class=code>front () const;</td> + </tr> + <tr> + <td class=code>seqref<T> </td> + <td class=code>front();</td> + </tr> + <tr> + <td class=code>const T </td> + <td class=code>back () const;</td> + </tr> + <tr> + <td class=code>seqref<T> </td> + <td class=code>back(); </td> + </tr> + <tr> + <td class=code>void </td> + <td class=code>verify_length(size_type required_size);</td> + </tr> + <tr> + <td class=code>void </td> + <td class=code>verify_length(size_type min_size, size_type max_size);</td> + </tr> + <tr> + <td class=code>class</td> + <td class=code>iterator;</td> + </tr> + <tr> + <td class=code>iterator </td> + <td class=code>begin (); </td> + </tr> + <tr> + <td class=code>iterator </td> + <td class=code>end ();</td> + </tr> + <tr> + <td class=code>class </td> + <td class=code>const_iterator;</td> + </tr> + <tr> + <td class=code>const_iterator </td> + <td class=code>begin () const;</td> + </tr> + <tr> + <td class=code>const_iterator </td> + <td class=code>end () const;</td> + </tr> +</table> + +<p>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. </p> + +<h2>Classes Char and String</h2> + +<p>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.</p> + +<p>The user interface for Char is limited. Unlike String, for example, it is not a +sequence.</p> + +<h3>The Char interface</h3> + +<p>Char inherits from Object.</p> + +<table cellspacing=0 cellpadding=3px width="95%"> + <tr> + <th>Type</td> + <th>Name</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Char (PyObject *pyob, bool owned = false)</td> + </tr> + <tr> + <td class=code></td> + <td class=code>Char (const Object& ob) </td> + </tr> + <tr> + <td class=code></td> + <td class=code>Char (const std::string& v = "") </td> + </tr> + <tr> + <td class=code></td> + <td class=code>Char (char v)</td> + </tr> + <tr> + <td class=code></td> + <td class=code>Char (Py_UNICODE v)</td> + </tr> + <tr> + <td class=code>Char&</td> + <td class=code>operator= (const std::string& v)</td> + </tr> + <tr> + <td class=code>Char&</td> + <td class=code>operator= (char v) </td> + </tr> + <tr> + <td class=code>Char&</td> + <td class=code>operator= (Py_UNICODE v) </td> + </tr> + <tr> + <td class=code>Char&</td> + <td class=code>operator= (std::basic_string<Py_UNICODE> v) </td> + </tr> + <tr> + <td class=code></td> + <td class=code>operator String() const</td> + </tr> + <tr> + <td class=code></td> + <td class=code>operator std::string () const </td> + </tr> +</table> + +<h3>The String Interface</h3> + +<p>String inherits from SeqBase<Char>.</p> + +<table cellspacing=0 cellpadding=3px width="95%"> + <tr> + <th>Type</td> + <th>Name</td> + </tr> + <tr> + <td class=code>explicit </td> + <td class=code>String (PyObject *pyob, bool owned = false)</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>String (const Object& ob)</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>String (const std::string& v = "")</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>String (const std::string& v, const char *encoding, const char *error="strict")</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>String (const char *s, const char *encoding, const char *error="strict")</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>String (const char *s, int len, const char *encoding, const char *error="strict")</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>String (const std::string& v, std::string::size_type vsize)</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>String (const char* v)</td> + </tr> + <tr> + <td class=code>String&</td> + <td class=code>operator= (const std::string& v) </td> + </tr> + <tr> + <td class=code>std::string</td> + <td class=code>operator std::string () const</td> + </tr> + <tr> + <td class=code>String</td> + <td class=code>encode( const char *encoding, const char *error="strict" )</td> + </tr> + <tr> + <td class=code>String</td> + <td class=code>decode( const char *encoding, const char *error="strict" )</td> + </tr> + <tr> + <td class=code>std::string</td> + <td class=code>as_std_string() const</td> + </tr> + <tr> + <td class=code>unicodestring</td> + <td class=code>as_unicodestring() const</td> + </tr> +</table> + +<h2>Class Tuple</h2> + +<p>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. </p> + +<p>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.</p> + +<p>Example: create a Tuple containing (1, 2, 4)</p> + +<pre>Tuple t(3) +t[0] = Int(1) +t[1] = Int(2) +t[2] = Int(4)</pre> + +<p>Example: create a Tuple from a list:</p> + +<pre>Dict d +... +Tuple t(d.keys())</pre> + +<h3>The Tuple Interface</h3> + +<p>Tuple inherits from Sequence.. Special run-time checks prevent modification if the +reference count is greater than one.</p> + +<table cellspacing=0 cellpadding=3px width="95%"> + <tr> + <th>Type</td> + <th>Name</td> + <th>Comment</td> + </tr> + <tr> + <td class=code>virtual void</td> + <td class=code>setItem (int offset, const Object&ob) </td> + <td>setItem is overriden to handle tuples properly. </td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Tuple (PyObject *pyob, bool owned = false)</td> + <td></td> + </tr> + <tr> + <td class=code> </td> + <td class=code>Tuple (const Object& ob)</td> + <td></td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Tuple (int size = 0)</td> + <td>Create a tuple of the given size. Items initialize to Py_None. Default is an empty + tuple.</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Tuple (const Sequence& s)</td> + <td>Create a tuple from any sequence.</td> + </tr> + <tr> + <td class=code>Tuple&</td> + <td class=code>operator= (const Object& rhs)</td> + <td></td> + </tr> + <tr> + <td class=code>Tuple&</td> + <td class=code>operator= (PyObject* rhsp)</td> + <td></td> + </tr> + <tr> + <td class=code>Tuple</td> + <td class=code>getSlice (int i, int j) const </td> + <td>Equivalent to python's t[i:j]</td> + </tr> +</table> + +<h2>Class List</h2> + +<p>Class List represents a Python list, and the methods available faithfully reproduce the +Python API for lists. A List is a Sequence.</p> + +<h3>The List Interface</h3> + +<p>List inherits from Sequence.</p> + +<table cellspacing=0 cellpadding=3px width="95%"> + <tr> + <th>Type</td> + <th>Name</td> + <th>Comment</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>List (PyObject *pyob, bool owned = false)</td> + <td></td> + </tr> + <tr> + <td class=code> </td> + <td class=code>List (const Object& ob)</td> + <td></td> + </tr> + <tr> + <td class=code> </td> + <td class=code>List (int size = 0)</td> + <td>Create a list of the given size. Items initialized to Py_None. Default is an empty list.</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>List (const Sequence& s)</td> + <td>Create a list from any sequence.</td> + </tr> + <tr> + <td class=code>List&</td> + <td class=code>operator= (const Object& rhs)</td> + <td></td> + </tr> + <tr> + <td class=code>List&</td> + <td class=code>operator= (PyObject* rhsp)</td> + <td></td> + </tr> + <tr> + <td class=code>List</td> + <td class=code>getSlice (int i, int j) const</td> + <td></td> + </tr> + <tr> + <td class=code>void</td> + <td class=code>setSlice (int i, int j, const Object& v) </td> + <td></td> + </tr> + <tr> + <td class=code>void</td> + <td class=code>append (const Object& ob)</td> + <td></td> + </tr> + <tr> + <td class=code>void</td> + <td class=code>insert (int i, const Object& ob)</td> + <td></td> + </tr> + <tr> + <td class=code>void</td> + <td class=code>sort ()</td> + <td>Sorts the list in place, using Python's member function. You can also use + the STL sort function on any List instance.</td> + </tr> + <tr> + <td class=code>void</td> + <td class=code>reverse ()</td> + <td>Reverses the list in place, using Python's member function.</td> + </tr> +</table> + +<h1>Mappings</h1> + +<p>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.</p> + +<p>For convenience, <cite>Mapping</cite> is a typedef for <cite>MapBase<Object></cite>.</p> + +<h3>The MapBase<T> interface</h3> + +<p>MapBase<T> inherits from Object. T should be chosen to reflect the kind of +element returned by the mapping.</p> + +<table cellspacing=0 cellpadding=3px width="95%"> + <tr> + <th>Type</td> + <th>Name</td> + <th>Comment</td> + </tr> + <tr> + <td class=code>T</td> + <td class=code>operator[](const std::string& key) const</td> + <td></td> + </tr> + <tr> + <td class=code>mapref<T> </td> + <td class=code>operator[](const std::string& key)</td> + <td></td> + </tr> + <tr> + <td class=code>int</td> + <td class=code>length () const</td> + <td>Number of entries.</td> + </tr> + <tr> + <td class=code>int</td> + <td class=code>hasKey (const std::string& s) const </td> + <td>Is m[s] defined?</td> + </tr> + <tr> + <td class=code>T</td> + <td class=code>getItem (const std::string& s) const</td> + <td>m[s]</td> + </tr> + <tr> + <td class=code>virtual void</td> + <td class=code>setItem (const std::string& s, const Object& ob)</td> + <td>m[s] = ob</td> + </tr> + <tr> + <td class=code>void</td> + <td class=code>delItem (const std::string& s)</td> + <td>del m[s]</td> + </tr> + <tr> + <td class=code>void</td> + <td class=code>delItem (const Object& s)</td> + <td></td> + </tr> + <tr> + <td class=code>List</td> + <td class=code>keys () const</td> + <td>A list of the keys.</td> + </tr> + <tr> + <td class=code>List</td> + <td class=code>values () const</td> + <td>A list of the values.</td> + </tr> + <tr> + <td class=code>List</td> + <td class=code>items () const</td> + <td>Each item is a key-value pair.</td> + </tr> +</table> + +<h2>Class Dict</h2> + +<p>Class Dict represents Python dictionarys. A Dict is a Mapping. Assignment to +subscripts can be used to set the components.</p> + +<pre>Dict d +d["Paul Dubois"] = "(925)-422-5426"</pre> + +<h3>Interface for Class Dict</h3> + +<p>Dict inherits from MapBase<Object>.</p> + +<table cellspacing=0 cellpadding=3px width="95%"> + <tr> + <th>Type</td> + <th>Name</td> + <th>Comment</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Dict (PyObject *pyob</a>, bool owned = false)</td> + <td></td> + </tr> + <tr> + <td class=code> </td> + <td class=code>Dict (const Dict& ob)</td> + <td></td> + </tr> + <tr> + <td class=code> </td> + <td class=code>Dict () </td> + <td>Creates an empty dictionary</td> + </tr> + <tr> + <td class=code>Dict&</td> + <td class=code>operator= (const Object& rhs)</td> + <td></td> + </tr> + <tr> + <td class=code>Dict&</td> + <td class=code>operator= (PyObject* rhsp)</td> + <td></td> + </tr> +</table> + +<h1>Other classes and facilities.</h1> + +<p>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.</p> + +<h3>Interface to class Callable</h3> + +<table cellspacing=0 cellpadding=3px width="95%"> + <tr> + <th>Type</td> + <th>Name</td> + <th>Comment</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Callable (PyObject *pyob</a>, bool owned = false)</td> + <td></td> + </tr> + <tr> + <td class=code>Callable& </td> + <td class=code>operator= (const Object& rhs)</td> + <td></td> + </tr> + <tr> + <td class=code>Callable& </td> + <td class=code>operator= (PyObject* rhsp)</td> + <td></td> + </tr> + <tr> + <td class=code>Object</td> + <td class=code>apply(const Tuple& args) const</td> + <td>Call the object with the given arguments</td> + </tr> + <tr> + <td class=code>Object</td> + <td class=code>apply(PyObject* pargs = 0) const </td> + <td>Call the object with args as the arguments. Checks that pargs is a tuple.</td> + </tr> +</table> + +<h3>Interface to class Module</h3> + +<table cellspacing=0 cellpadding=3px width="95%"> + <tr> + <th>Type</td> + <th>Name</td> + <th>Comment</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Module (PyObject* pyob, bool owned = false)</td> + <td></td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>Module (const std::string name)</td> + <td>Construct from name of module; does the import if needed.</td> + </tr> + <tr> + <td class=code></td> + <td class=code>Module (const Module& ob) </td> + <td>Copy constructor</td> + </tr> + <tr> + <td class=code>Module&</td> + <td class=code>operator= (const Object& rhs) </td> + <td>Assignment</td> + </tr> + <tr> + <td class=code>Module&</td> + <td class=code>operator= (PyObject* rhsp) </td> + <td>Assignment</td> + </tr> +</table> + +<h3>Numeric interface</h3> + +<p>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. </p> + +<p>The signature for coerce is:</p> + +<pre>inline std::pair<Object,Object> coerce(const Object& a, const Object& b)</pre> + +<p>Unlike the C API function, this simply returns the pair after coercion.</p> + +<h3>Stream I/O</h3> + +<p>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&).</p> + +<h2>Exceptions</h2> + +<p>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.</p> + +<h3>Class Exception and its children</h3> + +<p>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:</p> + +<pre>throw IndexError("Index too large in MyObject access.");</pre> + +<p>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.</p> + +<h3>List of Exceptions</h3> + +<p>The exception hierarchy mirrors the Python exception hierarchy. The concrete exception +classes are shown here.</p> + +<table cellspacing=0 cellpadding=3px width="95%"> + <tr> + <th>Type</th> + <th>Interface for class Exception</th> + </tr> + <tr> + <td class=code>explicit </td> + <td class=code>Exception()</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>Exception (const std::string& reason) </td> + </tr> + <tr> + <td class=code> </td> + <td class=code>Exception (PyObject* exception, const std::string& reason) </td> + </tr> + <tr> + <td class=code>void </td> + <td class=code>clear()</td> + </tr> + <tr> + <td class=code></td> + <td>Constructors for other children of class Exception</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>TypeError (const std::string& reason)</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>IndexError (const std::string& reason)</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>AttributeError (const std::string& reason)</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>NameError (const std::string& reason)</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>RuntimeError (const std::string& reason)</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>SystemError (const std::string& reason)</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>KeyError (const std::string& reason)</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>ValueError (const std::string& reason)</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>OverflowError (const std::string& reason)</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>ZeroDivisionError (const std::string& reason)</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>MemoryError (const std::string& reason)</td> + </tr> + <tr> + <td class=code> </td> + <td class=code>SystemExit (const std::string& reason)</td> + </tr> +</table> + +<h2>Using Exceptions in extension methods</h2> + +<p>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. </p> + +<p>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.</p> + +<h3>Catching Exceptions from the Python API or PyCXX.</h3> + +<p>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.</p> + +<pre>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 (); + } +} +</pre> + +<h3>How to clear an Exception</h3> + +<p>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.:</p> + +<pre>catch(Exception& e) + { + e.clear(); + ...now decide what to do about it... + } +</pre> + +<h2>Extension Facilities</h2> + +<p>CXX/Extensions.hxx provides facilities for: + +<ul> + <li>Creating a Python extension module</li> + <li>Creating new Python extension types</li> +</ul> + +<p>These facilities use CXX/Objects.hxx and its support file cxxsupport.cxx.</p> + +<p>If you use CXX/Extensions.hxx you must also include source files cxxextensions.c and +cxx_extensions.cxx</p> + +<h3>Creating an Python extension module</h3> + +<p>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: + +<ul> + <li>The initialization function must be declared to have external C linkage and to have the + expected name. This is a requirement imposed by Python</li> + <li>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.</li> +</ul> + +<p>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):</p> + +<pre>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) { ... } +}; +</pre> + +<p>To initialize the extension, you just instantiate one static instance (static so it +does not destroy itself!):</p> + +<pre>void initexample() +{ +static example_module* example = new example_module; +}</pre> + +<p>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:</p> + +<pre>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; + }</pre> + +<p>class ExtensionModule contains methods to return itself as a Module object, or to +return its dictionary.</p> + +<h3>Interface to class ExtensionModule</h3> + +<table cellspacing=0 cellpadding=3px width="95%"> + <tr> + <th>Type</td> + <th>Name</td> + <th>Comment</td> + </tr> + <tr> + <td class=code>explicit</td> + <td class=code>ExtensionModule (char* name) </td> + <td>Create an extension module named "name"</td> + </tr> + <tr> + <td class=code>virtual </td> + <td class=code>~ExtensionModule () </td> + <td>Destructor</td> + </tr> + <tr> + <td class=code>Dict</td> + <td class=code>moduleDictionary() const</td> + <td>Returns the module dictionary; module must be initialized.</td> + </tr> + <tr> + <td class=code>Module</td> + <td class=code>module() const</td> + <td>This module as a Module.</td> + </tr> + <tr> + <td class=code>void </td> + <td class=code>add_varargs_method (char *name, method_varargs_function_t method, char *documentation="")</td> + <td>Add a method to the module.</td> + </tr> + <tr> + <td class=code>void </td> + <td class=code>add_keyword_method (char *name, method_keyword_function_t method, char *documentation=""</td> + <td>Add a method that takes keywords</td> + </tr> + <tr> + <td class=code>void</td> + <td class=code>initialize() (protected, call from constructor)</td> + <td>Initialize the module once all methods have been added. </td> + </tr> +</table> + +<p>The signatures above are:</p> + +<pre>typedef Object (T::*method_varargs_function_t)( const Tuple &args ); +typedef Object (T::*method_keyword_function_t)( const Tuple &args, const Dict &kws +);</pre> + +<p>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.</p> + +<h2>Creating a Python extension type</h2> + +<p>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. </p> + +<p>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.</p> + +<p>The basic idea is to inherit from PythonExtension templated on your self</p> + +<pre>class MyObject: public PythonExtension<MyObject> {...}</pre> + +<p>As a consequence: + +<ul> + <li>MyObject is a child of PyObject, so that a MyObject* is-a PyObject*. </li> + <li>A static method <cite>check(PyObject*)</cite> 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.</li> + <li>The user can connect methods of MyObject to Python so that they are methods on MyObject + objects. Each such method has the signature:<br> + Object method_name (const Tuple& args).</li> + <li>The user can override virtual methods of PythonExtension in order to set behaviors.</li> + <li>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).</li> + <li>Both automatic and heap-based instances of MyObject can be created.</li> +</ul> + +<h3>Sample usage of PythonExtension</h3> + +<p>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. </p> + +<p>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.</p> + +<p>Hint: You can avoid needing to specify the Py:: prefix if you include the C++ statement +<cite>using Py;</cite> at the top of your files.</p> + +<pre>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); +};</pre> + +<p> +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.</p> + +<pre>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); +}</pre> +</a> + +<p>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.</p> + +<h3>Interface to PythonExtension <T></h3> + +<p>Your extension class T inherits PythonExtension<T>.</p> + +<table cellspacing=0 cellpadding=3px width="95%"> + <tr> + <th>Type</td> + <th>Name</td> + <th>Comment</td> + </tr> + <tr> + <td class=code>virtual </td> + <td class=code>~PythonExtension<T>() </td> + <td>Destructor</td> + </tr> + <tr> + <td class=code>PyTypeObject* </td> + <td class=code>type_object() const</td> + <td>Returns the object type object.</td> + </tr> + <tr> + <td class=code>int</td> + <td class=code>check (PyObject* p)</td> + <td>Is p a T?</td> + </tr> + <tr> + <td colspan="3"><strong>Protected </strong></td> + </tr> + <tr> + <td class=code>void </td> + <td class=code>add_varargs_method (char *name, method_keyword_function_t method, char *documentation=""</td> + <td>Add a method that takes arguments</td> + </tr> + <tr> + <td class=code>void </td> + <td class=code>add_keyword_method (char *name, method_keyword_function_t method, char *documentation=""</td> + <td>Add a method that takes keywords</td> + </tr> + <tr> + <td class=code>static PythonType&</td> + <td class=code>behaviors()</td> + <td>The type object</td> + </tr> + <tr> + <td class=code>void</td> + <td class=code>initialize() (protected, call from constructor)</td> + <td>Initialize the module once all methods have been added. </td> + </tr> +</table> + +<p>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.</p> + +<p>To set the behaviors of the object you override some or all of these methods from +PythonExtension<T>:</p> + +<pre> 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* );</pre> + +<p>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.</p> + +<h3>Type initialization</h3> + +<p>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.</p> + +<pre> 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);</pre> + +<p>Then call add_varargs_method or add_keyword_method to add any methods desired to the +object.</p> + +<h3>Notes on memory management and extension objects</h3> + +<p>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.</p> + +<p>If an extension object is created using operator new, as in:</p> + +<pre>range* my_r_ref = new range(1, 20, 3)</pre> + +<p>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).</p> + +<p>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.</p> + +<p>If the object is created with automatic scope, as in:</p> + +<pre>range my_r(1, 20, 3)</pre> + +<p>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.</p> + +<h2>Putting it all together</h2> + +<p>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.</p> + +<h2>Acknowledgment</h2> + +<p>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</p> + +</body> +</html> 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 @@ +<html> + +<head> +<title>PyCXX README</title> + +<style> +H1, H2, H3, H4 {color: #000099; + background-color: lightskyblue} +h3 {position: relative; left: 20px} + +p {position: relative; left: 20px; margin-right: 20px} +pre {color: #0000cc; background-color: #eeeeee; position: relative; left: 40px; margin-right: 80px; + border-style: solid; border-color: black; border-width: thin} +kbd {color: #990000} +p cite, ol cite, ul cite {font-family: monospace; font-style: normal; font-size: normal} +li var, pre var, p var, kbd var {color: #009900; font-style: italic} +li samp, pre samp, p samp, kbd samp {color: #009900; font-weight: bold} +li p {position: relative; left: 0} +table { position: relative; left: 20px; border: solid #888888 1px; background-color: #eeeeee} +table th {border: solid #888888 1px; background-color: #88dd88; color: black} +table td {border: solid #888888 1px} +table td.code {border: solid #888888 1px;font-family: monospace; font-style: normal; font-size: normal} +p.param {background-color: #eeeeee; border-top: lightskyblue solid 4} +</style> +</head> + +<body> + +<h1>PyCXX -- Python C++ Extensions Support</h1> + +<h2>Installation using distutils</h2> + +<h3>Windows Installation and Demo</h3> +<ol> +<li>Fetch <a href="http://prdownloads.sourceforge.net/cxx/pycxx_5_3_1.tar.gz"> +http://prdownloads.sourceforge.net/cxx/pycxx_5_3_1.tar.gz</a> +<li>Expand the archive into a directory of your choosing C:\ for example. Note: WinZip can expand .tar.gz files. +<li>Install the PyCXX files: +<ol> +<li><pre>C:> cd \pycxx_5_3_1</pre> +<li><pre>C:\pycxx_5_3_1> python setup.py install</pre> +</ol> +<li>Install the PyCXX Demo: +<ol> +<li><pre>C:> cd \PYCXX_5_3_1\Demo</pre> +<li><pre>C:\PYCXX_5_3_1\Demo> python setup.py install</pre> +</ol> +<li>Run the demo: +<ol> +<li><pre>C:> python</pre> +<li><pre>>>> import CXX.example</pre> +<li><pre>>>> CXX.example.test()</pre> +<li><pre>>>> r = CXX.example.range( 11, 100, 13 )</pre> +<li><pre>>>> for i in r: print i</pre> +<li><pre>...</pre> +</ol> +</ul> +</ol> + + +<h3>Unix Installation and Demo</h3> +<p>Note: distutils is not available for Python 1.5.2</p> + +<ol> +<li>Fetch <a href="http://prdownloads.sourceforge.net/cxx/pycxx_5_3_1.tar.gz"> +http://prdownloads.sourceforge.net/cxx/PyCXX-V5.3.0.tar.gz</a> +<li>Login as root. root access is typically needed on Unix systems to install the PyCXX files into the Python directories. +<li>Expand the archive into a directory of your choosing ~\ for example. +<li>Install the PyCXX files: +<ol> +<li><pre># cd ~\PYCXX_5_3_1</pre> +<li><pre># python setup.py install</pre> +</ol> +<li>Install the PyCXX Demo: +<ol> +<li><pre># cd ~\PYCXX_5_3_1\Demo</pre> +<li><pre># python setup.py install</pre> +</ol> +<li>Run the demo: +<ol> +<li><pre>~ python</pre> +<li><pre>>>> import CXX.example</pre> +<li><pre>>>> CXX.example.test()</pre> +<li><pre>>>> r = CXX.example.range( 11, 100, 13 )</pre> +<li><pre>>>> for i in r: print i</pre> +<li><pre>...</pre> +</ol> +</ul> +</ol> + +<h2>Installation using Project and Makefile</h2> + +<p>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.</p> + +<h3>Windows Installation and Demo</h3> +<p> +<ol> +<li>Fetch <a href="http://prdownloads.sourceforge.net/cxx/PyCXX-V5.3.0.tar.gz"> +http://prdownloads.sourceforge.net/cxx/pycxx_5_3_1.tar.gz</a> +<li>Expand the archive into a directory of your choosing C:\ for example. WinZip can expand .tar.gz files. +<li>Build the example. Using Microsoft Visual C++ 6.0 load the workspace corresponsing to the version of +Python you wish the work with. +<ul> +<li>example_py15.dsw - Python 1.5.2 +<li>example_py20.dsw - Python 2.0 and 2.0.1 +<li>example_py21.dsw - Python 2.1 and 2.1.1 +<li>example_py22.dsw - Python 2.2 and its maintanence release +<li>example_py23.dsw - Python 2.3 and its maintanence release +</ul> +<li>Run the example. (I'll assume you are testing Python 2.3) +<ul> +<li>cd c:\PYCXX_5_3_1\pyds23 +<li>c:\python21\python -c "import example;example.test()" +</ul> +</ol> +</p> +<h3>Unix Installation and Demo</h3> +<p> +<ol> +<li>Fetch <a href="http://prdownloads.sourceforge.net/cxx/PyCXX-V5.3.0.tar.gz"> +http://prdownloads.sourceforge.net/cxx/PyCXX-V5.3.0.tar.gz</a> +<li>Expand the archive into a directory of your choosing ~/ for example. +<li>Select to makefile for your system and python version. +<ul> +<li>example_freebsd_py15.mak - FreeBSD Python 1.5.2 (see <a href="#note_1_5_2">note</a> below) +<li>example_freebsd_py20.mak - FreeBSD Python 2.0, 2.0.1 +<li>example_freebsd_py21.mak - FreeBSD Python 2.1, 2.1.1 +<li>example_freebsd_py22.mak - FreeBSD Python 2.2 +<li>example_linux_py15.mak - Linux Python 1.5.2 +<li>example_linux_py20.mak - Linux Python 2.0, 2.0.1 +<li>example_linux_py21.mak - Linux Python 2.1, 2.1.1 +<li>example_linux_py22.mak - Linux Python 2.2 +</ul> +<li>Build the example. Use GNU make<br> +$ make -f <var>example-makefile</var> example.so +<li>Run the example.<br> +$ make -f <var>example-makefile</var> test +</ol> +</p> + +<p><a id="note_1_5_2">Note:</a> 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++.</p> + +<p>To create a makefile for another vendors Unix follow these steps:</p> +<ol> +<li>copy one of the example make files above. +<li>edit the variables to match your Python installation and C++ compile needs +<li>Proceed to build and test as above. +</ol> +<p>Note: most of the makefile rules to build PyCXX and its example are contained in example_common.mak. +</p> + +<h2>Revision History</h2> +<h3>Version 5.3.1 (19-Jan-2005)</h3> +<p>Support GCC4 and Microsoft .NET 2003 aka MSVC 7.1 + +<h3>Version 5.3 (21-Oct-2004)</h3> +<p>String object now support python string and unicode string objects. +<p>Fix most bugs reported on SourceForge + +<h3>Version 5.2 (27-Nov-2003)</h3> +<p>PyCXX supports Python version 2.3, 2.2, 2.1, 2.0 and 1.5.2 on Windows and Unix.</p> +<p>Fixed problems with keyword functions.</p> +<p>Improve Extension API to give access to names and docs +<p>Support GCC 3.</p> +<p>Added support for custom Exceptions</p> +<p></p> + +<h3>Version 5.1 (2-Aug-2001)</h3> +<p>I'm using the name PyCXX for this package, CXX is far to close to a compilers name.</p> + +<p>PyCXX supports Python version 2.2, 2.1.1, 2.1, 2.0, 2.0.1 and 1.5.2 on Windows and Unix.</p> + +<p>New in this release:</p> +<ul> +<li>Support for the Windows Linker /DELAYLOAD feature. Enable this feature by +defining PY_WIN32_DELAYLOAD_PYTHON_DLL when compiling IndirectPythonInterface.cxx +<li>Remove "CXX/Array.hxx" and associated code - its does not belong in PyCXX +<li>Work on the docs. Mostly to clean up the HTML to allow more extensive work. +<li>Reformated the sources to a consistent style. The mix of styles and tabs sizes +was making working on the sources error prone. +<li>Added workaround to setup.py to allow GCC to compile c++ code. +<li>Added Microsoft Visual C++ 6.0 project files for 1.5, 2.0 and 2.1 builds +<li>Added Unix make files for Linux (tested on RedHat 7.1) and FreeBSD (tested on 4.3) +<li>Merged changes from Tom Malcolmson +</ul> + +<h3>(July 9, 2000)</h3> +<p>Renamed all header files to reflect the CXX include name space and that they are +C++ header files. +<p> +<table cellspacing=0 cellpadding=3px> +<tr><th>Old</th><th>New</th></tr> +<tr><td>#include "CXX_Config.h"</td><td>#include "CXX/Config.hxx"</td> +<tr><td>#include "CXX_Exception.h"</td><td>#include "CXX/Exception.hxx"</td> +<tr><td>#include "CXX_Extensions.h"</td><td>#include "CXX/Extensions.hxx"</td> +<tr><td>#include "CXX_Objects.h"</td><td>#include "CXX/Objects.hxx"</td> +</table> + +<h3>Version 5 (May 18, 2000)</h3> +<p>This version adds Distutils support for installation and some code cleanup.</p> + +<h3>Version 4 (October 11, 1999)</h3> + +<p>This version contains a massive revision to the part of CXX that supports creating +extension objects and extension modules. Barry Scott contributed these changes.</p> + +<p>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.</p> + +<h3>Changes to CXX_Objects</h3> + +<h4>1. Owned option eliminates need for FromAPI in most cases</h4> + +<p>Object's constructor from PyObject* and method set have a new (backward compatible) +signature:</p> + +<pre> +Object (PyObject* pyob, bool owned = false); +void set(PyObject* pyob, bool owned = false); +</pre> + +<p>Users may call these with owned = true if they own the reference pyob already and want +the Object instance to take over ownership.</p> + +<p>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. </p> + +<p>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.</p> + +<p>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.</p> + +<h4>2. Namespace support in compiler assumed</h4> + +<p>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.</p> + +<h3>Changes to CXX_Extensions</h3> + +<p>The changes to CXX_Extensions.h are not backward compatible. However, they simplify +coding so much that we think it is worth the disruption.</p> + +<h4>1. Creating an extension module</h4> + +<p>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):</p> + +<pre>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) { ... } +}; +</pre> + +<p>To initialize the extension, you just instantiate one static instance (static so it +doesn't destroy itself!):</p> + +<pre> +void initexample() + { + static example_module* example = new example_module; + } +</pre> + +<p>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:</p> + +<pre> +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; + } +</pre> + +<p>class ExtensionModule contains methods to return itself as a Module object, or to +return its dictionary.</p> + +<h4>Creating extension objects</h4> + +<p>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.</p> + +<p>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. </p> + +<p>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.</p> + +<pre> +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); +}; +</pre> + +<p> +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.</p> + +<pre> +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); +} +</pre> + +<h3>Version 3 (June 18, 1999)</h3> + +<p>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:</p> + +<p>STANDARD_LIBRARY_HAS_ITERATOR_TRAITS</p> + +<p>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. </p> + +<p>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.</p> + +<h3>Version 2 (Dec. 28, 1998)</h3> + +<p>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.</p> + +<h3>Version 1</h3> + +<p>This is an experimental set of files for supporting the creation of Python extensions +in C++. </p> + +<p>Documentation is in progress at <a href="http://xfiles.llnl.gov">http://xfiles.llnl.gov</a>. +</p> + +<p>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.</p> + +<p>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:</p> + +<pre> +example.test() +</pre> + +<p>You can also play with the extension object whose constructor is named "range":</p> + +<pre> +s = range(1, 100, 2) +print s[2] # should print 5 +</pre> + +<p>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:</p> + +<pre> +setenv CCC "KCC -x" +</pre> + +<p>before running makethis.py.</p> + +<p>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.</p> + +<p>Comments to barry@barrys-emacs.org, please.</p> + +<p>Barry Scott</p> +</body> +</html> 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 <assert.h> + +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<char*>( method_name ); + m.ml_meth = f; + m.ml_flags = flags; + m.ml_doc = const_cast<char*>( 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<PyMethodDef>::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<ExtensionModuleBasePtr> + { +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<char *>( module_name.c_str() ), // name + method_table.table(), // methods + const_cast<char *>( 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<PyObject*>( table ) = py_object_initializer; + table->ob_type = _Type_Type(); + table->ob_size = 0; + table->tp_name = const_cast<char *>( 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<char *>( nam ); + } + +const char *PythonType::getName() const + { + return table->tp_name; + } + +void PythonType::doc( const char* d ) + { + table->tp_doc = const_cast<char *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<PythonExtensionBase *>( 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<ExtensionModuleBase *>( 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<ExtensionModuleBase *>( 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<char *>( 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<std::string>(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<std::string>(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 <Python.h> +#include <object.h> +#include <compile.h> +#include <eval.h> +#include <frameobject.h> + +// 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<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<PythonExtension>( + "", // 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<Kross::Api::Callable*>(m_object.data()); + if(callable) { + QMap<QString, Kross::Api::Object::Ptr> children = callable->getChildren(); + QMap<QString, Kross::Api::Object::Ptr>::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<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<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<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<Kross::Api::Object::Ptr> 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<Kross::Api::Object::Ptr> 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<QString, Kross::Api::Object::Ptr> 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<PythonExtension> 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<QString, QVariant>& map) +{ +#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::toPyObject(QMap<QString,QVariant>)") ); +#endif + Py::Dict d; + for(QMap<QString, QVariant>::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<QVariant>& list) +{ +#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG + krossdebug( QString("Kross::Python::PythonExtension::toPyObject(QValueList<QVariant>)") ); +#endif + Py::List l; + for(QValueList<QVariant>::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<Kross::Api::Variant*>( 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<Kross::Api::List*>( object.data() ); + QValueList<Kross::Api::Object::Ptr> valuelist = list->getValue(); + for(QValueList<Kross::Api::Object::Ptr>::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<Kross::Api::Dict*>( object.data() ); + QMap<QString, Kross::Api::Object::Ptr> valuedict = dict->getValue(); + for(QMap<QString, Kross::Api::Object::Ptr>::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<PythonExtension*>( 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<Kross::Api::Callable*>(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 <qstring.h> +#include <qstringlist.h> +#include <qvaluelist.h> +#include <qvaluevector.h> +#include <qmap.h> +#include <qvariant.h> + +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<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<QString, QVariant>& 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<QVariant>& 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<PythonExtension>* 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 <kglobal.h> +#include <kstandarddirs.h> + +#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<char*>("Kross")); + + /* + // Set arguments. + //char* comm[0]; + const char* comm = const_cast<char*>("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 + // <stdout> and <stderr> 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 <qstring.h> + +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 <qregexp.h> + +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<QString, PythonExtension*> m_modules; + + }; + +}} + +PythonModule::PythonModule(PythonInterpreter* interpreter) + : Py::ExtensionModule<PythonModule>("__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 <qstring.h> + +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<PythonModule> + { + 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 <qstring.h> +#include <qstringlist.h> + +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 <kapplication.h> + +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<PythonInterpreter*>(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>("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 <qstring.h> + +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<PythonSecurity> + { + 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 = "<string>"); + void compile_restricted_eval(const QString& source, const QString& filename = "<string>"); +#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, '<string>') + 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, '<string>', '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(<code>, <globals>) + + 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='<string>'): + """Compiles a restricted code suite.""" + gen = RModule(s, filename) + return compileAndTuplize(gen) + +def compile_restricted_eval(s, filename='<string>'): + """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, '<function parameters>', '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 <mail@dipe.org> + +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("<qt>%s</qt>" % caption.replace("\n","<br>")) + + 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 <st.h> + +#include <qmap.h> +#include <qstring.h> + +#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<Api::Object::Ptr> 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<Kross::Api::Callable*>(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<RubyExtension*>(object); + if(obj) + delete obj; +} + +void RubyExtension::delete_exception(void* object) +{ + Kross::Api::Exception* exc = static_cast<Kross::Api::Exception*>(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<QString, Kross::Api::Object::Ptr> mStrObj; + +int RubyExtension::convertHash_i(VALUE key, VALUE value, VALUE vmap) +{ + QMap<QString, Kross::Api::Object::Ptr>* 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<Kross::Api::Object::Ptr> 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<QString, Kross::Api::Object::Ptr> 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<QString, QVariant> map) +{ + VALUE h = rb_hash_new(); + for(QMap<QString, QVariant>::Iterator it = map.begin(); it != map.end(); ++it) + rb_hash_aset(h, toVALUE(it.key()), toVALUE(it.data()) ); + return h; + +} + +VALUE RubyExtension::toVALUE(QValueList<QVariant> list) +{ + VALUE l = rb_ary_new(); + for(QValueList<QVariant>::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<Kross::Api::Variant*>( object.data() )->getValue(); + return toVALUE(v); + } + + if(object->getClassName() == "Kross::Api::List") { + Kross::Api::List* list = static_cast<Kross::Api::List*>( object.data() ); + return toVALUE((Kross::Api::List::Ptr)list); + } + + if(object->getClassName() == "Kross::Api::Dict") { + Kross::Api::Dict* dict = static_cast<Kross::Api::Dict*>( 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 <ruby.h> + +#include <api/class.h> +#include <api/dict.h> +#include <api/list.h> +#include <api/object.h> + +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<QString, QVariant> map); + + /** + * Converts a QValueList to a VALUE. + * \param list The QValueList to convert. + * \return The converted QValueList. + */ + static VALUE toVALUE(QValueList<QVariant> 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 <map> + +#include <qregexp.h> +#include <ksharedptr.h> + +#include <api/exception.h> +#include <api/module.h> +#include <main/manager.h> + +#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<QString, VALUE> 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 <ruby.h> + +#include <api/interpreter.h> + +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 <ruby.h> + +#include <qstring.h> + +#include <api/object.h> +#include <api/module.h> + +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 <ruby.h> +#include <env.h> +#include <rubysig.h> +#include <node.h> + +#include <main/scriptcontainer.h> + +#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 <api/script.h> + +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 <string> +#include <iostream> + +// Qt +#include <qstring.h> +#include <qfile.h> + +// KDE +#include <kinstance.h> +#include <kapplication.h> +#include <kcmdlineargs.h> +#include <kaboutdata.h> +#include <ksharedptr.h> + +// 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 <qstring.h> +#include <qfile.h> + +// KDE +#include <kinstance.h> +#include <kapplication.h> +#include <kcmdlineargs.h> +#include <kaboutdata.h> +#include <ksharedptr.h> + +// for std namespace +#include <string> +#include <iostream> + +KApplication *app = 0; + + + +static KCmdLineOptions options[] = +{ + { "interpreter <interpretername>", I18N_NOOP("Name of the interpreter being used"), "python" }, + { "scriptfile <filename>", 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 <functioname>", I18N_NOOP("Execute the function in the defined script file."), "" }, + //{ "functionargs <functioarguments>", 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<Kross::Api::Object*> 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 <qobject.h> +#include <qwidget.h> +#include <qstring.h> + +#include <kaction.h> +#include <kactioncollection.h> + +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<QVariant>& 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 <iostream> // 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 << "<stdout> " << s.latin1() << std::endl; +} + +void TestObject::stderrSlot(const QString& s) +{ + Kross::krossdebug(QString("stderr: %1").arg(s)); + //std::cout << "<stderr> " << 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 <qobject.h> +#include <qstring.h> + +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<TestPluginObject>(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<TestPluginObject, TestPluginObject>("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<TestObject> *testobjectclass = + new Kross::Api::Event<TestObject>("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 <qobject.h> +#include <qstring.h> + +class TestPluginObject : public Kross::Api::Class<TestPluginObject> +{ + 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 @@ +<KrossScripting> + <ScriptAction + name="sa1name" + text="ScriptAction1Text" + description="Some describing text" + interpreter="python" + icon="exec"> +print "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +print "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + </ScriptAction> + + <ScriptAction + name="testcase" + text="TestCase" + description="Test some common functionality." + interpreter="python" + file="testcase.py" /> + + <ScriptAction + name="testgui" + text="TestGUI" + description="Test the GUI functionality." + interpreter="python" + file="testgui.py" /> + + <ScriptAction + name="testkexidb" + text="TestKexiDB" + description="Test the kexidb binding." + interpreter="python" + file="testkexidb.py" /> + +</KrossScripting> 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 <qlabel.h> +#include <qvbox.h> +#include <qvgroupbox.h> +//#include <qhgroupbox.h> +#include <qcombobox.h> +#include <qdir.h> +#include <qpopupmenu.h> + +#include <ktextedit.h> +#include <kpushbutton.h> +#include <kpopupmenu.h> +#include <kmenubar.h> +#include <kstandarddirs.h> + +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 <qobject.h> +#include <qstring.h> + +#include <kmainwindow.h> + +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 |