WIP: further generation of Python module (kind of slow currently + dependent modules need to be loaded manually)

This commit is contained in:
Matthias Koefferlein 2018-05-31 23:22:35 +02:00
parent b2f18c612a
commit 3882f6a3b1
12 changed files with 266 additions and 121 deletions

View File

@ -106,7 +106,9 @@ ClassBase::add_child_class (const ClassBase *cls)
// TODO: ugly const_cast hack
ClassBase *non_const_cls = const_cast<ClassBase *> (cls);
non_const_cls->set_parent (this);
m_child_classes.push_back (const_cast <ClassBase *> (cls));
// child classes inherit the module of their parent
non_const_cls->set_module (module ());
m_child_classes.push_back (non_const_cls);
}
bool
@ -497,7 +499,6 @@ ClassBase::merge_declarations ()
mp_class_collection->push_back (non_const_decl);
}
mp_new_class_collection->clear ();
}
void

View File

@ -58,25 +58,6 @@ class PYAObjectBase;
// --------------------------------------------------------------------------
#define PYA_TRY \
{ \
try {
#define PYA_CATCH(where) \
} catch (tl::ExitException &ex) { \
PyErr_SetObject (PyExc_SystemExit, c2python<int> (ex.status ())); \
} catch (std::exception &ex) { \
std::string msg = std::string(ex.what ()) + tl::to_string (QObject::tr (" in ")) + (where); \
PyErr_SetString (PyExc_RuntimeError, msg.c_str ()); \
} catch (tl::Exception &ex) { \
std::string msg = ex.msg () + tl::to_string (QObject::tr (" in ")) + (where); \
PyErr_SetString (PyExc_RuntimeError, msg.c_str ()); \
} catch (...) { \
std::string msg = tl::to_string (QObject::tr ("Unspecific exception in ")) + (where); \
PyErr_SetString (PyExc_RuntimeError, msg.c_str ()); \
} \
}
/**
* @brief The python interpreter instance
*/
@ -2265,6 +2246,18 @@ PythonModule::module ()
return mp_module.get ();
}
PyObject *
PythonModule::take_module ()
{
return mp_module.release ();
}
void
PythonModule::delete_module ()
{
mp_module = PythonRef ();
}
void
PythonModule::init (const char *mod_name, const char *description)
{
@ -2359,6 +2352,18 @@ PythonModule::make_classes (const char *mod_name)
{
PyObject *module = mp_module.get ();
// Prepare an __all__ index for the module
PythonRef all_list;
if (! PyObject_HasAttrString (module, "__all__")) {
all_list = PythonRef (PyList_New (0));
PyObject_SetAttrString (module, "__all__", all_list.get ());
} else {
all_list = PythonRef (PyObject_GetAttrString (module, "__all__"));
}
PyObject_SetAttrString (module, "__doc__", PythonRef (c2python (m_mod_description)).get ());
// Create a (built-in) base class for all objects exposed by this module
m_base_class_name = m_mod_name + ".__Base";
@ -2421,6 +2426,9 @@ PythonModule::make_classes (const char *mod_name)
for (tl::weak_collection<gsi::ClassBase>::const_iterator cc = c->begin_child_classes (); cc != c->end_child_classes (); ++cc) {
tl_assert (cc->declaration () != 0);
if (m_rev_cls_map.find (cc->declaration ()) == m_rev_cls_map.end ()) {
if (mod_name && cc->module () != mod_name) {
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Class %s from module %s depends on %s.%s (try 'import klayout.%s' before 'import klayout.%s')")), c->name (), mod_name, cc->module (), cc->name (), cc->module (), mod_name));
}
all_children_available = false;
break;
}
@ -2432,6 +2440,9 @@ PythonModule::make_classes (const char *mod_name)
}
if (c->base () && m_rev_cls_map.find (c->base ()) == m_rev_cls_map.end ()) {
if (mod_name && c->base ()->module () != mod_name) {
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Class %s from module %s depends on %s.%s (try 'import klayout.%s' before 'import klayout.%s')")), c->name (), mod_name, c->base ()->module (), c->base ()->name (), c->base ()->module (), mod_name));
}
// can't produce this class yet. The base class needs to be handled first.
more_classes = true;
continue;
@ -2464,6 +2475,7 @@ PythonModule::make_classes (const char *mod_name)
PyTypeObject *type = (PyTypeObject *) PyObject_Call ((PyObject *) &PyType_Type, args.get (), NULL);
tl_assert (type != NULL);
PyList_Append (all_list.get (), PythonRef (c2python (c->name ())).get ());
PyModule_AddObject (module, c->name ().c_str (), (PyObject *) type);
m_cls_map.insert (std::make_pair (type, &*c));

View File

@ -68,6 +68,42 @@ namespace pya
throw; \
}
/**
* Two helper macros that translate C++ exceptions into Python errors
*/
#define PYA_TRY \
{ \
try {
#define PYA_CATCH(where) \
} catch (tl::ExitException &ex) { \
PyErr_SetObject (PyExc_SystemExit, PyLong_FromLong (ex.status ())); \
} catch (std::exception &ex) { \
std::string msg = std::string(ex.what ()) + tl::to_string (QObject::tr (" in ")) + (where); \
PyErr_SetString (PyExc_RuntimeError, msg.c_str ()); \
} catch (tl::Exception &ex) { \
std::string msg; \
msg = ex.msg () + tl::to_string (QObject::tr (" in ")) + (where); \
PyErr_SetString (PyExc_RuntimeError, msg.c_str ()); \
} catch (...) { \
std::string msg = tl::to_string (QObject::tr ("Unspecific exception in ")) + (where); \
PyErr_SetString (PyExc_RuntimeError, msg.c_str ()); \
} \
}
#define PYA_CATCH_ANYWHERE \
} catch (tl::ExitException &ex) { \
PyErr_SetObject (PyExc_SystemExit, PyLong_FromLong (ex.status ())); \
} catch (std::exception &ex) { \
PyErr_SetString (PyExc_RuntimeError, ex.what ()); \
} catch (tl::Exception &ex) { \
PyErr_SetString (PyExc_RuntimeError, ex.msg ().c_str ()); \
} catch (...) { \
PyErr_SetString (PyExc_RuntimeError, tl::to_string (QObject::tr ("Unspecific exception in ")).c_str ()); \
} \
}
/**
* @brief A class encapsulating a python exception
*/
@ -143,10 +179,21 @@ public:
static std::string python_doc (const gsi::MethodBase *method);
/**
* @brief Gets the PyObject for the module
* @brief Gets the PyModule object
*/
PyObject *module ();
/**
* @brief Gets the PyModule object
* This method will release the ownership over the PyObject
*/
PyObject *take_module ();
/**
* @brief Deletes the PyModule object
*/
void delete_module ();
private:
void add_python_doc (const gsi::ClassBase &cls, const MethodTable *mt, int mid, const std::string &doc);
PyMethodDef *make_method_def ();

View File

@ -0,0 +1,21 @@
LIBDIR = $$OUT_PWD/../..
DESTDIR = $$LIBDIR/pymod
TARGET = QtCore
include($$PWD/../../lib.pri)
SOURCES = \
QtCoreMain.cc \
HEADERS += \
INCLUDEPATH += $$PYTHONINCLUDE $$TL_INC $$GSI_INC $$PYA_INC
DEPENDPATH += $$PYTHONINCLUDE $$TL_INC $$GSI_INC $$PYA_INC
LIBS += $$PYTHONLIBFILE -L$$LIBDIR -lklayout_tl -lklayout_db -lklayout_gsi -lklayout_pya -lklayout_QtCore
# Python is somewhat sloppy and relies on the compiler initializing fields
# of strucs to 0:
QMAKE_CXXFLAGS_WARN_ON += \
-Wno-missing-field-initializers

View File

@ -0,0 +1,25 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../pymodHelper.h"
DEFINE_PYMOD(QtCore, "QtCore", "KLayout/Qt module 'QtCore'")

21
src/pymod/QtGui/QtGui.pro Normal file
View File

@ -0,0 +1,21 @@
LIBDIR = $$OUT_PWD/../..
DESTDIR = $$LIBDIR/pymod
TARGET = QtGui
include($$PWD/../../lib.pri)
SOURCES = \
QtGuiMain.cc \
HEADERS += \
INCLUDEPATH += $$PYTHONINCLUDE $$TL_INC $$GSI_INC $$PYA_INC
DEPENDPATH += $$PYTHONINCLUDE $$TL_INC $$GSI_INC $$PYA_INC
LIBS += $$PYTHONLIBFILE -L$$LIBDIR -lklayout_tl -lklayout_db -lklayout_gsi -lklayout_pya -lklayout_QtGui -lklayout_QtCore
# Python is somewhat sloppy and relies on the compiler initializing fields
# of strucs to 0:
QMAKE_CXXFLAGS_WARN_ON += \
-Wno-missing-field-initializers

View File

@ -0,0 +1,25 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../pymodHelper.h"
DEFINE_PYMOD(QtGui, "QtGui", "KLayout/Qt module 'QtGui'")

View File

@ -20,37 +20,6 @@
*/
#include <Python.h>
#include "pya.h"
#include "gsi.h"
#include "gsiExpression.h"
#include "../pymodHelper.h"
static PyObject *module_init ()
{
gsi::initialize ();
// required for the tiling processor for example
gsi::initialize_expressions ();
static pya::PythonModule module;
module.init ("klayout.db", "KLayout core module (db)");
module.make_classes ("db");
return module.module ();
}
#if PY_MAJOR_VERSION < 3
PyMODINIT_FUNC
DEF_INSIDE_PUBLIC
initdb ()
{
module_init ();
}
#else
PyMODINIT_FUNC
DEF_INSIDE_PUBLIC
PyInit_db ()
{
return module_init();
}
#endif
DEFINE_PYMOD(db, "db", "KLayout core module 'db'")

View File

@ -20,37 +20,6 @@
*/
#include <Python.h>
#include "pya.h"
#include "gsi.h"
#include "gsiExpression.h"
#include "../pymodHelper.h"
static PyObject *module_init ()
{
gsi::initialize ();
// required for the tiling processor for example
gsi::initialize_expressions ();
static pya::PythonModule module;
module.init ("klayout.lay", "KLayout core module (lay)");
module.make_classes ("lay");
return module.module ();
}
#if PY_MAJOR_VERSION < 3
PyMODINIT_FUNC
DEF_INSIDE_PUBLIC
initlay ()
{
module_init ();
}
#else
PyMODINIT_FUNC
DEF_INSIDE_PUBLIC
PyInit_lay ()
{
return module_init();
}
#endif
DEFINE_PYMOD(lay, "lay", "KLayout core module 'lay'")

View File

@ -4,4 +4,6 @@ SUBDIRS = \
db \
tl \
lay \
QtGui \
QtCore \

84
src/pymod/pymodHelper.h Normal file
View File

@ -0,0 +1,84 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @brief A helper include file to implement the Python modules
*
* Use this helper file this way:
*
* #include "pymodHelper.h"
* DEFINE_PYMOD(mymod, "mymod", "KLayout Test module klayout.mymod")
*/
#include <Python.h>
#include "pya.h"
#include "gsi.h"
#include "gsiExpression.h"
static PyObject *
module_init (const char *mod_name, const char *mod_description)
{
static pya::PythonModule module;
std::string mod_qname (std::string ("klayout.") + mod_name);
std::string import_text ("'import " + mod_qname + "'");
PYA_TRY
gsi::initialize ();
// required for the tiling processor for example
gsi::initialize_expressions ();
module.init (mod_qname.c_str (), mod_description);
module.make_classes (mod_name);
return module.take_module ();
PYA_CATCH_ANYWHERE
module.delete_module ();
return 0;
}
#if PY_MAJOR_VERSION < 3
#define DEFINE_PYMOD(__name__, __name_str__, __description__) \
PyMODINIT_FUNC \
DEF_INSIDE_PUBLIC \
init##__name__ () \
{ \
module_init (__name_str__, __description__); \
} \
#else
#define DEFINE_PYMOD(__name__, __name_str__, __description__) \
PyMODINIT_FUNC \
DEF_INSIDE_PUBLIC \
PyInit_##__name__ () \
{ \
return module_init (__name_str__, __description__); \
} \
#endif

View File

@ -20,37 +20,6 @@
*/
#include <Python.h>
#include "pya.h"
#include "gsi.h"
#include "gsiExpression.h"
#include "../pymodHelper.h"
static PyObject *module_init ()
{
gsi::initialize ();
// required for the tiling processor for example
gsi::initialize_expressions ();
static pya::PythonModule module;
module.init ("klayout.tl", "KLayout core module (tl)");
module.make_classes ("tl");
return module.module ();
}
#if PY_MAJOR_VERSION < 3
PyMODINIT_FUNC
DEF_INSIDE_PUBLIC
inittl ()
{
module_init ();
}
#else
PyMODINIT_FUNC
DEF_INSIDE_PUBLIC
PyInit_tl ()
{
return module_init();
}
#endif
DEFINE_PYMOD(tl, "tl", "KLayout core module 'tl'")