Repository: spyder-ide/spyder

Issue:
Cannot runfile if the name of the file contains a `'` character in Windows
<!--- **PLEASE READ:** Before submitting here, please carefully consult our *Troubleshooting Guide* at https://github.com/spyder-ide/spyder/wiki/Troubleshooting-Guide-and-FAQ and search the issues page for your error/problem, as most posted bugs are duplicates or easy fixes.

If you don't find anything, please provide a detailed step-by-step description (in English) of the problem and what led up to it below. Issue reports without a clear way to reproduce them will be closed. Thanks! --->

## Problem Description

Trying to run a file named `test's.py ` in the IPython console with <kbd>F5</kbd> doesn't work in Windows.

This issue needs to be fixed before I can progress further on https://github.com/spyder-ide/spyder/pull/6750.

### What steps will reproduce the problem?

1. Create a simple python script and save it in file named it `test's.py ` or `test"s.py `
2. Run the file in the current IPython console with <kbd>F5</kbd>

### What is the expected output? What do you see instead?

I expect the file to run, but instead, there is an error message.


### Paste Traceback/Error Below (if applicable)
<!--- Copy from error dialog or View > Panes > Internal Console --->

```python-traceback

runfile('C:/test\'s.py', wdir='C:/test')
Traceback (most recent call last):

  File "<ipython-input-3-cfd6b8e87c59>", line 1, in <module>
    runfile('C:/test\'s.py', wdir='C:/test')

  File "E:\spyder\spyder\utils\site\sitecustomize.py", line 695, in runfile
    os.chdir(wdir)

FileNotFoundError: [WinError 2] The system cannot find the file specified: 'C:/test'

```

## Package Versions

* Spyder: Spyder Spyder 3.2.8.dev0 
* Python: Python 3.6.4 64bits
* Qt: Qt 5.9.1
* PyQt: PyQt5 5.9
* Operating System: Windows 10 amd64

### Dependencies
<!--- Please go to the menu entry Help > Dependencies,
press the Copy to clipboard button and paste below --->

```
IPython >=4.0    :  6.2.1 (OK)
cython >=0.21    :  0.27.3 (OK)
jedi >=0.9.0     :  0.11.1 (OK)
nbconvert >=4.0  :  5.3.1 (OK)
numpy >=1.7      :  1.13.0 (OK)
pandas >=0.13.1  :  0.22.0 (OK)
pycodestyle >=2.3:  2.3.1 (OK)
pyflakes >=0.6.0 :  1.6.0 (OK)
pygments >=2.0   :  2.2.0 (OK)
pylint >=0.25    :  1.8.2 (OK)
qtconsole >=4.2.0:  4.4.0.dev (OK)
rope >=0.9.4     :  0.10.7 (OK)
sphinx >=0.6.6   :  1.7.1 (OK)
sympy >=0.7.3    :  1.1.1 (OK)

```



Discussion:

Resolving patch:
diff --git a/spyder/plugins/editor.py b/spyder/plugins/editor.py
--- a/spyder/plugins/editor.py
+++ b/spyder/plugins/editor.py
@@ -2376,11 +2376,16 @@ def run_file(self, debug=False):
         if editorstack.save():
             editor = self.get_current_editor()
             fname = osp.abspath(self.get_current_filename())
-            
-            # Escape single and double quotes in fname (Fixes Issue 2158)
-            fname = fname.replace("'", r"\'")
-            fname = fname.replace('"', r'\"')
-            
+
+            # Get fname's dirname before we escape the single and double
+            # quotes (Fixes Issue #6771)
+            dirname = osp.dirname(fname)
+
+            # Escape single and double quotes in fname and dirname
+            # (Fixes Issue #2158)
+            fname = fname.replace("'", r"\'").replace('"', r'\"')
+            dirname = dirname.replace("'", r"\'").replace('"', r'\"')
+
             runconf = get_run_configuration(fname)
             if runconf is None:
                 dialog = RunConfigOneDialog(self)
@@ -2412,7 +2417,7 @@ def run_file(self, debug=False):
             clear_namespace = runconf.clear_namespace
 
             if runconf.file_dir:
-                wdir = osp.dirname(fname)
+                wdir = dirname
             elif runconf.cw_dir:
                 wdir = ''
             elif osp.isdir(runconf.dir):


Test patch:
diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py
--- a/spyder/app/tests/test_mainwindow.py
+++ b/spyder/app/tests/test_mainwindow.py
@@ -766,16 +766,19 @@ def test_set_new_breakpoints(main_window, qtbot):
 
 @pytest.mark.slow
 @flaky(max_runs=3)
-@pytest.mark.skipif(os.name == 'nt', reason="It times out sometimes on Windows")
-def test_run_code(main_window, qtbot):
+def test_run_code(main_window, qtbot, tmpdir):
     """Test all the different ways we have to run code"""
     # ---- Setup ----
+    p = tmpdir.mkdir(u"runtest's folder èáïü Øαôå 字分误").join(u"runtest's file èáïü Øαôå 字分误.py")
+    filepath = to_text_string(p)
+    shutil.copyfile(osp.join(LOCATION, 'script.py'), filepath)
+
     # Wait until the window is fully up
     shell = main_window.ipyconsole.get_current_shellwidget()
     qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT)
 
     # Load test file
-    main_window.editor.load(osp.join(LOCATION, 'script.py'))
+    main_window.editor.load(filepath)
 
     # Move to the editor's first line
     code_editor = main_window.editor.get_focus_widget()