diff --git a/src/klayout.pro b/src/klayout.pro
index 888def688..2a3e1760a 100644
--- a/src/klayout.pro
+++ b/src/klayout.pro
@@ -39,6 +39,8 @@ equals(HAVE_PYTHON, "1") {
SUBDIRS += pya
LANG_DEPENDS += pya
pya.depends += gsi db
+ SUBDIRS += pymod
+ pymod.depends += pya
} else {
SUBDIRS += pyastub
pyastub.depends += gsi
diff --git a/src/lay/lay/layGSIHelpProvider.cc b/src/lay/lay/layGSIHelpProvider.cc
index 8fbfaedc4..5d30e8a4d 100644
--- a/src/lay/lay/layGSIHelpProvider.cc
+++ b/src/lay/lay/layGSIHelpProvider.cc
@@ -1190,10 +1190,7 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const
const gsi::MethodBase::MethodSynonym &syn = i->second.first->begin_synonyms () [i->second.second];
DocumentationParser method_doc (i->second.first);
- std::string pydoc;
- if (pya::PythonInterpreter::instance ()) {
- pydoc = pya::PythonInterpreter::instance ()->python_doc (i->second.first);
- }
+ std::string pydoc = pya::PythonModule::python_doc (i->second.first);
os << "
";
if (i->first != prev_title) {
diff --git a/src/pya/pya/pya.cc b/src/pya/pya/pya.cc
index 755d1a2bc..cdd5e7382 100644
--- a/src/pya/pya/pya.cc
+++ b/src/pya/pya/pya.cc
@@ -60,7 +60,7 @@ class PYAObjectBase;
#define PYA_TRY \
{ \
- try {
+ try {
#define PYA_CATCH(where) \
} catch (tl::ExitException &ex) { \
@@ -75,8 +75,8 @@ class PYAObjectBase;
std::string msg = tl::to_string (QObject::tr ("Unspecific exception in ")) + (where); \
PyErr_SetString (PyExc_RuntimeError, msg.c_str ()); \
} \
- }
-
+ }
+
/**
* @brief The python interpreter instance
*/
@@ -241,9 +241,9 @@ public:
}
/**
- * @brief Adds a method to the table
+ * @brief Adds a method to the table
*/
- void add_method (const std::string &name, const gsi::MethodBase *mb)
+ void add_method (const std::string &name, const gsi::MethodBase *mb)
{
bool st = mb->is_static ();
@@ -392,7 +392,7 @@ public:
* This method must be called after the add_method calls have been used
* to fill the table. It will remove duplicate entries and clean up memory.
*/
- void finish ()
+ void finish ()
{
for (std::vector::iterator m = m_table.begin (); m != m_table.end (); ++m) {
m->finish ();
@@ -429,12 +429,12 @@ private:
/**
* @brief Constructor
* This constructor will create a method table for the given class and register
- * this table under this class.
+ * this table under this class.
* It is used by method_table_by_class only, hence it's private.
*/
MethodTable (const gsi::ClassBase *cls_decl)
: m_method_offset (0), m_property_offset (0), mp_cls_decl (cls_decl)
- {
+ {
if (cls_decl->base ()) {
const MethodTable *base_mt = method_table_by_class (cls_decl->base ());
tl_assert (base_mt);
@@ -452,8 +452,8 @@ class PythonStackTraceProvider
{
public:
PythonStackTraceProvider (PyFrameObject *frame, const std::string &scope)
- : m_scope (scope)
- {
+ : m_scope (scope)
+ {
while (frame != NULL) {
int line = frame->f_lineno;
@@ -468,7 +468,7 @@ public:
}
}
- virtual std::vector stack_trace () const
+ virtual std::vector stack_trace () const
{
return m_stack_trace;
}
@@ -500,7 +500,7 @@ private:
/**
* @brief Gets the method name from a method id
*/
-std::string
+std::string
method_name_from_id (int mid, PyObject *self)
{
const gsi::ClassBase *cls_decl = 0;
@@ -509,7 +509,7 @@ method_name_from_id (int mid, PyObject *self)
PYAObjectBase *p = (PYAObjectBase *) self;
cls_decl = p->cls_decl ();
} else {
- cls_decl = PythonInterpreter::instance ()->cls_for_type ((PyTypeObject *) self);
+ cls_decl = PythonModule::cls_for_type ((PyTypeObject *) self);
}
tl_assert (cls_decl != 0);
@@ -533,7 +533,7 @@ method_name_from_id (int mid, PyObject *self)
/**
* @brief Gets the method name from a method id
*/
-std::string
+std::string
property_name_from_id (int mid, PyObject *self)
{
const gsi::ClassBase *cls_decl = 0;
@@ -542,7 +542,7 @@ property_name_from_id (int mid, PyObject *self)
PYAObjectBase *p = (PYAObjectBase *) self;
cls_decl = p->cls_decl ();
} else {
- cls_decl = PythonInterpreter::instance ()->cls_for_type ((PyTypeObject *) self);
+ cls_decl = PythonModule::cls_for_type ((PyTypeObject *) self);
}
tl_assert (cls_decl != 0);
@@ -563,7 +563,7 @@ property_name_from_id (int mid, PyObject *self)
return cls_decl->name () + "." + mt->property_name (mid);
}
-static PyObject *
+static PyObject *
get_return_value (PYAObjectBase *self, gsi::SerialArgs &retlist, const gsi::MethodBase *meth, tl::Heap &heap)
{
PyObject *ret = NULL;
@@ -603,7 +603,7 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict)
p = (PYAObjectBase *) self;
cls_decl = p->cls_decl ();
} else {
- cls_decl = PythonInterpreter::instance ()->cls_for_type ((PyTypeObject *) self);
+ cls_decl = PythonModule::cls_for_type ((PyTypeObject *) self);
}
tl_assert (cls_decl != 0);
@@ -632,7 +632,7 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict)
if ((*m)->is_callback()) {
// ignore callbacks
-
+
} else if ((*m)->compatible_with_num_args (argc)) {
++candidates;
@@ -647,7 +647,7 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict)
if (! strict) {
return 0;
- }
+ }
std::set nargs;
for (MethodTableEntry::method_iterator m = mt->begin (mid); m != mt->end (mid); ++m) {
@@ -756,7 +756,7 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict)
static PyObject *
object_dup (PyObject *self, PyObject *args)
{
- const gsi::ClassBase *cls_decl_self = PythonInterpreter::instance ()->cls_for_type (Py_TYPE (self));
+ const gsi::ClassBase *cls_decl_self = PythonModule::cls_for_type (Py_TYPE (self));
tl_assert (cls_decl_self != 0);
if (! PyArg_ParseTuple (args, "")) {
@@ -781,7 +781,7 @@ object_dup (PyObject *self, PyObject *args)
static PyObject *
object_assign (PyObject *self, PyObject *args)
{
- const gsi::ClassBase *cls_decl_self = PythonInterpreter::instance ()->cls_for_type (Py_TYPE (self));
+ const gsi::ClassBase *cls_decl_self = PythonModule::cls_for_type (Py_TYPE (self));
tl_assert (cls_decl_self != 0);
PyObject *src = NULL;
@@ -789,12 +789,12 @@ object_assign (PyObject *self, PyObject *args)
return NULL;
}
- const gsi::ClassBase *cls_decl_src = PythonInterpreter::instance ()->cls_for_type (Py_TYPE (src));
+ const gsi::ClassBase *cls_decl_src = PythonModule::cls_for_type (Py_TYPE (src));
tl_assert (cls_decl_src != 0);
if (cls_decl_src != cls_decl_self) {
throw tl::Exception (tl::to_string (QObject::tr ("Type is not identical on assign")));
- }
+ }
if (! cls_decl_self->can_copy ()) {
throw tl::Exception (tl::to_string (QObject::tr ("No assignment provided for class '%s'")), cls_decl_self->name ());
}
@@ -858,7 +858,7 @@ object_default_le_impl (PyObject *self, PyObject *args)
PythonRef lt_res (PyObject_Call (lt_method, args, NULL));
if (! lt_res) {
return NULL;
- }
+ }
return c2python (python2c (eq_res.get ()) || python2c (lt_res.get ()));
}
@@ -881,7 +881,7 @@ object_default_gt_impl (PyObject *self, PyObject *args)
PythonRef lt_res (PyObject_Call (lt_method, args, NULL));
if (! lt_res) {
return NULL;
- }
+ }
return c2python (! (python2c (eq_res.get ()) || python2c (lt_res.get ())));
}
@@ -1032,7 +1032,7 @@ method_adaptor (int mid, PyObject *self, PyObject *args)
try {
- int i = 0;
+ int i = 0;
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); i < argc && a != meth->end_arguments (); ++a, ++i) {
push_arg (*a, arglist, PyTuple_GetItem (args, i), heap);
}
@@ -1065,7 +1065,7 @@ method_adaptor (int mid, PyObject *self, PyObject *args)
return ret;
}
-template
+template
PyObject *method_adaptor (PyObject *self, PyObject *args)
{
return method_adaptor (N, self, args);
@@ -1256,7 +1256,7 @@ property_getter_adaptor (int mid, PyObject *self, PyObject *args)
return ret;
}
-template
+template
PyObject *property_getter_adaptor (PyObject *self, PyObject *args)
{
return property_getter_adaptor (N, self, args);
@@ -1418,7 +1418,7 @@ property_setter_adaptor (int mid, PyObject *self, PyObject *args)
return ret;
}
-template
+template
PyObject *property_setter_adaptor (PyObject *self, PyObject *args)
{
return property_setter_adaptor (N, self, args);
@@ -1582,7 +1582,7 @@ method_init_adaptor (int mid, PyObject *self, PyObject *args)
try {
- int i = 0;
+ int i = 0;
int argc = args == NULL ? 0 : int (PyTuple_Size (args));
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); i < argc && a != meth->end_arguments (); ++a, ++i) {
push_arg (*a, arglist, PyTuple_GetItem (args, i), heap);
@@ -1623,7 +1623,7 @@ method_init_adaptor (int mid, PyObject *self, PyObject *args)
return NULL;
}
-template
+template
PyObject *method_init_adaptor (PyObject *self, PyObject *args)
{
return method_init_adaptor (N, self, args);
@@ -1789,7 +1789,7 @@ property_getter_impl (int mid, PyObject *self)
p = (PYAObjectBase *) self;
cls_decl = p->cls_decl ();
} else {
- cls_decl = PythonInterpreter::instance ()->cls_for_type ((PyTypeObject *) self);
+ cls_decl = PythonModule::cls_for_type ((PyTypeObject *) self);
}
const MethodTable *mt = MethodTable::method_table_by_class (cls_decl);
@@ -1860,7 +1860,7 @@ property_getter_func (PyObject *self, void *closure)
return ret;
}
-static PyObject *
+static PyObject *
property_setter_impl (int mid, PyObject *self, PyObject *value)
{
const gsi::ClassBase *cls_decl;
@@ -1870,7 +1870,7 @@ property_setter_impl (int mid, PyObject *self, PyObject *value)
p = (PYAObjectBase *) self;
cls_decl = p->cls_decl ();
} else {
- cls_decl = PythonInterpreter::instance ()->cls_for_type ((PyTypeObject *) self);
+ cls_decl = PythonModule::cls_for_type ((PyTypeObject *) self);
}
if (p && p->const_ref ()) {
@@ -2012,7 +2012,7 @@ property_setter_impl (int mid, PyObject *self, PyObject *value)
}
}
-static int
+static int
property_setter_func (PyObject *self, PyObject *value, void *closure)
{
int res = -1;
@@ -2052,7 +2052,7 @@ static int
pya_object_init (PyObject * /*self*/, PyObject *args, PyObject *kwds)
{
// no particular initialization
- static char *kwlist[] = {NULL};
+ static char *kwlist[] = {NULL};
if (! PyArg_ParseTupleAndKeywords (args, kwds, "", kwlist)) {
return -1;
} else {
@@ -2061,14 +2061,14 @@ pya_object_init (PyObject * /*self*/, PyObject *args, PyObject *kwds)
}
/**
- * @brief Factory for a base class object
+ * @brief Factory for a base class object
*/
static PyObject *
pya_object_new (PyTypeObject *type, PyObject * /*args*/, PyObject * /*kwds*/)
{
// create the object
PYAObjectBase *self = (PYAObjectBase *) type->tp_alloc (type, 0);
- new (self) PYAObjectBase (PythonInterpreter::instance ()->cls_for_type (type));
+ new (self) PYAObjectBase (PythonModule::cls_for_type (type));
return (PyObject *) self;
}
@@ -2114,7 +2114,7 @@ static bool is_reserved_word (const std::string &name)
}
/**
- * @brief Extracts the Python name from a generic name
+ * @brief Extracts the Python name from a generic name
*
* Returns an empty string if no Python name could be generated.
*/
@@ -2221,228 +2221,175 @@ static std::string extract_python_name (const std::string &name)
}
return name;
-
+
}
}
+
// --------------------------------------------------------------------------
-// The interpreter implementation
+// The PythonModule implementation
-static const char *pya_module_name = "pya";
+std::map PythonModule::m_python_doc;
+std::map PythonModule::m_cls_map;
+std::map PythonModule::m_rev_cls_map;
-#if PY_MAJOR_VERSION < 3
-
-static PyObject *
-init_pya_module ()
+PythonModule::PythonModule ()
+ : mp_mod_def (0)
{
+ // .. nothing yet ..
+}
+
+PythonModule::~PythonModule ()
+{
+ PYAObjectBase::clear_callbacks_cache ();
+
+ while (!m_methods_heap.empty ()) {
+ delete m_methods_heap.back ();
+ m_methods_heap.pop_back ();
+ }
+
+ while (!m_getseters_heap.empty ()) {
+ delete m_getseters_heap.back ();
+ m_getseters_heap.pop_back ();
+ }
+
+ m_string_heap.clear ();
+
+ if (mp_mod_def) {
+ delete[] mp_mod_def;
+ mp_mod_def = 0;
+ }
+}
+
+PyObject *
+PythonModule::module ()
+{
+ return mp_module.get ();
+}
+
+void
+PythonModule::init (const char *mod_name, const char *description)
+{
+ m_mod_name = mod_name;
+ m_mod_description = description;
+
static PyMethodDef module_methods[] = {
- {NULL} // Sentinel
+ {NULL} // Sentinel
};
- return Py_InitModule3 (pya_module_name, module_methods, "KLayout Python API.");
-}
+ PyObject *module = 0;
+
+#if PY_MAJOR_VERSION < 3
+ module = Py_InitModule3 (m_mod_name.c_str (), module_methods, m_mod_description.c_str ());
#else
-static PyObject *
-init_pya_module ()
+ static struct PyModuleDef mod_def = {
+ PyModuleDef_HEAD_INIT,
+ m_mod_name.c_str (),
+ NULL, // module documentation
+ -1, // size of per-interpreter state of the module,
+ // if the module keeps state in global variables.
+ module_methods
+ };
+
+ tl_assert (mp_mod_def == 0);
+
+ // prepare a persistent structure with the module definition
+ // and pass this one to PyModule_Create
+ mp_mod_def = new char[sizeof (PyModuleDef)];
+ memcpy ((void *) mp_mod_def, (const void *) &mod_def, sizeof (PyModuleDef));
+ module = PyModule_Create ((PyModuleDef *) mp_mod_def);
+
+#endif
+
+ mp_module = PythonRef (module);
+}
+
+void
+PythonModule::init (const char *mod_name, PyObject *module)
{
- static struct PyModuleDef moduledef = {
- PyModuleDef_HEAD_INIT,
- pya_module_name, // m_name
- "KLayout Python API.", // m_doc
- -1, // m_size
- NULL, // m_methods
- NULL, // m_reload
- NULL, // m_traverse
- NULL, // m_clear
- NULL, // m_free
- };
- return PyModule_Create (&moduledef);
+ m_mod_name = mod_name;
+ mp_module = PythonRef (module);
}
-#endif
+PyMethodDef *
+PythonModule::make_method_def ()
+{
+ static PyMethodDef md = { };
+ m_methods_heap.push_back (new PyMethodDef (md));
+ return m_methods_heap.back ();
+}
-PythonInterpreter::PythonInterpreter ()
- : mp_current_console (0), mp_current_exec_handler (0), m_current_exec_level (0),
- m_in_trace (false), m_block_exceptions (false), m_ignore_next_exception (false),
- mp_current_frame (NULL), mp_py3_app_name (0)
-{
- tl::SelfTimer timer (tl::verbosity () >= 21, "Initializing Python");
+PyGetSetDef *
+PythonModule::make_getset_def ()
+{
+ static PyGetSetDef gsd = { };
+ m_getseters_heap.push_back (new PyGetSetDef (gsd));
+ return m_getseters_heap.back ();
+}
- std::string app_path = tl::to_string (QCoreApplication::applicationFilePath ());
+char *
+PythonModule::make_string (const std::string &s)
+{
+ m_string_heap.push_back (s);
+ return const_cast (m_string_heap.back ().c_str ());
+}
-#if PY_MAJOR_VERSION >= 3
-
- // if set, use $KLAYOUT_PYTHONPATH to initialize the path
-# if defined(_WIN32)
-
- tl_assert (sizeof (wchar_t) == 2);
-
- const wchar_t *python_path = _wgetenv (L"KLAYOUT_PYTHONPATH");
- if (python_path) {
-
- Py_SetPath (python_path);
+void
+PythonModule::add_python_doc (const gsi::ClassBase & /*cls*/, const MethodTable *mt, int mid, const std::string &doc)
+{
+ for (MethodTableEntry::method_iterator m = mt->begin (mid); m != mt->end (mid); ++m) {
+ std::string &doc_string = m_python_doc [*m];
+ doc_string += doc;
+ doc_string += "\n\n";
+ }
+}
+std::string
+PythonModule::python_doc (const gsi::MethodBase *method)
+{
+ std::map::const_iterator d = m_python_doc.find (method);
+ if (d != m_python_doc.end ()) {
+ return d->second;
} else {
-
- // If present, read the paths from a file in INST_PATH/.python-paths.txt.
- // The content of this file is evaluated as an expression and the result
- // is placed inside the Python path.
-
- try {
-
- QString path;
-
- QDir inst_dir (QCoreApplication::applicationDirPath ());
- QFileInfo fi (inst_dir.absoluteFilePath (tl::to_qstring(".python-paths.txt")));
- if (fi.exists ()) {
-
- tl::log << tl::to_string (QObject::tr ("Reading Python path from ")) << tl::to_string (fi.filePath ());
-
- QFile paths_txt (fi.filePath ());
- paths_txt.open (QIODevice::ReadOnly);
-
- tl::Eval eval;
- eval.set_global_var ("inst_path", tl::Variant (tl::to_string (inst_dir.absolutePath ())));
- tl::Expression ex;
- eval.parse (ex, paths_txt.readAll ().constData ());
- tl::Variant v = ex.execute ();
-
- if (v.is_list ()) {
- for (tl::Variant::iterator i = v.begin (); i != v.end (); ++i) {
- if (! path.isEmpty ()) {
- path += tl::to_qstring (";");
- }
- path += tl::to_qstring (i->to_string ());
- }
- }
-
- }
-
- // note: this is a hack, but linking with toWCharArray fails since wchar_t is treated
- // as a built-in type in our build
- Py_SetPath ((const wchar_t *) path.utf16 ());
-
- } catch (tl::Exception &ex) {
- tl::error << tl::to_string (QObject::tr ("Evaluation of Python path expression failed")) << ": " << ex.msg ();
- } catch (...) {
- tl::error << tl::to_string (QObject::tr ("Evaluation of Python path expression failed"));
- }
-
+ return std::string ();
}
+}
-# else
+void
+PythonModule::make_classes ()
+{
+ PyObject *module = mp_module.get ();
- const char *python_path = getenv ("KLAYOUT_PYTHONPATH");
- if (python_path) {
+ // Create a (built-in) base class for all objects exposed by this module
- QString path = QString::fromLocal8Bit (python_path);
+ m_base_class_name = m_mod_name + ".__Base";
- if (sizeof (wchar_t) == 4) {
- Py_SetPath ((const wchar_t *) path.toUcs4 ().constData ());
- } else if (sizeof (wchar_t) == 2) {
- Py_SetPath ((const wchar_t *) path.utf16 ());
- }
-
- }
-
-# endif
-
-#endif
+ PyTypeObject *base_class = (PyTypeObject *) PyType_Type.tp_alloc (&PyType_Type, sizeof (PyTypeObject));
+ tl_assert (base_class != NULL);
+ mp_base_class = PythonRef ((PyObject *) base_class);
+ base_class->tp_base = &PyBaseObject_Type;
+ base_class->tp_name = m_base_class_name.c_str ();
+ base_class->tp_basicsize = sizeof (PYAObjectBase);
+ base_class->tp_init = &pya_object_init;
+ base_class->tp_new = &pya_object_new;
+ base_class->tp_dealloc = (destructor) &pya_object_deallocate;
+ base_class->tp_setattro = PyObject_GenericSetAttr;
+ base_class->tp_getattro = PyObject_GenericGetAttr;
#if PY_MAJOR_VERSION < 3
-
- Py_SetProgramName (make_string (app_path));
-
- Py_InitializeEx (0 /*don't set signals*/);
-
- // Set dummy argv[]
- // TODO: more?
- char *argv[1] = { make_string (app_path) };
-#if PY_MINOR_VERSION >= 7
- PySys_SetArgvEx (1, argv, 0);
+ base_class->tp_flags = Py_TPFLAGS_HEAPTYPE | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES;
#else
- PySys_SetArgv (1, argv);
+ base_class->tp_flags = Py_TPFLAGS_HEAPTYPE | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
#endif
- PyObject *module = init_pya_module ();
- if (module == NULL) {
+ if (PyType_Ready (base_class) < 0) {
check_error ();
return;
}
- PyImport_ImportModule (pya_module_name);
+ PyModule_AddObject (module, "__Base", (PyObject *) base_class);
-#else
-
- // Python 3 requires a unicode string for the application name
- PyObject *an = c2python (app_path);
- tl_assert (an != NULL);
- mp_py3_app_name = PyUnicode_AsWideCharString (an, NULL);
- tl_assert (mp_py3_app_name != NULL);
- Py_DECREF (an);
- Py_SetProgramName (mp_py3_app_name);
-
- PyImport_AppendInittab (pya_module_name, &init_pya_module);
- Py_InitializeEx (0 /*don't set signals*/);
-
- // Set dummy argv[]
- // TODO: more?
- wchar_t *argv[1] = { mp_py3_app_name };
- PySys_SetArgvEx (1, argv, 0);
-
- // Import the module
- PyObject *module = PyImport_ImportModule (pya_module_name);
- if (module == NULL) {
- check_error ();
- return;
- }
-
-#endif
-
- m_object_heap.push_back (PythonRef (module));
-
-
- // Create a (built-in) base class for all objects exposed by pya
-
- static PyTypeObject base_class = {
- PyVarObject_HEAD_INIT (&PyType_Type, 0)
- "pya.__Base", // tp_name
- sizeof (PYAObjectBase) // tp_size
- };
-
- m_object_heap.push_back (PythonRef ((PyObject *) &base_class));
- base_class.tp_base = &PyBaseObject_Type;
- base_class.tp_basicsize = sizeof (PYAObjectBase);
- base_class.tp_init = &pya_object_init;
- base_class.tp_new = &pya_object_new;
- base_class.tp_dealloc = (destructor) &pya_object_deallocate;
-#if PY_MAJOR_VERSION < 3
- base_class.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES;
-#else
- base_class.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
-#endif
- base_class.tp_setattro = PyObject_GenericSetAttr;
- base_class.tp_getattro = PyObject_GenericGetAttr;
-
- if (PyType_Ready (&base_class) < 0) {
- check_error ();
- return;
- }
-
- PyType_Ready (&base_class);
- Py_INCREF (&base_class);
-
- PyModule_AddObject (module, "__Base", (PyObject *) &base_class);
-
-
- // Build two objects that provide a way to redirect stdout, stderr
- // and instatiate them two times for stdout and stderr.
- PYAChannelObject::make_class (module);
- m_stdout_channel = PythonRef (PYAChannelObject::create (gsi::Console::OS_stdout));
- m_stdout = PythonPtr (m_stdout_channel.get ());
- m_stderr_channel = PythonRef (PYAChannelObject::create (gsi::Console::OS_stderr));
- m_stderr = PythonPtr (m_stderr_channel.get ());
// Build a class for descriptors for static attributes
PYAStaticAttributeDescriptorObject::make_class (module);
@@ -2493,17 +2440,17 @@ PythonInterpreter::PythonInterpreter ()
// Create the class as a heap object, since that way we can dynamically extend the objects
PythonRef bases (PyTuple_New (1));
- PyTypeObject *base = &base_class;
+ PyObject *base = mp_base_class.get ();
if (c->base () != 0) {
std::map ::const_iterator cb = m_rev_cls_map.find (c->base ());
tl_assert (cb != m_rev_cls_map.end ());
- base = cb->second;
+ base = (PyObject *) cb->second;
}
Py_INCREF (base);
- PyTuple_SetItem (bases.get (), 0, (PyObject *) base);
+ PyTuple_SetItem (bases.get (), 0, base);
PythonRef dict (PyDict_New ());
- PyDict_SetItemString (dict.get (), "__module__", PythonRef (c2python (pya_module_name)).get ());
+ PyDict_SetItemString (dict.get (), "__module__", PythonRef (c2python (m_mod_name)).get ());
PyDict_SetItemString (dict.get (), "__doc__", PythonRef (c2python (c->doc ())).get ());
PythonRef args (PyTuple_New (3));
@@ -2511,7 +2458,7 @@ PythonInterpreter::PythonInterpreter ()
PyTuple_SetItem (args.get (), 1, bases.release ());
PyTuple_SetItem (args.get (), 2, dict.release ());
- PyTypeObject *type = (PyTypeObject *) PyObject_Call ((PyObject *) &PyType_Type, args.get (), NULL);
+ PyTypeObject *type = (PyTypeObject *) PyObject_Call ((PyObject *) &PyType_Type, args.get (), NULL);
tl_assert (type != NULL);
PyModule_AddObject (module, c->name ().c_str (), (PyObject *) type);
@@ -2614,7 +2561,7 @@ PythonInterpreter::PythonInterpreter ()
std::string doc;
// add getter and setter documentation, create specific Python documentation
-
+
for (MethodTableEntry::method_iterator m = begin_getters; m != end_getters; ++m) {
if (! doc.empty ()) {
doc += "\n\n";
@@ -2638,8 +2585,8 @@ PythonInterpreter::PythonInterpreter ()
// non-static attribute getters/setters
PyGetSetDef *getset = make_getset_def ();
getset->name = make_string (name);
- getset->get = begin_getters != end_getters ? &property_getter_func : NULL;
- getset->set = begin_setters != end_setters ? &property_setter_func : NULL;
+ getset->get = begin_getters != end_getters ? &property_getter_func : NULL;
+ getset->set = begin_setters != end_setters ? &property_setter_func : NULL;
getset->doc = make_string (doc);
getset->closure = make_closure (getter_mid, setter_mid);
@@ -2758,7 +2705,7 @@ PythonInterpreter::PythonInterpreter ()
} else if (name == "size" && m_first->compatible_with_num_args (0)) {
- // The size method is also routed via the sequence methods protocol if there
+ // The size method is also routed via the sequence methods protocol if there
// is a [] function
add_python_doc (*c, mt, mid, tl::to_string (QObject::tr ("This method is also available as 'len(object)'")));
alt_names.push_back ("__len__");
@@ -2796,7 +2743,7 @@ PythonInterpreter::PythonInterpreter ()
set_type_attr (type, name, attr);
} else if (isupper (name [0]) || m_first->is_const ()) {
-
+
if ((mt->end (mid) - mt->begin (mid)) == 1 && m_first->begin_arguments () == m_first->end_arguments ()) {
// static methods without arguments which start with a capital letter are treated as constants
@@ -2849,13 +2796,13 @@ PythonInterpreter::PythonInterpreter ()
// Unlike Ruby, Python does not automatically implement != from == for example.
// We assume that "==" and "<" are the minimum requirements for full comparison
// and "==" is the minimum requirement for equality. Hence:
- // * If "==" is given, but no "!=", synthesize
+ // * If "==" is given, but no "!=", synthesize
// "a != b" by "!a == b"
// * If "==" and "<" are given, synthesize if required
// "a <= b" by "a < b || a == b"
// "a > b" by "!(a < b || a == b)" (could be b < a, but this avoids having to switch arguments)
// "a >= b" by "!a < b"
-
+
bool has_eq = mt->find_method (false, "==").first;
bool has_ne = mt->find_method (false, "!=").first;
bool has_lt = mt->find_method (false, "<").first;
@@ -2951,25 +2898,222 @@ PythonInterpreter::PythonInterpreter ()
}
}
+}
+
+const gsi::ClassBase *PythonModule::cls_for_type (PyTypeObject *type)
+{
+ while (type) {
+ std::map ::const_iterator t = m_cls_map.find (type);
+ if (t != m_cls_map.end ()) {
+ return t->second;
+ }
+ // not found - try base class
+ type = type->tp_base;
+ }
+ return 0;
+}
+
+PyTypeObject *PythonModule::type_for_cls (const gsi::ClassBase *cls)
+{
+ std::map ::const_iterator s = m_rev_cls_map.find (cls);
+ if (s == m_rev_cls_map.end ()) {
+ return NULL;
+ } else {
+ return s->second;
+ }
+}
+
+// --------------------------------------------------------------------------
+// The interpreter implementation
+
+static const char *pya_module_name = "pya";
+
+#if PY_MAJOR_VERSION < 3
+
+static PyObject *
+init_pya_module ()
+{
+ static PyMethodDef module_methods[] = {
+ {NULL} // Sentinel
+ };
+ return Py_InitModule3 (pya_module_name, module_methods, "KLayout Python API.");
+}
+
+#else
+
+static PyObject *
+init_pya_module ()
+{
+ static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ pya_module_name, // m_name
+ "KLayout Python API.", // m_doc
+ -1, // m_size
+ NULL, // m_methods
+ NULL, // m_reload
+ NULL, // m_traverse
+ NULL, // m_clear
+ NULL, // m_free
+ };
+ return PyModule_Create (&moduledef);
+}
+
+#endif
+
+PythonInterpreter::PythonInterpreter ()
+ : mp_current_console (0), mp_current_exec_handler (0), m_current_exec_level (0),
+ m_in_trace (false), m_block_exceptions (false), m_ignore_next_exception (false),
+ mp_current_frame (NULL), mp_py3_app_name (0)
+{
+ tl::SelfTimer timer (tl::verbosity () >= 21, "Initializing Python");
+
+ std::string app_path = tl::to_string (QCoreApplication::applicationFilePath ());
+
+#if PY_MAJOR_VERSION >= 3
+
+ // if set, use $KLAYOUT_PYTHONPATH to initialize the path
+# if defined(_WIN32)
+
+ tl_assert (sizeof (wchar_t) == 2);
+
+ const wchar_t *python_path = _wgetenv (L"KLAYOUT_PYTHONPATH");
+ if (python_path) {
+
+ Py_SetPath (python_path);
+
+ } else {
+
+ // If present, read the paths from a file in INST_PATH/.python-paths.txt.
+ // The content of this file is evaluated as an expression and the result
+ // is placed inside the Python path.
+
+ try {
+
+ QString path;
+
+ QDir inst_dir (QCoreApplication::applicationDirPath ());
+ QFileInfo fi (inst_dir.absoluteFilePath (tl::to_qstring(".python-paths.txt")));
+ if (fi.exists ()) {
+
+ tl::log << tl::to_string (QObject::tr ("Reading Python path from ")) << tl::to_string (fi.filePath ());
+
+ QFile paths_txt (fi.filePath ());
+ paths_txt.open (QIODevice::ReadOnly);
+
+ tl::Eval eval;
+ eval.set_global_var ("inst_path", tl::Variant (tl::to_string (inst_dir.absolutePath ())));
+ tl::Expression ex;
+ eval.parse (ex, paths_txt.readAll ().constData ());
+ tl::Variant v = ex.execute ();
+
+ if (v.is_list ()) {
+ for (tl::Variant::iterator i = v.begin (); i != v.end (); ++i) {
+ if (! path.isEmpty ()) {
+ path += tl::to_qstring (";");
+ }
+ path += tl::to_qstring (i->to_string ());
+ }
+ }
+
+ }
+
+ // note: this is a hack, but linking with toWCharArray fails since wchar_t is treated
+ // as a built-in type in our build
+ Py_SetPath ((const wchar_t *) path.utf16 ());
+
+ } catch (tl::Exception &ex) {
+ tl::error << tl::to_string (QObject::tr ("Evaluation of Python path expression failed")) << ": " << ex.msg ();
+ } catch (...) {
+ tl::error << tl::to_string (QObject::tr ("Evaluation of Python path expression failed"));
+ }
+
+ }
+
+# else
+
+ const char *python_path = getenv ("KLAYOUT_PYTHONPATH");
+ if (python_path) {
+
+ QString path = QString::fromLocal8Bit (python_path);
+
+ if (sizeof (wchar_t) == 4) {
+ Py_SetPath ((const wchar_t *) path.toUcs4 ().constData ());
+ } else if (sizeof (wchar_t) == 2) {
+ Py_SetPath ((const wchar_t *) path.utf16 ());
+ }
+
+ }
+
+# endif
+
+#endif
+
+#if PY_MAJOR_VERSION < 3
+
+ Py_SetProgramName (make_string (app_path));
+
+ Py_InitializeEx (0 /*don't set signals*/);
+
+ // Set dummy argv[]
+ // TODO: more?
+ char *argv[1] = { make_string (app_path) };
+#if PY_MINOR_VERSION >= 7
+ PySys_SetArgvEx (1, argv, 0);
+#else
+ PySys_SetArgv (1, argv);
+#endif
+
+ PyObject *module = init_pya_module ();
+ if (module == NULL) {
+ check_error ();
+ return;
+ }
+
+ PyImport_ImportModule (pya_module_name);
+
+#else
+
+ // Python 3 requires a unicode string for the application name
+ PyObject *an = c2python (app_path);
+ tl_assert (an != NULL);
+ mp_py3_app_name = PyUnicode_AsWideCharString (an, NULL);
+ tl_assert (mp_py3_app_name != NULL);
+ Py_DECREF (an);
+ Py_SetProgramName (mp_py3_app_name);
+
+ PyImport_AppendInittab (pya_module_name, &init_pya_module);
+ Py_InitializeEx (0 /*don't set signals*/);
+
+ // Set dummy argv[]
+ // TODO: more?
+ wchar_t *argv[1] = { mp_py3_app_name };
+ PySys_SetArgvEx (1, argv, 0);
+
+ // Import the module
+ PyObject *module = PyImport_ImportModule (pya_module_name);
+ if (module == NULL) {
+ check_error ();
+ return;
+ }
+
+#endif
+
+ // Build two objects that provide a way to redirect stdout, stderr
+ // and instatiate them two times for stdout and stderr.
+ PYAChannelObject::make_class (module);
+ m_stdout_channel = PythonRef (PYAChannelObject::create (gsi::Console::OS_stdout));
+ m_stdout = PythonPtr (m_stdout_channel.get ());
+ m_stderr_channel = PythonRef (PYAChannelObject::create (gsi::Console::OS_stderr));
+ m_stderr = PythonPtr (m_stderr_channel.get ());
+
+ m_pya_module.init (pya_module_name, module);
+ m_pya_module.make_classes ();
sp_interpreter = this;
}
-PythonInterpreter::~PythonInterpreter ()
+PythonInterpreter::~PythonInterpreter ()
{
- m_object_heap.clear ();
- PYAObjectBase::clear_callbacks_cache ();
-
- while (!m_methods_heap.empty ()) {
- delete m_methods_heap.back ();
- m_methods_heap.pop_back ();
- }
-
- while (!m_getseters_heap.empty ()) {
- delete m_getseters_heap.back ();
- m_getseters_heap.pop_back ();
- }
-
m_stdout_channel = PythonRef ();
m_stderr_channel = PythonRef ();
m_stdout = PythonPtr ();
@@ -2977,8 +3121,6 @@ PythonInterpreter::~PythonInterpreter ()
Py_Finalize ();
- m_string_heap.clear ();
-
if (mp_py3_app_name) {
PyMem_Free (mp_py3_app_name);
mp_py3_app_name = 0;
@@ -2987,40 +3129,7 @@ PythonInterpreter::~PythonInterpreter ()
sp_interpreter = 0;
}
-PyMethodDef *
-PythonInterpreter::make_method_def ()
-{
- static PyMethodDef md = { };
- m_methods_heap.push_back (new PyMethodDef (md));
- return m_methods_heap.back ();
-}
-
-PyGetSetDef *
-PythonInterpreter::make_getset_def ()
-{
- static PyGetSetDef gsd = { };
- m_getseters_heap.push_back (new PyGetSetDef (gsd));
- return m_getseters_heap.back ();
-}
-
-char *
-PythonInterpreter::make_string (const std::string &s)
-{
- m_string_heap.push_back (s);
- return const_cast (m_string_heap.back ().c_str ());
-}
-
-void
-PythonInterpreter::add_python_doc (const gsi::ClassBase & /*cls*/, const MethodTable *mt, int mid, const std::string &doc)
-{
- for (MethodTableEntry::method_iterator m = mt->begin (mid); m != mt->end (mid); ++m) {
- std::string &doc_string = m_python_doc [*m];
- doc_string += doc;
- doc_string += "\n\n";
- }
-}
-
-void
+void
PythonInterpreter::add_path (const std::string &p)
{
PyObject *path = PySys_GetObject ((char *) "path");
@@ -3052,19 +3161,19 @@ PythonInterpreter::require (const std::string & /*filename*/)
throw tl::Exception (tl::to_string (QObject::tr ("'require' not implemented for Python interpreter")));
}
-void
+void
PythonInterpreter::set_debugger_scope (const std::string &filename)
{
m_debugger_scope = filename;
}
-void
+void
PythonInterpreter::remove_debugger_scope ()
{
m_debugger_scope.clear ();
}
-void
+void
PythonInterpreter::ignore_next_exception ()
{
if (mp_current_exec_handler) {
@@ -3082,7 +3191,7 @@ PythonInterpreter::load_file (const std::string &filename)
/**
* @brief Gets the global and local variable lists for a given context index
*/
-void
+void
PythonInterpreter::get_context (int context, PythonRef &globals, PythonRef &locals, const char *file)
{
globals = PythonRef ();
@@ -3113,7 +3222,7 @@ PythonInterpreter::get_context (int context, PythonRef &globals, PythonRef &loca
globals = dict;
locals = dict;
-
+
if (file) {
PythonRef fn (c2python (file));
@@ -3154,7 +3263,7 @@ PythonInterpreter::eval_string (const char *expr, const char *file, int /*line*/
/**
* @brief Evaluates the given expression or executes the given statement
* The expression is given int "string". If "eval_expr" is true, the string is evaluated as expression
- * and the result is returned in the variant. If "eval_expr" is false, the string is evaluated, the
+ * and the result is returned in the variant. If "eval_expr" is false, the string is evaluated, the
* result is printed to the currently active console and a nil variant is returned.
*/
tl::Variant
@@ -3235,7 +3344,7 @@ PythonInterpreter::available () const
return true;
}
-void
+void
PythonInterpreter::initialize ()
{
// .. no implementation required ..
@@ -3247,19 +3356,19 @@ PythonInterpreter::prepare_trace (PyObject *fn_object)
std::map::const_iterator f = m_file_id_map.find (fn_object);
if (f == m_file_id_map.end ()) {
f = m_file_id_map.insert (std::make_pair (fn_object, mp_current_exec_handler->id_for_path (this, normalize_path (python2c (fn_object))))).first;
- }
+ }
return f->second;
}
// TODO: make the Python object the interpreter and don't use singleton instances (multi-threading support)
-static
+static
int pya_trace_func (PyObject * /*obj*/, PyFrameObject *frame, int event, PyObject *arg)
{
return PythonInterpreter::instance ()->trace_func (frame, event, arg);
}
-int
+int
PythonInterpreter::trace_func (PyFrameObject *frame, int event, PyObject *arg)
{
if (! mp_current_exec_handler || m_in_trace) {
@@ -3333,8 +3442,8 @@ PythonInterpreter::trace_func (PyFrameObject *frame, int event, PyObject *arg)
}
// TODO: really needed?
- // Ruby tends to call this callback twice - once from rb_f_raise and then
- // from rb_exc_raise. We use the m_block_exceptions flag to suppress the
+ // Ruby tends to call this callback twice - once from rb_f_raise and then
+ // from rb_exc_raise. We use the m_block_exceptions flag to suppress the
// second one
m_block_exceptions = true;
@@ -3352,7 +3461,7 @@ PythonInterpreter::trace_func (PyFrameObject *frame, int event, PyObject *arg)
return -1;
}
-void
+void
PythonInterpreter::push_exec_handler (gsi::ExecutionHandler *exec_handler)
{
if (mp_current_exec_handler) {
@@ -3364,7 +3473,7 @@ PythonInterpreter::push_exec_handler (gsi::ExecutionHandler *exec_handler)
mp_current_exec_handler = exec_handler;
m_file_id_map.clear ();
- // if we happen to push the exec handler inside the execution,
+ // if we happen to push the exec handler inside the execution,
// signal start of execution
if (m_current_exec_level > 0) {
mp_current_exec_handler->start_exec (this);
@@ -3376,7 +3485,7 @@ PythonInterpreter::remove_exec_handler (gsi::ExecutionHandler *exec_handler)
{
if (mp_current_exec_handler == exec_handler) {
- // if we happen to remove the exec handler inside the execution,
+ // if we happen to remove the exec handler inside the execution,
// signal end of execution
if (m_current_exec_level > 0) {
mp_current_exec_handler->end_exec (this);
@@ -3402,10 +3511,10 @@ PythonInterpreter::remove_exec_handler (gsi::ExecutionHandler *exec_handler)
}
}
-void
+void
PythonInterpreter::push_console (gsi::Console *console)
{
- if (! mp_current_console) {
+ if (! mp_current_console) {
PythonPtr current_stdout (PySys_GetObject ((char *) "stdout"));
std::swap (current_stdout, m_stdout);
@@ -3426,7 +3535,7 @@ PythonInterpreter::push_console (gsi::Console *console)
mp_current_console = console;
}
-void
+void
PythonInterpreter::remove_console (gsi::Console *console)
{
if (mp_current_console == console) {
@@ -3480,53 +3589,19 @@ gsi::Console *PythonInterpreter::current_console () const
return mp_current_console;
}
-const gsi::ClassBase *PythonInterpreter::cls_for_type (PyTypeObject *type) const
-{
- while (type) {
- std::map ::const_iterator t = m_cls_map.find (type);
- if (t != m_cls_map.end ()) {
- return t->second;
- }
- // not found - try base class
- type = type->tp_base;
- }
- return 0;
-}
-
-PyTypeObject *PythonInterpreter::type_for_cls (const gsi::ClassBase *cls) const
-{
- std::map ::const_iterator s = m_rev_cls_map.find (cls);
- if (s == m_rev_cls_map.end ()) {
- return NULL;
- } else {
- return s->second;
- }
-}
-
void PythonInterpreter::begin_execution ()
{
- m_file_id_map.clear ();
- m_block_exceptions = false;
- if (m_current_exec_level++ == 0 && mp_current_exec_handler) {
- mp_current_exec_handler->start_exec (this);
+ m_file_id_map.clear ();
+ m_block_exceptions = false;
+ if (m_current_exec_level++ == 0 && mp_current_exec_handler) {
+ mp_current_exec_handler->start_exec (this);
}
}
void PythonInterpreter::end_execution ()
{
- if (m_current_exec_level > 0 && --m_current_exec_level == 0 && mp_current_exec_handler) {
- mp_current_exec_handler->end_exec (this);
- }
-}
-
-std::string
-PythonInterpreter::python_doc (const gsi::MethodBase *method) const
-{
- std::map::const_iterator d = m_python_doc.find (method);
- if (d != m_python_doc.end ()) {
- return d->second;
- } else {
- return std::string ();
+ if (m_current_exec_level > 0 && --m_current_exec_level == 0 && mp_current_exec_handler) {
+ mp_current_exec_handler->end_exec (this);
}
}
diff --git a/src/pya/pya/pya.h b/src/pya/pya/pya.h
index 592b3337b..27eedd30a 100644
--- a/src/pya/pya/pya.h
+++ b/src/pya/pya/pya.h
@@ -34,6 +34,7 @@
#include
#include
#include
+#include
struct _typeobject;
typedef _typeobject PyTypeObject;
@@ -89,6 +90,84 @@ public:
class MethodTable;
+/**
+ * @brief A representative for a Python module
+ * Instantiate this object to represent a python module.
+ * If used externally (for a library), call init with the module name.
+ * Then call make_classes to create the individual classes.
+ */
+class PYA_PUBLIC PythonModule
+{
+public:
+ /**
+ * @brief Constructor
+ */
+ PythonModule ();
+
+ /**
+ * @brief Destructor
+ */
+ ~PythonModule ();
+
+ /**
+ * @brief Initializes the module
+ * This entry point is for external use where the module has not been created yet
+ */
+ void init (const char *mod_name, const char *description);
+
+ /**
+ * @brief Initializes the module
+ * This entry point is for internal use where the module is created by the interpreter
+ */
+ void init (const char *mod_name, PyObject *module);
+
+ /**
+ * @brief Creates the classes after init has been called
+ */
+ void make_classes ();
+
+ /**
+ * @brief Gets the GSI class for a Python class
+ */
+ static const gsi::ClassBase *cls_for_type (PyTypeObject *type);
+
+ /**
+ * @brief The reverse: gets a Python class for a GSI class or NULL if there is no binding
+ */
+ static PyTypeObject *type_for_cls (const gsi::ClassBase *cls);
+
+ /**
+ * @brief Returns additional Python-specific documentation for the given method
+ * If no specific documentation exists, an empty string is returned.
+ */
+ static std::string python_doc (const gsi::MethodBase *method);
+
+ /**
+ * @brief Gets the PyObject for the module
+ */
+ PyObject *module ();
+
+private:
+ void add_python_doc (const gsi::ClassBase &cls, const MethodTable *mt, int mid, const std::string &doc);
+ PyMethodDef *make_method_def ();
+ PyGetSetDef *make_getset_def ();
+ char *make_string (const std::string &s);
+
+ std::list m_string_heap;
+ std::vector m_methods_heap;
+ std::vector m_getseters_heap;
+
+ std::string m_mod_name, m_mod_description;
+ std::string m_base_class_name;
+ PythonRef mp_module;
+ PythonRef mp_base_class;
+ char *mp_mod_def;
+
+ static std::map m_python_doc;
+ static std::map m_cls_map;
+ static std::map m_rev_cls_map;
+};
+
/**
* @brief The python interpreter wrapper class
*/
@@ -214,16 +293,6 @@ public:
*/
std::string version () const;
- /**
- * @brief Gets the GSI class for a Python class
- */
- const gsi::ClassBase *cls_for_type (PyTypeObject *type) const;
-
- /**
- * @brief The reverse: gets a Python class for a GSI class or NULL if there is no binding
- */
- PyTypeObject *type_for_cls (const gsi::ClassBase *cls) const;
-
/**
* @brief Returns the current console
*/
@@ -246,12 +315,6 @@ public:
*/
int trace_func (PyFrameObject *frame, int event, PyObject *arg);
- /**
- * @brief Returns additional Python-specific documentation for the given method
- * If no specific documentation exists, an empty string is returned.
- */
- std::string python_doc (const gsi::MethodBase *method) const;
-
/**
* @brief Returns the singleton reference
*/
@@ -266,22 +329,11 @@ private:
size_t prepare_trace (PyObject *);
tl::Variant eval_int (const char *string, const char *filename, int line, bool eval_expr, int context);
void get_context (int context, PythonRef &globals, PythonRef &locals, const char *file);
- void add_python_doc (const gsi::ClassBase &cls, const MethodTable *mt, int mid, const std::string &doc);
- PyMethodDef *make_method_def ();
- PyGetSetDef *make_getset_def ();
- char *make_string (const std::string &s);
- std::list m_object_heap;
- std::list m_string_heap;
- std::map m_python_doc;
- std::set m_package_paths;
- std::vector m_methods_heap;
- std::vector m_getseters_heap;
PythonRef m_stdout_channel, m_stderr_channel;
PythonPtr m_stdout, m_stderr;
+ std::set m_package_paths;
- std::map m_cls_map;
- std::map m_rev_cls_map;
gsi::Console *mp_current_console;
std::vector m_consoles;
gsi::ExecutionHandler *mp_current_exec_handler;
@@ -296,6 +348,7 @@ private:
PyFrameObject *mp_current_frame;
std::map m_file_id_map;
wchar_t *mp_py3_app_name;
+ pya::PythonModule m_pya_module;
};
}
diff --git a/src/pya/pya/pyaCommon.h b/src/pya/pya/pyaCommon.h
index 90a30727c..e499da69b 100644
--- a/src/pya/pya/pyaCommon.h
+++ b/src/pya/pya/pyaCommon.h
@@ -20,32 +20,19 @@
*/
+#include "tlDefs.h"
#if !defined(HDR_pyaCommon_h)
# define HDR_pyaCommon_h
-# if defined _WIN32 || defined __CYGWIN__
-
-# ifdef MAKE_PYA_LIBRARY
-# define PYA_PUBLIC __declspec(dllexport)
-# else
-# define PYA_PUBLIC __declspec(dllimport)
-# endif
-# define PYA_LOCAL
-# define PYA_PUBLIC_TEMPLATE
-
+# ifdef MAKE_PYA_LIBRARY
+# define PYA_PUBLIC DEF_INSIDE_PUBLIC
+# define PYA_PUBLIC_TEMPLATE DEF_INSIDE_PUBLIC_TEMPLATE
+# define PYA_LOCAL DEF_INSIDE_LOCAL
# else
-
-# if __GNUC__ >= 4 || defined(__clang__)
-# define PYA_PUBLIC __attribute__ ((visibility ("default")))
-# define PYA_PUBLIC_TEMPLATE __attribute__ ((visibility ("default")))
-# define PYA_LOCAL __attribute__ ((visibility ("hidden")))
-# else
-# define PYA_PUBLIC
-# define PYA_PUBLIC_TEMPLATE
-# define PYA_LOCAL
-# endif
-
+# define PYA_PUBLIC DEF_OUTSIDE_PUBLIC
+# define PYA_PUBLIC_TEMPLATE DEF_OUTSIDE_PUBLIC_TEMPLATE
+# define PYA_LOCAL DEF_OUTSIDE_LOCAL
# endif
#endif
diff --git a/src/pya/pya/pyaConvert.cc b/src/pya/pya/pyaConvert.cc
index e6bf1c3c0..d8ba93cb9 100644
--- a/src/pya/pya/pyaConvert.cc
+++ b/src/pya/pya/pyaConvert.cc
@@ -309,7 +309,7 @@ tl::Variant python2c (PyObject *rval, tl::Heap *heap)
} else {
- const gsi::ClassBase *cls = PythonInterpreter::instance ()->cls_for_type (Py_TYPE (rval));
+ const gsi::ClassBase *cls = PythonModule::cls_for_type (Py_TYPE (rval));
if (cls) {
PYAObjectBase *p = (PYAObjectBase *) rval;
@@ -438,7 +438,7 @@ object_to_python (void *obj, PYAObjectBase *self, const gsi::ClassBase *cls, boo
// can't guarantee the lifetime of the container will exceed that
// of the exposed property. Hence copying is safer.
- PyTypeObject *type = PythonInterpreter::instance ()->type_for_cls (clsact);
+ PyTypeObject *type = PythonModule::type_for_cls (clsact);
tl_assert (type != NULL);
// create a instance and copy the value
@@ -459,7 +459,7 @@ object_to_python (void *obj, PYAObjectBase *self, const gsi::ClassBase *cls, boo
} else {
- PyTypeObject *type = PythonInterpreter::instance ()->type_for_cls (clsact);
+ PyTypeObject *type = PythonModule::type_for_cls (clsact);
tl_assert (type != NULL);
// create a instance and copy the value
diff --git a/src/pya/pya/pyaMarshal.cc b/src/pya/pya/pyaMarshal.cc
index d3dac6c6c..8356e7a35 100644
--- a/src/pya/pya/pyaMarshal.cc
+++ b/src/pya/pya/pyaMarshal.cc
@@ -36,9 +36,9 @@ namespace pya
// Serialization adaptors for strings, variants, vectors and maps
/**
- * @brief An adaptor for a string from ruby objects
+ * @brief An adaptor for a string from ruby objects
*/
-class PythonBasedStringAdaptor
+class PythonBasedStringAdaptor
: public gsi::StringAdaptor
{
public:
@@ -69,9 +69,9 @@ private:
};
/**
- * @brief An adaptor for a variant from ruby objects
+ * @brief An adaptor for a variant from ruby objects
*/
-class PythonBasedVariantAdaptor
+class PythonBasedVariantAdaptor
: public gsi::VariantAdaptor
{
public:
@@ -106,7 +106,7 @@ private:
/**
* @brief An adaptor for a vector from Python objects
*/
-class PythonBasedVectorAdaptor
+class PythonBasedVectorAdaptor
: public gsi::VectorAdaptor
{
public:
@@ -132,7 +132,7 @@ class PythonBasedMapAdaptorIterator
public:
PythonBasedMapAdaptorIterator (const PythonPtr &hash, const gsi::ArgType *ainner, const gsi::ArgType *ainner_k);
- virtual void get (gsi::SerialArgs &w, tl::Heap &heap) const;
+ virtual void get (gsi::SerialArgs &w, tl::Heap &heap) const;
virtual bool at_end () const;
virtual void inc ();
@@ -147,7 +147,7 @@ private:
/**
* @brief An adaptor for a map from Python objects
*/
-class PythonBasedMapAdaptor
+class PythonBasedMapAdaptor
: public gsi::MapAdaptor
{
public:
@@ -171,8 +171,8 @@ template
struct get_boxed_value_func
{
void operator() (void **ret, PyObject *arg, tl::Heap *heap)
- {
- const gsi::ClassBase *cls_decl = PythonInterpreter::instance ()->cls_for_type (Py_TYPE (arg));
+ {
+ const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg));
if (! cls_decl) {
R *v = new R (python2c (arg, heap));
@@ -187,7 +187,7 @@ struct get_boxed_value_func
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Passing an object to pointer or reference requires a boxed type (pya.%s)")), bt->name ()));
}
- PYAObjectBase *p = (PYAObjectBase *) arg;
+ PYAObjectBase *p = (PYAObjectBase *) arg;
gsi::Value *bo = reinterpret_cast (p->obj ());
if (bo) {
*ret = bo->value ().template morph ().native_ptr ();
@@ -254,8 +254,8 @@ struct writer
}
};
-/**
- * @brief Serialization for strings
+/**
+ * @brief Serialization for strings
*/
template <>
struct writer
@@ -362,7 +362,7 @@ struct writer
/**
* @brief A serialization wrapper (write mode)
- * Specialisation for objects
+ * Specialisation for objects
*/
template <>
struct writer
@@ -382,14 +382,14 @@ struct writer
if (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ()) {
- const gsi::ClassBase *cls_decl = PythonInterpreter::instance ()->cls_for_type (Py_TYPE (arg));
+ const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg));
if (! cls_decl) {
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), Py_TYPE (arg)->tp_name));
}
if (cls_decl->is_derived_from (atype.cls ())) {
- PYAObjectBase *p = (PYAObjectBase *) (arg);
+ PYAObjectBase *p = (PYAObjectBase *) (arg);
if (cls_decl->adapted_type_info ()) {
// resolved adapted type
@@ -400,7 +400,7 @@ struct writer
} else if (cls_decl->can_convert_to (atype.cls ())) {
- PYAObjectBase *p = (PYAObjectBase *) (arg);
+ PYAObjectBase *p = (PYAObjectBase *) (arg);
// We can convert objects for cref and cptr, but ownership over these objects is not transferred.
// Hence we have to keep them on the heap.
@@ -414,14 +414,14 @@ struct writer
} else {
- const gsi::ClassBase *cls_decl = PythonInterpreter::instance ()->cls_for_type (Py_TYPE (arg));
+ const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg));
if (! cls_decl) {
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), Py_TYPE (arg)->tp_name));
}
if (cls_decl->is_derived_from (atype.cls ())) {
- PYAObjectBase *p = (PYAObjectBase *) (arg);
+ PYAObjectBase *p = (PYAObjectBase *) (arg);
if (cls_decl->adapted_type_info ()) {
// resolved adapted type
@@ -432,7 +432,7 @@ struct writer
} else if (cls_decl->can_convert_to (atype.cls ())) {
- PYAObjectBase *p = (PYAObjectBase *) (arg);
+ PYAObjectBase *p = (PYAObjectBase *) (arg);
aa->write (atype.cls ()->create_obj_from (cls_decl, p->obj ()));
} else {
@@ -446,7 +446,7 @@ struct writer
/**
* @brief A serialization wrapper (write mode)
- * Specialisation for void
+ * Specialisation for void
*/
template <>
struct writer
@@ -464,11 +464,11 @@ push_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PyObject *arg, tl
}
/**
- * @brief Deseralisation wrapper
+ * @brief Deseralisation wrapper
*
* The default implementation is for POD types, strings and variants
*/
-template
+template
struct reader
{
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PyObject * /*self*/, const gsi::ArgType &arg, tl::Heap *heap)
@@ -503,7 +503,7 @@ struct reader
* Without that would would have to handle void *&, void * const &, ...
* TODO: right now these types are not supported.
*/
-template <>
+template <>
struct reader
{
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PyObject * /*self*/, const gsi::ArgType &arg, tl::Heap *heap)
@@ -519,7 +519,7 @@ struct reader
/**
* @brief Deseralisation wrapper: specialization for strings
*/
-template <>
+template <>
struct reader
{
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase * /*self*/, const gsi::ArgType &, tl::Heap *heap)
@@ -589,7 +589,7 @@ PyObject *object_from_variant (const tl::Variant &var, PYAObjectBase *self, cons
/**
* @brief Deseralisation wrapper: specialization for variants
*/
-template <>
+template <>
struct reader
{
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase *self, const gsi::ArgType &atype, tl::Heap *heap)
@@ -612,7 +612,7 @@ struct reader
/**
* @brief Deseralisation wrapper: specialization for vectors
*/
-template <>
+template <>
struct reader
{
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase * /*self*/, const gsi::ArgType &atype, tl::Heap *heap)
@@ -624,7 +624,7 @@ struct reader
*ret = PyList_New (0);
tl_assert (atype.inner () != 0);
PythonBasedVectorAdaptor t (*ret, atype.inner ());
- a->copy_to (&t, *heap);
+ a->copy_to (&t, *heap);
}
}
};
@@ -632,7 +632,7 @@ struct reader
/**
* @brief Deseralisation wrapper: specialization for maps
*/
-template <>
+template <>
struct reader
{
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase * /*self*/, const gsi::ArgType &atype, tl::Heap *heap)
@@ -651,9 +651,9 @@ struct reader
};
/**
- * @brief Deseralisation wrapper: specialization for object
+ * @brief Deseralisation wrapper: specialization for object
*/
-template <>
+template <>
struct reader
{
void operator() (gsi::SerialArgs *rr, PythonRef *ret, PYAObjectBase *self, const gsi::ArgType &atype, tl::Heap *heap)
@@ -670,7 +670,7 @@ struct reader
/**
* @brief Deseralisation wrapper: specialization for void
*/
-template <>
+template <>
struct reader
{
void operator() (gsi::SerialArgs *, PythonRef *, PYAObjectBase *, const gsi::ArgType &, tl::Heap *)
@@ -679,7 +679,7 @@ struct reader
}
};
-PythonRef
+PythonRef
pop_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PYAObjectBase *self, tl::Heap &heap)
{
PythonRef ret;
@@ -701,7 +701,7 @@ tl::Variant PythonBasedVariantAdaptor::var () const
return python2c (m_var.get ());
}
-void PythonBasedVariantAdaptor::set (const tl::Variant & /*v*/)
+void PythonBasedVariantAdaptor::set (const tl::Variant & /*v*/)
{
// TODO: is there a setter for a string?
}
@@ -715,7 +715,7 @@ PythonBasedVectorAdaptorIterator::PythonBasedVectorAdaptorIterator (const Python
// .. nothing yet ..
}
-void PythonBasedVectorAdaptorIterator::get (gsi::SerialArgs &w, tl::Heap &heap) const
+void PythonBasedVectorAdaptorIterator::get (gsi::SerialArgs &w, tl::Heap &heap) const
{
PyObject *member = NULL;
if (PyTuple_Check (m_array.get ())) {
@@ -726,12 +726,12 @@ void PythonBasedVectorAdaptorIterator::get (gsi::SerialArgs &w, tl::Heap &heap)
gsi::do_on_type () (mp_ainner->type (), &w, member, *mp_ainner, &heap);
}
-bool PythonBasedVectorAdaptorIterator::at_end () const
+bool PythonBasedVectorAdaptorIterator::at_end () const
{
return m_i == m_len;
}
-void PythonBasedVectorAdaptorIterator::inc ()
+void PythonBasedVectorAdaptorIterator::inc ()
{
++m_i;
}
@@ -750,7 +750,7 @@ gsi::VectorAdaptorIterator *PythonBasedVectorAdaptor::create_iterator () const
return new PythonBasedVectorAdaptorIterator (m_array, size (), mp_ainner);
}
-void PythonBasedVectorAdaptor::push (gsi::SerialArgs &r, tl::Heap &heap)
+void PythonBasedVectorAdaptor::push (gsi::SerialArgs &r, tl::Heap &heap)
{
if (PyList_Check (m_array.get ())) {
PythonRef member;
@@ -761,7 +761,7 @@ void PythonBasedVectorAdaptor::push (gsi::SerialArgs &r, tl::Heap &heap)
}
}
-void PythonBasedVectorAdaptor::clear ()
+void PythonBasedVectorAdaptor::clear ()
{
if (PySequence_Check (m_array.get ())) {
PySequence_DelSlice (m_array.get (), 0, PySequence_Length (m_array.get ()));
@@ -777,7 +777,7 @@ size_t PythonBasedVectorAdaptor::size () const
}
}
-size_t PythonBasedVectorAdaptor::serial_size () const
+size_t PythonBasedVectorAdaptor::serial_size () const
{
return mp_ainner->size ();
}
@@ -793,18 +793,18 @@ PythonBasedMapAdaptorIterator::PythonBasedMapAdaptorIterator (const PythonPtr &h
inc ();
}
-void PythonBasedMapAdaptorIterator::get (gsi::SerialArgs &w, tl::Heap &heap) const
+void PythonBasedMapAdaptorIterator::get (gsi::SerialArgs &w, tl::Heap &heap) const
{
gsi::do_on_type () (mp_ainner_k->type (), &w, m_key, *mp_ainner_k, &heap);
gsi::do_on_type () (mp_ainner->type (), &w, m_value, *mp_ainner, &heap);
}
-bool PythonBasedMapAdaptorIterator::at_end () const
+bool PythonBasedMapAdaptorIterator::at_end () const
{
return ! m_has_items;
}
-void PythonBasedMapAdaptorIterator::inc ()
+void PythonBasedMapAdaptorIterator::inc ()
{
m_has_items = PyDict_Next(m_hash.get (), &m_pos, &m_key, &m_value);
}
@@ -813,7 +813,7 @@ void PythonBasedMapAdaptorIterator::inc ()
// PythonBasedMapAdaptor implementation
PythonBasedMapAdaptor::PythonBasedMapAdaptor (const PythonPtr &hash, const gsi::ArgType *ainner, const gsi::ArgType *ainner_k)
- : mp_ainner (ainner), mp_ainner_k (ainner_k), m_hash (hash)
+ : mp_ainner (ainner), mp_ainner_k (ainner_k), m_hash (hash)
{
}
@@ -822,7 +822,7 @@ gsi::MapAdaptorIterator *PythonBasedMapAdaptor::create_iterator () const
return new PythonBasedMapAdaptorIterator (m_hash, mp_ainner, mp_ainner_k);
}
-void PythonBasedMapAdaptor::insert (gsi::SerialArgs &r, tl::Heap &heap)
+void PythonBasedMapAdaptor::insert (gsi::SerialArgs &r, tl::Heap &heap)
{
PythonRef k, v;
gsi::do_on_type () (mp_ainner_k->type (), &r, &k, (PYAObjectBase *) 0, *mp_ainner_k, &heap);
@@ -830,7 +830,7 @@ void PythonBasedMapAdaptor::insert (gsi::SerialArgs &r, tl::Heap &heap)
PyDict_SetItem (m_hash.get (), k.get (), v.get ());
}
-void PythonBasedMapAdaptor::clear ()
+void PythonBasedMapAdaptor::clear ()
{
PyDict_Clear (m_hash.get ());
}
@@ -840,7 +840,7 @@ size_t PythonBasedMapAdaptor::size () const
return PyDict_Size (m_hash.get ());
}
-size_t PythonBasedMapAdaptor::serial_size () const
+size_t PythonBasedMapAdaptor::serial_size () const
{
return mp_ainner_k->size () + mp_ainner->size ();
}
@@ -876,7 +876,7 @@ struct test_arg_func
if (atype.is_ptr () || atype.is_ref ()) {
// check if we have a boxed type
- const gsi::ClassBase *cls_decl = PythonInterpreter::instance ()->cls_for_type (Py_TYPE (arg));
+ const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg));
if (cls_decl) {
const gsi::ClassBase *bc = gsi::cls_decl ();
if (cls_decl->is_derived_from (bc)) {
@@ -914,11 +914,11 @@ struct test_arg_func
#if PY_MAJOR_VERSION < 3
if (PyString_Check (arg)) {
*ret = true;
- } else
+ } else
#else
if (PyBytes_Check (arg)) {
*ret = true;
- } else
+ } else
#endif
if (PyUnicode_Check (arg)) {
*ret = true;
@@ -981,7 +981,7 @@ struct test_arg_func
const gsi::ArgType &ainner = *atype.inner ();
const gsi::ArgType &ainner_k = *atype.inner ();
- // Note: we test key and value separately. That way we don't need to
+ // Note: we test key and value separately. That way we don't need to
// instantiate a 2d template with do_on_type2.
*ret = true;
@@ -1010,7 +1010,7 @@ struct test_arg_func
return;
}
- const gsi::ClassBase *cls_decl = PythonInterpreter::instance ()->cls_for_type (Py_TYPE (arg));
+ const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg));
if (! cls_decl) {
*ret = false;
return;
@@ -1037,6 +1037,5 @@ test_arg (const gsi::ArgType &atype, PyObject *arg, bool loose)
gsi::do_on_type () (atype.type (), &ret, arg, atype, loose);
return ret;
}
-
-}
+}
diff --git a/src/tl/tl/tl.pro b/src/tl/tl/tl.pro
index b0ab6a2f7..2621960ec 100644
--- a/src/tl/tl/tl.pro
+++ b/src/tl/tl/tl.pro
@@ -102,7 +102,8 @@ HEADERS = \
tlUnitTest.h \
tlInt128Support.h \
tlHttpStreamCurl.h \
- tlHttpStreamQt.h
+ tlHttpStreamQt.h \
+ tlDefs.h
INCLUDEPATH =
DEPENDPATH =
diff --git a/src/tl/tl/tlDefs.h b/src/tl/tl/tlDefs.h
new file mode 100644
index 000000000..4197cb53b
--- /dev/null
+++ b/src/tl/tl/tlDefs.h
@@ -0,0 +1,57 @@
+
+/*
+
+ 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
+
+*/
+
+#if !defined(HDR_tlDefs_h)
+# define HDR_tlDefs_h
+
+// templates provided for building the external symbol
+// declarations per library
+
+# if defined _WIN32 || defined __CYGWIN__
+
+# define DEF_INSIDE_PUBLIC __declspec(dllexport)
+# define DEF_INSIDE_LOCAL
+# define DEF_INSIDE_PUBLIC_TEMPLATE
+
+# define DEF_OUTSIDE_PUBLIC __declspec(dllimport)
+# define DEF_OUTSIDE_LOCAL
+# define DEF_OUTSIDE_PUBLIC_TEMPLATE
+
+# else
+
+# if __GNUC__ >= 4 || defined(__clang__)
+# define DEF_INSIDE_PUBLIC __attribute__ ((visibility ("default")))
+# define DEF_INSIDE_PUBLIC_TEMPLATE __attribute__ ((visibility ("default")))
+# define DEF_INSIDE_LOCAL __attribute__ ((visibility ("hidden")))
+# else
+# define DEF_INSIDE_PUBLIC
+# define DEF_INSIDE_PUBLIC_TEMPLATE
+# define DEF_INSIDE_LOCAL
+# endif
+
+# define DEF_OUTSIDE_PUBLIC DEF_INSIDE_PUBLIC
+# define DEF_OUTSIDE_PUBLIC_TEMPLATE DEF_INSIDE_PUBLIC_TEMPLATE
+# define DEF_OUTSIDE_LOCAL DEF_INSIDE_LOCAL
+
+# endif
+
+#endif
diff --git a/testdata/python/basic.py b/testdata/python/basic.py
index e458959e0..2bf3dbac1 100644
--- a/testdata/python/basic.py
+++ b/testdata/python/basic.py
@@ -25,6 +25,7 @@ import gc
# Set this to True to disable some tests involving exceptions
leak_check = "TEST_LEAK_CHECK" in os.environ
+print("@@@1")
# see test_21
class AEXT(pya.A):
def __init__(self):
@@ -134,16 +135,23 @@ class BasicTest(unittest.TestCase):
def test_00(self):
+ print("@@@00")
# all references of PA are released now:
ac0 = pya.A.a0()
+ print("@@@00-1")
a = pya.A.new_a(100)
self.assertEqual( pya.A.a0(), ac0 + 1 )
+ print("@@@00-2")
a = pya.A()
+ print("@@@00-31")
self.assertEqual(a.a1(), 17)
+ print("@@@00-32")
a.assign(pya.A(110))
+ print("@@@00-33")
self.assertEqual(a.a1(), 110)
+ print("@@@00-3")
a = None
self.assertEqual( pya.A.a0(), ac0 )
@@ -384,6 +392,7 @@ class BasicTest(unittest.TestCase):
def test_12(self):
+ print("@@@12")
a1 = pya.A()
a1.a5( -15 )
a2 = a1
@@ -584,6 +593,7 @@ class BasicTest(unittest.TestCase):
def test_13(self):
+ print("@@@13")
b = pya.B()
if not leak_check:
@@ -810,6 +820,7 @@ class BasicTest(unittest.TestCase):
def test_13b(self):
+ print("@@@13b")
b = pya.B()
bb = pya.B()
@@ -913,6 +924,7 @@ class BasicTest(unittest.TestCase):
def test_14(self):
+ print("@@@14")
a = pya.A()
a.a5( 22 )
@@ -926,6 +938,7 @@ class BasicTest(unittest.TestCase):
def test_15(self):
+ print("@@@15")
a = pya.A_NC()
self.assertEqual( True, isinstance(a, pya.A) )
a.a5( 22 )
@@ -940,6 +953,7 @@ class BasicTest(unittest.TestCase):
def test_16(self):
+ print("@@@16")
if leak_check:
return
@@ -966,6 +980,7 @@ class BasicTest(unittest.TestCase):
def test_17(self):
+ print("@@@17")
# test copies of objects being returned
b = pya.B()
@@ -991,6 +1006,7 @@ class BasicTest(unittest.TestCase):
def test_18(self):
+ print("@@@18")
# Test references to objects (returned by b.b7)
b = pya.B()
@@ -1017,6 +1033,7 @@ class BasicTest(unittest.TestCase):
def test_19(self):
+ print("@@@19")
c0 = pya.C()
self.assertEqual( c0.g("x"), 1977 );
@@ -1062,6 +1079,7 @@ class BasicTest(unittest.TestCase):
def test_20(self):
+ print("@@@20")
b = pya.B()
a1 = b.b14a( True )
@@ -1081,6 +1099,7 @@ class BasicTest(unittest.TestCase):
def test_21(self):
+ print("@@@21")
# Python does not allow extending built-in types - the following test
# is taken from the Ruby binding. I don't know how to implement it for
# Python however.
@@ -1112,6 +1131,7 @@ class BasicTest(unittest.TestCase):
def test_22(self):
+ print("@@@22")
# test client data binding to C++ objects
b = pya.B()
@@ -1230,6 +1250,7 @@ class BasicTest(unittest.TestCase):
def test_23(self):
+ print("@@@23")
b = pya.B()
a = pya.A()
@@ -1277,6 +1298,7 @@ class BasicTest(unittest.TestCase):
def test_24(self):
+ print("@@@24")
n = [ 0, 0 , "" ]
# Events
@@ -1362,6 +1384,7 @@ class BasicTest(unittest.TestCase):
def test_25(self):
+ print("@@@25")
# destruction of an instance via c++
pya.A.a20(None)
ac0 = pya.A.a0()
@@ -1414,6 +1437,7 @@ class BasicTest(unittest.TestCase):
def test_26(self):
+ print("@@@26")
# cyclic references - event bound to itself
base_count = EEXT.inst_count()
@@ -1457,6 +1481,7 @@ class BasicTest(unittest.TestCase):
def test_27(self):
+ print("@@@27")
# destruction of an instance via c++
pya.A.a20(None)
ac0 = pya.A.a0()
@@ -1493,6 +1518,7 @@ class BasicTest(unittest.TestCase):
def test_28(self):
+ print("@@@28")
self.assertEqual(pya.B.inst() == None, True)
self.assertEqual(pya.B.has_inst(), False)
@@ -1528,6 +1554,7 @@ class BasicTest(unittest.TestCase):
def test_30(self):
+ print("@@@30")
# some basic tests for the *Value boxing classes
val = pya.Value()
@@ -1704,6 +1731,7 @@ class BasicTest(unittest.TestCase):
def test_31(self):
+ print("@@@31")
# some basic tests with derived and base classes
pya.X.init()
@@ -1760,6 +1788,7 @@ class BasicTest(unittest.TestCase):
def test_32(self):
+ print("@@@32")
# run test only if we have Qt bindings
if not "QStringPair" in pya.__dict__:
return
@@ -1788,6 +1817,7 @@ class BasicTest(unittest.TestCase):
def test_33(self):
+ print("@@@33")
def str_from_bytearray(ba):
if (sys.version_info > (3, 0)):
return ba
@@ -1822,6 +1852,7 @@ class BasicTest(unittest.TestCase):
def test_34(self):
+ print("@@@34")
# run test only if we have Qt bindings
if not "QDialog" in pya.__dict__:
return
@@ -1840,6 +1871,7 @@ class BasicTest(unittest.TestCase):
def test_35(self):
+ print("@@@35")
# vectors of pointers
pya.X.init()
@@ -1940,12 +1972,14 @@ class BasicTest(unittest.TestCase):
def test_36(self):
+ print("@@@36")
x = XEdge()
self.assertEqual("XEdge", type(x).__name__)
self.assertEqual("(1,2;3,4)", str(x))
def test_37(self):
+ print("@@@37")
# This test is taken from the Ruby binding, but
# Python does not have protected methods:
return
@@ -1965,6 +1999,7 @@ class BasicTest(unittest.TestCase):
def test_38(self):
+ print("@@@38")
# mixed const / non-const reference and events
ec = pya.E.ic()
self.assertEqual(ec.is_const_object(), True)
@@ -1996,6 +2031,7 @@ class BasicTest(unittest.TestCase):
def test_39(self):
+ print("@@@39")
# mixed const / non-const reference and events
fc = pya.F.ic()
self.assertEqual(fc.is_const_object(), True)
@@ -2026,6 +2062,7 @@ class BasicTest(unittest.TestCase):
def test_40(self):
+ print("@@@40")
# optional arguments
g = pya.G()
@@ -2109,6 +2146,7 @@ class BasicTest(unittest.TestCase):
def test_41(self):
+ print("@@@41")
# maps
b = pya.B()
@@ -2170,6 +2208,7 @@ class BasicTest(unittest.TestCase):
def test_42(self):
+ print("@@@42")
# virtual functions and sub-classes
z = pya.Z()
self.assertEqual(z.f(None), "(nil)")
@@ -2198,6 +2237,7 @@ class BasicTest(unittest.TestCase):
def test_50(self):
+ print("@@@50")
# advanced containers and out parameters
b = pya.B()
@@ -2372,6 +2412,7 @@ class BasicTest(unittest.TestCase):
self.assertEqual(b.qhash_is(), {})
def test_51(self):
+ print("@@@51")
# new subclass and child class declarations
y2 = pya.Y2()
@@ -2389,6 +2430,7 @@ class BasicTest(unittest.TestCase):
self.assertEqual(isinstance(y4, pya.X), False)
def test_60(self):
+ print("@@@60")
class SignalCollector(object):
@@ -2482,6 +2524,7 @@ class BasicTest(unittest.TestCase):
self.assertEqual(sc.got_s2_2, None)
def test_61(self):
+ print("@@@61")
class SignalCollector(object):
@@ -2565,6 +2608,7 @@ class BasicTest(unittest.TestCase):
self.assertEqual(sc.got_s0b, 0)
def test_70(self):
+ print("@@@70")
class SignalCollector(object):
@@ -2613,6 +2657,7 @@ class BasicTest(unittest.TestCase):
self.assertEqual(sc.got_s2_2.tag, 111)
def test_71(self):
+ print("@@@71")
class SignalCollector(object):
@@ -2697,6 +2742,7 @@ class BasicTest(unittest.TestCase):
# Custom factory implemented in Python
def test_80(self):
+ print("@@@80")
gc = pya.GObject.g_inst_count()
gf = PyGFactory()
go = pya.GFactory.create_f(gf, 17)
@@ -2706,6 +2752,7 @@ class BasicTest(unittest.TestCase):
go = None
self.assertEqual(pya.GObject.g_inst_count(), gc)
+print("@@@2")
# run unit tests
if __name__ == '__main__':
suite = unittest.TestSuite()
@@ -2715,4 +2762,5 @@ if __name__ == '__main__':
if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful():
sys.exit(1)
+print("@@@3")