blob: b31fc40013e0959805a4614e93a5a3ce96cfa464 [file] [log] [blame]
Nick Coghlan260bd3e2009-11-16 06:49:25 +00001# Common utility functions used by various script execution tests
2# e.g. test_cmd_line, test_cmd_line_script and test_runpy
3
Antoine Pitrou25f85d42015-04-13 19:41:47 +02004import collections
Brett Cannonc8287ef2012-04-27 13:52:03 -04005import importlib
Nick Coghlan260bd3e2009-11-16 06:49:25 +00006import sys
7import os
8import os.path
9import tempfile
10import subprocess
11import py_compile
12import contextlib
13import shutil
14import zipfile
15
Brett Cannon9529fbf2013-06-15 17:11:25 -040016from importlib.util import source_from_cache
Nick Coghlan55175962013-07-28 22:11:50 +100017from test.support import make_legacy_pyc, strip_python_stderr, temp_dir
Barry Warsaw28a691b2010-04-17 00:19:56 +000018
Gregory P. Smithb9a3dd92015-02-04 00:59:40 -080019
20# Cached result of the expensive test performed in the function below.
21__cached_interp_requires_environment = None
22
23def _interpreter_requires_environment():
24 """
25 Returns True if our sys.executable interpreter requires environment
26 variables in order to be able to run at all.
27
28 This is designed to be used with @unittest.skipIf() to annotate tests
29 that need to use an assert_python*() function to launch an isolated
30 mode (-I) or no environment mode (-E) sub-interpreter process.
31
32 A normal build & test does not run into this situation but it can happen
33 when trying to run the standard library test suite from an interpreter that
34 doesn't have an obvious home with Python's current home finding logic.
35
36 Setting PYTHONHOME is one way to get most of the testsuite to run in that
37 situation. PYTHONPATH or PYTHONUSERSITE are other common envirnonment
38 variables that might impact whether or not the interpreter can start.
39 """
40 global __cached_interp_requires_environment
41 if __cached_interp_requires_environment is None:
42 # Try running an interpreter with -E to see if it works or not.
43 try:
44 subprocess.check_call([sys.executable, '-E',
45 '-c', 'import sys; sys.exit(0)'])
46 except subprocess.CalledProcessError:
47 __cached_interp_requires_environment = True
48 else:
49 __cached_interp_requires_environment = False
50
51 return __cached_interp_requires_environment
52
53
Antoine Pitrou25f85d42015-04-13 19:41:47 +020054_PythonRunResult = collections.namedtuple("_PythonRunResult",
55 ("rc", "out", "err"))
56
57
Nick Coghlan260bd3e2009-11-16 06:49:25 +000058# Executing the interpreter in a subprocess
Antoine Pitrou25f85d42015-04-13 19:41:47 +020059def run_python_until_end(*args, **env_vars):
Gregory P. Smithc3493aa2015-02-04 17:10:19 -080060 env_required = _interpreter_requires_environment()
Victor Stinnere8785ff2013-10-12 14:44:01 +020061 if '__isolated' in env_vars:
62 isolated = env_vars.pop('__isolated')
63 else:
Gregory P. Smithc3493aa2015-02-04 17:10:19 -080064 isolated = not env_vars and not env_required
Victor Stinner383a8202013-06-25 21:24:36 +020065 cmd_line = [sys.executable, '-X', 'faulthandler']
Victor Stinnere8785ff2013-10-12 14:44:01 +020066 if isolated:
67 # isolated mode: ignore Python environment variables, ignore user
68 # site-packages, and don't add the current directory to sys.path
69 cmd_line.append('-I')
Gregory P. Smithc3493aa2015-02-04 17:10:19 -080070 elif not env_vars and not env_required:
Victor Stinnere8785ff2013-10-12 14:44:01 +020071 # ignore Python environment variables
Antoine Pitrou9bc35682010-11-09 21:33:55 +000072 cmd_line.append('-E')
Antoine Pitrouadffced2010-11-09 22:04:44 +000073 # Need to preserve the original environment, for in-place testing of
74 # shared library builds.
75 env = os.environ.copy()
Georg Brandl2daf6ae2012-02-20 19:54:16 +010076 # But a special flag that can be set to override -- in this case, the
77 # caller is responsible to pass the full environment.
78 if env_vars.pop('__cleanenv', None):
79 env = {}
Antoine Pitrouadffced2010-11-09 22:04:44 +000080 env.update(env_vars)
Georg Brandl2daf6ae2012-02-20 19:54:16 +010081 cmd_line.extend(args)
Antoine Pitrouf51d8d32010-10-08 18:05:42 +000082 p = subprocess.Popen(cmd_line, stdin=subprocess.PIPE,
Antoine Pitrou9bc35682010-11-09 21:33:55 +000083 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
84 env=env)
Antoine Pitrouf51d8d32010-10-08 18:05:42 +000085 try:
86 out, err = p.communicate()
87 finally:
88 subprocess._cleanup()
Brian Curtinc4ac8872010-11-01 14:00:33 +000089 p.stdout.close()
90 p.stderr.close()
Antoine Pitrouf51d8d32010-10-08 18:05:42 +000091 rc = p.returncode
Eli Bendersky8f2c2bc2013-08-11 16:48:44 -070092 err = strip_python_stderr(err)
Antoine Pitrou25f85d42015-04-13 19:41:47 +020093 return _PythonRunResult(rc, out, err), cmd_line
94
95def _assert_python(expected_success, *args, **env_vars):
96 res, cmd_line = run_python_until_end(*args, **env_vars)
97 if (res.rc and expected_success) or (not res.rc and not expected_success):
Antoine Pitrouf51d8d32010-10-08 18:05:42 +000098 raise AssertionError(
Gregory P. Smithb5684c42015-01-20 17:19:47 -080099 "Process return code is %d, command line was: %r, "
Antoine Pitrou25f85d42015-04-13 19:41:47 +0200100 "stderr follows:\n%s" % (res.rc, cmd_line,
101 res.err.decode('ascii', 'ignore')))
102 return res
Antoine Pitrouf51d8d32010-10-08 18:05:42 +0000103
Antoine Pitrou9bc35682010-11-09 21:33:55 +0000104def assert_python_ok(*args, **env_vars):
105 """
106 Assert that running the interpreter with `args` and optional environment
Eli Bendersky8f2c2bc2013-08-11 16:48:44 -0700107 variables `env_vars` succeeds (rc == 0) and return a (return code, stdout,
108 stderr) tuple.
Victor Stinnere8785ff2013-10-12 14:44:01 +0200109
110 If the __cleanenv keyword is set, env_vars is used a fresh environment.
111
112 Python is started in isolated mode (command line option -I),
113 except if the __isolated keyword is set to False.
Antoine Pitrou9bc35682010-11-09 21:33:55 +0000114 """
115 return _assert_python(True, *args, **env_vars)
Antoine Pitrouf51d8d32010-10-08 18:05:42 +0000116
Antoine Pitrou9bc35682010-11-09 21:33:55 +0000117def assert_python_failure(*args, **env_vars):
118 """
119 Assert that running the interpreter with `args` and optional environment
Eli Bendersky8f2c2bc2013-08-11 16:48:44 -0700120 variables `env_vars` fails (rc != 0) and return a (return code, stdout,
121 stderr) tuple.
Victor Stinnere8785ff2013-10-12 14:44:01 +0200122
123 See assert_python_ok() for more options.
Antoine Pitrou9bc35682010-11-09 21:33:55 +0000124 """
125 return _assert_python(False, *args, **env_vars)
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000126
Antoine Pitrou9845c7e2014-05-11 13:42:17 +0200127def spawn_python(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw):
Eli Bendersky8f2c2bc2013-08-11 16:48:44 -0700128 """Run a Python subprocess with the given arguments.
129
130 kw is extra keyword args to pass to subprocess.Popen. Returns a Popen
131 object.
132 """
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000133 cmd_line = [sys.executable, '-E']
134 cmd_line.extend(args)
Antoine Pitrou6b4b8d02014-05-11 16:59:16 +0200135 # Under Fedora (?), GNU readline can output junk on stderr when initialized,
136 # depending on the TERM setting. Setting TERM=vt100 is supposed to disable
137 # that. References:
138 # - http://reinout.vanrees.org/weblog/2009/08/14/readline-invisible-character-hack.html
139 # - http://stackoverflow.com/questions/15760712/python-readline-module-prints-escape-character-during-import
140 # - http://lists.gnu.org/archive/html/bug-readline/2007-08/msg00004.html
Antoine Pitrou5e6b5f22014-05-11 19:13:43 +0200141 env = kw.setdefault('env', dict(os.environ))
142 env['TERM'] = 'vt100'
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000143 return subprocess.Popen(cmd_line, stdin=subprocess.PIPE,
Antoine Pitrou9845c7e2014-05-11 13:42:17 +0200144 stdout=stdout, stderr=stderr,
Victor Stinner024e37a2011-03-31 01:31:06 +0200145 **kw)
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000146
147def kill_python(p):
Eli Bendersky8f2c2bc2013-08-11 16:48:44 -0700148 """Run the given Popen process until completion and return stdout."""
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000149 p.stdin.close()
150 data = p.stdout.read()
151 p.stdout.close()
152 # try to cleanup the child so we don't appear to leak when running
Antoine Pitrou4e7dc5f2009-12-08 19:27:24 +0000153 # with regrtest -R.
154 p.wait()
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000155 subprocess._cleanup()
156 return data
157
Nick Coghlan720c7e22013-12-15 20:33:02 +1000158def make_script(script_dir, script_basename, source, omit_suffix=False):
159 script_filename = script_basename
160 if not omit_suffix:
161 script_filename += os.extsep + 'py'
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000162 script_name = os.path.join(script_dir, script_filename)
Florent Xicluna8de42e22010-02-27 16:12:22 +0000163 # The script should be encoded to UTF-8, the default string encoding
164 script_file = open(script_name, 'w', encoding='utf-8')
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000165 script_file.write(source)
166 script_file.close()
Brett Cannonc8287ef2012-04-27 13:52:03 -0400167 importlib.invalidate_caches()
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000168 return script_name
169
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000170def make_zip_script(zip_dir, zip_basename, script_name, name_in_zip=None):
171 zip_filename = zip_basename+os.extsep+'zip'
172 zip_name = os.path.join(zip_dir, zip_filename)
173 zip_file = zipfile.ZipFile(zip_name, 'w')
174 if name_in_zip is None:
Barry Warsaw28a691b2010-04-17 00:19:56 +0000175 parts = script_name.split(os.sep)
176 if len(parts) >= 2 and parts[-2] == '__pycache__':
177 legacy_pyc = make_legacy_pyc(source_from_cache(script_name))
178 name_in_zip = os.path.basename(legacy_pyc)
179 script_name = legacy_pyc
180 else:
181 name_in_zip = os.path.basename(script_name)
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000182 zip_file.write(script_name, name_in_zip)
183 zip_file.close()
Florent Xicluna02ea12b22010-07-28 16:39:41 +0000184 #if test.support.verbose:
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000185 # zip_file = zipfile.ZipFile(zip_name, 'r')
186 # print 'Contents of %r:' % zip_name
187 # zip_file.printdir()
188 # zip_file.close()
189 return zip_name, os.path.join(zip_name, name_in_zip)
190
Nick Coghland26c18a2010-08-17 13:06:11 +0000191def make_pkg(pkg_dir, init_source=''):
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000192 os.mkdir(pkg_dir)
Nick Coghland26c18a2010-08-17 13:06:11 +0000193 make_script(pkg_dir, '__init__', init_source)
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000194
195def make_zip_pkg(zip_dir, zip_basename, pkg_name, script_basename,
196 source, depth=1, compiled=False):
197 unlink = []
198 init_name = make_script(zip_dir, '__init__', '')
199 unlink.append(init_name)
200 init_basename = os.path.basename(init_name)
201 script_name = make_script(zip_dir, script_basename, source)
202 unlink.append(script_name)
203 if compiled:
Terry Jan Reedy5d828952014-06-20 17:49:10 -0400204 init_name = py_compile.compile(init_name, doraise=True)
205 script_name = py_compile.compile(script_name, doraise=True)
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000206 unlink.extend((init_name, script_name))
207 pkg_names = [os.sep.join([pkg_name]*i) for i in range(1, depth+1)]
208 script_name_in_zip = os.path.join(pkg_names[-1], os.path.basename(script_name))
209 zip_filename = zip_basename+os.extsep+'zip'
210 zip_name = os.path.join(zip_dir, zip_filename)
211 zip_file = zipfile.ZipFile(zip_name, 'w')
212 for name in pkg_names:
213 init_name_in_zip = os.path.join(name, init_basename)
214 zip_file.write(init_name, init_name_in_zip)
215 zip_file.write(script_name, script_name_in_zip)
216 zip_file.close()
217 for name in unlink:
218 os.unlink(name)
Florent Xicluna02ea12b22010-07-28 16:39:41 +0000219 #if test.support.verbose:
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000220 # zip_file = zipfile.ZipFile(zip_name, 'r')
221 # print 'Contents of %r:' % zip_name
222 # zip_file.printdir()
223 # zip_file.close()
224 return zip_name, os.path.join(zip_name, script_name_in_zip)