Show More
@@ -15,6 +15,7 b' of subprocess utilities, and it contains tools that are common to all of them.' | |||||
15 | # Imports |
|
15 | # Imports | |
16 | #----------------------------------------------------------------------------- |
|
16 | #----------------------------------------------------------------------------- | |
17 | import subprocess |
|
17 | import subprocess | |
|
18 | import shlex | |||
18 | import sys |
|
19 | import sys | |
19 |
|
20 | |||
20 | from IPython.utils import py3compat |
|
21 | from IPython.utils import py3compat | |
@@ -143,3 +144,27 b' def getoutputerror(cmd):' | |||||
143 | return '', '' |
|
144 | return '', '' | |
144 | out, err = out_err |
|
145 | out, err = out_err | |
145 | return py3compat.bytes_to_str(out), py3compat.bytes_to_str(err) |
|
146 | return py3compat.bytes_to_str(out), py3compat.bytes_to_str(err) | |
|
147 | ||||
|
148 | ||||
|
149 | def arg_split(s, posix=False): | |||
|
150 | """Split a command line's arguments in a shell-like manner. | |||
|
151 | ||||
|
152 | This is a modified version of the standard library's shlex.split() | |||
|
153 | function, but with a default of posix=False for splitting, so that quotes | |||
|
154 | in inputs are respected.""" | |||
|
155 | ||||
|
156 | # Unfortunately, python's shlex module is buggy with unicode input: | |||
|
157 | # http://bugs.python.org/issue1170 | |||
|
158 | # At least encoding the input when it's unicode seems to help, but there | |||
|
159 | # may be more problems lurking. Apparently this is fixed in python3. | |||
|
160 | is_unicode = False | |||
|
161 | if (not py3compat.PY3) and isinstance(s, unicode): | |||
|
162 | is_unicode = True | |||
|
163 | s = s.encode('utf-8') | |||
|
164 | lex = shlex.shlex(s, posix=posix) | |||
|
165 | lex.whitespace_split = True | |||
|
166 | tokens = list(lex) | |||
|
167 | if is_unicode: | |||
|
168 | # Convert the tokens back to unicode. | |||
|
169 | tokens = [x.decode('utf-8') for x in tokens] | |||
|
170 | return tokens |
@@ -23,7 +23,7 b' from IPython.external import pexpect' | |||||
23 |
|
23 | |||
24 | # Our own |
|
24 | # Our own | |
25 | from .autoattr import auto_attr |
|
25 | from .autoattr import auto_attr | |
26 | from ._process_common import getoutput |
|
26 | from ._process_common import getoutput, arg_split | |
27 | from IPython.utils import text |
|
27 | from IPython.utils import text | |
28 | from IPython.utils import py3compat |
|
28 | from IPython.utils import py3compat | |
29 |
|
29 | |||
@@ -192,3 +192,6 b' class ProcessHandler(object):' | |||||
192 | # programs think they are talking to a tty and produce highly formatted output |
|
192 | # programs think they are talking to a tty and produce highly formatted output | |
193 | # (ls is a good example) that makes them hard. |
|
193 | # (ls is a good example) that makes them hard. | |
194 | system = ProcessHandler().system |
|
194 | system = ProcessHandler().system | |
|
195 | ||||
|
196 | ||||
|
197 |
@@ -18,11 +18,15 b' from __future__ import print_function' | |||||
18 | # stdlib |
|
18 | # stdlib | |
19 | import os |
|
19 | import os | |
20 | import sys |
|
20 | import sys | |
|
21 | import ctypes | |||
21 |
|
22 | |||
|
23 | from ctypes import c_int, POINTER | |||
|
24 | from ctypes.wintypes import LPCWSTR, HLOCAL | |||
22 | from subprocess import STDOUT |
|
25 | from subprocess import STDOUT | |
23 |
|
26 | |||
24 | # our own imports |
|
27 | # our own imports | |
25 | from ._process_common import read_no_interrupt, process_handler |
|
28 | from ._process_common import read_no_interrupt, process_handler | |
|
29 | from . import py3compat | |||
26 | from . import text |
|
30 | from . import text | |
27 |
|
31 | |||
28 | #----------------------------------------------------------------------------- |
|
32 | #----------------------------------------------------------------------------- | |
@@ -146,3 +150,29 b' def getoutput(cmd):' | |||||
146 | if out is None: |
|
150 | if out is None: | |
147 | out = '' |
|
151 | out = '' | |
148 | return out |
|
152 | return out | |
|
153 | ||||
|
154 | try: | |||
|
155 | CommandLineToArgvW = ctypes.windll.shell32.CommandLineToArgvW | |||
|
156 | CommandLineToArgvW.arg_types = [LPCWSTR, POINTER(c_int)] | |||
|
157 | CommandLineToArgvW.res_types = [POINTER(LPCWSTR)] | |||
|
158 | LocalFree = ctypes.windll.kernel32.LocalFree | |||
|
159 | LocalFree.res_type = HLOCAL | |||
|
160 | LocalFree.arg_types = [HLOCAL] | |||
|
161 | ||||
|
162 | def arg_split(commandline, posix=False): | |||
|
163 | """Split a command line's arguments in a shell-like manner. | |||
|
164 | ||||
|
165 | This is a special version for windows that use a ctypes call to CommandLineToArgvW | |||
|
166 | to do the argv splitting. The posix paramter is ignored. | |||
|
167 | """ | |||
|
168 | #CommandLineToArgvW returns path to executable if called with empty string. | |||
|
169 | if commandline.strip() == "": | |||
|
170 | return [] | |||
|
171 | argvn = c_int() | |||
|
172 | result_pointer = CommandLineToArgvW(py3compat.cast_unicode(commandline.lstrip()), ctypes.byref(argvn)) | |||
|
173 | result_array_type = LPCWSTR * argvn.value | |||
|
174 | result = [arg for arg in result_array_type.from_address(result_pointer)] | |||
|
175 | retval = LocalFree(result_pointer) | |||
|
176 | return result | |||
|
177 | except AttributeError: | |||
|
178 | from ._process_common import arg_split |
@@ -22,9 +22,10 b' import shlex' | |||||
22 |
|
22 | |||
23 | # Our own |
|
23 | # Our own | |
24 | if sys.platform == 'win32': |
|
24 | if sys.platform == 'win32': | |
25 | from ._process_win32 import _find_cmd, system, getoutput, AvoidUNCPath |
|
25 | from ._process_win32 import _find_cmd, system, getoutput, AvoidUNCPath, arg_split | |
26 | else: |
|
26 | else: | |
27 | from ._process_posix import _find_cmd, system, getoutput |
|
27 | from ._process_posix import _find_cmd, system, getoutput, arg_split | |
|
28 | ||||
28 |
|
29 | |||
29 | from ._process_common import getoutputerror |
|
30 | from ._process_common import getoutputerror | |
30 | from IPython.utils import py3compat |
|
31 | from IPython.utils import py3compat | |
@@ -103,31 +104,6 b' def pycmd2argv(cmd):' | |||||
103 | else: |
|
104 | else: | |
104 | return [sys.executable, cmd] |
|
105 | return [sys.executable, cmd] | |
105 |
|
106 | |||
106 |
|
||||
107 | def arg_split(s, posix=False): |
|
|||
108 | """Split a command line's arguments in a shell-like manner. |
|
|||
109 |
|
||||
110 | This is a modified version of the standard library's shlex.split() |
|
|||
111 | function, but with a default of posix=False for splitting, so that quotes |
|
|||
112 | in inputs are respected.""" |
|
|||
113 |
|
||||
114 | # Unfortunately, python's shlex module is buggy with unicode input: |
|
|||
115 | # http://bugs.python.org/issue1170 |
|
|||
116 | # At least encoding the input when it's unicode seems to help, but there |
|
|||
117 | # may be more problems lurking. Apparently this is fixed in python3. |
|
|||
118 | is_unicode = False |
|
|||
119 | if (not py3compat.PY3) and isinstance(s, unicode): |
|
|||
120 | is_unicode = True |
|
|||
121 | s = s.encode('utf-8') |
|
|||
122 | lex = shlex.shlex(s, posix=posix) |
|
|||
123 | lex.whitespace_split = True |
|
|||
124 | tokens = list(lex) |
|
|||
125 | if is_unicode: |
|
|||
126 | # Convert the tokens back to unicode. |
|
|||
127 | tokens = [x.decode('utf-8') for x in tokens] |
|
|||
128 | return tokens |
|
|||
129 |
|
||||
130 |
|
||||
131 | def abbrev_cwd(): |
|
107 | def abbrev_cwd(): | |
132 | """ Return abbreviated version of cwd, e.g. d:mydir """ |
|
108 | """ Return abbreviated version of cwd, e.g. d:mydir """ | |
133 | cwd = os.getcwdu().replace('\\','/') |
|
109 | cwd = os.getcwdu().replace('\\','/') |
@@ -62,16 +62,32 b' def test_find_cmd_fail():' | |||||
62 | nt.assert_raises(FindCmdError,find_cmd,'asdfasdf') |
|
62 | nt.assert_raises(FindCmdError,find_cmd,'asdfasdf') | |
63 |
|
63 | |||
64 |
|
64 | |||
|
65 | @dec.skip_win32 | |||
65 | def test_arg_split(): |
|
66 | def test_arg_split(): | |
66 | """Ensure that argument lines are correctly split like in a shell.""" |
|
67 | """Ensure that argument lines are correctly split like in a shell.""" | |
67 | tests = [['hi', ['hi']], |
|
68 | tests = [['hi', ['hi']], | |
68 | [u'hi', [u'hi']], |
|
69 | [u'hi', [u'hi']], | |
69 | ['hello there', ['hello', 'there']], |
|
70 | ['hello there', ['hello', 'there']], | |
70 | [u'h\N{LATIN SMALL LETTER A WITH CARON}llo', [u'h\N{LATIN SMALL LETTER A WITH CARON}llo']], |
|
71 | # \u01ce == \N{LATIN SMALL LETTER A WITH CARON} | |
|
72 | # Do not use \N because the tests crash with syntax error in | |||
|
73 | # some cases, for example windows python2.6. | |||
|
74 | [u'h\u01cello', [u'h\u01cello']], | |||
71 | ['something "with quotes"', ['something', '"with quotes"']], |
|
75 | ['something "with quotes"', ['something', '"with quotes"']], | |
72 | ] |
|
76 | ] | |
73 | for argstr, argv in tests: |
|
77 | for argstr, argv in tests: | |
74 | nt.assert_equal(arg_split(argstr), argv) |
|
78 | nt.assert_equal(arg_split(argstr), argv) | |
|
79 | ||||
|
80 | @dec.skip_if_not_win32 | |||
|
81 | def test_arg_split_win32(): | |||
|
82 | """Ensure that argument lines are correctly split like in a shell.""" | |||
|
83 | tests = [['hi', ['hi']], | |||
|
84 | [u'hi', [u'hi']], | |||
|
85 | ['hello there', ['hello', 'there']], | |||
|
86 | [u'h\u01cello', [u'h\u01cello']], | |||
|
87 | ['something "with quotes"', ['something', 'with quotes']], | |||
|
88 | ] | |||
|
89 | for argstr, argv in tests: | |||
|
90 | nt.assert_equal(arg_split(argstr), argv) | |||
75 |
|
91 | |||
76 |
|
92 | |||
77 | class SubProcessTestCase(TestCase, tt.TempFileMixin): |
|
93 | class SubProcessTestCase(TestCase, tt.TempFileMixin): | |
@@ -100,6 +116,10 b' class SubProcessTestCase(TestCase, tt.TempFileMixin):' | |||||
100 | def test_getoutput_quoted(self): |
|
116 | def test_getoutput_quoted(self): | |
101 | out = getoutput('python -c "print (1)"') |
|
117 | out = getoutput('python -c "print (1)"') | |
102 | self.assertEquals(out.strip(), '1') |
|
118 | self.assertEquals(out.strip(), '1') | |
|
119 | ||||
|
120 | #Invalid quoting on windows | |||
|
121 | @dec.skip_win32 | |||
|
122 | def test_getoutput_quoted2(self): | |||
103 | out = getoutput("python -c 'print (1)'") |
|
123 | out = getoutput("python -c 'print (1)'") | |
104 | self.assertEquals(out.strip(), '1') |
|
124 | self.assertEquals(out.strip(), '1') | |
105 | out = getoutput("python -c 'print (\"1\")'") |
|
125 | out = getoutput("python -c 'print (\"1\")'") |
General Comments 0
You need to be logged in to leave comments.
Login now