View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0011207 | Scribus | Scripter | public | 2012-11-24 07:51 | 2019-12-08 21:24 |
Reporter | QuLogic | Assigned To | jghali | ||
Priority | normal | Severity | feature | Reproducibility | always |
Status | closed | Resolution | fixed | ||
Platform | Linux | OS | Fedora | OS Version | 17 |
Product Version | 1.5.0svn | ||||
Summary | 0011207: [PATCH] Scripter Python 3 compatibility | ||||
Description | CMake will happily allow building scribus against Python 3, even though the code doesn't work with it. Using the useful Porting to Python 3 guide [1], I have written a preliminary patch that enables the Scripting plugin to compile against Python3. It's possible that some things could be simplified, as I wasn't sure what the minimum Python version Scribus is expected to support. I'm no C++ or Qt expert either, so it's possible that I did something wrong/inefficiently/unconventionally. This patch is against 1.5.0svn, but mostly applies (with a bit of fuzz) to 1.4.2svn, except for objpdffile.cpp, which I guess must have changed a lot. [1] http://python3porting.com/cextensions.html | ||||
Steps To Reproduce | 1. Install Python3 development libraries. 2. Run cmake: No errors 3. Build scribus: Fails compiling scriptplugin | ||||
Tags | discussion, patch | ||||
Patch | Yes | ||||
|
scribus-python3.diff (17,357 bytes)
Index: scribus/plugins/scriptplugin/cmdgetsetprop.cpp =================================================================== --- scribus/plugins/scriptplugin/cmdgetsetprop.cpp (revision 17900) +++ scribus/plugins/scriptplugin/cmdgetsetprop.cpp (working copy) @@ -18,13 +18,13 @@ if (PyString_Check(arg)) // It's a string. Look for a pageItem by that name. Do NOT accept a // selection. - return getPageItemByName(QString::fromUtf8(PyString_AsString(arg))); - else if (PyCObject_Check(arg)) + return getPageItemByName(PyString_AsQString(arg)); + else if (PyCapsule_CheckExact(arg)) { - // It's a PyCObject, ie a wrapped pointer. Check it's not NULL + // It's a PyCapsule, ie a wrapped pointer. Check it's not NULL // and return it. // FIXME: Try to check that its a pointer to a QObject instance - QObject* tempObject = (QObject*)PyCObject_AsVoidPtr(arg); + QObject* tempObject = (QObject*)PyCapsule_GetPointer(arg, NULL); if (!tempObject) { PyErr_SetString(PyExc_TypeError, "INTERNAL: Passed NULL PyCObject"); @@ -44,7 +44,7 @@ PyObject* wrapQObject(QObject* obj) { - return PyCObject_FromVoidPtr((void*)obj, NULL); + return PyCapsule_New((void*)obj, NULL, NULL); } @@ -414,7 +414,7 @@ { matched = true; if (PyString_Check(objValue)) - success = obj->setProperty(propertyName, QString::fromUtf8(PyString_AsString(objValue))); + success = obj->setProperty(propertyName, PyString_AsQString(objValue)); else if (PyUnicode_Check(objValue)) { // Get a pointer to the internal buffer of the Py_Unicode object, which is UCS2 formatted @@ -432,7 +432,7 @@ { // FIXME: should raise an exception instead of mangling the string when // out of charset chars present. - QString utfString = QString::fromUtf8(PyString_AsString(objValue)); + QString utfString = PyString_AsQString(objValue); success = obj->setProperty(propertyName, utfString.toAscii()); } else if (PyUnicode_Check(objValue)) @@ -465,7 +465,7 @@ if (!objRepr) return NULL; // Extract the repr() string - QString reprString = QString::fromUtf8(PyString_AsString(objRepr)); + QString reprString = PyString_AsQString(objRepr); Py_DECREF(objRepr); // And return an error Index: scribus/plugins/scriptplugin/cmdmani.cpp =================================================================== --- scribus/plugins/scriptplugin/cmdmani.cpp (revision 17900) +++ scribus/plugins/scriptplugin/cmdmani.cpp (working copy) @@ -354,7 +354,6 @@ PyObject *scribus_groupobj(PyObject* /* self */, PyObject* args) { - char *Name = const_cast<char*>(""); PyObject *il = 0; if (!PyArg_ParseTuple(args, "|O", &il)) return NULL; @@ -378,8 +377,7 @@ // FIXME: We might need to explicitly get this string as utf8 // but as sysdefaultencoding is utf8 it should be a no-op to do // so anyway. - Name = PyString_AsString(PyList_GetItem(il, i)); - PageItem *ic = GetUniqueItem(QString::fromUtf8(Name)); + PageItem *ic = GetUniqueItem(PyString_AsQString(PyList_GetItem(il, i))); if (ic == NULL) { delete tempSelection; Index: scribus/plugins/scriptplugin/cmdvar.h =================================================================== --- scribus/plugins/scriptplugin/cmdvar.h (revision 17900) +++ scribus/plugins/scriptplugin/cmdvar.h (working copy) @@ -36,7 +36,46 @@ #include "scribus.h" +#if PY_MAJOR_VERSION >= 3 + #define PyString_Check PyUnicode_Check + #ifdef PyUnicode_GET_LENGTH + #define PyString_Size PyUnicode_GET_LENGTH + #else + #define PyString_Size PyUnicode_GET_SIZE + #endif + #define PyString_FromString PyUnicode_FromString + #define PyString_FromStringAndSize PyUnicode_FromStringAndSize + inline QString PyString_AsQString(PyObject *arg) { + PyObject *tmp = PyUnicode_AsUTF8String(arg); + QString qs = QString::fromUtf8(PyBytes_AsString(tmp)); + Py_DECREF(tmp); + return qs; + } + #define PyInt_Check PyLong_Check + #define PyInt_FromLong PyLong_FromLong + #define PyInt_AsLong PyLong_AsLong + #if PY_MINOR_VERSION == 0 + #define PyCapsule_Type PyCObject_Type + #define PyCapsule_CheckExact(capsule) PyCObject_Check(capsule) + #define PyCapsule_New(pointer, name, destructor) PyCObject_FromVoidPtr(pointer, destructor) + #define PyCapsule_GetPointer(capsule, name) PyCObject_AsVoidPtr(capsule) + #endif +#else + #define PyString_AsQString(arg) QString::fromUtf8(PyString_AsString((arg))) + #if PY_MINOR_VERSION < 7 + #define PyCapsule_Type PyCObject_Type + #define PyCapsule_CheckExact(capsule) PyCObject_Check(capsule) + #define PyCapsule_New(pointer, name, destructor) PyCObject_FromVoidPtr(pointer, destructor) + #define PyCapsule_GetPointer(capsule, name) PyCObject_AsVoidPtr(capsule) + #if PY_MINOR_VERSION <= 5 + #define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, + #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) + #endif + #endif +#endif + + class ScripterCore; // Globals for testing Qt properties and probably other more intresting future Index: scribus/plugins/scriptplugin/objimageexport.cpp =================================================================== --- scribus/plugins/scriptplugin/objimageexport.cpp (revision 17900) +++ scribus/plugins/scriptplugin/objimageexport.cpp (working copy) @@ -32,7 +32,7 @@ Py_XDECREF(self->name); Py_XDECREF(self->type); Py_XDECREF(self->allTypes); - self->ob_type->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free((PyObject *)self); } static PyObject * ImageExport_new(PyTypeObject *type, PyObject * /*args*/, PyObject * /*kwds*/) @@ -154,11 +154,22 @@ int dpi = qRound(100.0 / 2.54 * self->dpi); im.setDotsPerMeterY(dpi); im.setDotsPerMeterX(dpi); +#if PY_MAJOR_VERSION >= 3 + PyObject *type = PyUnicode_AsUTF8String(self->type); + if (!im.save(PyString_AsQString(self->name), PyBytes_AsString(type))) + { + Py_DECREF(type); + PyErr_SetString(ScribusException, QObject::tr("Failed to export image", "python error").toLocal8Bit().constData()); + return NULL; + } + Py_DECREF(type); +#else if (!im.save(PyString_AsString(self->name), PyString_AsString(self->type))) { PyErr_SetString(ScribusException, QObject::tr("Failed to export image", "python error").toLocal8Bit().constData()); return NULL; } +#endif // Py_INCREF(Py_True); // return True not None for backward compat // return Py_True; // Py_RETURN_TRUE; @@ -185,11 +196,22 @@ int dpi = qRound(100.0 / 2.54 * self->dpi); im.setDotsPerMeterY(dpi); im.setDotsPerMeterX(dpi); +#if PY_MAJOR_VERSION >= 3 + PyObject *type = PyUnicode_AsUTF8String(self->type); + if (!im.save(value, PyBytes_AsString(type))) + { + Py_DECREF(type); + PyErr_SetString(ScribusException, QObject::tr("Failed to export image", "python error").toLocal8Bit().constData()); + return NULL; + } + Py_DECREF(type); +#else if (!im.save(value, PyString_AsString(self->type))) { PyErr_SetString(ScribusException, QObject::tr("Failed to export image", "python error").toLocal8Bit().constData()); return NULL; } +#endif // Py_INCREF(Py_True); // return True not None for backward compat // return Py_True; // Py_RETURN_TRUE; @@ -203,8 +225,7 @@ }; PyTypeObject ImageExport_Type = { - PyObject_HEAD_INIT(NULL) // PyObject_VAR_HEAD - 0, + PyVarObject_HEAD_INIT(NULL, 0) // PyObject_VAR_HEAD const_cast<char*>("ImageExport"), // char *tp_name; /* For printing, in format "<module>.<name>" */ sizeof(ImageExport), // int tp_basicsize, /* For allocation */ 0, // int tp_itemsize; /* For allocation */ Index: scribus/plugins/scriptplugin/objpdffile.cpp =================================================================== --- scribus/plugins/scriptplugin/objpdffile.cpp (revision 17900) +++ scribus/plugins/scriptplugin/objpdffile.cpp (working copy) @@ -111,7 +111,7 @@ Py_XDECREF(self->imagepr); Py_XDECREF(self->printprofc); Py_XDECREF(self->info); - self->ob_type->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free((PyObject *)self); } static PyObject * PDFfile_new(PyTypeObject *type, PyObject * /*args*/, PyObject * /*kwds*/) @@ -1010,12 +1010,12 @@ int n = PyList_Size(self->fonts); for ( int i=0; i<n; ++i){ QString tmpFon; - tmpFon = QString(PyString_AsString(PyList_GetItem(self->fonts, i))); + tmpFon = PyString_AsQString(PyList_GetItem(self->fonts, i)); ScCore->primaryMainWindow()->doc->pdfOptions().EmbedList.append(tmpFon); } // apply file attribute QString fn; - fn = QString(PyString_AsString(self->file)); + fn = PyString_AsQString(self->file); ScCore->primaryMainWindow()->doc->pdfOptions().fileName = fn; // apply pages attribute std::vector<int> pageNs; @@ -1107,7 +1107,7 @@ // } // ScCore->primaryMainWindow()->doc->pdfOptions().LPISettings[QString(s)]=lpi; QString st; - st = QString(PyString_AsString(PyList_GetItem(t,0))); + st = PyString_AsQString(PyList_GetItem(t,0)); lpi.Frequency = PyInt_AsLong(PyList_GetItem(t, 1)); lpi.Angle = PyInt_AsLong(PyList_GetItem(t, 2)); lpi.SpotFunc = PyInt_AsLong(PyList_GetItem(t, 3)); @@ -1136,8 +1136,8 @@ if (self->aanot) Perm += 32; ScCore->primaryMainWindow()->doc->pdfOptions().Permissions = Perm; - ScCore->primaryMainWindow()->doc->pdfOptions().PassOwner = QString(PyString_AsString(self->owner)); - ScCore->primaryMainWindow()->doc->pdfOptions().PassUser = QString(PyString_AsString(self->user)); + ScCore->primaryMainWindow()->doc->pdfOptions().PassOwner = PyString_AsQString(self->owner); + ScCore->primaryMainWindow()->doc->pdfOptions().PassUser = PyString_AsQString(self->user); } if (self->outdst == 0) { @@ -1157,9 +1157,9 @@ self->intenti = minmaxi(self->intenti, 0, 3); ScCore->primaryMainWindow()->doc->pdfOptions().Intent2 = self->intenti; ScCore->primaryMainWindow()->doc->pdfOptions().EmbeddedI = self->noembicc; - ScCore->primaryMainWindow()->doc->pdfOptions().SolidProf = PyString_AsString(self->solidpr); - ScCore->primaryMainWindow()->doc->pdfOptions().ImageProf = PyString_AsString(self->imagepr); - ScCore->primaryMainWindow()->doc->pdfOptions().PrintProf = PyString_AsString(self->printprofc); + ScCore->primaryMainWindow()->doc->pdfOptions().SolidProf = PyString_AsQString(self->solidpr); + ScCore->primaryMainWindow()->doc->pdfOptions().ImageProf = PyString_AsQString(self->imagepr); + ScCore->primaryMainWindow()->doc->pdfOptions().PrintProf = PyString_AsQString(self->printprofc); if (ScCore->primaryMainWindow()->doc->pdfOptions().Version == PDFOptions::PDFVersion_X3) { ScColorProfile profile; @@ -1171,7 +1171,7 @@ Components = 4; if (profile.colorSpace() == ColorSpace_Cmy) Components = 3; - ScCore->primaryMainWindow()->doc->pdfOptions().Info = PyString_AsString(self->info); + ScCore->primaryMainWindow()->doc->pdfOptions().Info = PyString_AsQString(self->info); self->bleedt = minmaxd(self->bleedt, 0, ScCore->primaryMainWindow()->view->Doc->pageHeight()*ScCore->primaryMainWindow()->view->Doc->unitRatio()); ScCore->primaryMainWindow()->doc->pdfOptions().bleeds.Top = self->bleedt/ScCore->primaryMainWindow()->view->Doc->unitRatio(); self->bleedl = minmaxd(self->bleedl, 0, ScCore->primaryMainWindow()->view->Doc->pageWidth()*ScCore->primaryMainWindow()->view->Doc->unitRatio()); @@ -1219,8 +1219,7 @@ }; PyTypeObject PDFfile_Type = { - PyObject_HEAD_INIT(NULL) // PyObject_VAR_HEAD - 0, // + PyVarObject_HEAD_INIT(NULL, 0) // PyObject_VAR_HEAD const_cast<char*>("PDFfile"), // char *tp_name; /* For printing, in format "<module>.<name>" */ sizeof(PDFfile), // int tp_basicsize, /* For allocation */ 0, // int tp_itemsize; /* For allocation */ Index: scribus/plugins/scriptplugin/objprinter.cpp =================================================================== --- scribus/plugins/scriptplugin/objprinter.cpp (revision 17900) +++ scribus/plugins/scriptplugin/objprinter.cpp (working copy) @@ -62,7 +62,7 @@ Py_XDECREF(self->cmd); Py_XDECREF(self->pages); Py_XDECREF(self->separation); - self->ob_type->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free((PyObject *)self); } static PyObject * Printer_new(PyTypeObject *type, PyObject * /*args*/, PyObject * /*kwds*/) @@ -407,16 +407,16 @@ PSfile = false; // ReOrderText(ScCore->primaryMainWindow()->doc, ScCore->primaryMainWindow()->view); - prn = QString(PyString_AsString(self->printer)); - fna = QString(PyString_AsString(self->file)); - fil = (QString(PyString_AsString(self->printer)) == QString("File")) ? true : false; + prn = PyString_AsQString(self->printer); + fna = PyString_AsQString(self->file); + fil = (PyString_AsQString(self->printer) == QString("File")) ? true : false; std::vector<int> pageNs; PrintOptions options; for (int i = 0; i < PyList_Size(self->pages); ++i) { options.pageNumbers.push_back((int)PyInt_AsLong(PyList_GetItem(self->pages, i))); } int Nr = (self->copies < 1) ? 1 : self->copies; - SepName = QString(PyString_AsString(self->separation)); + SepName = PyString_AsQString(self->separation); options.printer = prn; options.prnEngine = (PrintEngine) self->pslevel; options.toFile = fil; @@ -438,7 +438,7 @@ options.bleeds.Bottom = 0.0; if (!PrinterUtil::checkPrintEngineSupport(options.printer, options.prnEngine, options.toFile)) options.prnEngine = PrinterUtil::getDefaultPrintEngine(options.printer, options.toFile); - printcomm = QString(PyString_AsString(self->cmd)); + printcomm = PyString_AsQString(self->cmd); QMap<QString, QMap<uint, FPointArray> > ReallyUsed; ReallyUsed.clear(); ScCore->primaryMainWindow()->doc->getUsedFonts(ReallyUsed); @@ -527,8 +527,7 @@ }; PyTypeObject Printer_Type = { - PyObject_HEAD_INIT(NULL) // PyObject_VAR_HEAD - 0, // + PyVarObject_HEAD_INIT(NULL, 0) // PyObject_VAR_HEAD const_cast<char*>("Printer"), // char *tp_name; /* For printing, in format "<module>.<name>" */ sizeof(Printer), // int tp_basicsize, /* For allocation */ 0, // int tp_itemsize; /* For allocation */ Index: scribus/plugins/scriptplugin/scriptercore.cpp =================================================================== --- scribus/plugins/scriptplugin/scriptercore.cpp (revision 17900) +++ scribus/plugins/scriptplugin/scriptercore.cpp (working copy) @@ -253,7 +253,6 @@ PyThreadState *state = NULL; QFileInfo fi(fileName); - QByteArray na = fi.fileName().toLocal8Bit(); // Set up a sub-interpreter if needed: PyThreadState* global_state = NULL; if (!inMainInterpreter) @@ -272,8 +271,21 @@ // Init the scripter module in the sub-interpreter initscribus(ScCore->primaryMainWindow()); } +#if PY_MAJOR_VERSION >= 3 // Make sure sys.argv[0] is the path to the script + wchar_t* comm[2]; + std::wstring na = fi.fileName().toStdWString(); + comm[0] = const_cast<wchar_t*>(na.c_str()); + // and tell the script if it's running in the main intepreter or + // a subinterpreter using the second argument, ie sys.argv[1] + if (inMainInterpreter) + comm[1] = const_cast<wchar_t*>(L"ext"); + else + comm[1] = const_cast<wchar_t*>(L"sub"); +#else + // Make sure sys.argv[0] is the path to the script char* comm[2]; + QByteArray na = fi.fileName().toLocal8Bit(); comm[0] = na.data(); // and tell the script if it's running in the main intepreter or // a subinterpreter using the second argument, ie sys.argv[1] @@ -281,6 +293,7 @@ comm[1] = const_cast<char*>("ext"); else comm[1] = const_cast<char*>("sub"); +#endif PySys_SetArgv(2, comm); // call python script PyObject* m = PyImport_AddModule((char*)"__main__"); @@ -344,7 +357,7 @@ } else { - QString errorMsg = PyString_AsString(errorMsgPyStr); + QString errorMsg = PyString_AsQString(errorMsgPyStr); // Display a dialog to the user with the exception QClipboard *cp = QApplication::clipboard(); cp->setText(errorMsg); Index: scribus/plugins/scriptplugin/scriptplugin.cpp =================================================================== --- scribus/plugins/scriptplugin/scriptplugin.cpp (revision 17900) +++ scribus/plugins/scriptplugin/scriptplugin.cpp (working copy) @@ -171,11 +171,13 @@ } #endif Py_Initialize(); +#if PY_MAJOR_VERSION < 3 if (PyUnicode_SetDefaultEncoding("utf-8")) { qDebug("Failed to set default encoding to utf-8.\n"); PyErr_Clear(); } +#endif scripterCore = new ScripterCore(ScCore->primaryMainWindow()); Q_CHECK_PTR(scripterCore); @@ -564,6 +566,21 @@ {NULL, (PyCFunction)(0), 0, NULL} /* sentinel */ }; +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "scribus", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + scribus_methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ +}; +#endif + + void initscribus_failed(const char* fileName, int lineNo) { qDebug("Scripter setup failed (%s:%i)", fileName, lineNo); @@ -585,7 +602,11 @@ PyType_Ready(&Printer_Type); PyType_Ready(&PDFfile_Type); PyType_Ready(&ImageExport_Type); +#if PY_MAJOR_VERSION >= 3 + m = PyModule_Create(&moduledef); +#else m = Py_InitModule((char*)"scribus", scribus_methods); +#endif Py_INCREF(&Printer_Type); PyModule_AddObject(m, (char*)"Printer", (PyObject *) &Printer_Type); Py_INCREF(&PDFfile_Type); |
|
Hi QuLogic, apologies for not knowing about this. The Scripter code since you published this patch has changed. Would you be interested to help port it to the newest iteration of Scribus Scripter2 ? |
|
i'll be working on porting the new scripter to python 3 and pyqt5 in this repository: https://github.com/aoloe/scribus-plugin-scripter collaboration, hints, and co. are welcome :-) |
|
I can certainly look into it, though I will have to see about updating my build environment first. |
|
QuLogic, great to hear from you. Thank you for your contribution. Check-in with ale when you are ready. Cheers! |
|
This patch is for the old scripter. Personally, I'm focused on getting the new scripter to work again... But the patch might be helpful for finding out what has to be changed. |
|
bump |
|
i guess that this is fixed now |
Date Modified | Username | Field | Change |
---|---|---|---|
2012-11-24 07:51 | QuLogic | New Issue | |
2012-11-24 07:51 | QuLogic | File Added: scribus-python3.diff | |
2012-11-24 07:54 | QuLogic | Tag Attached: patch | |
2014-07-13 19:20 | Kunda | Note Added: 0032725 | |
2014-07-13 19:20 | Kunda | Status | new => feedback |
2014-07-14 01:29 | Kunda | Summary | Python 3 compatibility => [PATCH] Scripter Python 3 compatibility |
2014-07-14 14:11 | ale | Note Added: 0032735 | |
2014-07-16 07:08 | QuLogic | Note Added: 0032776 | |
2014-07-16 07:08 | QuLogic | Status | feedback => new |
2014-07-16 16:21 | Kunda | Note Added: 0032781 | |
2014-07-16 16:37 | Kunda | Assigned To | => ale |
2014-07-16 16:37 | Kunda | Status | new => assigned |
2014-08-03 18:53 | ale | Note Added: 0033162 | |
2014-10-22 03:56 | Kunda | Note Added: 0034118 | |
2014-10-24 22:43 | cbradney | Patch | => Yes |
2016-05-16 22:23 | Kunda | Tag Attached: discussion | |
2019-11-23 13:16 | ale | Assigned To | ale => jghali |
2019-11-23 13:16 | ale | Status | assigned => resolved |
2019-11-23 13:16 | ale | Resolution | open => fixed |
2019-11-23 13:16 | ale | Note Added: 0047120 | |
2019-12-08 21:24 | cbradney | Status | resolved => closed |