Repository: spyder-ide/spyder

Issue:
Spyder Variable explorer crashes when double clicking a 2 line DataFrame
## Description

**What steps will reproduce the problem?**

1. place a csv version of the attached fuct.csv on c drive
2. Run the below:

        import pandas as pd
        dfraw = pd.read_csv(r"C:\fuct.csv")

3. go to Variable explorer and double click to view dfraw

I attach the crash message in Spyder

![spyder_crash](https://cloud.githubusercontent.com/assets/24395731/21567946/32bac9b2-cea8-11e6-84f0-d6394ba07a4d.png)

The file can be saved as csv (the input to dataframe)
[fuct.txt](https://github.com/spyder-ide/spyder/files/678862/fuct.txt)

**What is the expected output? What do you see instead?**


**Please provide any additional information below**


## Version and main components

* Spyder Version: 3.0.2 
* Python Version: 2.7.12
* Qt Versions: 5.6.0, PyQt5 5
[fuct.txt](https://github.com/spyder-ide/spyder/files/678855/fuct.txt)

.6 on Windows

## Dependencies
```
pyflakes >=0.5.0 :  1.3.0 (OK)
pep8 >=0.6       :  1.7.0 (OK)
pygments >=2.0   :  2.1.3 (OK)
qtconsole >=4.2.0:  4.2.1 (OK)
nbconvert >=4.0  :  4.2.0 (OK)
pandas >=0.13.1  :  0.18.1 (OK)
numpy >=1.7      :  1.11.1 (OK)
sphinx >=0.6.6   :  1.4.6 (OK)
rope >=0.9.4     :  0.9.4 (OK)
jedi >=0.8.1     :  0.9.0 (OK)
matplotlib >=1.0 :  1.5.3 (OK)
sympy >=0.7.3    :  1.0 (OK)
pylint >=0.25    :  1.5.4 (OK)

```



Discussion:
@dalthviz, please take a look at this one.

Resolving patch:
diff --git a/spyder/widgets/variableexplorer/dataframeeditor.py b/spyder/widgets/variableexplorer/dataframeeditor.py
--- a/spyder/widgets/variableexplorer/dataframeeditor.py
+++ b/spyder/widgets/variableexplorer/dataframeeditor.py
@@ -29,7 +29,8 @@
 from spyder.config.base import _
 from spyder.config.fonts import DEFAULT_SMALL_DELTA
 from spyder.config.gui import get_font, config_shortcut
-from spyder.py3compat import io, is_text_string, PY2, to_text_string
+from spyder.py3compat import (io, is_text_string, PY2, to_text_string,
+                              TEXT_TYPES)
 from spyder.utils import encoding
 from spyder.utils import icon_manager as ima
 from spyder.utils.qthelpers import (add_actions, create_action,
@@ -197,6 +198,20 @@ def headerData(self, section, orientation, role=Qt.DisplayRole):
                 except:
                     header = to_text_string(self.df_header[0])
                 return to_qvariant(header)
+            elif isinstance(self.df_header[section-1], TEXT_TYPES):
+                # Get the proper encoding of the text in the header.
+                # Fixes Issue 3896
+                if not PY2:
+                    try:
+                        header = self.df_header[section-1].encode('utf-8')
+                        coding = 'utf-8-sig'
+                    except:
+                        header = self.df_header[section-1].encode('utf-8')
+                        coding = encoding.get_coding(header)
+                else:
+                    header = self.df_header[section-1]
+                    coding = encoding.get_coding(header)
+                return to_qvariant(to_text_string(header, encoding=coding))
             else:
                 return to_qvariant(to_text_string(self.df_header[section-1]))
         else:


Test patch:
diff --git a/spyder/widgets/variableexplorer/tests/issue_2514.csv b/spyder/widgets/variableexplorer/tests/issue_2514.csv
new file mode 100644
--- /dev/null
+++ b/spyder/widgets/variableexplorer/tests/issue_2514.csv
@@ -0,0 +1,3 @@
+﻿Date (MMM-YY),"El Salvador, San Salvador, Sorghum (Maicillo), Retail, (USD/Kg)","El Salvador, San Salvador, Sorghum (Maicillo), Wholesale, (USD/Kg)","El Salvador, San Salvador, Wheat (flour), Retail, (USD/Kg)","El Salvador, San Salvador, Wheat (flour), Wholesale, (USD/Kg)"
+Jan-2006,0.28,0.19,0.54,0.41
+Feb-2006,0.28,0.19,0.54,0.41
diff --git a/spyder/widgets/variableexplorer/tests/issue_3896.csv b/spyder/widgets/variableexplorer/tests/issue_3896.csv
new file mode 100644
--- /dev/null
+++ b/spyder/widgets/variableexplorer/tests/issue_3896.csv
@@ -0,0 +1,2 @@
+﻿,﻿Unieke_Idcode,a,b,c,d,f,g,h,i,j,k,l,m,n,o,p,q
+0,flooooo,0.55,10100103,595,sdsdsdf,sadsf,70,..,X1,30,01/02/2014,8.72E+12,sdsf,dsffds,dsdfbvdfg,ff,
diff --git a/spyder/widgets/variableexplorer/tests/test_dataframeeditor.py b/spyder/widgets/variableexplorer/tests/test_dataframeeditor.py
--- a/spyder/widgets/variableexplorer/tests/test_dataframeeditor.py
+++ b/spyder/widgets/variableexplorer/tests/test_dataframeeditor.py
@@ -14,16 +14,21 @@
     from unittest.mock import Mock
 except ImportError:
     from mock import Mock # Python 2
+import os
 
 # Third party imports
-from pandas import DataFrame, date_range
+from pandas import DataFrame, date_range, read_csv
 from qtpy.QtGui import QColor
+from qtpy.QtCore import Qt
 import pytest
 
 # Local imports
 from spyder.widgets.variableexplorer import dataframeeditor
 from spyder.widgets.variableexplorer.dataframeeditor import (
     DataFrameEditor, DataFrameModel)
+from spyder.py3compat import PY2
+
+FILES_PATH = os.path.dirname(os.path.realpath(__file__))
 
 # Helper functions
 def colorclose(color, hsva_expected):
@@ -174,6 +179,26 @@ def test_change_format_with_format_not_starting_with_percent(qtbot, monkeypatch)
     with qtbot.assertNotEmitted(editor.sig_option_changed):
         editor.change_format()
 
+def test_header_bom():
+    df = read_csv(os.path.join(FILES_PATH, 'issue_2514.csv'))
+    editor = DataFrameEditor(None)
+    editor.setup_and_check(df)
+    model = editor.dataModel
+    assert model.headerData(1, orientation=Qt.Horizontal) == "Date (MMM-YY)"
+
+def test_header_encoding():
+    df = read_csv(os.path.join(FILES_PATH, 'issue_3896.csv'))
+    editor = DataFrameEditor(None)
+    editor.setup_and_check(df)
+    model = editor.dataModel
+    assert model.headerData(0, orientation=Qt.Horizontal) == "Index"
+    assert model.headerData(1, orientation=Qt.Horizontal) == "Unnamed: 0"
+    assert model.headerData(2, orientation=Qt.Horizontal) == "Unieke_Idcode"
+    assert model.headerData(3, orientation=Qt.Horizontal) == "a"
+    assert model.headerData(4, orientation=Qt.Horizontal) == "b"
+    assert model.headerData(5, orientation=Qt.Horizontal) == "c"
+    assert model.headerData(6, orientation=Qt.Horizontal) == "d"
+
 
 if __name__ == "__main__":
     pytest.main()