| import ast |
| import os |
| import sys |
| import tempfile |
| import textwrap |
| import unittest |
| from collections import UserDict |
| from test import support |
| from test.support import import_helper |
| from test.support.os_helper import unlink, TESTFN, TESTFN_ASCII, TESTFN_UNDECODABLE |
| |
| _testcapi = import_helper.import_module('_testcapi') |
| _testlimitedcapi = import_helper.import_module('_testlimitedcapi') |
| |
| |
| NULL = None |
| Py_single_input = _testcapi.Py_single_input |
| Py_file_input = _testcapi.Py_file_input |
| Py_eval_input = _testcapi.Py_eval_input |
| INVALID_START = Py_single_input - 1 |
| STDIN = '<stdin>' |
| STDERR_FD = 2 |
| PyCF_ONLY_AST = _testcapi.PyCF_ONLY_AST |
| PyCF_IGNORE_COOKIE = _testcapi.PyCF_IGNORE_COOKIE |
| |
| # Code raising a SyntaxError |
| SYNTAX_ERROR = 'True = 1' |
| |
| # Raise a SystemExit with exit code 42 |
| CODE_EXIT_42 = 'raise SystemExit(42)' |
| |
| |
| def create_text_file(filename, text): |
| with open(filename, 'w', encoding='utf8') as fp: |
| fp.write(text) |
| |
| |
| class DictSubclass(dict): |
| pass |
| |
| |
| class capture_excepthook: |
| def __init__(self): |
| self.exc = None |
| self._old_hook = None |
| |
| def _hook(self, exc_type, exc_value, exc_tb): |
| # Storing the exception instance creates a reference cycle. |
| self.exc = exc_value |
| |
| def __enter__(self): |
| self._old_hook = sys.excepthook |
| sys.excepthook = self._hook |
| return self |
| |
| def __exit__(self, *exc_info): |
| sys.excepthook = self._old_hook |
| self.exc = None |
| |
| |
| class CAPITest(unittest.TestCase): |
| def check_run_string(self, func): |
| def run(s, *args): |
| return func(s, Py_file_input, *args) |
| |
| ns = {} |
| self.assertIsNone(run(b'x = 1', ns, ns)) |
| self.assertEqual(ns['x'], 1) |
| |
| with self.assertRaises(SyntaxError) as cm: |
| run(SYNTAX_ERROR.encode(), {}) |
| self.assertEqual(cm.exception.filename, '<string>') |
| |
| with self.assertRaises(ValueError) as cm: |
| run(b'raise ValueError("BUG")', {}) |
| self.assertEqual(str(cm.exception), 'BUG') |
| |
| with self.assertRaises(ValueError): |
| func(b'x = 1', INVALID_START, {}) |
| |
| self.assertIsNone(run(b'a\n', dict(a=1))) |
| self.assertIsNone(run(b'a\n', dict(a=1), {})) |
| self.assertIsNone(run(b'a\n', {}, dict(a=1))) |
| self.assertIsNone(run(b'a\n', {}, UserDict(a=1))) |
| |
| self.assertRaises(NameError, run, b'a\n', {}) |
| self.assertRaises(NameError, run, b'a\n', {}, {}) |
| self.assertRaises(TypeError, run, b'a\n', dict(a=1), []) |
| self.assertRaises(TypeError, run, b'a\n', dict(a=1), 1) |
| |
| self.assertIsNone(run(b'a\n', DictSubclass(a=1))) |
| self.assertIsNone(run(b'a\n', DictSubclass(), dict(a=1))) |
| self.assertRaises(NameError, run, b'a\n', DictSubclass()) |
| |
| self.assertIsNone(run(b'\xc3\xa4\n', {'\xe4': 1})) |
| self.assertRaises(SyntaxError, run, b'\xe4\n', {}) |
| |
| self.assertRaises(SystemError, run, b'a\n', NULL) |
| self.assertRaises(SystemError, run, b'a\n', NULL, {}) |
| self.assertRaises(SystemError, run, b'a\n', NULL, dict(a=1)) |
| self.assertRaises(SystemError, run, b'a\n', UserDict()) |
| self.assertRaises(SystemError, run, b'a\n', UserDict(), {}) |
| self.assertRaises(SystemError, run, b'a\n', UserDict(), dict(a=1)) |
| |
| # CRASHES run(NULL, {}) |
| |
| def test_run_string(self): |
| # Test PyRun_String() |
| self.check_run_string(_testcapi.run_string) |
| |
| def test_run_stringflags(self): |
| # Test PyRun_StringFlags(). |
| self.check_run_string(_testcapi.run_stringflags) |
| |
| def check_run_file(self, func, support_closeit): |
| # XXX: fopen() uses different path encoding than Python on Windows. |
| filename = os.fsencode(TESTFN if os.name != 'nt' else TESTFN_ASCII) |
| create_text_file(filename, 'a\n') |
| self.addCleanup(unlink, filename) |
| def run(*args): |
| return func(filename, Py_file_input, *args) |
| |
| self.assertIsNone(run(dict(a=1))) |
| self.assertIsNone(run(dict(a=1), {})) |
| self.assertIsNone(run({}, dict(a=1))) |
| self.assertIsNone(run({}, UserDict(a=1))) |
| if support_closeit: |
| closeit = 1 |
| self.assertIsNone(run(dict(a=1), {}, closeit)) |
| |
| with self.assertRaises(ValueError): |
| func(filename, INVALID_START, {}) |
| |
| self.assertRaises(NameError, run, {}) |
| self.assertRaises(NameError, run, {}, {}) |
| self.assertRaises(TypeError, run, dict(a=1), []) |
| self.assertRaises(TypeError, run, dict(a=1), 1) |
| |
| self.assertIsNone(run(DictSubclass(a=1))) |
| self.assertIsNone(run(DictSubclass(), dict(a=1))) |
| self.assertRaises(NameError, run, DictSubclass()) |
| |
| self.assertRaises(SystemError, run, NULL) |
| self.assertRaises(SystemError, run, NULL, {}) |
| self.assertRaises(SystemError, run, NULL, dict(a=1)) |
| self.assertRaises(SystemError, run, UserDict()) |
| self.assertRaises(SystemError, run, UserDict(), {}) |
| self.assertRaises(SystemError, run, UserDict(), dict(a=1)) |
| |
| # syntax error |
| create_text_file(filename, SYNTAX_ERROR) |
| with self.assertRaises(SyntaxError) as cm: |
| self.assertIsNone(run({})) |
| self.assertEqual(cm.exception.filename, os.fsdecode(filename)) |
| |
| # raise exception |
| create_text_file(filename, 'raise ValueError("BUG")') |
| with self.assertRaises(ValueError) as cm: |
| self.assertIsNone(run({})) |
| self.assertEqual(str(cm.exception), 'BUG') |
| |
| # Test undecodable filename |
| if TESTFN_UNDECODABLE and os.name != 'nt': |
| try: |
| create_text_file(TESTFN_UNDECODABLE, 'a\n') |
| self.addCleanup(unlink, TESTFN_UNDECODABLE) |
| except OSError: |
| # undecodable paths are not supported |
| pass |
| else: |
| self.assertIsNone(func(TESTFN_UNDECODABLE, Py_file_input, dict(a=1))) |
| |
| def test_run_file(self): |
| # Test PyRun_File(). |
| self.check_run_file(_testcapi.run_file, False) |
| |
| def test_run_fileex(self): |
| # Test PyRun_FileEx(). |
| self.check_run_file(_testcapi.run_fileex, True) |
| |
| def test_run_fileflags(self): |
| # Test PyRun_FileFlags(). |
| self.check_run_file(_testcapi.run_fileflags, True) |
| |
| def test_run_fileexflags(self): |
| # Test PyRun_FileExFlags(). |
| self.check_run_file(_testcapi.run_fileexflags, True) |
| |
| def check_run_simplefile(self, func_name, have_closeit): |
| run = getattr(_testcapi, func_name) |
| |
| open_filename = TESTFN |
| with open(open_filename, 'w') as fp: |
| print('import sys', file=fp) |
| print('print(__file__)', file=fp) |
| print('mod = sys.modules["__main__"]', file=fp) |
| print('print(type(mod.__loader__).__name__)', file=fp) |
| self.addCleanup(unlink, open_filename) |
| |
| class MockLoader: |
| pass |
| loader = MockLoader() |
| |
| for closeit in (0, 1): |
| for set_file in (False, True): |
| for filename in (open_filename, 'custom filename', STDIN): |
| set_loader = (filename != STDIN) |
| with self.subTest(closeit=closeit, |
| set_file=set_file, |
| filename=filename): |
| mod = sys.modules['__main__'] |
| mod_file = mod.__file__ |
| with (support.captured_stdout() as stdout, |
| support.swap_attr(mod, '__loader__', loader)): |
| try: |
| if set_file: |
| del mod.__file__ |
| filename_arg = os.fsencode(filename) |
| if have_closeit: |
| res = run(open_filename, filename_arg, closeit) |
| else: |
| res = run(open_filename, filename_arg) |
| if set_file: |
| self.assertFalse(hasattr(mod, '__file__')) |
| finally: |
| mod.__file__ = mod_file |
| |
| self.assertEqual(res, 0) |
| expected = [filename if set_file else mod_file] |
| if set_loader: |
| expected.append('SourceFileLoader') |
| else: |
| expected.append('MockLoader') |
| self.assertEqual(stdout.getvalue().splitlines(), |
| expected) |
| |
| def test_run_simplefile(self): |
| # Test PyRun_SimpleFile() |
| self.check_run_simplefile('run_simplefile', False) |
| |
| def test_run_simplefileex(self): |
| # Test PyRun_SimpleFileEx() |
| self.check_run_simplefile('run_simplefileex', True) |
| |
| def test_run_simplefileexflags(self): |
| # Test PyRun_SimpleFileExFlags() |
| self.check_run_simplefile('run_simplefileexflags', True) |
| |
| def test_run_anyfile(self): |
| # Test PyRun_AnyFile() |
| self.check_run_simplefile('run_anyfile', False) |
| |
| def test_run_anyfileex(self): |
| # Test PyRun_AnyFileEx() |
| self.check_run_simplefile('run_anyfileex', True) |
| |
| def test_run_anyfileflags(self): |
| # Test PyRun_AnyFileFlags() |
| self.check_run_simplefile('run_anyfileflags', True) |
| |
| def _check_run_interactive(self, run, encode_filename, use_loop): |
| open_filename = TESTFN |
| if use_loop: |
| with open(open_filename, 'w') as fp: |
| print('welcome = "hello REPL"', file=fp) |
| print('print(welcome)', file=fp) |
| else: |
| create_text_file(open_filename, 'print("hello REPL")') |
| self.addCleanup(unlink, open_filename) |
| |
| for filename in ('filename', STDIN): |
| with self.subTest(filename=filename): |
| with support.captured_stdout() as stdout: |
| if encode_filename: |
| filename = filename.encode() |
| self.assertEqual(run(open_filename, filename), 0) |
| self.assertEqual(stdout.getvalue(), 'hello REPL\n') |
| |
| create_text_file(open_filename, SYNTAX_ERROR) |
| filename = 'custom filename' |
| if encode_filename: |
| filename_arg = filename.encode() |
| else: |
| filename_arg = filename |
| with capture_excepthook() as excepthook: |
| expected = (0 if use_loop else -1) |
| self.assertEqual(run(open_filename, filename_arg), expected) |
| self.assertIsInstance(excepthook.exc, SyntaxError) |
| self.assertEqual(excepthook.exc.filename, filename) |
| |
| create_text_file(open_filename, 'raise ValueError("BUG")') |
| with capture_excepthook() as excepthook: |
| expected = (0 if use_loop else -1) |
| self.assertEqual(run(open_filename, filename_arg), expected) |
| self.assertIsInstance(excepthook.exc, ValueError) |
| self.assertEqual(str(excepthook.exc), 'BUG') |
| |
| if not encode_filename: |
| # wrong type for the second parameter (filename) |
| with capture_excepthook() as excepthook: |
| self.assertEqual(run(open_filename, b'bytes'), -1) |
| self.assertIsInstance(excepthook.exc, TypeError) |
| |
| def check_run_interactive(self, run, encode_filename, use_loop=False): |
| # Redirect stderr to a temporary file to hide '>>> ' from the REPL |
| try: |
| stderr_copy = os.dup(STDERR_FD) |
| except OSError: |
| # On WASI, dup(STDERR_FD) fails with "OSError: [Errno 58] Not |
| # supported". In this case, run the test without redirecting |
| # stderr to a temporary file. |
| self._check_run_interactive(run, encode_filename, use_loop) |
| return |
| |
| with tempfile.TemporaryFile() as tmp: |
| try: |
| os.dup2(tmp.fileno(), STDERR_FD) |
| self._check_run_interactive(run, encode_filename, use_loop) |
| finally: |
| os.dup2(stderr_copy, STDERR_FD) |
| os.close(stderr_copy) |
| |
| def test_run_interactiveone(self): |
| # Test PyRun_InteractiveOne() |
| run = _testcapi.run_interactiveone |
| self.check_run_interactive(run, True) |
| |
| def test_run_interactiveoneflags(self): |
| # Test PyRun_InteractiveOneFlags() |
| run = _testcapi.run_interactiveoneflags |
| self.check_run_interactive(run, True) |
| |
| def test_run_interactiveoneobject(self): |
| # Test PyRun_InteractiveOneObject() |
| run = _testcapi.run_interactiveoneobject |
| self.check_run_interactive(run, False) |
| |
| def test_run_interactiveloop(self): |
| # Test PyRun_InteractiveLoop() |
| run = _testcapi.run_interactiveloop |
| self.check_run_interactive(run, True, use_loop=True) |
| |
| def test_run_interactiveloopflags(self): |
| # Test PyRun_InteractiveLoopFlags() |
| run = _testcapi.run_interactiveloopflags |
| self.check_run_interactive(run, True, use_loop=True) |
| |
| def test_run_anyfileexflags(self): |
| # Test PyRun_AnyFileExFlags() |
| self.check_run_simplefile('run_anyfileexflags', True) |
| |
| def check_run_simplestring(self, run): |
| with support.captured_stdout() as stdout: |
| run(b'print("simple hello")') |
| self.assertEqual(stdout.getvalue(), 'simple hello\n') |
| |
| with capture_excepthook() as excepthook: |
| self.assertEqual(run(SYNTAX_ERROR.encode()), -1) |
| self.assertIsInstance(excepthook.exc, SyntaxError) |
| self.assertEqual(excepthook.exc.filename, '<string>') |
| |
| with capture_excepthook() as excepthook: |
| self.assertEqual(run(b'raise ValueError("BUG")'), -1) |
| self.assertIsInstance(excepthook.exc, ValueError) |
| self.assertEqual(str(excepthook.exc), 'BUG') |
| |
| def test_run_simplestring(self): |
| # Test PyRun_SimpleString() |
| self.check_run_simplestring(_testcapi.run_simplestring) |
| |
| def test_run_simplestringflags(self): |
| # Test PyRun_SimpleStringFlags() |
| self.check_run_simplestring(_testcapi.run_simplestringflags) |
| |
| def check_compilestring(self, compilestring, has_flags, encode_filename=True): |
| filename_str = TESTFN |
| if encode_filename: |
| filename = os.fsencode(filename_str) |
| else: |
| filename = filename_str |
| |
| def check_code(co, name, value): |
| ns = {} |
| exec(co, ns, ns) |
| self.assertEqual(ns[name], value) |
| |
| co = compilestring(b'x = 1', filename, Py_file_input) |
| self.assertEqual(co.co_filename, filename_str) |
| check_code(co, 'x', 1) |
| |
| if has_flags: |
| code = textwrap.dedent(""" |
| # encoding: latin1 |
| x = 'a\xe9' |
| """) |
| co = compilestring(code.encode(), filename, Py_file_input, PyCF_IGNORE_COOKIE) |
| self.assertEqual(co.co_filename, filename_str) |
| check_code(co, 'x', 'a\xe9') |
| |
| co = compilestring(code.encode(), filename, Py_file_input) |
| self.assertEqual(co.co_filename, filename_str) |
| check_code(co, 'x', 'a\xc3\xa9') |
| |
| tree = compilestring(b'x = 1', filename, Py_file_input, PyCF_ONLY_AST) |
| self.assertIsInstance(tree, ast.AST) |
| |
| co = compilestring(b'raise ValueError("BUG")', filename, Py_file_input) |
| with self.assertRaises(ValueError): |
| exec(co, {}) |
| |
| with self.assertRaises(SyntaxError) as cm: |
| compilestring(SYNTAX_ERROR.encode(), filename, Py_file_input) |
| |
| with self.assertRaises(ValueError): |
| compilestring(b'x = 1', filename, INVALID_START) |
| |
| def test_compilestring(self): |
| # Test Py_CompileString() |
| self.check_compilestring(_testlimitedcapi.run_compilestring, False) |
| |
| def test_compilestringflags(self): |
| # Test Py_CompileStringFlags() |
| self.check_compilestring(_testcapi.run_compilestringflags, True) |
| |
| def test_compilestringexflags(self): |
| # Test Py_CompileStringExFlags() |
| self.check_compilestring(_testcapi.run_compilestringexflags, True) |
| |
| def test_compilestringobject(self): |
| # Test Py_CompileStringObject() |
| self.check_compilestring(_testcapi.run_compilestringobject, True, |
| encode_filename=False) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |