Repository: spyder-ide/spyder

Issue:
Failed to import data in Variable Explorer on Windows
## Description of your problem

Import data is not working.

**What steps will reproduce the problem?**

1. In variable explorer, export data as `a.spydata`
2. Import `a.spydata`

**What is the expected output? What do you see instead?**


**Please provide any additional information below**

In source code `spyder/widgets/ipythonconsole/namespacebrowser.py`
```python
    def load_data(self, filename, ext):
        # Wait until the kernel tries to load the file
        wait_loop = QEventLoop()
        self.sig_got_reply.connect(wait_loop.quit)
        self.silent_exec_method(
HERE--->        "get_ipython().kernel.load_data('%s', '%s')" % (filename, ext))
        wait_loop.exec_()
```
I'm using Windows, so `filename` contains backslash, which leads to this error. Escaping should be done here.

It's so hard to find out the solution to this problem... And I think all `silent_execute` is not safe because the same problem could happen.

But weirdly, spyder with python3 works fine.

## Versions and main components

* Spyder Version: 3.0.2
* Python Version: Python 2.7.10 (default, May 23 2015, 09:44:00) [MSC v.1500 64 bit (AMD64)]
* Operating system: Windows 10


## Dependencies

Please go to the menu entry `Help > Optional Dependencies` (or
`Help > Dependencies`), press the button `Copy to clipboard`
and paste the contents below:



Discussion:
Sorry for inconvenience, we didn't test this functionality on Windows. We'll fix it for Spdyer 3.1.

@andfoy, please work on this one.
I tried to reproduce this error on Windows, without any success. Can we have more information about the reproduction scenario?


## Version and main components

* Spyder Version: 3.1.0.dev0 8f2101c4
* Python Version: Python 2.7.12 |Anaconda custom (64-bit)| (default, Jun 29 2016, 11:07:13) [MSC v.1500 64 bit (AMD64)] on win32
* Qt Versions: 5.6.0, PyQt5 5.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.6.4 (OK)

```
You must name the saved spydata file `xxx:\a.spydata`, since '\a' will become '\x07' which leads to this error. 
What's more, I'm using PyQt4. Maybe it's not a problem in PyQt5.

## Version and main components

Spyder Version: 3.0.2
Python Version: Python 2.7.10 64-bit
Qt Versions: 4.8.7, PyQt4 4.11.4 on Windows

## Dependencies

jedi >=0.8.1     :  0.9.0 (OK)
matplotlib >=1.0 :  1.4.3 (OK)
nbconvert >=4.0  :  5.0.0 (OK)
numpy >=1.7      :  1.11.3 (OK)
pandas >=0.13.1  :  0.19.2 (OK)
pep8 >=0.6       :  1.7.0 (OK)
pyflakes >=0.5.0 :  1.4.0 (OK)
pygments >=2.0   :  2.1.3 (OK)
pylint >=0.25    :  1.6.4 (OK)
qtconsole >=4.2.0:  4.2.1 (OK)
rope >=0.9.4     :  0.10.3 (OK)
sphinx >=0.6.6   :  1.5.1 (OK)
sympy >=0.7.3    :  0.7.6 (OK)


Resolving patch:
diff --git a/spyder/utils/iofuncs.py b/spyder/utils/iofuncs.py
--- a/spyder/utils/iofuncs.py
+++ b/spyder/utils/iofuncs.py
@@ -16,6 +16,7 @@
 import sys
 import os
 import tarfile
+import tempfile
 import os.path as osp
 import shutil
 import warnings
@@ -338,13 +339,15 @@ def load_dictionary(filename):
     """Load dictionary from .spydata file"""
     filename = osp.abspath(filename)
     old_cwd = getcwd()
-    os.chdir(osp.dirname(filename))
+    tmp_folder = tempfile.mkdtemp()
+    os.chdir(tmp_folder)
     data = None
     error_message = None
     try:
         tar = tarfile.open(filename, "r")
         tar.extractall()
-        pickle_filename = osp.splitext(filename)[0]+'.pickle'
+        data_file = osp.basename(filename)
+        pickle_filename = osp.splitext(data_file)[0]+'.pickle'
         try:
             # Old format (Spyder 2.0-2.1 for Python 2)
             with open(pickle_filename, 'U') as fdesc:
@@ -359,7 +362,7 @@ def load_dictionary(filename):
             try:
                 saved_arrays = data.pop('__saved_arrays__')
                 for (name, index), fname in list(saved_arrays.items()):
-                    arr = np.load( osp.join(osp.dirname(filename), fname) )
+                    arr = np.load( osp.join(tmp_folder, fname) )
                     if index is None:
                         data[name] = arr
                     elif isinstance(data[name], dict):
@@ -368,11 +371,13 @@ def load_dictionary(filename):
                         data[name].insert(index, arr)
             except KeyError:
                 pass
-        for fname in [pickle_filename]+[fn for fn in list(saved_arrays.values())]:
-            os.remove(fname)
     except (EOFError, ValueError) as error:
         error_message = to_text_string(error)
     os.chdir(old_cwd)
+    try:
+        shutil.rmtree(tmp_folder)
+    except OSError as error:
+        error_message = to_text_string(error)
     return data, error_message
 
 
diff --git a/spyder/widgets/ipythonconsole/namespacebrowser.py b/spyder/widgets/ipythonconsole/namespacebrowser.py
--- a/spyder/widgets/ipythonconsole/namespacebrowser.py
+++ b/spyder/widgets/ipythonconsole/namespacebrowser.py
@@ -116,7 +116,7 @@ def load_data(self, filename, ext):
         wait_loop = QEventLoop()
         self.sig_got_reply.connect(wait_loop.quit)
         self.silent_exec_method(
-                "get_ipython().kernel.load_data('%s', '%s')" % (filename, ext))
+                r"get_ipython().kernel.load_data('%s', '%s')" % (filename, ext))
         wait_loop.exec_()
 
         # Remove loop connection and loop
@@ -129,7 +129,7 @@ def save_namespace(self, filename):
         # Wait until the kernel tries to save the file
         wait_loop = QEventLoop()
         self.sig_got_reply.connect(wait_loop.quit)
-        self.silent_exec_method("get_ipython().kernel.save_namespace('%s')" %
+        self.silent_exec_method(r"get_ipython().kernel.save_namespace('%s')" %
                                 filename)
         wait_loop.exec_()
 


Test patch:
diff --git a/spyder/utils/tests/test_export.spydata b/spyder/utils/tests/test_export.spydata
new file mode 100644
Binary files /dev/null and b/spyder/utils/tests/test_export.spydata differ
diff --git a/spyder/utils/tests/test_iofuncs.py b/spyder/utils/tests/test_iofuncs.py
--- a/spyder/utils/tests/test_iofuncs.py
+++ b/spyder/utils/tests/test_iofuncs.py
@@ -16,6 +16,19 @@
 LOCATION = os.path.realpath(os.path.join(os.getcwd(),
                                          os.path.dirname(__file__)))
 
+@pytest.fixture
+def spydata_values():
+    """
+    Define spydata file ground truth values.
+
+    The file test_export.spydata contains four variables to be loaded, this
+    function declares those variables in a static way.
+    """
+    A = 1
+    B = 'ham'
+    C = np.eye(3)
+    D = {'a':1}
+    return {'A':A, 'B':B, 'C':C, 'D':D}
 
 @pytest.fixture
 def real_values():
@@ -57,3 +70,17 @@ def test_matlab_import(real_values):
     for var in sorted(real_values.keys()):
         valid = valid and bool(np.mean(real_values[var] == inf[var]))
     assert valid
+
+def test_spydata_import(spydata_values):
+    """
+    Test spydata handling and variable importing.
+
+    This test loads all the variables contained inside a spydata tar
+    container and compares them against their static values.
+    """
+    path = os.path.join(LOCATION, 'test_export.spydata')
+    data, error = io.load_dictionary(path)
+    valid = True
+    for var in sorted(spydata_values.keys()):
+        valid = valid and bool(np.mean(spydata_values[var] == data[var]))
+    assert valid
\ No newline at end of file