--- /dev/null
+import os
+import unittest
+from collections import UserDict
+from test.support import import_helper
+from test.support.os_helper import unlink, TESTFN, TESTFN_UNDECODABLE
+
+NULL = None
+_testcapi = import_helper.import_module('_testcapi')
+Py_single_input = _testcapi.Py_single_input
+Py_file_input = _testcapi.Py_file_input
+Py_eval_input = _testcapi.Py_eval_input
+
+
+class CAPITest(unittest.TestCase):
+ # TODO: Test the following functions:
+ #
+ # PyRun_SimpleStringFlags
+ # PyRun_AnyFileExFlags
+ # PyRun_SimpleFileExFlags
+ # PyRun_InteractiveOneFlags
+ # PyRun_InteractiveOneObject
+ # PyRun_InteractiveLoopFlags
+ # PyRun_String (may be a macro)
+ # PyRun_AnyFile (may be a macro)
+ # PyRun_AnyFileEx (may be a macro)
+ # PyRun_AnyFileFlags (may be a macro)
+ # PyRun_SimpleString (may be a macro)
+ # PyRun_SimpleFile (may be a macro)
+ # PyRun_SimpleFileEx (may be a macro)
+ # PyRun_InteractiveOne (may be a macro)
+ # PyRun_InteractiveLoop (may be a macro)
+ # PyRun_File (may be a macro)
+ # PyRun_FileEx (may be a macro)
+ # PyRun_FileFlags (may be a macro)
+
+ def test_run_stringflags(self):
+ # Test PyRun_StringFlags().
+ def run(s, *args):
+ return _testcapi.run_stringflags(s, Py_file_input, *args)
+ source = b'a\n'
+
+ 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'\xc3\xa4\n', {'\xe4': 1}))
+ self.assertRaises(SyntaxError, run, b'\xe4\n', {})
+
+ # CRASHES run(b'a\n', NULL)
+ # CRASHES run(b'a\n', NULL, {})
+ # CRASHES run(b'a\n', NULL, dict(a=1))
+ # CRASHES run(b'a\n', UserDict())
+ # CRASHES run(b'a\n', UserDict(), {})
+ # CRASHES run(b'a\n', UserDict(), dict(a=1))
+
+ # CRASHES run(NULL, {})
+
+ def test_run_fileexflags(self):
+ # Test PyRun_FileExFlags().
+ filename = os.fsencode(TESTFN)
+ with open(filename, 'wb') as fp:
+ fp.write(b'a\n')
+ self.addCleanup(unlink, filename)
+ def run(*args):
+ return _testcapi.run_fileexflags(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)))
+ self.assertIsNone(run(dict(a=1), {}, 1)) # closeit = True
+
+ self.assertRaises(NameError, run, {})
+ self.assertRaises(NameError, run, {}, {})
+ self.assertRaises(TypeError, run, dict(a=1), [])
+ self.assertRaises(TypeError, run, dict(a=1), 1)
+
+ # CRASHES run(NULL)
+ # CRASHES run(NULL, {})
+ # CRASHES run(NULL, dict(a=1))
+ # CRASHES run(UserDict())
+ # CRASHES run(UserDict(), {})
+ # CRASHES run(UserDict(), dict(a=1))
+
+ @unittest.skipUnless(TESTFN_UNDECODABLE, 'only works if there are undecodable paths')
+ def test_run_fileexflags_with_undecodable_filename(self):
+ run = _testcapi.run_fileexflags
+ try:
+ with open(TESTFN_UNDECODABLE, 'wb') as fp:
+ fp.write(b'a\n')
+ self.addCleanup(unlink, TESTFN_UNDECODABLE)
+ except OSError:
+ self.skipTest('undecodable paths are not supported')
+ self.assertIsNone(run(TESTFN_UNDECODABLE, Py_file_input, dict(a=1)))
+
+
+if __name__ == '__main__':
+ unittest.main()
--- /dev/null
+#include "parts.h"
+#include "util.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+
+static PyObject *
+run_stringflags(PyObject *mod, PyObject *pos_args)
+{
+ const char *str;
+ Py_ssize_t size;
+ int start;
+ PyObject *globals = NULL;
+ PyObject *locals = NULL;
+ PyCompilerFlags flags = _PyCompilerFlags_INIT;
+ PyCompilerFlags *pflags = NULL;
+ int cf_flags = 0;
+ int cf_feature_version = 0;
+
+ if (!PyArg_ParseTuple(pos_args, "z#iO|Oii",
+ &str, &size, &start, &globals, &locals,
+ &cf_flags, &cf_feature_version)) {
+ return NULL;
+ }
+
+ NULLABLE(globals);
+ NULLABLE(locals);
+ if (cf_flags || cf_feature_version) {
+ flags.cf_flags = cf_flags;
+ flags.cf_feature_version = cf_feature_version;
+ pflags = &flags;
+ }
+
+ return PyRun_StringFlags(str, start, globals, locals, pflags);
+}
+
+static PyObject *
+run_fileexflags(PyObject *mod, PyObject *pos_args)
+{
+ PyObject *result = NULL;
+ const char *filename = NULL;
+ Py_ssize_t filename_size;
+ int start;
+ PyObject *globals = NULL;
+ PyObject *locals = NULL;
+ int closeit = 0;
+ PyCompilerFlags flags = _PyCompilerFlags_INIT;
+ PyCompilerFlags *pflags = NULL;
+ int cf_flags = 0;
+ int cf_feature_version = 0;
+
+ FILE *fp = NULL;
+
+ if (!PyArg_ParseTuple(pos_args, "z#iO|Oiii",
+ &filename, &filename_size, &start, &globals, &locals,
+ &closeit, &cf_flags, &cf_feature_version)) {
+ return NULL;
+ }
+
+ NULLABLE(globals);
+ NULLABLE(locals);
+ if (cf_flags || cf_feature_version) {
+ flags.cf_flags = cf_flags;
+ flags.cf_feature_version = cf_feature_version;
+ pflags = &flags;
+ }
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename);
+ return NULL;
+ }
+
+ result = PyRun_FileExFlags(fp, filename, start, globals, locals, closeit, pflags);
+
+#if !defined(__wasi__)
+ /* The behavior of fileno() after fclose() is undefined. */
+ if (closeit && result && fileno(fp) >= 0) {
+ PyErr_SetString(PyExc_AssertionError, "File was not closed after excution");
+ Py_DECREF(result);
+ fclose(fp);
+ return NULL;
+ }
+#endif
+ if (!closeit && fileno(fp) < 0) {
+ PyErr_SetString(PyExc_AssertionError, "Bad file descriptor after excution");
+ Py_XDECREF(result);
+ return NULL;
+ }
+
+ if (!closeit) {
+ fclose(fp); /* don't need open file any more*/
+ }
+
+ return result;
+}
+
+static PyMethodDef test_methods[] = {
+ {"run_stringflags", run_stringflags, METH_VARARGS},
+ {"run_fileexflags", run_fileexflags, METH_VARARGS},
+ {NULL},
+};
+
+int
+_PyTestCapi_Init_Run(PyObject *mod)
+{
+ if (PyModule_AddFunctions(mod, test_methods) < 0) {
+ return -1;
+ }
+ return 0;
+}