Repository: silx-kit/silx
Issue: [plot] Infinite loop when the backend is specified with unicode in Python2

It affect silx 0.11 release.

I guess t is the same if we specify it in binary with python3.

Feed back from @woutdenolf 

As silx view save the backend in a configuration file. When he reach it back, it is in unicode. Or maybe it was unicode because it was generated by another silx view installation using Python 3.

Anyway:
```
ERROR:silx.gui.qt._qt:<type 'exceptions.RuntimeError'> maximum recursion depth exceeded in cmp   File "/users/opid21/.conda/envs/pymcadev/lib/python2.7/site-packages/silx/gui/data/DataViewer.py", line 233, in __setDataInView
    self.__currentView.setData(self.__displayedData)
  File "/users/opid21/.conda/envs/pymcadev/lib/python2.7/site-packages/silx/gui/data/DataViews.py", line 579, in setData
    self.__updateDisplayedView()
  File "/users/opid21/.conda/envs/pymcadev/lib/python2.7/site-packages/silx/gui/data/DataViews.py", line 557, in __updateDisplayedView
    w = self.__currentView.getWidget()
  File "/users/opid21/.conda/envs/pymcadev/lib/python2.7/site-packages/silx/gui/data/DataViews.py", line 336, in getWidget
    self.__widget = self.createWidget(self.__parent)
  File "/users/opid21/.conda/envs/pymcadev/lib/python2.7/site-packages/silx/gui/data/DataViews.py", line 1391, in createWidget
    widget = ArrayCurvePlot(parent)
  File "/users/opid21/.conda/envs/pymcadev/lib/python2.7/site-packages/silx/gui/data/NXdataWidgets.py", line 78, in __init__
    self._plot = Plot1D(self)
  File "/users/opid21/.conda/envs/pymcadev/lib/python2.7/site-packages/silx/gui/plot/PlotWindow.py", line 805, in __init__
    roi=True, mask=False, fit=True)
  File "/users/opid21/.conda/envs/pymcadev/lib/python2.7/site-packages/silx/gui/plot/PlotWindow.py", line 114, in __init__
    super(PlotWindow, self).__init__(parent=parent, backend=backend)
  File "/users/opid21/.conda/envs/pymcadev/lib/python2.7/site-packages/silx/gui/plot/PlotWidget.py", line 222, in __init__
    self._setBackend(backend)
  File "/users/opid21/.conda/envs/pymcadev/lib/python2.7/site-packages/silx/gui/plot/PlotWidget.py", line 361, in _setBackend
    self._backend = self.__getBackendClass(backend)(self, self)
  File "/users/opid21/.conda/envs/pymcadev/lib/python2.7/site-packages/silx/gui/plot/PlotWidget.py", line 343, in __getBackendClass
    return self.__getBackendClass(b)
  File "/users/opid21/.conda/envs/pymcadev/lib/python2.7/site-packages/silx/gui/plot/PlotWidget.py", line 343, in __getBackendClass
    return self.__getBackendClass(b)
  File "/users/opid21/.conda/envs/pymcadev/lib/python2.7/site-packages/silx/gui/plot/PlotWidget.py", line 343, in __getBackendClass
    return self.__getBackendClass(b)
...
    return self.__getBackendClass(b)
  File "/users/opid21/.conda/envs/pymcadev/lib/python2.7/site-packages/silx/gui/plot/PlotWidget.py", line 343, in __getBackendClass
    return self.__getBackendClass(b)
  File "/users/opid21/.conda/envs/pymcadev/lib/python2.7/site-packages/silx/gui/plot/PlotWidget.py", line 340, in __getBackendClass
    elif isinstance(backend, abc.Iterable):
  File "/users/opid21/.conda/envs/pymcadev/lib/python2.7/abc.py", line 132, in __instancecheck__
    if subclass is not None and subclass in cls._abc_cache:
  File "/users/opid21/.conda/envs/pymcadev/lib/python2.7/_weakrefset.py", line 75, in __contains__
    return wr in self.data
```

Changed files:
silx/gui/plot/test/testPlotWidget.py, silx/gui/plot/PlotWidget.py

diff --git a/silx/gui/plot/PlotWidget.py b/silx/gui/plot/PlotWidget.py
old mode 100644
new mode 100755
index 0e4603e1a..b0b80189a
--- a/silx/gui/plot/PlotWidget.py
+++ b/silx/gui/plot/PlotWidget.py
@@ -39,10 +39,6 @@ _logger = logging.getLogger(__name__)
 
 
 from collections import OrderedDict, namedtuple
-try:
-    from collections import abc
-except ImportError:  # Python2 support
-    import collections as abc
 from contextlib import contextmanager
 import datetime as dt
 import itertools
@@ -60,6 +56,7 @@ try:
 except ImportError:
     _logger.debug("matplotlib not available")
 
+import six
 from ..colors import Colormap
 from .. import colors
 from . import PlotInteraction
@@ -311,7 +308,7 @@ class PlotWidget(qt.QMainWindow):
         if callable(backend):
             return backend
 
-        elif isinstance(backend, str):
+        elif isinstance(backend, six.string_types):
             backend = backend.lower()
             if backend in ('matplotlib', 'mpl'):
                 try:
@@ -337,7 +334,7 @@ class PlotWidget(qt.QMainWindow):
 
             return backendClass
 
-        elif isinstance(backend, abc.Iterable):
+        elif isinstance(backend, (tuple, list)):
             for b in backend:
                 try:
                     return self.__getBackendClass(b)
diff --git a/silx/gui/plot/test/testPlotWidget.py b/silx/gui/plot/test/testPlotWidget.py
old mode 100644
new mode 100755
index 7449c12d3..341e39368
--- a/silx/gui/plot/test/testPlotWidget.py
+++ b/silx/gui/plot/test/testPlotWidget.py
@@ -32,6 +32,7 @@ __date__ = "03/01/2019"
 import unittest
 import logging
 import numpy
+import sys
 
 from silx.utils.testutils import ParametricTestCase, parameterize
 from silx.gui.utils.testutils import SignalListener
@@ -57,6 +58,19 @@ DATA_2D = numpy.arange(SIZE ** 2).reshape(SIZE, SIZE)
 logger = logging.getLogger(__name__)
 
 
+class TestSpecialBackend(PlotWidgetTestCase, ParametricTestCase):
+
+    def __init__(self, methodName='runTest', backend=None):
+        TestCaseQt.__init__(self, methodName=methodName)
+        self.__backend = backend
+
+    def _createPlot(self):
+        return PlotWidget(backend=self.__backend)
+
+    def testPlot(self):
+        self.assertIsNotNone(self.plot)
+
+
 class TestPlotWidget(PlotWidgetTestCase, ParametricTestCase):
     """Basic tests for PlotWidget"""
 
@@ -1581,6 +1595,10 @@ def suite():
     for testClass in testClasses:
         test_suite.addTest(parameterize(testClass, backend=None))
 
+    test_suite.addTest(parameterize(TestSpecialBackend, backend=u"mpl"))
+    if sys.version_info[0] == 2:
+        test_suite.addTest(parameterize(TestSpecialBackend, backend=b"mpl"))
+
     if test_options.WITH_GL_TEST:
         # Tests with OpenGL backend
         for testClass in testClasses: