Repository: spyder-ide/spyder

Issue:
"ValueError: malformed node or string" when evaluating code that contains Path elements
## Description

**What steps will reproduce the problem?**

1. 
2. 
3. 

**What is the expected output? What do you see instead?**


**Please provide any additional information below**

  File "/usr/local/lib/python2.7/dist-packages/qtconsole/base_frontend_mixin.py", line 163, in _dispatch
    handler(msg)
  File "/usr/local/lib/python2.7/dist-packages/spyder/widgets/ipythonconsole/namespacebrowser.py", line 220, in _handle_execute_reply
    self.handle_exec_method(msg)
  File "/usr/local/lib/python2.7/dist-packages/spyder/widgets/ipythonconsole/shell.py", line 351, in handle_exec_method
    view = ast.literal_eval(literal)
  File "/usr/lib/python2.7/ast.py", line 80, in literal_eval
    return _convert(node_or_string)
  File "/usr/lib/python2.7/ast.py", line 63, in _convert
    in zip(node.keys, node.values))
  File "/usr/lib/python2.7/ast.py", line 62, in <genexpr>
    return dict((_convert(k), _convert(v)) for k, v
  File "/usr/lib/python2.7/ast.py", line 63, in _convert
    in zip(node.keys, node.values))
  File "/usr/lib/python2.7/ast.py", line 62, in <genexpr>
    return dict((_convert(k), _convert(v)) for k, v
  File "/usr/lib/python2.7/ast.py", line 79, in _convert
    raise ValueError('malformed string')
ValueError: malformed string


## Version and main components

* Spyder Version: 3.2.3 
* Python Version: 2.7.12
* Qt Versions: 4.8.7, PyQt4 (API v2) 4.11.4 on Linux

## Dependencies
```
pyflakes >=0.5.0  :  1.6.0 (OK)
pycodestyle >=2.3 :  2.3.1 (OK)
pygments >=2.0    :  2.2.0 (OK)
pandas >=0.13.1   :  0.16.2 (OK)
numpy >=1.7       :  1.12.1 (OK)
sphinx >=0.6.6    :  1.6.3 (OK)
rope >=0.9.4      :  0.10.7 (OK)
jedi >=0.9.0      :  0.10.2 (OK)
psutil >=0.3      :  5.3.1 (OK)
nbconvert >=4.0   :  5.3.1 (OK)
sympy >=0.7.3     :  0.7.6 (OK)
cython >=0.21     :  0.22.1 (OK)
qtconsole >=4.2.0 :  4.3.1 (OK)
IPython >=4.0;<6.0:  5.4.1 (OK)
pylint >=0.25     :  1.7.2 (OK)

```



Discussion:
What code did you evaluate in the IPython console before getting this error?
We have seen this error before, but we need a clear and reproducible way to reproduce it to fix it. So please help us with that.
Okay, after some more digging, it seems to be a problem with using `path`.  If I run the following code either in the iPython console directly or by running a script:
```python
from path import Path
x = Path('blah-blah-blah')
```
then I get the following dialog from spyder:

> Spyder has encountered a problem.
> Sorry for the inconvenience.
> 
> You can automatically submit this error to our Github issues tracker.
> 
> Note: You need a Github account for that.

which contains the error information above.  Any time afterwards, if I enter something into the iPython console I get the same dialog.  If I restart the iPython kernel, this behavior stops, but if I run the script (or code) again the dialog returns.
Ok, great!! Thanks a lot for finding an easy way to reproduce this problem.
Happy help.  Hope it can be sorted out!
Yep, having a way to reproduce it helps a lot!
@dalthviz, please take a look at this one.

Resolving patch:
diff --git a/spyder/py3compat.py b/spyder/py3compat.py
--- a/spyder/py3compat.py
+++ b/spyder/py3compat.py
@@ -79,9 +79,20 @@
     import reprlib
     import queue as Queue
 
+
 #==============================================================================
 # Strings
 #==============================================================================
+def is_type_text_string(obj):
+    """Return True if `obj` is type text string, False if it is anything else,
+    like an instance of a class that extends the basestring class."""
+    if PY2:
+        # Python 2
+        return type(obj) in [str, unicode]
+    else:
+        # Python 3
+        return type(obj) in [str, bytes]
+
 def is_text_string(obj):
     """Return True if `obj` is a text string, False if it is anything else,
     like binary data (Python 3) or QString (Python 2, PyQt API #1)"""
diff --git a/spyder/widgets/variableexplorer/utils.py b/spyder/widgets/variableexplorer/utils.py
--- a/spyder/widgets/variableexplorer/utils.py
+++ b/spyder/widgets/variableexplorer/utils.py
@@ -16,7 +16,8 @@
 # Local imports
 from spyder.config.base import get_supported_types
 from spyder.py3compat import (NUMERIC_TYPES, TEXT_TYPES, to_text_string,
-                              is_text_string, is_binary_string, PY2,
+                              is_text_string, is_type_text_string,
+                              is_binary_string, PY2,
                               to_binary_string, iteritems)
 from spyder.utils import programs
 from spyder import dependencies
@@ -410,17 +411,28 @@ def value_to_display(value, minmax=False, level=0):
             else:
                 display = 'DatetimeIndex'
         elif is_binary_string(value):
-            try:
-                display = to_text_string(value, 'utf8')
-            except:
-                display = value
-            if level > 0:
-                display = (to_binary_string("'") + display +
-                           to_binary_string("'"))
+            # We don't apply this to classes that extend string types
+            # See issue 5636
+            if is_type_text_string(value):
+                try:
+                    display = to_text_string(value, 'utf8')
+                    if level > 0:
+                        display = u"'" + display + u"'"
+                except:
+                    display = value
+                    if level > 0:
+                        display = b"'" + display + b"'"
+            else:
+                display = default_display(value)
         elif is_text_string(value):
-            display = value
-            if level > 0:
-                display = u"'" + display + u"'"
+            # We don't apply this to classes that extend string types
+            # See issue 5636
+            if is_type_text_string(value):
+                display = value
+                if level > 0:
+                    display = u"'" + display + u"'"
+            else:
+                display = default_display(value)
         elif (isinstance(value, datetime.date) or
               isinstance(value, datetime.timedelta)):
             display = str(value)


Test patch:
diff --git a/spyder/widgets/variableexplorer/tests/test_utils.py b/spyder/widgets/variableexplorer/tests/test_utils.py
--- a/spyder/widgets/variableexplorer/tests/test_utils.py
+++ b/spyder/widgets/variableexplorer/tests/test_utils.py
@@ -17,9 +17,10 @@
 
 # Local imports
 from spyder.config.base import get_supported_types
+from spyder.py3compat import PY2
 from spyder.widgets.variableexplorer.utils import (sort_against,
-    is_supported, value_to_display)
-
+                                                   is_supported,
+                                                   value_to_display)
 
 def generate_complex_object():
     """Taken from issue #4221."""
@@ -65,6 +66,16 @@ def test_none_values_are_supported():
     assert is_supported(none_tuple, filters=tuple(supported_types[mode]))
 
 
+def test_str_subclass_display():
+    """Test for value_to_display of subclasses of str/basestring."""
+    class Test(str):
+        def __repr__(self):
+            return 'test'
+    value = Test()
+    value_display = value_to_display(value)
+    assert 'Test object' in value_display
+
+
 def test_default_display():
     """Tests for default_display."""
     # Display of defaultdict
@@ -199,5 +210,17 @@ def test_datetime_display():
             ("{0:2017-12-18, 1:2017-12-18 13:43:02, 2:1:00:00}"))
 
 
+def test_str_in_container_display():
+    """Test that strings are displayed correctly inside lists or dicts."""
+    # Assert that both bytes and unicode return the right display
+    assert value_to_display([b'a', u'b']) == "['a', 'b']"
+
+    # Encoded unicode gives bytes and it can't be transformed to
+    # unicode again. So this test the except part of
+    # is_binary_string(value) in value_to_display
+    if PY2:
+        assert value_to_display([u'Э'.encode('cp1251')]) == "['\xdd']"
+
+
 if __name__ == "__main__":
     pytest.main()