Repository: ipython/ipython

Issue:
Lots of tests fail due to space in sys.executable
I have just created a virtualenv in my home directory, but unfortunately I have a space in my windows username, so that my home directory sits at
C:\users\Firstname Lastname. This causes a lot of tests to fail.
This is all under Win8.1x64, python2.7.5x64, and ipython's latest master branch.

Here are some fail messages:

<pre>

======================================================================
FAIL: IPython.terminal.tests.test_help.test_profile_create_help
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\Firstname Lastname\temp\ipython\lib\site-packages\nose\case.py", lin
e 197, in runTest
    self.test(*self.arg)
  File "c:\users\firstname lastname\documents\github\ipython\IPython\terminal\tests\t
est_help.py", line 31, in test_profile_create_help
    tt.help_all_output_test("profile create")
  File "c:\users\firstname lastname\documents\github\ipython\IPython\testing\tools.py
", line 441, in help_all_output_test
    nt.assert_equal(rc, 0, err)
AssertionError: 'C:\Users\Firstname' is not recognized as an internal or external c
ommand,
operable program or batch file.

    '1 != 0' = '%s != %s' % (safe_repr(1), safe_repr(0))
    "'C:\\Users\\Firstname' is not recognized as an internal or external command,\r
\noperable program or batch file.\r\n" = self._formatMessage("'C:\\Users\\Firstname
' is not recognized as an internal or external command,\r\noperable program or b
atch file.\r\n", '1 != 0')
>>  raise self.failureException("'C:\\Users\\Firstname' is not recognized as an int
ernal or external command,\r\noperable program or batch file.\r\n")


======================================================================
FAIL: IPython.terminal.tests.test_help.test_locate_help
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\Firstname Lastname\temp\ipython\lib\site-packages\nose\case.py", lin
e 197, in runTest
    self.test(*self.arg)
  File "c:\users\firstname lastname\documents\github\ipython\IPython\terminal\tests\t
est_help.py", line 34, in test_locate_help
    tt.help_all_output_test("locate")
  File "c:\users\firstname lastname\documents\github\ipython\IPython\testing\tools.py
", line 441, in help_all_output_test
    nt.assert_equal(rc, 0, err)
AssertionError: 'C:\Users\Firstname' is not recognized as an internal or external c
ommand,
operable program or batch file.

    '1 != 0' = '%s != %s' % (safe_repr(1), safe_repr(0))
    "'C:\\Users\\Firstname' is not recognized as an internal or external command,\r
\noperable program or batch file.\r\n" = self._formatMessage("'C:\\Users\\Firstname
' is not recognized as an internal or external command,\r\noperable program or b
atch file.\r\n", '1 != 0')
>>  raise self.failureException("'C:\\Users\\Firstname' is not recognized as an int
ernal or external command,\r\noperable program or batch file.\r\n")


======================================================================
FAIL: IPython.terminal.tests.test_help.test_locate_profile_help
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\Firstname Lastname\temp\ipython\lib\site-packages\nose\case.py", lin
e 197, in runTest
    self.test(*self.arg)
  File "c:\users\firstname lastname\documents\github\ipython\IPython\terminal\tests\t
est_help.py", line 37, in test_locate_profile_help
    tt.help_all_output_test("locate profile")
  File "c:\users\firstname lastname\documents\github\ipython\IPython\testing\tools.py
", line 441, in help_all_output_test
    nt.assert_equal(rc, 0, err)
AssertionError: 'C:\Users\Firstname' is not recognized as an internal or external c
ommand,
operable program or batch file.

    '1 != 0' = '%s != %s' % (safe_repr(1), safe_repr(0))
    "'C:\\Users\\Firstname' is not recognized as an internal or external command,\r
\noperable program or batch file.\r\n" = self._formatMessage("'C:\\Users\\Firstname
' is not recognized as an internal or external command,\r\noperable program or b
atch file.\r\n", '1 != 0')
>>  raise self.failureException("'C:\\Users\\Firstname' is not recognized as an int
ernal or external command,\r\noperable program or batch file.\r\n")


----------------------------------------------------------------------

======================================================================
FAIL: IPython.utils.tests.test_path.test_get_long_path_name_win32
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\Firstname Lastname\temp\ipython\lib\site-packages\nose\case.py", lin
e 197, in runTest
    self.test(*self.arg)
  File "c:\users\firstname lastname\documents\github\ipython\IPython\testing\decorato
rs.py", line 225, in skipper_func
    return f(*args, **kwargs)
  File "c:\users\firstname lastname\documents\github\ipython\IPython\utils\tests\test
_path.py", line 463, in test_get_long_path_name_win32
    nt.assert_equal(evaluated_path.lower(), long_path.lower())
AssertionError: u'c:\\users\\firstname lastname\\appdata\\local\\temp\\tmpdnrflj\\thi
s is my long pat [truncated]... != u'c:\\users\\firstname~1\\appdata\\local\\temp\\
tmpdnrflj\\this is my long path nam [truncated]...
- c:\users\firstname lastname\appdata\local\temp\tmpdnrflj\this is my long path name
?                ^^^^^^^
+ c:\users\firstname~1\appdata\local\temp\tmpdnrflj\this is my long path name
?                ^^

    """Fail immediately, with the given message."""
>>  raise self.failureException(u"u'c:\\\\users\\\\firstname lastname\\\\appdata\\\\l
ocal\\\\temp\\\\tmpdnrflj\\\\this is my long pat [truncated]... != u'c:\\\\users
\\\\firstname~1\\\\appdata\\\\local\\\\temp\\\\tmpdnrflj\\\\this is my long path na
m [truncated]...\n- c:\\users\\firstname lastname\\appdata\\local\\temp\\tmpdnrflj\\t
his is my long path name\n?                ^^^^^^^\n+ c:\\users\\firstname~1\\appda
ta\\local\\temp\\tmpdnrflj\\this is my long path name\n?                ^^\n")


----------------------------------------------------------------------
</pre>


It seems to me that the problem is that the problem is this: 
https://github.com/ipython/ipython/blob/master/IPython/testing/tools.py#L439 naively assembles the command by doing a " ".join([sys.executable, args]), instead of escaping spaces correctly as described in the subprocess module (http://hg.python.org/cpython/file/tip/Lib/subprocess.py#l622). 

However the escaping is different for unix/windows, so it shouldn't really be done here, but it should rather be possible to pass the list of sys.executable and arguments to https://github.com/ipython/ipython/blob/master/IPython/utils/_process_common.py#L42

Regards



Discussion:

Resolving patch:
diff --git a/IPython/utils/_process_common.py b/IPython/utils/_process_common.py
--- a/IPython/utils/_process_common.py
+++ b/IPython/utils/_process_common.py
@@ -47,9 +47,10 @@ def process_handler(cmd, callback, stderr=subprocess.PIPE):
 
     Parameters
     ----------
-    cmd : str
-      A string to be executed with the underlying system shell (by calling
-    :func:`Popen` with ``shell=True``.
+    cmd : str or list
+      A command to be executed by the system, using :class:`subprocess.Popen`.
+      If a string is passed, it will be run in the system shell. If a list is
+      passed, it will be used directly as arguments.
 
     callback : callable
       A one-argument function that will be called with the Popen object.
@@ -68,7 +69,7 @@ def process_handler(cmd, callback, stderr=subprocess.PIPE):
     sys.stderr.flush()
     # On win32, close_fds can't be true when using pipes for stdin/out/err
     close_fds = sys.platform != 'win32'
-    p = subprocess.Popen(cmd, shell=True,
+    p = subprocess.Popen(cmd, shell=isinstance(cmd, py3compat.string_types),
                          stdin=subprocess.PIPE,
                          stdout=subprocess.PIPE,
                          stderr=stderr,
@@ -107,7 +108,7 @@ def getoutput(cmd):
 
     Parameters
     ----------
-    cmd : str
+    cmd : str or list
       A command to be executed in the system shell.
 
     Returns
@@ -131,7 +132,7 @@ def getoutputerror(cmd):
 
     Parameters
     ----------
-    cmd : str
+    cmd : str or list
       A command to be executed in the system shell.
 
     Returns
@@ -149,7 +150,7 @@ def get_output_error_code(cmd):
 
     Parameters
     ----------
-    cmd : str
+    cmd : str or list
       A command to be executed in the system shell.
 
     Returns
diff --git a/IPython/utils/_process_win32.py b/IPython/utils/_process_win32.py
--- a/IPython/utils/_process_win32.py
+++ b/IPython/utils/_process_win32.py
@@ -112,7 +112,7 @@ def system(cmd):
 
     Parameters
     ----------
-    cmd : str
+    cmd : str or list
       A command to be executed in the system shell.
 
     Returns
@@ -138,7 +138,7 @@ def getoutput(cmd):
 
     Parameters
     ----------
-    cmd : str
+    cmd : str or list
       A command to be executed in the system shell.
 
     Returns


Test patch:
diff --git a/IPython/testing/tools.py b/IPython/testing/tools.py
--- a/IPython/testing/tools.py
+++ b/IPython/testing/tools.py
@@ -425,7 +425,7 @@ def monkeypatch(obj, name, attr):
 
 def help_output_test(subcommand=''):
     """test that `ipython [subcommand] -h` works"""
-    cmd = ' '.join(get_ipython_cmd() + [subcommand, '-h'])
+    cmd = get_ipython_cmd() + [subcommand, '-h']
     out, err, rc = get_output_error_code(cmd)
     nt.assert_equal(rc, 0, err)
     nt.assert_not_in("Traceback", err)
@@ -436,7 +436,7 @@ def help_output_test(subcommand=''):
 
 def help_all_output_test(subcommand=''):
     """test that `ipython [subcommand] --help-all` works"""
-    cmd = ' '.join(get_ipython_cmd() + [subcommand, '--help-all'])
+    cmd = get_ipython_cmd() + [subcommand, '--help-all']
     out, err, rc = get_output_error_code(cmd)
     nt.assert_equal(rc, 0, err)
     nt.assert_not_in("Traceback", err)