diff --git a/src/gsi/gsi/gsiClassBase.cc b/src/gsi/gsi/gsiClassBase.cc index 0b609f590..f2b8baaa2 100644 --- a/src/gsi/gsi/gsiClassBase.cc +++ b/src/gsi/gsi/gsiClassBase.cc @@ -106,7 +106,9 @@ ClassBase::add_child_class (const ClassBase *cls) // TODO: ugly const_cast hack ClassBase *non_const_cls = const_cast (cls); non_const_cls->set_parent (this); - m_child_classes.push_back (const_cast (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 diff --git a/src/pya/pya/pya.cc b/src/pya/pya/pya.cc index 3da342664..625452425 100644 --- a/src/pya/pya/pya.cc +++ b/src/pya/pya/pya.cc @@ -58,25 +58,6 @@ class PYAObjectBase; // -------------------------------------------------------------------------- -#define PYA_TRY \ - { \ - try { - -#define PYA_CATCH(where) \ - } catch (tl::ExitException &ex) { \ - PyErr_SetObject (PyExc_SystemExit, c2python (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::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)); diff --git a/src/pya/pya/pya.h b/src/pya/pya/pya.h index 2561c3339..d429d933b 100644 --- a/src/pya/pya/pya.h +++ b/src/pya/pya/pya.h @@ -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 (); diff --git a/src/pymod/QtCore/QtCore.pro b/src/pymod/QtCore/QtCore.pro new file mode 100644 index 000000000..7d2ba4efc --- /dev/null +++ b/src/pymod/QtCore/QtCore.pro @@ -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 + diff --git a/src/pymod/QtCore/QtCoreMain.cc b/src/pymod/QtCore/QtCoreMain.cc new file mode 100644 index 000000000..a325ad7db --- /dev/null +++ b/src/pymod/QtCore/QtCoreMain.cc @@ -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'") diff --git a/src/pymod/QtGui/QtGui.pro b/src/pymod/QtGui/QtGui.pro new file mode 100644 index 000000000..681523ceb --- /dev/null +++ b/src/pymod/QtGui/QtGui.pro @@ -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 + diff --git a/src/pymod/QtGui/QtGuiMain.cc b/src/pymod/QtGui/QtGuiMain.cc new file mode 100644 index 000000000..43b77f742 --- /dev/null +++ b/src/pymod/QtGui/QtGuiMain.cc @@ -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'") diff --git a/src/pymod/db/dbMain.cc b/src/pymod/db/dbMain.cc index a63d96da1..a0886149d 100644 --- a/src/pymod/db/dbMain.cc +++ b/src/pymod/db/dbMain.cc @@ -20,37 +20,6 @@ */ -#include -#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'") diff --git a/src/pymod/lay/layMain.cc b/src/pymod/lay/layMain.cc index 4e313ee11..30b945094 100644 --- a/src/pymod/lay/layMain.cc +++ b/src/pymod/lay/layMain.cc @@ -20,37 +20,6 @@ */ -#include -#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'") diff --git a/src/pymod/pymod.pro b/src/pymod/pymod.pro index a5c87b566..28ab71b6b 100644 --- a/src/pymod/pymod.pro +++ b/src/pymod/pymod.pro @@ -4,4 +4,6 @@ SUBDIRS = \ db \ tl \ lay \ + QtGui \ + QtCore \ diff --git a/src/pymod/pymodHelper.h b/src/pymod/pymodHelper.h new file mode 100644 index 000000000..034b8efdf --- /dev/null +++ b/src/pymod/pymodHelper.h @@ -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 +#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 + diff --git a/src/pymod/tl/tlMain.cc b/src/pymod/tl/tlMain.cc index 46b1666fa..db6a278fb 100644 --- a/src/pymod/tl/tlMain.cc +++ b/src/pymod/tl/tlMain.cc @@ -20,37 +20,6 @@ */ -#include -#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'")