blob: 8743dba79e5f9d7593650515b3b91fbb1135e91a [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
Brett Cannonc8287ef2012-04-27 13:52:03 -04004import importlib
Nick Coghlan260bd3e2009-11-16 06:49:25 +00005import sys
6import os
7import os.path
8import tempfile
9import subprocess
10import py_compile
11import contextlib
12import shutil
13import zipfile
14
Brett Cannon9529fbf2013-06-15 17:11:25 -040015from importlib.util import source_from_cache
Nick Coghlan55175962013-07-28 22:11:50 +100016from test.support import make_legacy_pyc, strip_python_stderr, temp_dir
Barry Warsaw28a691b2010-04-17 00:19:56 +000017
Gregory P. Smithb9a3dd92015-02-04 00:59:40 -080018
19# Cached result of the expensive test performed in the function below.
20__cached_interp_requires_environment = None
21
Gregory P. Smith8f2fae12015-02-04 01:04:31 -080022def interpreter_requires_environment():
Gregory P. Smithb9a3dd92015-02-04 00:59:40 -080023 """
24 Returns True if our sys.executable interpreter requires environment
25 variables in order to be able to run at all.
26
27 This is designed to be used with @unittest.skipIf() to annotate tests
28 that need to use an assert_python*() function to launch an isolated
29 mode (-I) or no environment mode (-E) sub-interpreter process.
30
31 A normal build & test does not run into this situation but it can happen
32 when trying to run the standard library test suite from an interpreter that
33 doesn't have an obvious home with Python's current home finding logic.
34
35 Setting PYTHONHOME is one way to get most of the testsuite to run in that
36 situation. PYTHONPATH or PYTHONUSERSITE are other common envirnonment
37 variables that might impact whether or not the interpreter can start.
38 """
39 global __cached_interp_requires_environment
40 if __cached_interp_requires_environment is None:
41 # Try running an interpreter with -E to see if it works or not.
42 try:
43 subprocess.check_call([sys.executable, '-E',
44 '-c', 'import sys; sys.exit(0)'])
45 except subprocess.CalledProcessError:
46 __cached_interp_requires_environment = True
47 else:
48 __cached_interp_requires_environment = False
49
50 return __cached_interp_requires_environment
51
52
Nick Coghlan260bd3e2009-11-16 06:49:25 +000053# Executing the interpreter in a subprocess
Antoine Pitrou9bc35682010-11-09 21:33:55 +000054def _assert_python(expected_success, *args, **env_vars):
Gregory P. Smith7c60eb82015-02-04 17:16:30 -080055 env_required = interpreter_requires_environment()
Victor Stinnere8785ff2013-10-12 14:44:01 +020056 if '__isolated' in env_vars:
57 isolated = env_vars.pop('__isolated')
58 else:
Gregory P. Smithc3493aa2015-02-04 17:10:19 -080059 isolated = not env_vars and not env_required
Victor Stinner383a8202013-06-25 21:24:36 +020060 cmd_line = [sys.executable, '-X', 'faulthandler']
Victor Stinnere8785ff2013-10-12 14:44:01 +020061 if isolated:
62 # isolated mode: ignore Python environment variables, ignore user
63 # site-packages, and don't add the current directory to sys.path
64 cmd_line.append('-I')
Gregory P. Smithc3493aa2015-02-04 17:10:19 -080065 elif not env_vars and not env_required:
Victor Stinnere8785ff2013-10-12 14:44:01 +020066 # ignore Python environment variables
Antoine Pitrou9bc35682010-11-09 21:33:55 +000067 cmd_line.append('-E')
Antoine Pitrouadffced2010-11-09 22:04:44 +000068 # Need to preserve the original environment, for in-place testing of
69 # shared library builds.
70 env = os.environ.copy()
Georg Brandl2daf6ae2012-02-20 19:54:16 +010071 # But a special flag that can be set to override -- in this case, the
72 # caller is responsible to pass the full environment.
73 if env_vars.pop('__cleanenv', None):
74 env = {}
Antoine Pitrouadffced2010-11-09 22:04:44 +000075 env.update(env_vars)
Georg Brandl2daf6ae2012-02-20 19:54:16 +010076 cmd_line.extend(args)
Antoine Pitrouf51d8d32010-10-08 18:05:42 +000077 p = subprocess.Popen(cmd_line, stdin=subprocess.PIPE,
Antoine Pitrou9bc35682010-11-09 21:33:55 +000078 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
79 env=env)
Antoine Pitrouf51d8d32010-10-08 18:05:42 +000080 try:
81 out, err = p.communicate()
82 finally:
83 subprocess._cleanup()
Brian Curtinc4ac8872010-11-01 14:00:33 +000084 p.stdout.close()
85 p.stderr.close()
Antoine Pitrouf51d8d32010-10-08 18:05:42 +000086 rc = p.returncode
Eli Bendersky8f2c2bc2013-08-11 16:48:44 -070087 err = strip_python_stderr(err)
Antoine Pitrouf51d8d32010-10-08 18:05:42 +000088 if (rc and expected_success) or (not rc and not expected_success):
Victor Stinner1335ca52015-03-20 13:38:08 +010089 # Limit to 80 lines to ASCII characters
90 maxlen = 80 * 100
91 if len(out) > maxlen:
92 out = b'(... truncated stdout ...)' + out[-maxlen:]
93 if len(err) > maxlen:
94 err = b'(... truncated stderr ...)' + err[-maxlen:]
95 out = out.decode('ascii', 'replace').rstrip()
96 err = err.decode('ascii', 'replace').rstrip()
97 raise AssertionError("Process return code is %d\n"
98 "command line: %r\n"
99 "\n"
100 "stdout:\n"
101 "---\n"
102 "%s\n"
103 "---\n"
104 "\n"
105 "stderr:\n"
106 "---\n"
107 "%s\n"
108 "---"
109 % (rc, cmd_line,
110 out,
111 err))
Antoine Pitrouf51d8d32010-10-08 18:05:42 +0000112 return rc, out, err
113
Antoine Pitrou9bc35682010-11-09 21:33:55 +0000114def assert_python_ok(*args, **env_vars):
115 """
116 Assert that running the interpreter with `args` and optional environment
Eli Bendersky8f2c2bc2013-08-11 16:48:44 -0700117 variables `env_vars` succeeds (rc == 0) and return a (return code, stdout,
118 stderr) tuple.
Victor Stinnere8785ff2013-10-12 14:44:01 +0200119
120 If the __cleanenv keyword is set, env_vars is used a fresh environment.
121
122 Python is started in isolated mode (command line option -I),
123 except if the __isolated keyword is set to False.
Antoine Pitrou9bc35682010-11-09 21:33:55 +0000124 """
125 return _assert_python(True, *args, **env_vars)
Antoine Pitrouf51d8d32010-10-08 18:05:42 +0000126
Antoine Pitrou9bc35682010-11-09 21:33:55 +0000127def assert_python_failure(*args, **env_vars):
128 """
129 Assert that running the interpreter with `args` and optional environment
Eli Bendersky8f2c2bc2013-08-11 16:48:44 -0700130 variables `env_vars` fails (rc != 0) and return a (return code, stdout,
131 stderr) tuple.
Victor Stinnere8785ff2013-10-12 14:44:01 +0200132
133 See assert_python_ok() for more options.
Antoine Pitrou9bc35682010-11-09 21:33:55 +0000134 """
135 return _assert_python(False, *args, **env_vars)
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000136
Antoine Pitrou9845c7e2014-05-11 13:42:17 +0200137def spawn_python(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw):
Eli Bendersky8f2c2bc2013-08-11 16:48:44 -0700138 """Run a Python subprocess with the given arguments.
139
140 kw is extra keyword args to pass to subprocess.Popen. Returns a Popen
141 object.
142 """
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000143 cmd_line = [sys.executable, '-E']
144 cmd_line.extend(args)
Antoine Pitrou6b4b8d02014-05-11 16:59:16 +0200145 # Under Fedora (?), GNU readline can output junk on stderr when initialized,
146 # depending on the TERM setting. Setting TERM=vt100 is supposed to disable
147 # that. References:
148 # - http://reinout.vanrees.org/weblog/2009/08/14/readline-invisible-character-hack.html
149 # - http://stackoverflow.com/questions/15760712/python-readline-module-prints-escape-character-during-import
150 # - http://lists.gnu.org/archive/html/bug-readline/2007-08/msg00004.html
Antoine Pitrou5e6b5f22014-05-11 19:13:43 +0200151 env = kw.setdefault('env', dict(os.environ))
152 env['TERM'] = 'vt100'
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000153 return subprocess.Popen(cmd_line, stdin=subprocess.PIPE,
Antoine Pitrou9845c7e2014-05-11 13:42:17 +0200154 stdout=stdout, stderr=stderr,
Victor Stinner024e37a2011-03-31 01:31:06 +0200155 **kw)
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000156
157def kill_python(p):
Eli Bendersky8f2c2bc2013-08-11 16:48:44 -0700158 """Run the given Popen process until completion and return stdout."""
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000159 p.stdin.close()
160 data = p.stdout.read()
161 p.stdout.close()
162 # try to cleanup the child so we don't appear to leak when running
Antoine Pitrou4e7dc5f2009-12-08 19:27:24 +0000163 # with regrtest -R.
164 p.wait()
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000165 subprocess._cleanup()
166 return data
167
Nick Coghlan720c7e22013-12-15 20:33:02 +1000168def make_script(script_dir, script_basename, source, omit_suffix=False):
169 script_filename = script_basename
170 if not omit_suffix:
171 script_filename += os.extsep + 'py'
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000172 script_name = os.path.join(script_dir, script_filename)
Florent Xicluna8de42e22010-02-27 16:12:22 +0000173 # The script should be encoded to UTF-8, the default string encoding
174 script_file = open(script_name, 'w', encoding='utf-8')
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000175 script_file.write(source)
176 script_file.close()
Brett Cannonc8287ef2012-04-27 13:52:03 -0400177 importlib.invalidate_caches()
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000178 return script_name
179
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000180def make_zip_script(zip_dir, zip_basename, script_name, name_in_zip=None):
181 zip_filename = zip_basename+os.extsep+'zip'
182 zip_name = os.path.join(zip_dir, zip_filename)
183 zip_file = zipfile.ZipFile(zip_name, 'w')
184 if name_in_zip is None:
Barry Warsaw28a691b2010-04-17 00:19:56 +0000185 parts = script_name.split(os.sep)
186 if len(parts) >= 2 and parts[-2] == '__pycache__':
187 legacy_pyc = make_legacy_pyc(source_from_cache(script_name))
188 name_in_zip = os.path.basename(legacy_pyc)
189 script_name = legacy_pyc
190 else:
191 name_in_zip = os.path.basename(script_name)
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000192 zip_file.write(script_name, name_in_zip)
193 zip_file.close()
Florent Xicluna02ea12b22010-07-28 16:39:41 +0000194 #if test.support.verbose:
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000195 # zip_file = zipfile.ZipFile(zip_name, 'r')
196 # print 'Contents of %r:' % zip_name
197 # zip_file.printdir()
198 # zip_file.close()
199 return zip_name, os.path.join(zip_name, name_in_zip)
200
Nick Coghland26c18a2010-08-17 13:06:11 +0000201def make_pkg(pkg_dir, init_source=''):
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000202 os.mkdir(pkg_dir)
Nick Coghland26c18a2010-08-17 13:06:11 +0000203 make_script(pkg_dir, '__init__', init_source)
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000204
205def make_zip_pkg(zip_dir, zip_basename, pkg_name, script_basename,
206 source, depth=1, compiled=False):
207 unlink = []
208 init_name = make_script(zip_dir, '__init__', '')
209 unlink.append(init_name)
210 init_basename = os.path.basename(init_name)
211 script_name = make_script(zip_dir, script_basename, source)
212 unlink.append(script_name)
213 if compiled:
Terry Jan Reedy5d828952014-06-20 17:49:10 -0400214 init_name = py_compile.compile(init_name, doraise=True)
215 script_name = py_compile.compile(script_name, doraise=True)
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000216 unlink.extend((init_name, script_name))
217 pkg_names = [os.sep.join([pkg_name]*i) for i in range(1, depth+1)]
218 script_name_in_zip = os.path.join(pkg_names[-1], os.path.basename(script_name))
219 zip_filename = zip_basename+os.extsep+'zip'
220 zip_name = os.path.join(zip_dir, zip_filename)
221 zip_file = zipfile.ZipFile(zip_name, 'w')
222 for name in pkg_names:
223 init_name_in_zip = os.path.join(name, init_basename)
224 zip_file.write(init_name, init_name_in_zip)
225 zip_file.write(script_name, script_name_in_zip)
226 zip_file.close()
227 for name in unlink:
228 os.unlink(name)
Florent Xicluna02ea12b22010-07-28 16:39:41 +0000229 #if test.support.verbose:
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000230 # zip_file = zipfile.ZipFile(zip_name, 'r')
231 # print 'Contents of %r:' % zip_name
232 # zip_file.printdir()
233 # zip_file.close()
234 return zip_name, os.path.join(zip_name, script_name_in_zip)