blob: 5531a34b622a6eb411acfe01c938a2bd5d3dd485 [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):
89 raise AssertionError(
Gregory P. Smithb5684c42015-01-20 17:19:47 -080090 "Process return code is %d, command line was: %r, "
91 "stderr follows:\n%s" % (rc, cmd_line,
92 err.decode('ascii', 'ignore')))
Antoine Pitrouf51d8d32010-10-08 18:05:42 +000093 return rc, out, err
94
Antoine Pitrou9bc35682010-11-09 21:33:55 +000095def assert_python_ok(*args, **env_vars):
96 """
97 Assert that running the interpreter with `args` and optional environment
Eli Bendersky8f2c2bc2013-08-11 16:48:44 -070098 variables `env_vars` succeeds (rc == 0) and return a (return code, stdout,
99 stderr) tuple.
Victor Stinnere8785ff2013-10-12 14:44:01 +0200100
101 If the __cleanenv keyword is set, env_vars is used a fresh environment.
102
103 Python is started in isolated mode (command line option -I),
104 except if the __isolated keyword is set to False.
Antoine Pitrou9bc35682010-11-09 21:33:55 +0000105 """
106 return _assert_python(True, *args, **env_vars)
Antoine Pitrouf51d8d32010-10-08 18:05:42 +0000107
Antoine Pitrou9bc35682010-11-09 21:33:55 +0000108def assert_python_failure(*args, **env_vars):
109 """
110 Assert that running the interpreter with `args` and optional environment
Eli Bendersky8f2c2bc2013-08-11 16:48:44 -0700111 variables `env_vars` fails (rc != 0) and return a (return code, stdout,
112 stderr) tuple.
Victor Stinnere8785ff2013-10-12 14:44:01 +0200113
114 See assert_python_ok() for more options.
Antoine Pitrou9bc35682010-11-09 21:33:55 +0000115 """
116 return _assert_python(False, *args, **env_vars)
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000117
Antoine Pitrou9845c7e2014-05-11 13:42:17 +0200118def spawn_python(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw):
Eli Bendersky8f2c2bc2013-08-11 16:48:44 -0700119 """Run a Python subprocess with the given arguments.
120
121 kw is extra keyword args to pass to subprocess.Popen. Returns a Popen
122 object.
123 """
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000124 cmd_line = [sys.executable, '-E']
125 cmd_line.extend(args)
Antoine Pitrou6b4b8d02014-05-11 16:59:16 +0200126 # Under Fedora (?), GNU readline can output junk on stderr when initialized,
127 # depending on the TERM setting. Setting TERM=vt100 is supposed to disable
128 # that. References:
129 # - http://reinout.vanrees.org/weblog/2009/08/14/readline-invisible-character-hack.html
130 # - http://stackoverflow.com/questions/15760712/python-readline-module-prints-escape-character-during-import
131 # - http://lists.gnu.org/archive/html/bug-readline/2007-08/msg00004.html
Antoine Pitrou5e6b5f22014-05-11 19:13:43 +0200132 env = kw.setdefault('env', dict(os.environ))
133 env['TERM'] = 'vt100'
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000134 return subprocess.Popen(cmd_line, stdin=subprocess.PIPE,
Antoine Pitrou9845c7e2014-05-11 13:42:17 +0200135 stdout=stdout, stderr=stderr,
Victor Stinner024e37a2011-03-31 01:31:06 +0200136 **kw)
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000137
138def kill_python(p):
Eli Bendersky8f2c2bc2013-08-11 16:48:44 -0700139 """Run the given Popen process until completion and return stdout."""
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000140 p.stdin.close()
141 data = p.stdout.read()
142 p.stdout.close()
143 # try to cleanup the child so we don't appear to leak when running
Antoine Pitrou4e7dc5f2009-12-08 19:27:24 +0000144 # with regrtest -R.
145 p.wait()
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000146 subprocess._cleanup()
147 return data
148
Nick Coghlan720c7e22013-12-15 20:33:02 +1000149def make_script(script_dir, script_basename, source, omit_suffix=False):
150 script_filename = script_basename
151 if not omit_suffix:
152 script_filename += os.extsep + 'py'
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000153 script_name = os.path.join(script_dir, script_filename)
Florent Xicluna8de42e22010-02-27 16:12:22 +0000154 # The script should be encoded to UTF-8, the default string encoding
155 script_file = open(script_name, 'w', encoding='utf-8')
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000156 script_file.write(source)
157 script_file.close()
Brett Cannonc8287ef2012-04-27 13:52:03 -0400158 importlib.invalidate_caches()
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000159 return script_name
160
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000161def make_zip_script(zip_dir, zip_basename, script_name, name_in_zip=None):
162 zip_filename = zip_basename+os.extsep+'zip'
163 zip_name = os.path.join(zip_dir, zip_filename)
164 zip_file = zipfile.ZipFile(zip_name, 'w')
165 if name_in_zip is None:
Barry Warsaw28a691b2010-04-17 00:19:56 +0000166 parts = script_name.split(os.sep)
167 if len(parts) >= 2 and parts[-2] == '__pycache__':
168 legacy_pyc = make_legacy_pyc(source_from_cache(script_name))
169 name_in_zip = os.path.basename(legacy_pyc)
170 script_name = legacy_pyc
171 else:
172 name_in_zip = os.path.basename(script_name)
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000173 zip_file.write(script_name, name_in_zip)
174 zip_file.close()
Florent Xicluna02ea12b22010-07-28 16:39:41 +0000175 #if test.support.verbose:
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000176 # zip_file = zipfile.ZipFile(zip_name, 'r')
177 # print 'Contents of %r:' % zip_name
178 # zip_file.printdir()
179 # zip_file.close()
180 return zip_name, os.path.join(zip_name, name_in_zip)
181
Nick Coghland26c18a2010-08-17 13:06:11 +0000182def make_pkg(pkg_dir, init_source=''):
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000183 os.mkdir(pkg_dir)
Nick Coghland26c18a2010-08-17 13:06:11 +0000184 make_script(pkg_dir, '__init__', init_source)
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000185
186def make_zip_pkg(zip_dir, zip_basename, pkg_name, script_basename,
187 source, depth=1, compiled=False):
188 unlink = []
189 init_name = make_script(zip_dir, '__init__', '')
190 unlink.append(init_name)
191 init_basename = os.path.basename(init_name)
192 script_name = make_script(zip_dir, script_basename, source)
193 unlink.append(script_name)
194 if compiled:
Terry Jan Reedy5d828952014-06-20 17:49:10 -0400195 init_name = py_compile.compile(init_name, doraise=True)
196 script_name = py_compile.compile(script_name, doraise=True)
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000197 unlink.extend((init_name, script_name))
198 pkg_names = [os.sep.join([pkg_name]*i) for i in range(1, depth+1)]
199 script_name_in_zip = os.path.join(pkg_names[-1], os.path.basename(script_name))
200 zip_filename = zip_basename+os.extsep+'zip'
201 zip_name = os.path.join(zip_dir, zip_filename)
202 zip_file = zipfile.ZipFile(zip_name, 'w')
203 for name in pkg_names:
204 init_name_in_zip = os.path.join(name, init_basename)
205 zip_file.write(init_name, init_name_in_zip)
206 zip_file.write(script_name, script_name_in_zip)
207 zip_file.close()
208 for name in unlink:
209 os.unlink(name)
Florent Xicluna02ea12b22010-07-28 16:39:41 +0000210 #if test.support.verbose:
Nick Coghlan260bd3e2009-11-16 06:49:25 +0000211 # zip_file = zipfile.ZipFile(zip_name, 'r')
212 # print 'Contents of %r:' % zip_name
213 # zip_file.printdir()
214 # zip_file.close()
215 return zip_name, os.path.join(zip_name, script_name_in_zip)