1import os
2import sys
3from tempfile import TemporaryFile
4
5from numpy.distutils import exec_command
6from numpy.distutils.exec_command import get_pythonexe
7from numpy.testing import tempdir, assert_, assert_warns
8
9# In python 3 stdout, stderr are text (unicode compliant) devices, so to
10# emulate them import StringIO from the io module.
11from io import StringIO
12
13class redirect_stdout:
14    """Context manager to redirect stdout for exec_command test."""
15    def __init__(self, stdout=None):
16        self._stdout = stdout or sys.stdout
17
18    def __enter__(self):
19        self.old_stdout = sys.stdout
20        sys.stdout = self._stdout
21
22    def __exit__(self, exc_type, exc_value, traceback):
23        self._stdout.flush()
24        sys.stdout = self.old_stdout
25        # note: closing sys.stdout won't close it.
26        self._stdout.close()
27
28class redirect_stderr:
29    """Context manager to redirect stderr for exec_command test."""
30    def __init__(self, stderr=None):
31        self._stderr = stderr or sys.stderr
32
33    def __enter__(self):
34        self.old_stderr = sys.stderr
35        sys.stderr = self._stderr
36
37    def __exit__(self, exc_type, exc_value, traceback):
38        self._stderr.flush()
39        sys.stderr = self.old_stderr
40        # note: closing sys.stderr won't close it.
41        self._stderr.close()
42
43class emulate_nonposix:
44    """Context manager to emulate os.name != 'posix' """
45    def __init__(self, osname='non-posix'):
46        self._new_name = osname
47
48    def __enter__(self):
49        self._old_name = os.name
50        os.name = self._new_name
51
52    def __exit__(self, exc_type, exc_value, traceback):
53        os.name = self._old_name
54
55
56def test_exec_command_stdout():
57    # Regression test for gh-2999 and gh-2915.
58    # There are several packages (nose, scipy.weave.inline, Sage inline
59    # Fortran) that replace stdout, in which case it doesn't have a fileno
60    # method.  This is tested here, with a do-nothing command that fails if the
61    # presence of fileno() is assumed in exec_command.
62
63    # The code has a special case for posix systems, so if we are on posix test
64    # both that the special case works and that the generic code works.
65
66    # Test posix version:
67    with redirect_stdout(StringIO()):
68        with redirect_stderr(TemporaryFile()):
69            with assert_warns(DeprecationWarning):
70                exec_command.exec_command("cd '.'")
71
72    if os.name == 'posix':
73        # Test general (non-posix) version:
74        with emulate_nonposix():
75            with redirect_stdout(StringIO()):
76                with redirect_stderr(TemporaryFile()):
77                    with assert_warns(DeprecationWarning):
78                        exec_command.exec_command("cd '.'")
79
80def test_exec_command_stderr():
81    # Test posix version:
82    with redirect_stdout(TemporaryFile(mode='w+')):
83        with redirect_stderr(StringIO()):
84            with assert_warns(DeprecationWarning):
85                exec_command.exec_command("cd '.'")
86
87    if os.name == 'posix':
88        # Test general (non-posix) version:
89        with emulate_nonposix():
90            with redirect_stdout(TemporaryFile()):
91                with redirect_stderr(StringIO()):
92                    with assert_warns(DeprecationWarning):
93                        exec_command.exec_command("cd '.'")
94
95
96class TestExecCommand:
97    def setup(self):
98        self.pyexe = get_pythonexe()
99
100    def check_nt(self, **kws):
101        s, o = exec_command.exec_command('cmd /C echo path=%path%')
102        assert_(s == 0)
103        assert_(o != '')
104
105        s, o = exec_command.exec_command(
106         '"%s" -c "import sys;sys.stderr.write(sys.platform)"' % self.pyexe)
107        assert_(s == 0)
108        assert_(o == 'win32')
109
110    def check_posix(self, **kws):
111        s, o = exec_command.exec_command("echo Hello", **kws)
112        assert_(s == 0)
113        assert_(o == 'Hello')
114
115        s, o = exec_command.exec_command('echo $AAA', **kws)
116        assert_(s == 0)
117        assert_(o == '')
118
119        s, o = exec_command.exec_command('echo "$AAA"', AAA='Tere', **kws)
120        assert_(s == 0)
121        assert_(o == 'Tere')
122
123        s, o = exec_command.exec_command('echo "$AAA"', **kws)
124        assert_(s == 0)
125        assert_(o == '')
126
127        if 'BBB' not in os.environ:
128            os.environ['BBB'] = 'Hi'
129            s, o = exec_command.exec_command('echo "$BBB"', **kws)
130            assert_(s == 0)
131            assert_(o == 'Hi')
132
133            s, o = exec_command.exec_command('echo "$BBB"', BBB='Hey', **kws)
134            assert_(s == 0)
135            assert_(o == 'Hey')
136
137            s, o = exec_command.exec_command('echo "$BBB"', **kws)
138            assert_(s == 0)
139            assert_(o == 'Hi')
140
141            del os.environ['BBB']
142
143            s, o = exec_command.exec_command('echo "$BBB"', **kws)
144            assert_(s == 0)
145            assert_(o == '')
146
147
148        s, o = exec_command.exec_command('this_is_not_a_command', **kws)
149        assert_(s != 0)
150        assert_(o != '')
151
152        s, o = exec_command.exec_command('echo path=$PATH', **kws)
153        assert_(s == 0)
154        assert_(o != '')
155
156        s, o = exec_command.exec_command(
157             '"%s" -c "import sys,os;sys.stderr.write(os.name)"' %
158             self.pyexe, **kws)
159        assert_(s == 0)
160        assert_(o == 'posix')
161
162    def check_basic(self, *kws):
163        s, o = exec_command.exec_command(
164                     '"%s" -c "raise \'Ignore me.\'"' % self.pyexe, **kws)
165        assert_(s != 0)
166        assert_(o != '')
167
168        s, o = exec_command.exec_command(
169             '"%s" -c "import sys;sys.stderr.write(\'0\');'
170             'sys.stderr.write(\'1\');sys.stderr.write(\'2\')"' %
171             self.pyexe, **kws)
172        assert_(s == 0)
173        assert_(o == '012')
174
175        s, o = exec_command.exec_command(
176                 '"%s" -c "import sys;sys.exit(15)"' % self.pyexe, **kws)
177        assert_(s == 15)
178        assert_(o == '')
179
180        s, o = exec_command.exec_command(
181                     '"%s" -c "print(\'Heipa\'")' % self.pyexe, **kws)
182        assert_(s == 0)
183        assert_(o == 'Heipa')
184
185    def check_execute_in(self, **kws):
186        with tempdir() as tmpdir:
187            fn = "file"
188            tmpfile = os.path.join(tmpdir, fn)
189            with open(tmpfile, 'w') as f:
190                f.write('Hello')
191
192            s, o = exec_command.exec_command(
193                 '"%s" -c "f = open(\'%s\', \'r\'); f.close()"' %
194                 (self.pyexe, fn), **kws)
195            assert_(s != 0)
196            assert_(o != '')
197            s, o = exec_command.exec_command(
198                     '"%s" -c "f = open(\'%s\', \'r\'); print(f.read()); '
199                     'f.close()"' % (self.pyexe, fn), execute_in=tmpdir, **kws)
200            assert_(s == 0)
201            assert_(o == 'Hello')
202
203    def test_basic(self):
204        with redirect_stdout(StringIO()):
205            with redirect_stderr(StringIO()):
206                with assert_warns(DeprecationWarning):
207                    if os.name == "posix":
208                        self.check_posix(use_tee=0)
209                        self.check_posix(use_tee=1)
210                    elif os.name == "nt":
211                        self.check_nt(use_tee=0)
212                        self.check_nt(use_tee=1)
213                    self.check_execute_in(use_tee=0)
214                    self.check_execute_in(use_tee=1)
215