Skip to content

Using matplotlib 2.1.0rc1 seems to corrupt PySide #9162

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
vallsv opened this issue Sep 6, 2017 · 9 comments
Closed

Using matplotlib 2.1.0rc1 seems to corrupt PySide #9162

vallsv opened this issue Sep 6, 2017 · 9 comments
Labels
Release critical For bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.
Milestone

Comments

@vallsv
Copy link
Contributor

vallsv commented Sep 6, 2017

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.
@t20100
Copy link
Contributor

t20100 commented Sep 6, 2017

It looks to come from _allow_super_init which temporarily monkey-patch the QWidget.__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:

                try:
                    # Restore __init__ to sip.simplewrapper.__init__.
                    del QtWidgets.QWidget.__init__
                except AttributeError:
                    pass

by QtWidgets.QWidget.__init__ = qwidget_init in the wrapper
solves 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...

@vallsv vallsv changed the title Using matplotlib 2.1.0rc1 looks to corrupt PySide Using matplotlib 2.1.0rc1 seems to corrupt PySide Sep 6, 2017
@dopplershift dopplershift added the Release critical For bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions. label Sep 6, 2017
@dopplershift dopplershift added this to the 2.1 (next point release) milestone Sep 6, 2017
@dopplershift
Copy link
Contributor

attn: @anntzer

@anntzer
Copy link
Contributor

anntzer commented Sep 6, 2017

In my hand failure with pyside bisects back to #8394 and reverting it with

diff --git a/lib/matplotlib/backends/backend_qt5.py b/lib/matplotlib/backends/backend_qt5.py
index 1d3c7cd53..7ed83e295 100644
--- a/lib/matplotlib/backends/backend_qt5.py
+++ b/lib/matplotlib/backends/backend_qt5.py
@@ -121,7 +121,7 @@ def _create_qApp():
                 if display is None or not re.search(r':\d', display):
                     raise RuntimeError('Invalid DISPLAY variable')
 
-            qApp = QtWidgets.QApplication(["matplotlib"])
+            qApp = QtWidgets.QApplication([])
             qApp.lastWindowClosed.connect(qApp.quit)
         else:
             qApp = app

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
Copy link
Contributor

anntzer commented Sep 6, 2017

Looks like the first thing to do is actually

diff --git a/lib/matplotlib/backends/backend_qt5.py b/lib/matplotlib/backends/backend_qt5.py
index 1d3c7cd53..8536313a6 100644
--- a/lib/matplotlib/backends/backend_qt5.py
+++ b/lib/matplotlib/backends/backend_qt5.py
@@ -121,7 +121,7 @@ def _create_qApp():
                 if display is None or not re.search(r':\d', display):
                     raise RuntimeError('Invalid DISPLAY variable')
 
-            qApp = QtWidgets.QApplication(["matplotlib"])
+            qApp = QtWidgets.QApplication([b"matplotlib"])
             qApp.lastWindowClosed.connect(qApp.quit)
         else:
             qApp = app

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
Copy link
Contributor

anntzer commented Sep 6, 2017

And the patch proposed by the OP also looks fine to me.

@t20100
Copy link
Contributor

t20100 commented 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__.

--- a/lib/matplotlib/backends/backend_qt5.py
+++ b/lib/matplotlib/backends/backend_qt5.py
@@ -233,12 +233,16 @@ class FigureCanvasQT(QtWidgets.QWidget, FigureCanvasBase):
         dpi = self._dpi_ratio * self.figure._original_dpi
         self.figure._set_dpi(dpi, forward=False)

-    @_allow_super_init
     def __init__(self, figure):
         _create_qApp()
         figure._original_dpi = figure.dpi

-        super(FigureCanvasQT, self).__init__(figure=figure)
+        if QT_API == "PyQt5":
+            super(FigureCanvasQT, self).__init__(figure=figure)
+
+        else:
+            QtWidgets.QWidget.__init__(self)
+            FigureCanvasBase.__init__(self, figure=figure)

         self.figure = figure
         self._update_figure_dpi()

(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
Copy link
Contributor

anntzer commented 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
Copy link
Contributor

t20100 commented 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 if in the __init__ was apparently disliked (in the end this if ends up in the _allow_super_init).

I made 2 branches with the two patches:

@dopplershift
Copy link
Contributor

Closed by #9169.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Release critical For bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.
Projects
None yet
Development

No branches or pull requests

4 participants