From 10073b6fe3b9d54b298c09d99f98387cf0a79b83 2016-06-02 14:23:03 From: Thomas Kluyver Date: 2016-06-02 14:23:03 Subject: [PATCH] Merge pull request #9516 from anntzer/windows-spaces On Windows, quote paths instead of escaping them. --- diff --git a/IPython/core/completer.py b/IPython/core/completer.py index f11d60f..16c2942 100644 --- a/IPython/core/completer.py +++ b/IPython/core/completer.py @@ -117,9 +117,14 @@ def has_open_quotes(s): def protect_filename(s): """Escape a string to protect certain characters.""" + if set(s) & set(PROTECTABLES): + if sys.platform == "win32": + return '"' + s + '"' + else: + return "".join(("\\" + c if c in PROTECTABLES else c) for c in s) + else: + return s - return "".join([(ch in PROTECTABLES and '\\' + ch or ch) - for ch in s]) def expand_user(path): """Expand '~'-style usernames in strings. diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 7c20eed..1ade688 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -67,7 +67,7 @@ from IPython.utils.decorators import undoc from IPython.utils.io import ask_yes_no from IPython.utils.ipstruct import Struct from IPython.paths import get_ipython_dir -from IPython.utils.path import get_home_dir, get_py_filename, unquote_filename, ensure_dir_exists +from IPython.utils.path import get_home_dir, get_py_filename, ensure_dir_exists from IPython.utils.process import system, getoutput from IPython.utils.py3compat import (builtin_mod, unicode_type, string_types, with_metaclass, iteritems) @@ -3128,10 +3128,9 @@ class InteractiveShell(SingletonConfigurable): code = self.extract_input_lines(target, raw=raw) # Grab history if code: return code - utarget = unquote_filename(target) try: - if utarget.startswith(('http://', 'https://')): - return openpy.read_py_url(utarget, skip_encoding_cookie=skip_encoding_cookie) + if target.startswith(('http://', 'https://')): + return openpy.read_py_url(target, skip_encoding_cookie=skip_encoding_cookie) except UnicodeDecodeError: if not py_only : # Deferred import @@ -3141,7 +3140,7 @@ class InteractiveShell(SingletonConfigurable): from urllib import urlopen response = urlopen(target) return response.read().decode('latin1') - raise ValueError(("'%s' seem to be unreadable.") % utarget) + raise ValueError(("'%s' seem to be unreadable.") % target) potential_target = [target] try : diff --git a/IPython/core/magics/basic.py b/IPython/core/magics/basic.py index 818a90c..b9da12d 100644 --- a/IPython/core/magics/basic.py +++ b/IPython/core/magics/basic.py @@ -13,7 +13,6 @@ from IPython.core.magic import Magics, magics_class, line_magic, magic_escapes from IPython.utils.text import format_screen, dedent, indent from IPython.testing.skipdoctest import skip_doctest from IPython.utils.ipstruct import Struct -from IPython.utils.path import unquote_filename from IPython.utils.py3compat import unicode_type from warnings import warn from logging import error @@ -583,7 +582,6 @@ Defaulting color scheme to 'NoColor'""" args = magic_arguments.parse_argstring(self.notebook, s) from nbformat import write, v4 - args.filename = unquote_filename(args.filename) if args.export: cells = [] hist = list(self.shell.history_manager.get_range()) diff --git a/IPython/core/magics/code.py b/IPython/core/magics/code.py index 4581fff..a865d68 100644 --- a/IPython/core/magics/code.py +++ b/IPython/core/magics/code.py @@ -32,7 +32,7 @@ from IPython.testing.skipdoctest import skip_doctest from IPython.utils import py3compat from IPython.utils.py3compat import string_types from IPython.utils.contexts import preserve_keys -from IPython.utils.path import get_py_filename, unquote_filename +from IPython.utils.path import get_py_filename from warnings import warn from logging import error from IPython.utils.text import get_text_list @@ -189,7 +189,7 @@ class CodeMagics(Magics): append = 'a' in opts mode = 'a' if append else 'w' ext = u'.ipy' if raw else u'.py' - fname, codefrom = unquote_filename(args[0]), " ".join(args[1:]) + fname, codefrom = args[0], " ".join(args[1:]) if not fname.endswith((u'.py',u'.ipy')): fname += ext file_exists = os.path.isfile(fname) @@ -369,7 +369,6 @@ class CodeMagics(Magics): def make_filename(arg): "Make a filename from the given args" - arg = unquote_filename(arg) try: filename = get_py_filename(arg) except IOError: diff --git a/IPython/core/magics/execution.py b/IPython/core/magics/execution.py index 4edadc9..6ab3d24 100644 --- a/IPython/core/magics/execution.py +++ b/IPython/core/magics/execution.py @@ -42,7 +42,7 @@ from IPython.utils.contexts import preserve_keys from IPython.utils.capture import capture_output from IPython.utils.ipstruct import Struct from IPython.utils.module_paths import find_mod -from IPython.utils.path import get_py_filename, unquote_filename, shellglob +from IPython.utils.path import get_py_filename, shellglob from IPython.utils.timing import clock, clock2 from warnings import warn from logging import error @@ -338,12 +338,10 @@ python-profiler package from non-free.""") dump_file = opts.D[0] text_file = opts.T[0] if dump_file: - dump_file = unquote_filename(dump_file) prof.dump_stats(dump_file) print('\n*** Profile stats marshalled to file',\ repr(dump_file)+'.',sys_exit) if text_file: - text_file = unquote_filename(text_file) pfile = open(text_file,'w') pfile.write(output) pfile.close() diff --git a/IPython/core/magics/osm.py b/IPython/core/magics/osm.py index 5c3db3e..352cf2d 100644 --- a/IPython/core/magics/osm.py +++ b/IPython/core/magics/osm.py @@ -34,7 +34,6 @@ from IPython.core.magic import ( ) from IPython.testing.skipdoctest import skip_doctest from IPython.utils.openpy import source_to_unicode -from IPython.utils.path import unquote_filename from IPython.utils.process import abbrev_cwd from IPython.utils import py3compat from IPython.utils.py3compat import unicode_type @@ -324,10 +323,7 @@ class OSMagics(Magics): else: - #turn all non-space-escaping backslashes to slashes, - # for c:\windows\directory\names\ - parameter_s = re.sub(r'\\(?! )','/', parameter_s) - opts,ps = self.parse_options(parameter_s,'qb',mode='string') + opts, ps = self.parse_options(parameter_s, 'qb', mode='string') # jump to previous if ps == '-': try: @@ -348,8 +344,6 @@ class OSMagics(Magics): raise UsageError("Bookmark '%s' not found. " "Use '%%bookmark -l' to see your bookmarks." % ps) - # strip extra quotes on Windows, because os.chdir doesn't like them - ps = unquote_filename(ps) # at this point ps should point to the target dir if ps: try: @@ -443,7 +437,7 @@ class OSMagics(Magics): """ dir_s = self.shell.dir_stack - tgt = os.path.expanduser(unquote_filename(parameter_s)) + tgt = os.path.expanduser(parameter_s) cwd = py3compat.getcwd().replace(self.shell.home_dir,'~') if tgt: self.cd(parameter_s) @@ -781,8 +775,8 @@ class OSMagics(Magics): The file will be overwritten unless the -a (--append) flag is specified. """ args = magic_arguments.parse_argstring(self.writefile, line) - filename = os.path.expanduser(unquote_filename(args.filename)) - + filename = os.path.expanduser(args.filename) + if os.path.exists(filename): if args.append: print("Appending to %s" % filename) diff --git a/IPython/core/tests/test_completer.py b/IPython/core/tests/test_completer.py index 4f3ff80..da7b538 100644 --- a/IPython/core/tests/test_completer.py +++ b/IPython/core/tests/test_completer.py @@ -36,32 +36,38 @@ def greedy_completion(): ip.Completer.greedy = greedy_original def test_protect_filename(): - pairs = [ ('abc','abc'), - (' abc',r'\ abc'), - ('a bc',r'a\ bc'), - ('a bc',r'a\ \ bc'), - (' bc',r'\ \ bc'), - ] - # On posix, we also protect parens and other special characters - if sys.platform != 'win32': - pairs.extend( [('a(bc',r'a\(bc'), - ('a)bc',r'a\)bc'), - ('a( )bc',r'a\(\ \)bc'), - ('a[1]bc', r'a\[1\]bc'), - ('a{1}bc', r'a\{1\}bc'), - ('a#bc', r'a\#bc'), - ('a?bc', r'a\?bc'), - ('a=bc', r'a\=bc'), - ('a\\bc', r'a\\bc'), - ('a|bc', r'a\|bc'), - ('a;bc', r'a\;bc'), - ('a:bc', r'a\:bc'), - ("a'bc", r"a\'bc"), - ('a*bc', r'a\*bc'), - ('a"bc', r'a\"bc'), - ('a^bc', r'a\^bc'), - ('a&bc', r'a\&bc'), - ] ) + if sys.platform == 'win32': + pairs = [ ('abc','abc'), + (' abc',"' abc'"), + ('a bc',"'a bc'"), + ('a bc',"'a bc'"), + (' bc',"' bc'"), + ] + else: + pairs = [ ('abc','abc'), + (' abc',r'\ abc'), + ('a bc',r'a\ bc'), + ('a bc',r'a\ \ bc'), + (' bc',r'\ \ bc'), + # On posix, we also protect parens and other special characters + ('a(bc',r'a\(bc'), + ('a)bc',r'a\)bc'), + ('a( )bc',r'a\(\ \)bc'), + ('a[1]bc', r'a\[1\]bc'), + ('a{1}bc', r'a\{1\}bc'), + ('a#bc', r'a\#bc'), + ('a?bc', r'a\?bc'), + ('a=bc', r'a\=bc'), + ('a\\bc', r'a\\bc'), + ('a|bc', r'a\|bc'), + ('a;bc', r'a\;bc'), + ('a:bc', r'a\:bc'), + ("a'bc", r"a\'bc"), + ('a*bc', r'a\*bc'), + ('a"bc', r'a\"bc'), + ('a^bc', r'a\^bc'), + ('a&bc', r'a\&bc'), + ] # run the actual tests for s1, s2 in pairs: s1p = completer.protect_filename(s1) diff --git a/IPython/utils/path.py b/IPython/utils/path.py index 94b3d95..1d2c9e8 100644 --- a/IPython/utils/path.py +++ b/IPython/utils/path.py @@ -73,15 +73,20 @@ def get_long_path_name(path): def unquote_filename(name, win32=(sys.platform=='win32')): """ On Windows, remove leading and trailing quotes from filenames. + + This function has been deprecated and should not be used any more: + unquoting is now taken care of by :func:`IPython.utils.process.arg_split`. """ + warn("'unquote_filename' is deprecated", DeprecationWarning) if win32: if name.startswith(("'", '"')) and name.endswith(("'", '"')): name = name[1:-1] return name + def compress_user(path): """Reverse of :func:`os.path.expanduser` - """ + """ path = py3compat.unicode_to_str(path, sys.getfilesystemencoding()) home = os.path.expanduser('~') if path.startswith(home): diff --git a/IPython/utils/tests/test_path.py b/IPython/utils/tests/test_path.py index 1775d72..2378e51 100644 --- a/IPython/utils/tests/test_path.py +++ b/IPython/utils/tests/test_path.py @@ -302,19 +302,6 @@ def test_not_writable_ipdir(): ipdir = paths.get_ipython_dir() env.pop('IPYTHON_DIR', None) -def test_unquote_filename(): - for win32 in (True, False): - nt.assert_equal(path.unquote_filename('foo.py', win32=win32), 'foo.py') - nt.assert_equal(path.unquote_filename('foo bar.py', win32=win32), 'foo bar.py') - nt.assert_equal(path.unquote_filename('"foo.py"', win32=True), 'foo.py') - nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=True), 'foo bar.py') - nt.assert_equal(path.unquote_filename("'foo.py'", win32=True), 'foo.py') - nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=True), 'foo bar.py') - nt.assert_equal(path.unquote_filename('"foo.py"', win32=False), '"foo.py"') - nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=False), '"foo bar.py"') - nt.assert_equal(path.unquote_filename("'foo.py'", win32=False), "'foo.py'") - nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=False), "'foo bar.py'") - @with_environment def test_get_py_filename(): os.chdir(TMP_TEST_DIR)