def test_consistent_sys_path_for_direct_execution(self): # This test case ensures that the following all give the same # sys.path configuration: # # ./python -s script_dir/__main__.py # ./python -s script_dir # ./python -I script_dir script = textwrap.dedent("""\ import sys for entry in sys.path: print(entry) """) # Always show full path diffs on errors self.maxDiff = None with support.temp_dir() as work_dir, support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__', script) # Reference output comes from directly executing __main__.py # We omit PYTHONPATH and user site to align with isolated mode p = spawn_python("-Es", script_name, cwd=work_dir) out_by_name = kill_python(p).decode().splitlines() self.assertEqual(out_by_name[0], script_dir) self.assertNotIn(work_dir, out_by_name) # Directory execution should give the same output p = spawn_python("-Es", script_dir, cwd=work_dir) out_by_dir = kill_python(p).decode().splitlines() self.assertEqual(out_by_dir, out_by_name) # As should directory execution in isolated mode p = spawn_python("-I", script_dir, cwd=work_dir) out_by_dir_isolated = kill_python(p).decode().splitlines() self.assertEqual(out_by_dir_isolated, out_by_dir, out_by_name)
def do_in_main_process(): import os import signal import subprocess import time from test.support.script_helper import spawn_python ok = False def step(p, expected): s = p.stdout.readline() if s != expected: raise Exception(f"Unexpected line: got {s}, expected '{expected}'") # ensure that child process gets to run_forever time.sleep(0.5) os.kill(p.pid, signal.CTRL_C_EVENT) with spawn_python(__file__, "--child") as p: try: # ignore ctrl-c in current process signal.signal(signal.SIGINT, signal.SIG_IGN) step(p, b"1\r\n") step(p, b"2\r\n") exit_code = p.wait(timeout=5) ok = exit_code = 255 except Exception as e: sys.stderr.write(repr(e)) p.kill() sys.exit(255 if ok else 1)
def get_output(self, code, filename=None, fd=None): """ Run the specified code in Python (in a new child process) and read the output from the standard error or from a file (if filename is set). Return the output lines as a list. Strip the reference count from the standard error for Python debug build, and replace "Current thread 0x00007f8d8fbd9700" by "Current thread XXX". """ code = dedent(code).strip() pass_fds = [] if fd is not None: pass_fds.append(fd) with support.SuppressCrashReport(): process = script_helper.spawn_python('-c', code, pass_fds=pass_fds) with process: stdout, stderr = process.communicate() exitcode = process.wait() output = support.strip_python_stderr(stdout) output = output.decode('ascii', 'backslashreplace') if filename: self.assertEqual(output, '') with open(filename, "rb") as fp: output = fp.read() output = output.decode('ascii', 'backslashreplace') elif fd is not None: self.assertEqual(output, '') os.lseek(fd, os.SEEK_SET, 0) with open(fd, "rb", closefd=False) as fp: output = fp.read() output = output.decode('ascii', 'backslashreplace') return output.splitlines(), exitcode
def readpipe_interrupted(self, interrupt): """Perform a read during which a signal will arrive. Return True if the read is interrupted by the signal and raises an exception. Return False if it returns normally. """ # use a subprocess to have only one thread, to have a timeout on the # blocking read and to not touch signal handling in this process code = """if 1: import errno import os import signal import sys interrupt = %r r, w = os.pipe() def handler(signum, frame): 1 / 0 signal.signal(signal.SIGALRM, handler) if interrupt is not None: signal.siginterrupt(signal.SIGALRM, interrupt) print("ready") sys.stdout.flush() # run the test twice try: for loop in range(2): # send a SIGALRM in a second (during the read) signal.alarm(1) try: # blocking call: read from a pipe without data os.read(r, 1) except ZeroDivisionError: pass else: sys.exit(2) sys.exit(3) finally: os.close(r) os.close(w) """ % (interrupt, ) with spawn_python('-c', code) as process: try: # wait until the child process is loaded and has started first_line = process.stdout.readline() stdout, stderr = process.communicate(timeout=5.0) except subprocess.TimeoutExpired: process.kill() return False else: stdout = first_line + stdout exitcode = process.wait() if exitcode not in (2, 3): raise Exception("Child error (exit code %s): %r" % (exitcode, stdout)) return (exitcode == 3)
def readpipe_interrupted(self, interrupt): """Perform a read during which a signal will arrive. Return True if the read is interrupted by the signal and raises an exception. Return False if it returns normally. """ # use a subprocess to have only one thread, to have a timeout on the # blocking read and to not touch signal handling in this process code = """if 1: import errno import os import signal import sys interrupt = %r r, w = os.pipe() def handler(signum, frame): 1 / 0 signal.signal(signal.SIGALRM, handler) if interrupt is not None: signal.siginterrupt(signal.SIGALRM, interrupt) print("ready") sys.stdout.flush() # run the test twice try: for loop in range(2): # send a SIGALRM in a second (during the read) signal.alarm(1) try: # blocking call: read from a pipe without data os.read(r, 1) except ZeroDivisionError: pass else: sys.exit(2) sys.exit(3) finally: os.close(r) os.close(w) """ % (interrupt,) with spawn_python('-c', code) as process: try: # wait until the child process is loaded and has started first_line = process.stdout.readline() stdout, stderr = process.communicate(timeout=5.0) except subprocess.TimeoutExpired: process.kill() return False else: stdout = first_line + stdout exitcode = process.wait() if exitcode not in (2, 3): raise Exception("Child error (exit code %s): %r" % (exitcode, stdout)) return (exitcode == 3)
def test_unbuffered_input(self): code = 'import sys; sys.stdout.write(sys.stdin.read(1))' p = spawn_python('-u', '-c', code) p.stdin.write(b'x') p.stdin.flush() data, rc = _kill_python_and_exit_code(p) self.assertEqual(rc, 0) self.assertTrue(data.startswith(b'x'), data)
def interactive_python(self, separate_stderr=False): if separate_stderr: p = spawn_python('-i', bufsize=1, stderr=subprocess.PIPE) stderr = p.stderr else: p = spawn_python('-i', bufsize=1, stderr=subprocess.STDOUT) stderr = p.stdout try: while True: data = stderr.read(4) if data == b'>>> ': break stderr.readline() yield p finally: kill_python(p) stderr.close()
def test_stdin_loader(self): p = spawn_python() try: p.stdin.write(b'print(__loader__)\n') p.stdin.flush() finally: out = kill_python(p) expected = repr(importlib.machinery.BuiltinImporter).encode('utf-8') self.assertIn(expected, out)
def test_consistent_sys_path_for_direct_execution(self): script = textwrap.dedent(""" import sys for entry in sys.path: print(entry) """) self.maxDiff = None with support.temp_dir() as work_dir, support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__', script) p = spawn_python('-Es', script_name, cwd=work_dir) out_by_name = kill_python(p).decode().splitlines() self.assertEqual(out_by_name[0], script_dir) self.assertNotIn(work_dir, out_by_name) p = spawn_python('-Es', script_dir, cwd=work_dir) out_by_dir = kill_python(p).decode().splitlines() self.assertEqual(out_by_dir, out_by_name) p = spawn_python('-I', script_dir, cwd=work_dir) out_by_dir_isolated = kill_python(p).decode().splitlines() self.assertEqual(out_by_dir_isolated, out_by_dir, out_by_name)
def test_fill(self): data = self._merge_helper(FIRST, SECOND).decode("ascii") with tempfile.NamedTemporaryFile(mode="w+") as merged: merged.file.write(data) merged.file.flush() p = spawn_python('csvprogs/src/csvfill.py', '-k', 'position', merged.name) data = kill_python(p) self.assertEqual(data, FILLED)
def interactive_python(self, separate_stderr=False): if separate_stderr: p = spawn_python('-i', bufsize=1, stderr=subprocess.PIPE) stderr = p.stderr else: p = spawn_python('-i', bufsize=1, stderr=subprocess.STDOUT) stderr = p.stdout try: # Drain stderr until prompt while True: data = stderr.read(4) if data == b">>> ": break stderr.readline() yield p finally: kill_python(p) stderr.close()
def interactive_python(self, separate_stderr=False): if separate_stderr: p = spawn_python('-i', stderr=subprocess.PIPE) stderr = p.stderr else: p = spawn_python('-i', stderr=subprocess.STDOUT) stderr = p.stdout try: # Drain stderr until prompt while True: data = stderr.read(4) if data == b">>> ": break stderr.readline() yield p finally: kill_python(p) stderr.close()
def test_unbuffered_input(self): # sys.stdin still works with '-u' code = ("import sys; sys.stdout.write(sys.stdin.read(1))") p = spawn_python('-u', '-c', code) p.stdin.write(b'x') p.stdin.flush() data, rc = _kill_python_and_exit_code(p) self.assertEqual(rc, 0) self.assertTrue(data.startswith(b'x'), data)
def test_run_module_bug1764407(self): # -m and -i need to play well together # Runs the timeit module and checks the __main__ # namespace has been populated appropriately p = spawn_python('-i', '-m', 'timeit', '-n', '1') p.stdin.write(b'Timer\n') p.stdin.write(b'exit()\n') data = kill_python(p) self.assertTrue(data.find(b'1 loop') != -1) self.assertTrue(data.find(b'__main__.Timer') != -1)
def test_pdb_issue4201(self): test_src = textwrap.dedent(""" def f(): pass import pdb pdb.Pdb(nosigint=True).runcall(f) """) with test.support.temp_dir() as d: script_name = make_script(d, 'script', test_src) p = spawn_python(script_name) p.stdin.write(b'l\n') data = kill_python(p) self.assertIn(os.path.normcase(script_name.encode('utf-8')), data) zip_name, run_name = make_zip_script(d, 'test_zip', script_name, '__main__.py') p = spawn_python(zip_name) p.stdin.write(b'l\n') data = kill_python(p) self.assertIn(os.path.normcase(run_name.encode('utf-8')), data)
def _merge_helper(self, first, second): with tempfile.NamedTemporaryFile(mode="w+") as first: with tempfile.NamedTemporaryFile(mode="w+") as second: writer = csv.writer(first.file) writer.writerows(FIRST) first.file.flush() writer = csv.writer(second.file) writer.writerows(SECOND) second.file.flush() p = spawn_python('csvprogs/src/csvmerge.py', '-k', 'time', first.name, second.name) return kill_python(p)
def test_include_on_stdin(self): f1 = script_helper.make_script(self.pkgdir, 'f1', '') f2 = script_helper.make_script(self.pkgdir, 'f2', '') f3 = script_helper.make_script(self.pkgdir, 'f3', '') f4 = script_helper.make_script(self.pkgdir, 'f4', '') p = script_helper.spawn_python(*(self._get_run_args(()) + ['-i', '-'])) p.stdin.write((f3 + os.linesep).encode('ascii')) script_helper.kill_python(p) self.assertNotCompiled(f1) self.assertNotCompiled(f2) self.assertCompiled(f3) self.assertNotCompiled(f4)
def test_stdin_loader(self): # Unfortunately, there's no way to automatically test the fully # interactive REPL, since that code path only gets executed when # stdin is an interactive tty. p = spawn_python() try: p.stdin.write(b"print(__loader__)\n") p.stdin.flush() finally: out = kill_python(p) expected = repr(importlib.machinery.BuiltinImporter).encode("utf-8") self.assertIn(expected, out)
def test_include_on_stdin(self): f1 = script_helper.make_script(self.pkgdir, "f1", "") f2 = script_helper.make_script(self.pkgdir, "f2", "") f3 = script_helper.make_script(self.pkgdir, "f3", "") f4 = script_helper.make_script(self.pkgdir, "f4", "") p = script_helper.spawn_python(*(self._get_run_args(()) + ["-i", "-"])) p.stdin.write((f3 + os.linesep).encode("ascii")) script_helper.kill_python(p) self.assertNotCompiled(f1) self.assertNotCompiled(f2) self.assertCompiled(f3) self.assertNotCompiled(f4)
def test_include_on_stdin(self): f1 = script_helper.make_script(self.pkgdir, 'f1', '') f2 = script_helper.make_script(self.pkgdir, 'f2', '') f3 = script_helper.make_script(self.pkgdir, 'f3', '') f4 = script_helper.make_script(self.pkgdir, 'f4', '') p = script_helper.spawn_python(*(self._get_run_args(()) + ['-i', '-'])) p.stdin.write((f3+os.linesep).encode('ascii')) script_helper.kill_python(p) self.assertNotCompiled(f1) self.assertNotCompiled(f2) self.assertCompiled(f3) self.assertNotCompiled(f4)
def test_consistent_sys_path_for_module_execution(self): # This test case ensures that the following both give the same # sys.path configuration: # ./python -sm script_pkg.__main__ # ./python -sm script_pkg # # And that this fails as unable to find the package: # ./python -Im script_pkg script = textwrap.dedent("""\ import sys for entry in sys.path: print(entry) """) # Always show full path diffs on errors self.maxDiff = None with support.temp_dir() as work_dir: script_dir = os.path.join(work_dir, "script_pkg") os.mkdir(script_dir) script_name = _make_test_script(script_dir, '__main__', script) # Reference output comes from `-m script_pkg.__main__` # We omit PYTHONPATH and user site to better align with the # direct execution test cases p = spawn_python("-sm", "script_pkg.__main__", cwd=work_dir) out_by_module = kill_python(p).decode().splitlines() if sys.platform == 'OpenVMS': work_dir = work_dir.replace('/tmp/', tmp_folder_real) script_dir = script_dir.replace('/tmp/', tmp_folder_real) self.assertEqual(out_by_module[0], work_dir) self.assertNotIn(script_dir, out_by_module) # Package execution should give the same output p = spawn_python("-sm", "script_pkg", cwd=work_dir) out_by_package = kill_python(p).decode().splitlines() self.assertEqual(out_by_package, out_by_module) # Isolated mode should fail with an import error exitcode, stdout, stderr = assert_python_failure("-Im", "script_pkg", cwd=work_dir) traceback_lines = stderr.decode().splitlines() self.assertIn("No module named script_pkg", traceback_lines[-1])
def test_consistent_sys_path_for_module_execution(self): script = textwrap.dedent(""" import sys for entry in sys.path: print(entry) """) self.maxDiff = None with support.temp_dir() as work_dir: script_dir = os.path.join(work_dir, 'script_pkg') os.mkdir(script_dir) script_name = _make_test_script(script_dir, '__main__', script) p = spawn_python('-sm', 'script_pkg.__main__', cwd=work_dir) out_by_module = kill_python(p).decode().splitlines() self.assertEqual(out_by_module[0], '') self.assertNotIn(script_dir, out_by_module) p = spawn_python('-sm', 'script_pkg', cwd=work_dir) out_by_package = kill_python(p).decode().splitlines() self.assertEqual(out_by_package, out_by_module) exitcode, stdout, stderr = assert_python_failure('-Im', 'script_pkg', cwd=work_dir) traceback_lines = stderr.decode().splitlines() self.assertIn('No module named script_pkg', traceback_lines[-1])
def test_pdb_issue4201(self): test_src = textwrap.dedent("""\ def f(): pass import pdb pdb.Pdb(nosigint=True).runcall(f) """) with test.support.temp_dir() as d: script_name = make_script(d, 'script', test_src) p = spawn_python(script_name) p.stdin.write(b'l\n') data = kill_python(p) # bdb/pdb applies normcase to its filename before displaying self.assertIn(os.path.normcase(script_name.encode('utf-8')), data) zip_name, run_name = make_zip_script(d, "test_zip", script_name, '__main__.py') p = spawn_python(zip_name) p.stdin.write(b'l\n') data = kill_python(p) # bdb/pdb applies normcase to its filename before displaying self.assertIn(os.path.normcase(run_name.encode('utf-8')), data)
def test_pdb_issue4201(self): test_src = textwrap.dedent("""\ def f(): pass import pdb pdb.Pdb(nosigint=True).runcall(f) """) with os_helper.temp_dir() as d: script_name = make_script(d, 'script', test_src) p = spawn_python(script_name) p.stdin.write(b'l\n') data = kill_python(p) # bdb/pdb applies normcase to its filename before displaying self.assertIn(os.path.normcase(script_name.encode('utf-8')), data) zip_name, run_name = make_zip_script(d, "test_zip", script_name, '__main__.py') p = spawn_python(zip_name) p.stdin.write(b'l\n') data = kill_python(p) # bdb/pdb applies normcase to its filename before displaying self.assertIn(os.path.normcase(run_name.encode('utf-8')), data)
def test_ctrl_c(self): from .test_ctrl_c_in_proactor_loop_helper import __file__ as f # ctrl-c will be sent to all processes that share the same console # in order to isolate the effect of raising ctrl-c we'll create # a process with a new console flags = subprocess.CREATE_NEW_CONSOLE with spawn_python(f, creationflags=flags) as p: try: exit_code = p.wait(timeout=5) self.assertEqual(exit_code, 255) except: p.kill() raise
def test_nonexisting_script(self): # bpo-34783: "./python script.py" must not crash # if the script file doesn't exist. # (Skip test for macOS framework builds because sys.excutable name # is not the actual Python executable file name. script = 'nonexistingscript.py' self.assertFalse(os.path.exists(script)) proc = spawn_python(script, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = proc.communicate() self.assertIn(": can't open file ", err) self.assertNotEqual(proc.returncode, 0)
def test_consistent_sys_path_for_module_execution(self): # This test case ensures that the following both give the same # sys.path configuration: # ./python -sm script_pkg.__main__ # ./python -sm script_pkg # # And that this fails as unable to find the package: # ./python -Im script_pkg script = textwrap.dedent("""\ import sys for entry in sys.path: print(entry) """) # Always show full path diffs on errors self.maxDiff = None with support.temp_dir() as work_dir: script_dir = os.path.join(work_dir, "script_pkg") os.mkdir(script_dir) script_name = _make_test_script(script_dir, '__main__', script) # Reference output comes from `-m script_pkg.__main__` # We omit PYTHONPATH and user site to better align with the # direct execution test cases p = spawn_python("-sm", "script_pkg.__main__", cwd=work_dir) out_by_module = kill_python(p).decode().splitlines() self.assertEqual(out_by_module[0], '') self.assertNotIn(script_dir, out_by_module) # Package execution should give the same output p = spawn_python("-sm", "script_pkg", cwd=work_dir) out_by_package = kill_python(p).decode().splitlines() self.assertEqual(out_by_package, out_by_module) # Isolated mode should fail with an import error exitcode, stdout, stderr = assert_python_failure( "-Im", "script_pkg", cwd=work_dir ) traceback_lines = stderr.decode().splitlines() self.assertIn("No module named script_pkg", traceback_lines[-1])
def test_nonexisting_script(self): # bpo-34783: "./python script.py" must not crash # if the script file doesn't exist. script = 'nonexistingscript.py' self.assertFalse(os.path.exists(script)) # Only test the base name, since the error message can use # a relative path, whereas sys.executable can be an asolution path. program = os.path.basename(sys.executable) proc = spawn_python(script, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = proc.communicate() # "./python" must be in the error message: # "./python: can't open file (...)" self.assertIn(program, err) self.assertNotEqual(proc.returncode, 0)
def test_pthread_kill_main_thread(self): code = """if True: import threading import signal import sys def handler(signum, frame): sys.exit(3) signal.signal(signal.SIGUSR1, handler) signal.pthread_kill(threading.get_ident(), signal.SIGUSR1) sys.exit(2) """ with spawn_python('-c', code) as process: stdout, stderr = process.communicate() exitcode = process.wait() if exitcode != 3: raise Exception('Child error (exit code %s): %s' % (exitcode, stdout))
def test_still_running_at_exit(self): script = dedent(f""" from textwrap import dedent import threading import _xxsubinterpreters as _interpreters def f(): _interpreters.run_string(id, dedent(''' import time # Give plenty of time for the main interpreter to finish. time.sleep(1_000_000) ''')) t = threading.Thread(target=f) t.start() """) with support.temp_dir() as dirname: filename = script_helper.make_script(dirname, 'interp', script) with script_helper.spawn_python(filename) as proc: retcode = proc.wait() self.assertEqual(retcode, 0)
def test_pthread_kill_main_thread(self): # Test that a signal can be sent to the main thread with pthread_kill() # before any other thread has been created (see issue #12392). code = """if True: import threading import signal import sys def handler(signum, frame): sys.exit(3) signal.signal(signal.SIGUSR1, handler) signal.pthread_kill(threading.get_ident(), signal.SIGUSR1) sys.exit(2) """ with spawn_python('-c', code) as process: stdout, stderr = process.communicate() exitcode = process.wait() if exitcode != 3: raise Exception("Child error (exit code %s): %s" % (exitcode, stdout))
def get_output(self, code, filename=None, fd=None): """ Run the specified code in Python (in a new child process) and read the output from the standard error or from a file (if filename is set). Return the output lines as a list. Strip the reference count from the standard error for Python debug build, and replace "Current thread 0x00007f8d8fbd9700" by "Current thread XXX". """ code = dedent(code).strip() pass_fds = [] if fd is not None: pass_fds.append(fd) with support.SuppressCrashReport(): process = script_helper.spawn_python('-c', code, pass_fds=pass_fds) with process: stdout, stderr = process.communicate() exitcode = process.wait() output = support.strip_python_stderr(stdout) output = output.decode('ascii', 'backslashreplace') if sys.platform == 'OpenVMS': skip_lines = [ '%CMA-F-EXIT_THREAD, current thread has been requested to exit' ] for line in skip_lines: output = output.replace(line, '') if filename: self.assertEqual(output, '') with open(filename, "rb") as fp: output = fp.read() output = output.decode('ascii', 'backslashreplace') elif fd is not None: self.assertEqual(output, '') os.lseek(fd, os.SEEK_SET, 0) with open(fd, "rb", closefd=False) as fp: output = fp.read() output = output.decode('ascii', 'backslashreplace') return output.splitlines(), exitcode
def test_delete_builtins_import(self): args = ["-c", "del __builtins__.__import__; import os"] popen = script_helper.spawn_python(*args) stdout, stderr = popen.communicate() self.assertIn(b"ImportError", stdout)
def test_encode_from_stdin(self): with script_helper.spawn_python('-m', 'base64', '-e') as proc: out, err = proc.communicate(b'a\xffb\n') self.assertEqual(out.rstrip(), b'Yf9iCg==') self.assertIsNone(err)