Repository navigation
Description
Bug report
Bug summary
Hi. We are using CI of our project with PySide and pre releases of matplotlib. We notice that since 2.1.0rc1 all our tests fails with this kind or error message:
TypeError: PySide.QtCore.QObject isn't a direct base class of ClassBlahblahblah
After introspection we think that the problem come from matplotlib 2.1.0rc1, but it was too difficult to identify why. Using the previour version of matplotlib does not occur any problem.
The problem looks critical.
Any idea why this issue happens?
Code for reproduction
import logging
logging.basicConfig()
from PySide import QtGui
logger = logging.getLogger("test")
app = QtGui.QApplication([])
class Foo_super(QtGui.QWidget):
def __init__(self, parent=None):
super(Foo_super, self).__init__(parent)
class Foo_oldschool(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
import matplotlib
matplotlib.use('Qt4Agg')
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
fig = Figure(figsize=(600,600), dpi=72, facecolor=(1,1,1), edgecolor=(0,0,0))
ax = fig.add_subplot(111)
ax.plot([0,1])
try:
a = Foo_super()
except Exception as e:
logger.error("Backtrace 1", exc_info=True)
try:
a = Foo_oldschool()
except Exception as e:
logger.error("Backtrace 2", exc_info=True)
canvas = FigureCanvas(fig)
try:
a = Foo_super()
except Exception as e:
logger.error("Backtrace 3", exc_info=True)
try:
a = Foo_oldschool()
except Exception as e:
logger.error("Backtrace 4", exc_info=True)Actual outcome
ERROR:test:Backtrace 3
Traceback (most recent call last):
File "test_matplotlib_vs_pyside.py", line 47, in <module>
a = Foo_super()
File "test_matplotlib_vs_pyside.py", line 14, in __init__
super(Foo_super, self).__init__(parent)
TypeError: PySide.QtCore.QObject isn't a direct base class of Foo_super
ERROR:test:Backtrace 4
Traceback (most recent call last):
File "test_matplotlib_vs_pyside.py", line 52, in <module>
a = Foo_oldschool()
File "test_matplotlib_vs_pyside.py", line 19, in __init__
QtGui.QWidget.__init__(self, parent)
TypeError: PySide.QtCore.QObject isn't a direct base class of Foo_oldschool
Expected outcome
As you can see, after the use of canvas = FigureCanvas(fig), we are not able anymore to instantiate a very simple custom widget. Using old or new way to call inherited __init__ do not change anything.
Matplotlib version
Our CI is on Windows.
But I can reproduce it on my system with a fresh virtualenv, compiling everything using pip: numpy, matplotlib and PySide.
- Operating System: Debian 8
- Matplotlib Version: 2.1.0rc1
- Python Version: Python 2.7.9
- Other Libraries:
Package Version
---------------------------------- --------
backports.functools-lru-cache 1.4
backports.shutil-get-terminal-size 1.0.0
cycler 0.10.0
decorator 4.1.2
enum34 1.1.6
ipython 5.4.1
ipython-genutils 0.2.0
matplotlib 2.1.0rc1
numpy 1.13.1
pathlib2 2.3.0
pexpect 4.2.1
pickleshare 0.7.4
pip 9.0.1
prompt-toolkit 1.0.15
ptyprocess 0.5.2
Pygments 2.2.0
pyparsing 2.2.0
PySide 1.2.4
python-dateutil 2.6.1
pytz 2017.2
scandir 1.5
setuptools 36.4.0
simplegeneric 0.8.1
six 1.10.0
subprocess32 3.2.7
traitlets 4.3.2
wcwidth 0.1.7
wheel 0.30.0a0
- There is no problem with the same environment and using matplotlib 2.0.0 or 2.0.2.
Activity
t20100 commentedon Sep 6, 2017
It looks to come from
_allow_super_initwhich temporarily monkey-patch theQWidget.__init__https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/backends/backend_qt5.py#L137
and apparently does not undo the monkey-patch properly with PySide.
From what I tested, replacing:
by
QtWidgets.QWidget.__init__ = qwidget_initin the wrappersolves the issue and works for PySide (1.2.4) and PyQt4 (4.11.4) with python2.7 and PyQt5 (5.6) with python3.5 on Windows.
Yet I'am not sure about side effects...
[-]Using matplotlib 2.1.0rc1 looks to corrupt PySide[/-][+]Using matplotlib 2.1.0rc1 seems to corrupt PySide[/+]dopplershift commentedon Sep 6, 2017
attn: @anntzer
anntzer commentedon Sep 6, 2017
In my hand failure with pyside bisects back to #8394 and reverting it with
appears to fix the issue (although it would be nice to know why).
I was going to say we definitely need to test pyside too, but right now the interactive backend mini-test-suite is Py3 only and this issue appears to be Py2 only.
anntzer commentedon Sep 6, 2017
Looks like the first thing to do is actually
to fix https://bugreports.qt.io/browse/PYSIDE-75 -- this appears to also work with pyqt5-py3. I haven't tried all the combos but it does make sense to pass bytes as argv anyways.
This does not fix the issue with the example code by the OP though (which is not subject to this bug as they explicitly create the QApp themselves).
anntzer commentedon Sep 6, 2017
And the patch proposed by the OP also looks fine to me.
t20100 commentedon Sep 7, 2017
This patch also solves the issue and seems to work for what I tested (PySide, PyQt4 on python2, PyQt5 (5.6 and 5.9) on python3 on Windows).
It avoids monkey-patching
QWidget.__init__.(patch updated after more testing)
If you are happy with this patch or the previous one, I can test further and make a PR.
anntzer commentedon Sep 7, 2017
See #9040 for why we switched to using super throughout. Please check whether your patch is subject to the same issue (I can't tell).
t20100 commentedon Sep 8, 2017
I could not reproduced #9040 with both patches.
(Tested on Debian8 with python 2.7 and 3.4 with PySide, PyQt4 and PyQt5.. not tested with PySide2).
By decorating
__init__as in #9040, I also checked that the second patch was not calling multiple times the__init__.In #8618, a patch similar to the second one I proposed was already proposed but the
ifin the__init__was apparently disliked (in the end thisifends up in the_allow_super_init).I made 2 branches with the two patches:
_allow_super_init: https://github.com/t20100/matplotlib/tree/remove-qwidget-init-monkey-patchdopplershift commentedon Sep 18, 2017
Closed by #9169.