Throw ImportError instead of cryptic failure when importing extension module from incompatible version of Python

This commit is contained in:
rdb 2015-11-28 19:22:57 +01:00
parent 75caef56aa
commit 33d183d4f7
2 changed files with 55 additions and 24 deletions

View File

@ -128,6 +128,28 @@ int write_python_table_native(ostream &out) {
out << "extern void Dtool_" << *ii << "_BuildInstants(PyObject *module);\n";
}
out.put('\n');
out << "#if PY_MAJOR_VERSION >= 3 || !defined(NDEBUG)\n"
<< "#ifdef _WIN32\n"
<< "extern \"C\" __declspec(dllexport) PyObject *PyInit_" << library_name << "();\n"
<< "#elif __GNUC__ >= 4\n"
<< "extern \"C\" __attribute__((visibility(\"default\"))) PyObject *PyInit_" << library_name << "();\n"
<< "#else\n"
<< "extern \"C\" PyObject *PyInit_" << library_name << "();\n"
<< "#endif\n"
<< "#endif\n";
out << "#if PY_MAJOR_VERSION < 3 || !defined(NDEBUG)\n"
<< "#ifdef _WIN32\n"
<< "extern \"C\" __declspec(dllexport) void init" << library_name << "();\n"
<< "#elif __GNUC__ >= 4\n"
<< "extern \"C\" __attribute__((visibility(\"default\"))) void init" << library_name << "();\n"
<< "#else\n"
<< "extern \"C\" void init" << library_name << "();\n"
<< "#endif\n"
<< "#endif\n";
out << "\n"
<< "#if PY_MAJOR_VERSION >= 3\n"
<< "static struct PyModuleDef py_" << library_name << "_module = {\n"
@ -139,14 +161,6 @@ int write_python_table_native(ostream &out) {
<< " NULL, NULL, NULL, NULL\n"
<< "};\n"
<< "\n"
<< "#ifdef _WIN32\n"
<< "extern \"C\" __declspec(dllexport) PyObject *PyInit_" << library_name << "();\n"
<< "#elif __GNUC__ >= 4\n"
<< "extern \"C\" __attribute__((visibility(\"default\"))) PyObject *PyInit_" << library_name << "();\n"
<< "#else\n"
<< "extern \"C\" PyObject *PyInit_" << library_name << "();\n"
<< "#endif\n"
<< "\n"
<< "PyObject *PyInit_" << library_name << "() {\n";
if (track_interpreter) {
@ -184,15 +198,16 @@ int write_python_table_native(ostream &out) {
<< " return module;\n"
<< "}\n"
<< "\n"
<< "#else // Python 2 case\n"
<< "\n"
<< "#ifdef _WIN32\n"
<< "extern \"C\" __declspec(dllexport) void init" << library_name << "();\n"
<< "#elif __GNUC__ >= 4\n"
<< "extern \"C\" __attribute__((visibility(\"default\"))) void init" << library_name << "();\n"
<< "#else\n"
<< "extern \"C\" void init" << library_name << "();\n"
<< "#ifndef NDEBUG\n"
<< "void init" << library_name << "() {\n"
<< " PyErr_SetString(PyExc_ImportError, \"" << module_name << " was "
<< "compiled for Python \" PY_VERSION \", which is incompatible "
<< "with Python 2\");\n"
<< "}\n"
<< "#endif\n"
<< "#else // Python 2 case\n"
<< "\n"
<< "void init" << library_name << "() {\n";
@ -228,6 +243,15 @@ int write_python_table_native(ostream &out) {
out << " }\n"
<< "}\n"
<< "\n"
<< "#ifndef NDEBUG\n"
<< "PyObject *PyInit_" << library_name << "() {\n"
<< " PyErr_SetString(PyExc_ImportError, \"" << module_name << " was "
<< "compiled for Python \" PY_VERSION \", which is incompatible "
<< "with Python 3\");\n"
<< " return (PyObject *)NULL;\n"
<< "}\n"
<< "#endif\n"
<< "#endif\n"
<< "\n";

View File

@ -597,17 +597,18 @@ PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const char *modulename) {
#endif
// Check the version so we can print a helpful error if it doesn't match.
string version = Py_GetVersion();
if (interrogatedb_cat.is_debug()) {
interrogatedb_cat.debug()
<< "Python " << version << "\n";
}
if (version[0] != '0' + PY_MAJOR_VERSION ||
version[2] != '0' + PY_MINOR_VERSION) {
interrogatedb_cat.error()
<< "This module was compiled for Python "
<< PY_MAJOR_VERSION << "." << PY_MINOR_VERSION << ", which is incompatible "
<< "with Python " << version << ".\n";
// Raise a helpful error message. We can safely do this because the
// signature and behavior for PyErr_SetString has remained consistent.
ostringstream errs;
errs << "this module was compiled for Python "
<< PY_MAJOR_VERSION << "." << PY_MINOR_VERSION << ", which is "
<< "incompatible with Python " << version.substr(0, 3);
string error = errs.str();
PyErr_SetString(PyExc_ImportError, error.c_str());
return (PyObject *)NULL;
}
// Initialize the base class of everything.
@ -649,6 +650,12 @@ PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const char *modulename) {
// to do that. Perhaps we'll find a better place for this in the future.
static bool initialized_main_dir = false;
if (!initialized_main_dir) {
if (interrogatedb_cat.is_debug()) {
// Good opportunity to print this out once, at startup.
interrogatedb_cat.debug()
<< "Python " << version << "\n";
}
// Grab the __main__ module.
PyObject *main_module = PyImport_ImportModule("__main__");
if (main_module == NULL) {