-1

edit: Tried the equivalent code with pyqt5 compiled with cython. It works swimmingly so this seems to be a pyside2 bug.

Latest version of cython. My app works fine when i run it with python but when i make it into a module and import the module from a simple launcher script suddenly it doesn't seem to see any data from the internet and it also gives me these: RecursionError: maximum recursion depth exceeded while calling a Python object.

Nuitka has the same problem but doesn't give me these recursionerrors.

I have no idea if it's relevant but when compiling the program(making object code) with gcc i also get this error/warning/note(the only one):

$ python setup.py build_ext --inplace
Compiling prog.py because it changed.
[1/1] Cythonizing prog.py
running build_ext
building 'prog' extension
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 
-Wall -Wstrict-prototypes -march=x86-64 -mtune=generic -O2 -pipe -
fstack-protector-strong -fno-plt -march=x86-64 -mtune=generic -O2 -
pipe -fstack-protector-strong -fno-plt -march=x86-64 -mtune=generic -
O2 -pipe -fstack-protector-strong -fno-plt -fPIC -
I/usr/include/python3.6m -c prog.c -o build/temp.linux-x86_64-
3.6/prog.o
prog.c: In function ‘__pyx_pf_4prog_13Ui_MainWindow_setupUi.isra.76’:
prog.c:41235:18: note: variable tracking size limit exceeded with -
fvar-tracking-assignments, retrying without
static PyObject *__pyx_pf_4prog_13Ui_MainWindow_setupUi(CYTHON_UNUSED 
PyObject *__pyx_self, PyObject *__pyx_v_self, PyObject *__pyx_v_MainWindow) {
              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Also not sure if relevant but i have a bunch of classes(threads) that calls the next class when one is finished making a loop. Maybe this is a nono even if it works fine in python?

I'm using pyside2, requests, delorean and humanize. Im not even sure how to start debugging this, it's a 4000 line program.

edit: Ok so i managed to reproduce the exact problem in a minimal example. This code will work perfectly before you compile it with cython or nuitka. If you compile it with cython it will output:

('thread1:', <Response [200]>)
RecursionError: maximum recursion depth exceeded while calling a Python object

and hang but it will show the window contents. With nuitka it will never show the contents of the window but the threads will run as expected. If anyone can explain why it happens and how i can fix it that would be great:

from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtCore import Slot, Signal, QThread
import sys
import requests

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(240, 100, 97, 34))
        self.pushButton.setObjectName("pushButton")
        self.plainTextEdit = QtWidgets.QPlainTextEdit(self.centralwidget)
        self.plainTextEdit.setGeometry(QtCore.QRect(200, 160, 451, 271))
        self.plainTextEdit.setPlainText("")
        self.plainTextEdit.setObjectName("plainTextEdit")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 30))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "OK"))

class MainUIClass(QtWidgets.QMainWindow, Ui_MainWindow):
    def closeEvent(self, evnt):
        print("CLOSED")
        self.thread1.quit()
        self.thread2.quit()
        self.thread3.quit()
        self.thread1.wait()
        self.thread2.wait()
        self.thread3.wait()
        print("thread1 is running?:", self.thread1.isRunning())
        print("thread2 is running?:", self.thread2.isRunning())
        print("thread3 is running?:", self.thread3.isRunning())
        sys.exit()

    def __init__(self, parent=None):
        super().__init__()
        self.setupUi(self)

        self.thread1 = QThread()
        self.thread2 = QThread()
        self.thread3 = QThread()

        self.Class1 = Class1()
        self.Class2 = Class2()
        self.Class3 = Class3()

        self.Class1.moveToThread(self.thread1)
        self.Class2.moveToThread(self.thread2)
        self.Class3.moveToThread(self.thread3)

        self.thread1.started.connect(lambda: self.Class1.startThread())

        self.Class1.startThread2.connect(self.Class2.startThread)
        self.Class2.startThread3.connect(self.Class3.startThread)
        self.Class3.startThread1.connect(self.Class1.startThread)

        self.thread1.start()
        self.thread2.start()
        self.thread3.start()

class Class1(QtCore.QObject):
    startThread2 = Signal()

    def __init__(self):
        super().__init__()

    def startThread(self):
        data = requests.get("https://www.google.com")
        print("thread1:", data)
        self.startThread2.emit()

class Class2(QtCore.QObject):
    startThread3 = Signal()

    def __init__(self):
        super().__init__()

    def startThread(self):
        data = requests.get("https://www.google.com")
        print("thread2:", data)
        self.startThread3.emit()

class Class3(QtCore.QObject):
    startThread1 = Signal()

    def __init__(self):
        super().__init__()

    def startThread(self):
        data = requests.get("https://www.google.com")
        print("thread3:", data)
        self.startThread1.emit()

a = QtWidgets.QApplication(sys.argv)
app = MainUIClass()

a.setQuitOnLastWindowClosed(False)
app.setWindowTitle("Pyside2 thread compiled test")
app.show()

sys.exit(a.exec_())
n00p
  • 269
  • 4
  • 11
  • `It's a proprietary app and i don't want to give away the sourcecode` -- it's hard to tell what is going wrong if you do not provide code. RecursionError means that you're calling a recursive function more often that allowed. You can see it with: `import sys; sys.getrecursionlimit()`. You can also set recursion limit to higher level, but I wouldn't recommened that: `sys.setrecursionlimit(1500)`. It's a hack not a solution. – colidyre May 29 '18 at 10:56
  • I understand that, but i don't have much choice do i? You don't have to downvote. Sometimes one has no choice in these things... I just think it's strange that i get recursion errors when i compile it but not when running with python? Iv'e already tried setting the recursionlimit higher but it didnt work. – n00p May 29 '18 at 11:00
  • 3
    Create a [MCVE] and post it, if it doesn't solve your problem. – Thomas Sablik May 29 '18 at 11:04
  • @n00p we actually don't want the full source code (no one would bother reading it whole anyway), but a MCVE. For what it's worth, it's far from uncommon that setting up a MCVE is by itself enough to spot and fix the issue. – bruno desthuilliers May 29 '18 at 11:09
  • I fear you would say that. The problem is im not exactly sure where to start making this minimal example. I will try to see if i can pinpoint the problem. This is of course more problematic for me since i don't speak C. – n00p May 29 '18 at 11:16
  • @n00p I think this _may_ be because Cython doesn't automatically release the GIL, so doesn't let the other threads have a chance to run. Try adding `with nogil: pass` to the end of all the startthread functions and possibly also after `app.show`. This is a guess though, and I'm not at all confident it will work. – DavidW May 29 '18 at 17:58
  • @DavidW Doesn't seem to do anything. I tried with both nuitka(i guess its no reason for it to work with nuitka anyway) and cython. – n00p May 29 '18 at 18:53
  • I can reproduce your problem. Can't see a solution currently. For the sake of simplifying the example further, you can delete `Class3` and replace the body of `startThread` in `Class2` with `pass` and the error still happens. (If you don't connect `Class2.startThread` then the error goes away) – DavidW May 30 '18 at 17:13
  • ... and I can get the Nuitka behaviour (window frozen by threads working) in Cython by doing `self.Class1.startThread2.connect(lambda: self.Class2.startThread)` etc (i.e. adding `lambda`). I don't think I'm going to get any further with this personally! – DavidW May 30 '18 at 17:27
  • One final point - if I change from `PySide2` to `PyQt5` then your code works fine (but the version with `lambda` added hangs the GUI) – DavidW May 30 '18 at 17:30

1 Answers1

2

It is possible that there are different settings of recursion limit in each environment. Try to adjust the recursion limit

import sys
sys.setrecursionlimit(3000)

(3000 is just a guess). You can see the limit by using

import sys
sys.getrecursionlimit()

This is only a hack, not a recommended way. You should think to refactor code using iteration instead of recursion or make the recursion work better. It's hard to tell more with provided information.

I've seen others setting also recursion depths like this: Setting stacksize in a python script . Maybe it's worth a try, too.

UPDATE: After you have updated your question, I've seen that you also have problems with variable tracking size. You can also try that: Adjust Variable Tracking Assignment Length

colidyre
  • 4,170
  • 12
  • 37
  • 53