Skip to content

Commit 81bcdfb

Browse files
authored
refs qoretechnologies/qore#4289 fixed additional namespace clashes and namespace allocation issues for imported Python classes in Qore (#53)
refs qoretechnologies/qore#4290 do not shut down the Python library on exit, as it will cause a crash in exit handlers in Python modules that require the library to be in place (ex: h5py module v3.3.0)
1 parent 9694e86 commit 81bcdfb

File tree

5 files changed

+57
-23
lines changed

5 files changed

+57
-23
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ project(qore-python-module)
33

44
set (VERSION_MAJOR 1)
55
set (VERSION_MINOR 0)
6-
set (VERSION_PATCH 2)
6+
set (VERSION_PATCH 3)
77

88
set(PROJECT_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
99

docs/mainpage.dox.tmpl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,13 @@ PythonProgram::setSaveObjectCallback(callback);
500500
501501
@section pythonreleasenotes python Module Release Notes
502502
503+
@subsection python_1_0_3 python Module Version 1.0.3
504+
- do not shut down the %Python library on exit, as it will cause a crash in exit handlers in %Python modules that
505+
require the library to be in place (ex: h5py module v 3.3.0)
506+
(<a href="https://github.com/qorelanguage/qore/issues/4290">issue 4290</a>)
507+
- fixed additional namespace clashes and namespace allocation issues for imported %Python classes in %Qore
508+
(<a href="https://github.com/qorelanguage/qore/issues/4289">issue 4289</a>)
509+
503510
@subsection python_1_0_2 python Module Version 1.0.2
504511
- fixed issues handling the case when the python module is initialized with no Program context
505512
(<a href="https://github.com/qorelanguage/qore/issues/4153">issue 4153</a>)

src/QorePythonProgram.cpp

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -593,8 +593,8 @@ QorePythonThreadInfo QorePythonProgram::setContext() const {
593593
if (!python) {
594594
python = PyThreadState_New(interpreter);
595595

596-
//printd(5, "QorePythonProgram::setContext() this: %p created new thread context: %p (py_thr_map: %p " \
597-
// "size: %d)\n", this, python, &py_thr_map, (int)py_thr_map.size());
596+
printd(5, "QorePythonProgram::setContext() this: %p created new thread context: %p (py_thr_map: %p "
597+
"size: %d)\n", this, python, &py_thr_map, (int)py_thr_map.size());
598598
assert(python);
599599
assert(python->gilstate_counter == 1);
600600
// the thread state will be deleted when the thread terminates or the interpreter is deleted
@@ -1179,7 +1179,8 @@ void QorePythonProgram::exportClass(ExceptionSink* xsink, QoreString& arg) {
11791179
ns = ns->findCreateNamespacePathAll(ns_str.c_str());
11801180
}
11811181

1182-
addClassToNamespaceIntern(xsink, ns, (PyTypeObject*)*obj, strpath.back().c_str(), i);
1182+
strset_t nsset;
1183+
addClassToNamespaceIntern(xsink, ns, (PyTypeObject*)*obj, strpath.back().c_str(), i, nsset);
11831184
}
11841185

11851186
void QorePythonProgram::addModulePath(ExceptionSink* xsink, QoreString& arg) {
@@ -2068,7 +2069,8 @@ QoreClass* QorePythonProgram::getCreateQorePythonClass(ExceptionSink* xsink, PyT
20682069
return nullptr;
20692070
}
20702071

2071-
return getCreateQorePythonClassIntern(xsink, type, nullptr, flags);
2072+
strset_t nsset;
2073+
return getCreateQorePythonClassIntern(xsink, type, nsset, nullptr, flags);
20722074
}
20732075

20742076
QoreNamespace* QorePythonProgram::getNamespaceForObject(PyObject* obj) {
@@ -2115,7 +2117,7 @@ QoreNamespace* QorePythonProgram::getNamespaceForObject(PyObject* obj) {
21152117
}
21162118

21172119
QoreClass* QorePythonProgram::getCreateQorePythonClassIntern(ExceptionSink* xsink, PyTypeObject* type,
2118-
const char* cname, int flags) {
2120+
strset_t& nsset, const char* cname, int flags) {
21192121
//printd(5, "QorePythonProgram::getCreateQorePythonClassIntern() class: '%s'\n", type->tp_name);
21202122
// see if the Python type already represents a Qore class
21212123
if (PyQoreObjectType_Check(type)) {
@@ -2131,11 +2133,11 @@ QoreClass* QorePythonProgram::getCreateQorePythonClassIntern(ExceptionSink* xsin
21312133
}
21322134

21332135
// get relative path to class and class name
2134-
std::string rpath_str;
2136+
QoreString rpath_str;
21352137
if (!cname) {
21362138
const char* p = strrchr(type->tp_name, '.');
21372139
if (p) {
2138-
rpath_str = std::string(type->tp_name, p - type->tp_name);
2140+
rpath_str.set(type->tp_name, p - type->tp_name);
21392141
cname = p + 1;
21402142
} else {
21412143
cname = type->tp_name;
@@ -2144,42 +2146,60 @@ QoreClass* QorePythonProgram::getCreateQorePythonClassIntern(ExceptionSink* xsin
21442146

21452147
// create new QorePythonClass
21462148
QoreNamespace* ns = getNamespaceForObject(reinterpret_cast<PyObject*>(type));
2147-
return addClassToNamespaceIntern(xsink, ns, type, cname, i, flags);
2149+
if (!rpath_str.empty() && rpath_str.startsWith(ns->getName())) {
2150+
size_t len = strlen(ns->getName());
2151+
if (rpath_str[len] == '.' && rpath_str.size() > len) {
2152+
rpath_str.replace(0, len + 1, nullptr);
2153+
rpath_str.replaceAll(".", "::");
2154+
ns = ns->findCreateNamespacePathAll(rpath_str.c_str());
2155+
}
2156+
}
2157+
return addClassToNamespaceIntern(xsink, ns, type, cname, i, nsset, flags);
21482158
}
21492159

21502160
QorePythonClass* QorePythonProgram::addClassToNamespaceIntern(ExceptionSink* xsink, QoreNamespace* ns,
2151-
PyTypeObject* type, const char* cname, clmap_t::iterator i, int flags) {
2161+
PyTypeObject* type, const char* cname, clmap_t::iterator i, strset_t& nsset, int flags) {
21522162
// get a unique name for the class
21532163
QoreString cname_str = cname;
2164+
// namespace path
2165+
std::string ns_path = ns->getPath(true);
2166+
// full path of class with ns
2167+
std::string full_path;
2168+
strset_t::iterator nsi;
21542169
{
21552170
int base = 0;
2156-
while (ns->findLocalClass(cname_str.c_str())) {
2171+
while (true) {
2172+
full_path = ns_path + "::" + cname_str.c_str();
2173+
nsi = nsset.find(full_path);
2174+
if (nsi == nsset.end() && !ns->findLocalClass(cname_str.c_str())) {
2175+
break;
2176+
}
21572177
cname_str.clear();
21582178
cname_str.sprintf("%s_base_%d", cname, base++);
21592179
}
21602180
}
21612181
cname = cname_str.c_str();
21622182

21632183
// create new class
2164-
std::string path = ns->getPath(true);
2165-
path += "::";
2166-
path += cname;
2167-
std::unique_ptr<QorePythonClass> cls(new QorePythonClass(this, cname, path.c_str()));
2184+
std::unique_ptr<QorePythonClass> cls(new QorePythonClass(this, cname, full_path.c_str()));
2185+
2186+
// add to nsset
2187+
nsset.insert(nsi, full_path);
21682188

21692189
// insert into map
21702190
clmap.insert(i, clmap_t::value_type(type, cls.get()));
21712191

21722192
//printd(5, "QorePythonProgram::addClassToNamespaceIntern() ns: '%s' cls: '%s' (%s id: %d)\n", ns->getName(),
21732193
// cls->getName(), type->tp_name, cls->getID());
21742194

2175-
return setupQorePythonClass(xsink, ns, type, cls, flags);
2195+
return setupQorePythonClass(xsink, ns, type, cls, nsset, flags);
21762196
}
21772197

21782198
static constexpr int static_meth_flags = QCF_USES_EXTRA_ARGS;
21792199
static constexpr int normal_meth_flags = static_meth_flags | QCF_ABSTRACT_OVERRIDE_ALL;
21802200

21812201
QorePythonClass* QorePythonProgram::setupQorePythonClass(ExceptionSink* xsink, QoreNamespace* ns, PyTypeObject* type,
2182-
std::unique_ptr<QorePythonClass>& cls, int flags) {
2202+
std::unique_ptr<QorePythonClass>& cls, strset_t& nsset, int flags) {
21832203
//printd(5, "QorePythonProgram::setupQorePythonClass() ns: '%s' cls: '%s' (%s) flags: %d\n", ns->getName(),
21842204
// cls->getName(), type->tp_name, flags);
21852205
cls->addConstructor((void*)type, (q_external_constructor_t)execPythonConstructor, Public,
@@ -2193,7 +2213,7 @@ QorePythonClass* QorePythonProgram::setupQorePythonClass(ExceptionSink* xsink, Q
21932213

21942214
// add single base class
21952215
if (type->tp_base) {
2196-
QoreClass* bclass = getCreateQorePythonClassIntern(xsink, type->tp_base);
2216+
QoreClass* bclass = getCreateQorePythonClassIntern(xsink, type->tp_base, nsset);
21972217
if (!bclass) {
21982218
assert(*xsink);
21992219
return nullptr;
@@ -2737,7 +2757,8 @@ int QorePythonProgram::importSymbol(ExceptionSink* xsink, PyObject* value, const
27372757
if (PyType_Check(value)) {
27382758
//printd(5, "QorePythonProgram::importSymbol() class sym: '%s' -> '%s' (%p)\n", symbol,
27392759
// reinterpret_cast<PyTypeObject*>(value)->tp_name, value);
2740-
getCreateQorePythonClassIntern(xsink, reinterpret_cast<PyTypeObject*>(value));
2760+
strset_t nsset;
2761+
getCreateQorePythonClassIntern(xsink, reinterpret_cast<PyTypeObject*>(value), nsset);
27412762
if (*xsink) {
27422763
return -1;
27432764
}

src/QorePythonProgram.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ struct QorePythonThreadStateInfo {
6363
class QorePythonProgram : public AbstractQoreProgramExternalData {
6464
friend class PythonModuleContextHelper;
6565
public:
66+
typedef std::set<std::string> strset_t;
67+
6668
//! Python context using the main interpreter
6769
DLLLOCAL QorePythonProgram();
6870

@@ -289,7 +291,7 @@ class QorePythonProgram : public AbstractQoreProgramExternalData {
289291

290292
//! Populates the QoreClass based on the Python class
291293
DLLLOCAL QorePythonClass* setupQorePythonClass(ExceptionSink* xsink, QoreNamespace* ns, PyTypeObject* type,
292-
std::unique_ptr<QorePythonClass>& cls, int flags = 0);
294+
std::unique_ptr<QorePythonClass>& cls, strset_t& nsset, int flags = 0);
293295

294296
//! Creates ot retrieves a QoreClass for the given Python type
295297
DLLLOCAL QoreClass* getCreateQorePythonClass(ExceptionSink* xsink, PyTypeObject* type, int flags = 0);
@@ -429,7 +431,6 @@ class QorePythonProgram : public AbstractQoreProgramExternalData {
429431
pyobj_set_t mod_set;
430432

431433
//! set of unique strings
432-
typedef std::set<std::string> strset_t;
433434
strset_t strset;
434435

435436
//! mutex for thread state map
@@ -474,11 +475,11 @@ class QorePythonProgram : public AbstractQoreProgramExternalData {
474475

475476
DLLLOCAL QoreNamespace* getNamespaceForObject(PyObject* type);
476477

477-
DLLLOCAL QoreClass* getCreateQorePythonClassIntern(ExceptionSink* xsink, PyTypeObject* type,
478+
DLLLOCAL QoreClass* getCreateQorePythonClassIntern(ExceptionSink* xsink, PyTypeObject* type, strset_t& nsset,
478479
const char* cls_name = nullptr, int flags = 0);
479480

480481
DLLLOCAL QorePythonClass* addClassToNamespaceIntern(ExceptionSink* xsink, QoreNamespace* ns, PyTypeObject* type,
481-
const char* cname, clmap_t::iterator i, int flags = 0);
482+
const char* cname, clmap_t::iterator i, strset_t& nsset, int flags = 0);
482483

483484
//! Call a method and and return the result
484485
DLLLOCAL QoreValue callCFunctionMethod(ExceptionSink* xsink, PyObject* func, const QoreListNode* args,

src/python-module.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,12 @@ static QoreStringNode* python_module_init_intern(bool repeat) {
233233
}
234234

235235
Py_InitializeEx(0);
236+
#ifndef QORE_ALLOW_PYTHON_SHUTDOWN
237+
// issue# 4290: if we actively shut down Python on exit, then exit handlers in modules
238+
// (such as the h5py module in version 3.3.0) will cause a crash when the process exits,
239+
// as it requires the Python library to be still in place and initialized
236240
python_needs_shutdown = true;
241+
#endif
237242
//printd(5, "python_module_init() Python initialized\n");
238243
}
239244

0 commit comments

Comments
 (0)