New exception in Python 3.7.4 when bad code tries to PyQt5.uic.loadUi()

Kyle Altendorf <[email protected]>
Newsgroups gmane.comp.python.pyqt-pykde
Message-ID <[email protected]>
I have many custom widgets with pyqtProperty's and GUI elements loaded 
from .ui files.  In various ways, my existing code tries to access 
not-yet-present attributes in pyqtProperty's while the .ui is being 
loaded.  This is obviously bad and I plan to fix my code.  I share this 
example because I already finished it and because this is an odd change 
of behavior from a micro version bump of Python.  Also, it is odd that 
each first time my code raises an exception there is not necessarily a 
Qt failure.  For example, WidgetB must be constructed twice to get the 
traceback.  I understand that this may well not get 'fixed' since it 
probably never should have worked to begin with.

Code and output from 3.7.3/3.7.4 on Linux/Windows are attached and also 
available at:
     https://gist.github.com/altendky/f15f8d180269a4080c25dec83cf556a0

Here is the basic traceback:

     AttributeError: __getattribute__

     The above exception was the direct cause of the following exception:

     Traceback (most recent call last):
       File "bad.py", line 146, in main
         widgets.append(WidgetA(broken=True))
       File "bad.py", line 53, in __init__
         PyQt5.uic.loadUi(io.StringIO(ui_text), self)
       File 
"C:\epc\bug\venv3.7-32\lib\site-packages\PyQt5\uic\__init__.py", line 
227, in loadUi
         return DynamicUILoader(package).loadUi(uifile, baseinstance, 
resource_suffix)
       File 
"C:\epc\bug\venv3.7-32\lib\site-packages\PyQt5\uic\Loader\loader.py", 
line 72, in loadUi
         return self.parse(filename, resource_suffix, basedir)
       File 
"C:\epc\bug\venv3.7-32\lib\site-packages\PyQt5\uic\uiparser.py", line 
1030, in parse
         actor(elem)
       File 
"C:\epc\bug\venv3.7-32\lib\site-packages\PyQt5\uic\uiparser.py", line 
942, in createConnections
         QtCore.QMetaObject.connectSlotsByName(self.toplevelWidget)
     SystemError: <built-in function connectSlotsByName> returned a 
result with an error set

Cheers,
-kyle

_______________________________________________
PyQt mailing list    [email protected]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt
bad.py (text/x-c++, 3.5 KB)
import io
import sys
import traceback

import PyQt5.QtCore
import PyQt5.QtWidgets
import PyQt5.uic
import sip


system_info = f'''\
sys.version: {sys.version}
sys.platform: {sys.platform}
QT_VERSION_STR: {PyQt5.QtCore.QT_VERSION_STR}
PYQT_VERSION_STR: {PyQt5.QtCore.PYQT_VERSION_STR}
SIP_VERSION_STR: {sip.SIP_VERSION_STR}'''


ui_text = '''\
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <widget class="QLabel" name="label">
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>
'''


class WidgetA(PyQt5.QtWidgets.QWidget):
    def __init__(self, broken, parent=None):
        super().__init__(parent=parent)

        if broken:
            PyQt5.uic.loadUi(io.StringIO(ui_text), self)

        self._a = 0
        self._b = 1

        if not broken:
            PyQt5.uic.loadUi(io.StringIO(ui_text), self)

    # removing either of the following also makes it not fail
            
    @PyQt5.QtCore.pyqtProperty(int)
    def property_a(self):
        print('getting a')
        result = self._a
        print('got a')
        return result

    @PyQt5.QtCore.pyqtProperty(int)
    def property_b(self):
        print('getting b')
        result = self._b
        print('got b')
        return result


class WidgetB(PyQt5.QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent=parent)

        self.ui = PyQt5.uic.loadUi(io.StringIO(ui_text), self)

    @PyQt5.QtCore.pyqtProperty(int)
    def property_a(self):
        print('getting a:')
        print("           hasattr(self, 'ui') == {}".format(hasattr(self, 'ui')))
        print("           hasattr(self, 'label') == {}".format(hasattr(self, 'label')))
        result = self.ui.label.text()
        print('got a')
        return result


Ui, UiBase = PyQt5.uic.loadUiType(io.StringIO(ui_text))


class WidgetC(UiBase):
    def __init__(self, parent=None):
        super().__init__(parent=parent)

        self.ui = Ui()
        self.ui.setupUi(self)

    @PyQt5.QtCore.pyqtProperty(int)
    def property_a(self):
        print('getting a:')
        print("           hasattr(self, 'ui') == {}".format(hasattr(self, 'ui')))
        print("           hasattr(self.ui, 'label') == {}".format(hasattr(self.ui, 'label')))
        result = self.ui.label.text()
        print('got a')
        return result


def main():
    print(system_info)
    app = PyQt5.QtWidgets.QApplication([])
    
    widgets = []

    print()
    print('     ------ creating WidgetC and...')
    widgets.append(WidgetC())

    print()
    print('     ------ creating WidgetC and... now it still works')
    widgets.append(WidgetC())

    print()
    print('     ------ creating WidgetB and...')
    widgets.append(WidgetB())

    print()
    print('     ------ creating WidgetB and... now it breaks')
    try:
        widgets.append(WidgetB())
    except Exception:
        traceback.print_exc()

    print()
    print('     ------ creating WidgetA without breaking')
    widgets.append(WidgetA(broken=False))

    print()
    print('     ------ creating WidgetA but breaking in the process')
    try:
        widgets.append(WidgetA(broken=True))
    except Exception:
        traceback.print_exc()
    

main()
linux_3.7.3 (text/plain, 7 KB)
 ~/tmp   master ●  venv3.7.3/bin/python bad.py
sys.version: 3.7.3 (default, Apr 16 2019, 16:54:41)                                                                                                                                            
[GCC 6.3.0 20170516]                                                                                                                                                                           
sys.platform: linux                                                                                                                                                                            
QT_VERSION_STR: 5.13.0                                                                                                                                                                         
PYQT_VERSION_STR: 5.13.0                                                                                                                                                                       
SIP_VERSION_STR: 4.19.18                                                                                                                                                                       
                                                                                                                                                                                               
     ------ creating WidgetC and...                                                                                                                                                            
getting a:                                                                                                                                                                                     
           hasattr(self, 'ui') == True                                                                                                                                                         
           hasattr(self.ui, 'label') == True                                                                                                                                                   
got a                                                                                                                                                                                          
                                                                                                                                                                                               
     ------ creating WidgetC and... now it still works                                                                                                                                         
getting a:                                                                                                                                                                                     
           hasattr(self, 'ui') == True                                                                                                                                                         
           hasattr(self.ui, 'label') == True                                                                                                                                                   
got a                                                                                                                                                                                          
                                                                                                                                                                                               
     ------ creating WidgetB and...                                                                                                                                                            
getting a:                                                                                                                                                                                     
           hasattr(self, 'ui') == False                                                                                                                                                        
           hasattr(self, 'label') == True                                                                                                                                                      
                                                                                                                                                                                               
     ------ creating WidgetB and... now it breaks                                                                                                                                              
getting a:                                                                                                                                                                                     
           hasattr(self, 'ui') == False                                                                                                                                                        
           hasattr(self, 'label') == True                                                                                                                                                      
                                                                                                                                                                                               
     ------ creating WidgetA without breaking                                                                                                                                                  
getting a                                                                                                                                                                                      
got a                                                                                                                                                                                          
getting b                                                                                                                                                                                      
got b                                                                                                                                                                                          
                                                                                                                                                                                               
     ------ creating WidgetA but breaking in the process                                                                                                                                       
getting a                                                                                                                                                                                      
getting b
linux_3.7.4 (text/plain, 3.3 KB)
 ~/tmp   master ●  venv3.7.4/bin/python bad.py
sys.version: 3.7.4 (default, Aug  2 2019, 12:58:45)                                                                                                                                            
[GCC 6.3.0 20170516]                                                                                                                                                                           
sys.platform: linux
QT_VERSION_STR: 5.13.0
PYQT_VERSION_STR: 5.13.0
SIP_VERSION_STR: 4.19.18

     ------ creating WidgetC and...
getting a:
           hasattr(self, 'ui') == True
           hasattr(self.ui, 'label') == True
got a

     ------ creating WidgetC and... now it still works
getting a:
           hasattr(self, 'ui') == True
           hasattr(self.ui, 'label') == True
got a

     ------ creating WidgetB and...
getting a:
           hasattr(self, 'ui') == False
           hasattr(self, 'label') == True

     ------ creating WidgetB and... now it breaks
getting a:
           hasattr(self, 'ui') == False
           hasattr(self, 'label') == True
AttributeError: __getattribute__

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "bad.py", line 135, in main
    widgets.append(WidgetB())
  File "bad.py", line 82, in __init__
    self.ui = PyQt5.uic.loadUi(io.StringIO(ui_text), self)
  File "/home/altendky/tmp/venv3.7.4/lib/python3.7/site-packages/PyQt5/uic/__init__.py", line 227, in loadUi
    return DynamicUILoader(package).loadUi(uifile, baseinstance, resource_suffix)
  File "/home/altendky/tmp/venv3.7.4/lib/python3.7/site-packages/PyQt5/uic/Loader/loader.py", line 72, in loadUi
    return self.parse(filename, resource_suffix, basedir)
  File "/home/altendky/tmp/venv3.7.4/lib/python3.7/site-packages/PyQt5/uic/uiparser.py", line 1030, in parse
    actor(elem)
  File "/home/altendky/tmp/venv3.7.4/lib/python3.7/site-packages/PyQt5/uic/uiparser.py", line 942, in createConnections
    QtCore.QMetaObject.connectSlotsByName(self.toplevelWidget)
SystemError: <built-in function connectSlotsByName> returned a result with an error set

     ------ creating WidgetA without breaking
getting a
got a
getting b
got b

     ------ creating WidgetA but breaking in the process
getting a
getting b
AttributeError: __getattribute__

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "bad.py", line 146, in main
    widgets.append(WidgetA(broken=True))
  File "bad.py", line 53, in __init__
    PyQt5.uic.loadUi(io.StringIO(ui_text), self)
  File "/home/altendky/tmp/venv3.7.4/lib/python3.7/site-packages/PyQt5/uic/__init__.py", line 227, in loadUi
    return DynamicUILoader(package).loadUi(uifile, baseinstance, resource_suffix)
  File "/home/altendky/tmp/venv3.7.4/lib/python3.7/site-packages/PyQt5/uic/Loader/loader.py", line 72, in loadUi
    return self.parse(filename, resource_suffix, basedir)
  File "/home/altendky/tmp/venv3.7.4/lib/python3.7/site-packages/PyQt5/uic/uiparser.py", line 1030, in parse
    actor(elem)
  File "/home/altendky/tmp/venv3.7.4/lib/python3.7/site-packages/PyQt5/uic/uiparser.py", line 942, in createConnections
    QtCore.QMetaObject.connectSlotsByName(self.toplevelWidget)
SystemError: <built-in function connectSlotsByName> returned a result with an error set
windows_3.7.3 (text/plain, 950 B)
PS C:\epc\bug> venv/scripts/python bad.py
sys.version: 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 21:26:53) [MSC v.1916 32 bit (Intel)]
sys.platform: win32
QT_VERSION_STR: 5.13.0
PYQT_VERSION_STR: 5.13.0
SIP_VERSION_STR: 4.19.18

     ------ creating WidgetC and...
getting a:
           hasattr(self, 'ui') == True
           hasattr(self.ui, 'label') == True
got a

     ------ creating WidgetC and... now it still works
getting a:
           hasattr(self, 'ui') == True
           hasattr(self.ui, 'label') == True
got a

     ------ creating WidgetB and...
getting a:
           hasattr(self, 'ui') == False
           hasattr(self, 'label') == True

     ------ creating WidgetB and... now it breaks
getting a:
           hasattr(self, 'ui') == False
           hasattr(self, 'label') == True

     ------ creating WidgetA without breaking
getting a
got a
getting b
got b

     ------ creating WidgetA but breaking in the process
getting a
getting b
windows_3.7.4 (text/plain, 2.8 KB)
PS C:\epc\bug> venv3.7-32/scripts/python bad.py
sys.version: 3.7.4 (tags/v3.7.4:e09359112e, Jul  8 2019, 19:29:22) [MSC v.1916 32 bit (Intel)]
sys.platform: win32
QT_VERSION_STR: 5.13.0
PYQT_VERSION_STR: 5.13.0
SIP_VERSION_STR: 4.19.18

     ------ creating WidgetC and...
getting a:
           hasattr(self, 'ui') == True
           hasattr(self.ui, 'label') == True
got a

     ------ creating WidgetC and... now it still works
getting a:
           hasattr(self, 'ui') == True
           hasattr(self.ui, 'label') == True
got a

     ------ creating WidgetB and...
getting a:
           hasattr(self, 'ui') == False
           hasattr(self, 'label') == True

     ------ creating WidgetB and... now it breaks
           hasattr(self, 'ui') == False
           hasattr(self, 'label') == True
AttributeError: __getattribute__

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "bad.py", line 135, in main
    widgets.append(WidgetB())
  File "bad.py", line 82, in __init__
    self.ui = PyQt5.uic.loadUi(io.StringIO(ui_text), self)
  File "C:\epc\bug\venv3.7-32\lib\site-packages\PyQt5\uic\__init__.py", line 227, in loadUi
    return DynamicUILoader(package).loadUi(uifile, baseinstance, resource_suffix)
  File "C:\epc\bug\venv3.7-32\lib\site-packages\PyQt5\uic\Loader\loader.py", line 72, in loadUi
    return self.parse(filename, resource_suffix, basedir)
  File "C:\epc\bug\venv3.7-32\lib\site-packages\PyQt5\uic\uiparser.py", line 1030, in parse
  File "C:\epc\bug\venv3.7-32\lib\site-packages\PyQt5\uic\uiparser.py", line 942, in createConnections
    QtCore.QMetaObject.connectSlotsByName(self.toplevelWidget)
SystemError: <built-in function connectSlotsByName> returned a result with an error set

     ------ creating WidgetA without breaking
got a
getting b
got b

     ------ creating WidgetA but breaking in the process
AttributeError: __getattribute__

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "bad.py", line 146, in main
    widgets.append(WidgetA(broken=True))
  File "bad.py", line 53, in __init__
    PyQt5.uic.loadUi(io.StringIO(ui_text), self)
  File "C:\epc\bug\venv3.7-32\lib\site-packages\PyQt5\uic\__init__.py", line 227, in loadUi
    return DynamicUILoader(package).loadUi(uifile, baseinstance, resource_suffix)
  File "C:\epc\bug\venv3.7-32\lib\site-packages\PyQt5\uic\Loader\loader.py", line 72, in loadUi
    return self.parse(filename, resource_suffix, basedir)
  File "C:\epc\bug\venv3.7-32\lib\site-packages\PyQt5\uic\uiparser.py", line 1030, in parse
    actor(elem)
  File "C:\epc\bug\venv3.7-32\lib\site-packages\PyQt5\uic\uiparser.py", line 942, in createConnections
    QtCore.QMetaObject.connectSlotsByName(self.toplevelWidget)
SystemError: <built-in function connectSlotsByName> returned a result with an error set
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.