Repository: spyder-ide/spyder

Issue:
Can't set other Python interpreter
## Description

### What steps will reproduce the problem?

<!--- You can use Markdown here --->

- Open spyder;
- Try to set an other interpreter;

The error occurs when setting the path manualy. When I try to set it browsing the files spyder freeze.

### Traceback
```python-traceback
  File "/usr/lib/python3.7/site-packages/spyder/preferences/configdialog.py", line 222, in button_clicked
    configpage.apply_changes()
  File "/usr/lib/python3.7/site-packages/spyder/preferences/configdialog.py", line 96, in apply_changes
    self.save_to_conf()
  File "/usr/lib/python3.7/site-packages/spyder/preferences/configdialog.py", line 425, in save_to_conf
    if lineedit.content_type == list:
AttributeError: 'QLineEdit' object has no attribute 'content_type'
```

## Versions

* Spyder version: 4.0.0.dev0 
* Python version: 3.7.3
* Qt version: 5.12.3
* PyQt5 version: 5.12.2
* Operating System: Linux 4.19.49-1-MANJARO

### Dependencies

```
pygments >=2.0    :  2.4.2 (OK)
qdarkstyle >=2.6.4:  2.7 (OK)
sphinx >=0.6.6    :  2.1.0 (OK)
psutil >=0.3      :  5.6.2 (OK)
pyls >=0.27.0     :  0.26.1 (NOK)
nbconvert >=4.0   :  5.5.0.dev0 (OK)
pandas >=0.13.1   :  None (NOK)
numpy >=1.7       :  None (NOK)
sympy >=0.7.3     :  None (NOK)
cython >=0.21     :  None (NOK)
qtconsole >=4.5.0 :  4.5.1 (OK)
IPython >=4.0     :  7.5.0 (OK)
matplotlib >=2.0.0:  None (NOK)
pylint >=0.25     :  2.3.1 (OK)
```



Discussion:
@goanpeca, I think you introduced this bug. Please take a look at it.
>@goanpeca, I think you introduced this bug. Please take a look at it.

Yes!

Ok, I will add tests for this to catch these regressions.




Resolving patch:
diff --git a/spyder/preferences/configdialog.py b/spyder/preferences/configdialog.py
--- a/spyder/preferences/configdialog.py
+++ b/spyder/preferences/configdialog.py
@@ -392,10 +392,13 @@ def load_from_conf(self):
              ), (option, default) in list(self.scedits.items()):
             edit = clayout.lineedit
             btn = clayout.colorbtn
-            color, bold, italic = self.get_option(option, default)
-            edit.setText(color)
-            cb_bold.setChecked(bold)
-            cb_italic.setChecked(italic)
+            options = self.get_option(option, default)
+            if options:
+                color, bold, italic = options
+                edit.setText(color)
+                cb_bold.setChecked(bold)
+                cb_italic.setChecked(italic)
+
             edit.textChanged.connect(lambda _foo, opt=option:
                                      self.has_been_modified(opt))
             # QAbstractButton works differently for PySide and PyQt
@@ -422,19 +425,21 @@ def save_to_conf(self):
             self.set_option(option, radiobutton.isChecked())
         for lineedit, (option, _default) in list(self.lineedits.items()):
             data = lineedit.text()
-            if lineedit.content_type == list:
+            content_type = getattr(lineedit, 'content_type', None)
+            if content_type == list:
                 data = [item.strip() for item in data.split(',')]
             else:
                 data = to_text_string(data)
             self.set_option(option, data)
         for textedit, (option, _default) in list(self.textedits.items()):
             data = textedit.toPlainText()
-            if textedit.content_type == dict:
+            content_type = getattr(textedit, 'content_type', None)
+            if content_type == dict:
                 if data:
                     data = ast.literal_eval(data)
                 else:
                     data = textedit.content_type()
-            elif textedit.content_type in (tuple, list):
+            elif content_type in (tuple, list):
                 data = [item.strip() for item in data.split(',')]
             else:
                 data = to_text_string(data)


Test patch:
diff --git a/spyder/preferences/tests/conftest.py b/spyder/preferences/tests/conftest.py
new file mode 100644
--- /dev/null
+++ b/spyder/preferences/tests/conftest.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+# ----------------------------------------------------------------------------
+# Copyright © Spyder Project Contributors
+#
+# Licensed under the terms of the MIT License
+# ----------------------------------------------------------------------------
+
+"""
+Testing utilities to be used with pytest.
+"""
+
+try:
+    from unittest.mock import Mock
+except ImportError:
+    from mock import Mock  # Python 2
+
+# Third party imports
+import pytest
+
+# Local imports
+from spyder.preferences.appearance import AppearanceConfigPage
+from spyder.preferences.configdialog import ConfigDialog
+from spyder.preferences.general import MainConfigPage
+from spyder.preferences.shortcuts import ShortcutsConfigPage
+
+
+class MainWindowMock:
+    def __init__(self):
+        self.default_style = None
+        self.widgetlist = []
+        self.thirdparty_plugins = []
+
+        for attr in ['mem_status', 'cpu_status']:
+            mock_attr = Mock()
+            setattr(mock_attr, 'toolTip', lambda: '')
+            setattr(mock_attr, 'setToolTip', lambda x: '')
+            setattr(mock_attr, 'is_supported', lambda: True)
+            setattr(self, attr, mock_attr)
+
+
+@pytest.fixture
+def global_config_dialog(qtbot):
+    """
+    Fixture that includes the general preferences options.
+
+    These options are the ones not tied to a specific plugin.
+    """
+    dlg = ConfigDialog()
+    dlg.show()
+
+    from spyder.preferences.maininterpreter import MainInterpreterConfigPage
+    from spyder.preferences.runconfig import RunConfigPage
+
+    qtbot.addWidget(dlg)
+    for widget_class in [AppearanceConfigPage, MainConfigPage,
+                         MainInterpreterConfigPage, ShortcutsConfigPage,
+                         RunConfigPage]:
+        widget = widget_class(dlg, main=MainWindowMock())
+        widget.initialize()
+        dlg.add_page(widget)
+    return dlg
diff --git a/spyder/preferences/tests/test_config_dialog.py b/spyder/preferences/tests/test_config_dialog.py
new file mode 100644
--- /dev/null
+++ b/spyder/preferences/tests/test_config_dialog.py
@@ -0,0 +1,17 @@
+# -*- coding: utf-8 -*-
+# ----------------------------------------------------------------------------
+# Copyright © Spyder Project Contributors
+#
+# Licensed under the terms of the MIT License
+# ----------------------------------------------------------------------------
+
+"""
+Tests for configdialog.py
+"""
+
+
+def test_config_dialog_save_to_conf(qtbot, global_config_dialog):
+    for index in range(global_config_dialog.pages_widget.count()):
+        configpage = global_config_dialog.get_page(index)
+        configpage.save_to_conf()
+        assert configpage.is_valid()