Skip to content

Commit 33ff0cc

Browse files
committed
runfile/debugfile in evaluated strings: replaced backslashes by slashes in paths
to avoid unicode decoding errors in Python 3 (or in Python 2 when future 'unicode_literals' has been imported). Update Issue 1438 Status: Fixed This change may have side effects. I strongly recommend further testing of running/debugging scripts in current intepreter (Python *and* IPython consoles) with filenames having "\U" characters for example.
1 parent ccb60fc commit 33ff0cc

File tree

6 files changed

+35
-25
lines changed

6 files changed

+35
-25
lines changed

spyderlib/interpreter.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
# Local imports:
2121
from spyderlib.utils.dochelpers import isdefined
2222
from spyderlib.utils import encoding
23+
from spyderlib.utils.misc import remove_backslashes
2324

2425
# Force Python to search modules in the current directory first:
2526
sys.path.insert(0, '')
@@ -173,7 +174,7 @@ def run_command(self, cmd, new_prompt=True):
173174
# run command
174175
elif run_match:
175176
filename = guess_filename(run_match.groups()[0])
176-
cmd = 'runfile(r"%s", args=None)' % filename
177+
cmd = "runfile('%s', args=None)" % remove_backslashes(filename)
177178
# !cd system command
178179
elif cd_match:
179180
cmd = 'import os; os.chdir(r"%s")' % cd_match.groups()[0].strip()

spyderlib/plugins/console.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
# Local imports
2525
from spyderlib.baseconfig import _
2626
from spyderlib.config import CONF
27-
from spyderlib.utils.misc import get_error_match
27+
from spyderlib.utils.misc import get_error_match, remove_backslashes
2828
from spyderlib.utils.qthelpers import (get_icon, create_action, add_actions,
2929
mimedata2url, DialogManager)
3030
from spyderlib.utils.environ import EnvDialog
@@ -222,12 +222,13 @@ def run_script(self, filename=None, silent=False, set_focus=False,
222222
os.getcwdu(), _("Python scripts")+" (*.py ; *.pyw ; *.ipy)")
223223
self.shell.interpreter.redirect_stds()
224224
if filename:
225-
os.chdir( os.path.dirname(filename) )
226-
filename = os.path.basename(filename)
225+
os.chdir( osp.dirname(filename) )
226+
filename = osp.basename(filename)
227227
else:
228228
return
229-
command = "runfile(%s, args=%s)" % (repr(osp.abspath(filename)),
230-
repr(args))
229+
filename = osp.abspath(filename)
230+
rbs = remove_backslashes
231+
command = "runfile('%s', args='%s')" % (rbs(filename), rbs(args))
231232
if set_focus:
232233
self.shell.setFocus()
233234
if self.dockwidget and not self.ismaximized:

spyderlib/plugins/externalconsole.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@
3030
from spyderlib.config import CONF
3131
from spyderlib.utils import programs
3232
from spyderlib.utils.misc import (get_error_match, get_python_executable,
33-
remove_trailing_single_backslash,
34-
is_python_script)
33+
remove_backslashes, is_python_script)
3534
from spyderlib.utils.qthelpers import get_icon, create_action, mimedata2url
3635
from spyderlib.widgets.tabs import Tabs
3736
from spyderlib.widgets.externalshell.pythonshell import ExternalPythonShell
@@ -659,9 +658,9 @@ def get_running_python_shell(self):
659658

660659
def run_script_in_current_shell(self, filename, wdir, args, debug):
661660
"""Run script in current shell, if any"""
662-
line = "%s(r'%s'" % ('debugfile' if debug else 'runfile',
663-
unicode(filename))
664-
norm = lambda text: remove_trailing_single_backslash(unicode(text))
661+
norm = lambda text: remove_backslashes(unicode(text))
662+
line = "%s('%s'" % ('debugfile' if debug else 'runfile',
663+
norm(filename))
665664
if args:
666665
line += ", args=r'%s'" % norm(args)
667666
if wdir:

spyderlib/plugins/ipythonconsole.py

+6-7
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@
3434
# Local imports
3535
from spyderlib.baseconfig import get_conf_path, _
3636
from spyderlib.utils import programs
37-
from spyderlib.utils.misc import (get_error_match,
38-
remove_trailing_single_backslash)
37+
from spyderlib.utils.misc import get_error_match, remove_backslashes
3938
from spyderlib.utils.qthelpers import (get_icon, get_std_icon, create_action,
4039
create_toolbutton, add_actions)
4140
from spyderlib.widgets.tabs import Tabs
@@ -690,17 +689,17 @@ def get_current_client(self):
690689

691690
def run_script_in_current_client(self, filename, wdir, args, debug):
692691
"""Run script in current client, if any"""
693-
norm = lambda text: remove_trailing_single_backslash(unicode(text))
692+
norm = lambda text: remove_backslashes(unicode(text))
694693
client = self.get_current_client()
695694
if client is not None:
696695
# Internal kernels, use runfile
697696
if client.kernel_widget_id is not None:
698-
line = "%s(r'%s'" % ('debugfile' if debug else 'runfile',
699-
unicode(filename))
697+
line = "%s('%s'" % ('debugfile' if debug else 'runfile',
698+
norm(filename))
700699
if args:
701-
line += ", args=r'%s'" % norm(args)
700+
line += ", args='%s'" % norm(args)
702701
if wdir:
703-
line += ", wdir=r'%s'" % norm(wdir)
702+
line += ", wdir='%s'" % norm(wdir)
704703
line += ")"
705704
else: # External kernels, use %run
706705
line = "%run "

spyderlib/utils/misc.py

+15-7
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,23 @@ def fix_reference_name(name, blacklist=None):
140140
return name
141141

142142

143-
def remove_trailing_single_backslash(text):
144-
"""Remove trailing single backslash in *text*
143+
def remove_backslashes(path):
144+
"""Remove backslashes in *path*
145+
146+
For Windows platforms only.
147+
Returns the path unchanged on other platforms.
145148
146149
This is especially useful when formatting path strings on
147-
Windows platforms for which folder paths may end with such
148-
a character"""
149-
if text.endswith('\\') and not text.endswith('\\\\'):
150-
text = text[:-1]
151-
return text
150+
Windows platforms for which folder paths may contain backslashes
151+
and provoke unicode decoding errors in Python 3 (or in Python 2
152+
when future 'unicode_literals' symbol has been imported)."""
153+
if os.name == 'nt':
154+
# Removing trailing single backslash
155+
if path.endswith('\\') and not path.endswith('\\\\'):
156+
path = path[:-1]
157+
# Replacing backslashes by slashes
158+
path = path.replace('\\', '/')
159+
return path
152160

153161

154162
def get_error_match(text):

spyderlib/widgets/externalshell/sitecustomize.py

+2
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,8 @@ def debugfile(filename, args=None, wdir=None):
553553
debugger._wait_for_mainpyfile = 1
554554
debugger.mainpyfile = filename
555555
debugger._user_requested_quit = 0
556+
if os.name == 'nt':
557+
filename = filename.replace('\\', '/')
556558
debugger.run("runfile(%r, args=%r, wdir=%r)" % (filename, args, wdir))
557559

558560
__builtin__.debugfile = debugfile

0 commit comments

Comments
 (0)