summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--numpy/distutils/exec_command.py308
-rwxr-xr-xtools/travis-test.sh3
2 files changed, 56 insertions, 255 deletions
diff --git a/numpy/distutils/exec_command.py b/numpy/distutils/exec_command.py
index 4a4bc67f2..9df48cc27 100644
--- a/numpy/distutils/exec_command.py
+++ b/numpy/distutils/exec_command.py
@@ -57,12 +57,12 @@ __all__ = ['exec_command', 'find_executable']
import os
import sys
import shlex
+import subprocess
from numpy.distutils.misc_util import is_sequence, make_temp_file
from numpy.distutils import log
from numpy.distutils.compat import get_exception
-from numpy.compat import open_latin1
def temp_file_name():
fo, name = make_temp_file()
@@ -211,28 +211,10 @@ def exec_command(command, execute_in='', use_shell=None, use_tee=None,
_update_environment( **env )
try:
- # _exec_command is robust but slow, it relies on
- # usable sys.std*.fileno() descriptors. If they
- # are bad (like in win32 Idle, PyCrust environments)
- # then _exec_command_python (even slower)
- # will be used as a last resort.
- #
- # _exec_command_posix uses os.system and is faster
- # but not on all platforms os.system will return
- # a correct status.
- if (_with_python and _supports_fileno(sys.stdout) and
- sys.stdout.fileno() == -1):
- st = _exec_command_python(command,
- exec_command_dir = exec_dir,
- **env)
- elif os.name=='posix':
- st = _exec_command_posix(command,
- use_shell=use_shell,
- use_tee=use_tee,
- **env)
- else:
- st = _exec_command(command, use_shell=use_shell,
- use_tee=use_tee,**env)
+ st = _exec_command(command,
+ use_shell=use_shell,
+ use_tee=use_tee,
+ **env)
finally:
if oldcwd!=execute_in:
os.chdir(oldcwd)
@@ -241,250 +223,66 @@ def exec_command(command, execute_in='', use_shell=None, use_tee=None,
return st
-def _exec_command_posix( command,
- use_shell = None,
- use_tee = None,
- **env ):
- log.debug('_exec_command_posix(...)')
-
- if is_sequence(command):
- command_str = ' '.join(list(command))
- else:
- command_str = command
-
- tmpfile = temp_file_name()
- stsfile = None
- if use_tee:
- stsfile = temp_file_name()
- filter = ''
- if use_tee == 2:
- filter = r'| tr -cd "\n" | tr "\n" "."; echo'
- command_posix = '( %s ; echo $? > %s ) 2>&1 | tee %s %s'\
- % (command_str, stsfile, tmpfile, filter)
- else:
- stsfile = temp_file_name()
- command_posix = '( %s ; echo $? > %s ) > %s 2>&1'\
- % (command_str, stsfile, tmpfile)
- #command_posix = '( %s ) > %s 2>&1' % (command_str,tmpfile)
-
- log.debug('Running os.system(%r)' % (command_posix))
- status = os.system(command_posix)
-
- if use_tee:
- if status:
- # if command_tee fails then fall back to robust exec_command
- log.warn('_exec_command_posix failed (status=%s)' % status)
- return _exec_command(command, use_shell=use_shell, **env)
-
- if stsfile is not None:
- f = open_latin1(stsfile, 'r')
- status_text = f.read()
- status = int(status_text)
- f.close()
- os.remove(stsfile)
-
- f = open_latin1(tmpfile, 'r')
- text = f.read()
- f.close()
- os.remove(tmpfile)
-
- if text[-1:]=='\n':
- text = text[:-1]
-
- return status, text
-
-
-def _exec_command_python(command,
- exec_command_dir='', **env):
- log.debug('_exec_command_python(...)')
-
- python_exe = get_pythonexe()
- cmdfile = temp_file_name()
- stsfile = temp_file_name()
- outfile = temp_file_name()
-
- f = open(cmdfile, 'w')
- f.write('import os\n')
- f.write('import sys\n')
- f.write('sys.path.insert(0,%r)\n' % (exec_command_dir))
- f.write('from exec_command import exec_command\n')
- f.write('del sys.path[0]\n')
- f.write('cmd = %r\n' % command)
- f.write('os.environ = %r\n' % (os.environ))
- f.write('s,o = exec_command(cmd, _with_python=0, **%r)\n' % (env))
- f.write('f=open(%r,"w")\nf.write(str(s))\nf.close()\n' % (stsfile))
- f.write('f=open(%r,"w")\nf.write(o)\nf.close()\n' % (outfile))
- f.close()
-
- cmd = '%s %s' % (python_exe, cmdfile)
- status = os.system(cmd)
- if status:
- raise RuntimeError("%r failed" % (cmd,))
- os.remove(cmdfile)
-
- f = open_latin1(stsfile, 'r')
- status = int(f.read())
- f.close()
- os.remove(stsfile)
-
- f = open_latin1(outfile, 'r')
- text = f.read()
- f.close()
- os.remove(outfile)
-
- return status, text
-
-def quote_arg(arg):
- if arg[0]!='"' and ' ' in arg:
- return '"%s"' % arg
- return arg
-
-def _exec_command( command, use_shell=None, use_tee = None, **env ):
- log.debug('_exec_command(...)')
+def _exec_command(command, use_shell=None, use_tee = None, **env):
+ """
+ Internal workhorse for exec_command().
+ """
if use_shell is None:
use_shell = os.name=='posix'
if use_tee is None:
use_tee = os.name=='posix'
- using_command = 0
- if use_shell:
- # We use shell (unless use_shell==0) so that wildcards can be
- # used.
+
+ executable = None
+
+ if os.name == 'posix' and use_shell:
+ # On POSIX, subprocess always uses /bin/sh, override
sh = os.environ.get('SHELL', '/bin/sh')
if is_sequence(command):
- argv = [sh, '-c', ' '.join(list(command))]
- else:
- argv = [sh, '-c', command]
- else:
- # On NT, DOS we avoid using command.com as it's exit status is
- # not related to the exit status of a command.
- if is_sequence(command):
- argv = command[:]
- else:
- argv = shlex.split(command)
-
- # `spawn*p` family with path (vp, vpe, ...) are not available on windows.
- # Also prefer spawn{v,vp} in favor of spawn{ve,vpe} if no env
- # modification is actually requested as the *e* functions are not thread
- # safe on windows (https://bugs.python.org/issue6476)
- if hasattr(os, 'spawnvpe'):
- spawn_command = os.spawnvpe if env else os.spawnvp
- else:
- spawn_command = os.spawnve if env else os.spawnv
- argv[0] = find_executable(argv[0]) or argv[0]
- if not os.path.isfile(argv[0]):
- log.warn('Executable %s does not exist' % (argv[0]))
- if os.name in ['nt', 'dos']:
- # argv[0] might be internal command
- argv = [os.environ['COMSPEC'], '/C'] + argv
- using_command = 1
-
- _so_has_fileno = _supports_fileno(sys.stdout)
- _se_has_fileno = _supports_fileno(sys.stderr)
- so_flush = sys.stdout.flush
- se_flush = sys.stderr.flush
- if _so_has_fileno:
- so_fileno = sys.stdout.fileno()
- so_dup = os.dup(so_fileno)
- if _se_has_fileno:
- se_fileno = sys.stderr.fileno()
- se_dup = os.dup(se_fileno)
-
- outfile = temp_file_name()
- fout = open(outfile, 'w')
- if using_command:
- errfile = temp_file_name()
- ferr = open(errfile, 'w')
-
- log.debug('Running %s(%s,%r,%r,os.environ)' \
- % (spawn_command.__name__, os.P_WAIT, argv[0], argv))
-
- if env and sys.version_info[0] >= 3 and os.name == 'nt':
- # Pre-encode os.environ, discarding un-encodable entries,
- # to avoid it failing during encoding as part of spawn. Failure
- # is possible if the environment contains entries that are not
- # encoded using the system codepage as windows expects.
- #
- # This is not necessary on unix, where os.environ is encoded
- # using the surrogateescape error handler and decoded using
- # it as part of spawn.
- encoded_environ = {}
- for k, v in os.environ.items():
- try:
- encoded_environ[k.encode(sys.getfilesystemencoding())] = v.encode(
- sys.getfilesystemencoding())
- except UnicodeEncodeError:
- log.debug("ignoring un-encodable env entry %s", k)
- else:
- encoded_environ = os.environ
-
- argv0 = argv[0]
- if not using_command:
- argv[0] = quote_arg(argv0)
-
- so_flush()
- se_flush()
- if _so_has_fileno:
- os.dup2(fout.fileno(), so_fileno)
-
- if _se_has_fileno:
- if using_command:
- #XXX: disabled for now as it does not work from cmd under win32.
- # Tests fail on msys
- os.dup2(ferr.fileno(), se_fileno)
+ command = [sh, '-c', ' '.join(command)]
else:
- os.dup2(fout.fileno(), se_fileno)
+ command = [sh, '-c', command]
+ use_shell = False
+
+ elif os.name == 'nt' and is_sequence(command):
+ # On Windows, join the string for CreateProcess() ourselves as
+ # subprocess does it a bit differently
+ command = ' '.join(_quote_arg(arg) for arg in command)
+
+ # Inherit environment by default
+ env = env or None
try:
- # Use spawnv in favor of spawnve, unless necessary
- if env:
- status = spawn_command(os.P_WAIT, argv0, argv, encoded_environ)
- else:
- status = spawn_command(os.P_WAIT, argv0, argv)
- except Exception:
- errmess = str(get_exception())
- status = 999
- sys.stderr.write('%s: %s'%(errmess, argv[0]))
-
- so_flush()
- se_flush()
- if _so_has_fileno:
- os.dup2(so_dup, so_fileno)
- os.close(so_dup)
- if _se_has_fileno:
- os.dup2(se_dup, se_fileno)
- os.close(se_dup)
-
- fout.close()
- fout = open_latin1(outfile, 'r')
- text = fout.read()
- fout.close()
- os.remove(outfile)
-
- if using_command:
- ferr.close()
- ferr = open_latin1(errfile, 'r')
- errmess = ferr.read()
- ferr.close()
- os.remove(errfile)
- if errmess and not status:
- # Not sure how to handle the case where errmess
- # contains only warning messages and that should
- # not be treated as errors.
- #status = 998
- if text:
- text = text + '\n'
- #text = '%sCOMMAND %r FAILED: %s' %(text,command,errmess)
- text = text + errmess
- print (errmess)
- if text[-1:]=='\n':
+ proc = subprocess.Popen(command, shell=use_shell, env=env,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ universal_newlines=True)
+ except EnvironmentError:
+ # Return 127, as os.spawn*() and /bin/sh do
+ return '', 127
+ text, err = proc.communicate()
+ # Only append stderr if the command failed, as otherwise
+ # the output may become garbled for parsing
+ if proc.returncode:
+ if text:
+ text += "\n"
+ text += err
+ # Another historical oddity
+ if text[-1:] == '\n':
text = text[:-1]
- if status is None:
- status = 0
-
if use_tee:
- print (text)
+ print(text)
+ return proc.returncode, text
- return status, text
+
+def _quote_arg(arg):
+ """
+ Quote the argument for safe use in a shell command line.
+ """
+ # If there is a quote in the string, assume relevants parts of the
+ # string are already quoted (e.g. '-I"C:\\Program Files\\..."')
+ if '"' not in arg and ' ' in arg:
+ return '"%s"' % arg
+ return arg
def test_nt(**kws):
diff --git a/tools/travis-test.sh b/tools/travis-test.sh
index e050b4ccb..2a57c9873 100755
--- a/tools/travis-test.sh
+++ b/tools/travis-test.sh
@@ -175,6 +175,9 @@ elif [ -n "$USE_SDIST" ] && [ $# -eq 0 ]; then
elif [ -n "$USE_CHROOT" ] && [ $# -eq 0 ]; then
DIR=/chroot
setup_chroot $DIR
+ # the chroot'ed environment will not have the current locale,
+ # avoid any warnings which may disturb testing
+ export LANG=C LC_ALL=C
# run again in chroot with this time testing
sudo linux32 chroot $DIR bash -c \
"cd numpy && PYTHON=python PIP=pip IN_CHROOT=1 $0 test"