summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--numpy/distutils/exec_command.py51
-rw-r--r--numpy/distutils/tests/test_exec_command.py81
2 files changed, 118 insertions, 14 deletions
diff --git a/numpy/distutils/exec_command.py b/numpy/distutils/exec_command.py
index f05b56429..a469fc5e0 100644
--- a/numpy/distutils/exec_command.py
+++ b/numpy/distutils/exec_command.py
@@ -141,6 +141,19 @@ def _update_environment( **env ):
for name,value in env.items():
os.environ[name] = value or ''
+def _supports_fileno(stream):
+ """
+ Returns True if 'stream' supports the file descriptor and allows fileno().
+ """
+ if hasattr(stream, 'fileno'):
+ try:
+ r = stream.fileno()
+ return True
+ except IOError:
+ return False
+ else:
+ return False
+
def exec_command( command,
execute_in='', use_shell=None, use_tee = None,
_with_python = 1,
@@ -195,7 +208,8 @@ def exec_command( command,
# _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 sys.stdout.fileno() == -1:
+ if (_with_python and _supports_fileno(sys.stdout) and
+ sys.stdout.fileno() == -1):
st = _exec_command_python(command,
exec_command_dir = exec_dir,
**env)
@@ -349,12 +363,16 @@ def _exec_command( command, use_shell=None, use_tee = None, **env ):
argv = [os.environ['COMSPEC'],'/C'] + argv
using_command = 1
- so_fileno = sys.stdout.fileno()
- se_fileno = sys.stderr.fileno()
+ _so_has_fileno = _supports_fileno(sys.stdout)
+ _se_has_fileno = _supports_fileno(sys.stderr)
so_flush = sys.stdout.flush
se_flush = sys.stderr.flush
- so_dup = os.dup(so_fileno)
- se_dup = os.dup(se_fileno)
+ 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')
@@ -371,13 +389,16 @@ def _exec_command( command, use_shell=None, use_tee = None, **env ):
so_flush()
se_flush()
- os.dup2(fout.fileno(),so_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)
- else:
- os.dup2(fout.fileno(),se_fileno)
+ 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)
+ else:
+ os.dup2(fout.fileno(),se_fileno)
try:
status = spawn_command(os.P_WAIT,argv0,argv,os.environ)
except OSError:
@@ -387,8 +408,10 @@ def _exec_command( command, use_shell=None, use_tee = None, **env ):
so_flush()
se_flush()
- os.dup2(so_dup,so_fileno)
- os.dup2(se_dup,se_fileno)
+ if _so_has_fileno:
+ os.dup2(so_dup,so_fileno)
+ if _se_has_fileno:
+ os.dup2(se_dup,se_fileno)
fout.close()
fout = open_latin1(outfile,'r')
diff --git a/numpy/distutils/tests/test_exec_command.py b/numpy/distutils/tests/test_exec_command.py
new file mode 100644
index 000000000..d23a4485a
--- /dev/null
+++ b/numpy/distutils/tests/test_exec_command.py
@@ -0,0 +1,81 @@
+import os
+import sys
+import StringIO
+from tempfile import TemporaryFile
+
+from numpy.distutils import exec_command
+
+
+class redirect_stdout(object):
+ """Context manager to redirect stdout for exec_command test."""
+ def __init__(self, stdout=None):
+ self._stdout = stdout or sys.stdout
+
+ def __enter__(self):
+ self.old_stdout = sys.stdout
+ sys.stdout = self._stdout
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self._stdout.flush()
+ sys.stdout = self.old_stdout
+
+class redirect_stderr(object):
+ """Context manager to redirect stderr for exec_command test."""
+ def __init__(self, stderr=None):
+ self._stderr = stderr or sys.stderr
+
+ def __enter__(self):
+ self.old_stderr = sys.stderr
+ sys.stderr = self._stderr
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self._stderr.flush()
+ sys.stderr = self.old_stderr
+
+class emulate_nonposix(object):
+ """Context manager to emulate os.name != 'posix' """
+ def __init__(self, osname='non-posix'):
+ self._new_name = osname
+
+ def __enter__(self):
+ self._old_name = os.name
+ os.name = self._new_name
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ os.name = self._old_name
+
+
+def test_exec_command_stdout():
+ # Regression test for gh-2999 and gh-2915.
+ # There are several packages (nose, scipy.weave.inline, Sage inline
+ # Fortran) that replace stdout, in which case it doesn't have a fileno
+ # method. This is tested here, with a do-nothing command that fails if the
+ # presence of fileno() is assumed in exec_command.
+
+ # The code has a special case for posix systems, so if we are on posix test
+ # both that the special case works and that the generic code works.
+
+ # Test posix version:
+ with redirect_stdout(StringIO.StringIO()):
+ with redirect_stderr(TemporaryFile()):
+ exec_command.exec_command("cd '.'")
+
+ if os.name == 'posix':
+ # Test general (non-posix) version:
+ with emulate_nonposix():
+ with redirect_stdout(StringIO.StringIO()):
+ with redirect_stderr(TemporaryFile()):
+ exec_command.exec_command("cd '.'")
+
+def test_exec_command_stderr():
+ # Test posix version:
+ with redirect_stdout(TemporaryFile()):
+ with redirect_stderr(StringIO.StringIO()):
+ exec_command.exec_command("cd '.'")
+
+ if os.name == 'posix':
+ # Test general (non-posix) version:
+ with emulate_nonposix():
+ with redirect_stdout(TemporaryFile()):
+ with redirect_stderr(StringIO.StringIO()):
+ exec_command.exec_command("cd '.'")