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