mirror of https://github.com/python/cpython
gh-117968: Add tests for the part of the PyRun family of the C API (GH-117982)
Co-authored-by: NGRsoftlab <78017794+NGRsoftlab@users.noreply.github.com> Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
This commit is contained in:
parent
c1d7147c82
commit
6078f2033e
|
@ -0,0 +1,104 @@
|
|||
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()
|
|
@ -162,7 +162,7 @@
|
|||
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
|
||||
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
|
||||
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
|
||||
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c
|
||||
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c
|
||||
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c
|
||||
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
|
||||
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
|
||||
|
|
|
@ -51,6 +51,7 @@ int _PyTestCapi_Init_Exceptions(PyObject *module);
|
|||
int _PyTestCapi_Init_Code(PyObject *module);
|
||||
int _PyTestCapi_Init_Buffer(PyObject *module);
|
||||
int _PyTestCapi_Init_PyAtomic(PyObject *module);
|
||||
int _PyTestCapi_Init_Run(PyObject *module);
|
||||
int _PyTestCapi_Init_File(PyObject *module);
|
||||
int _PyTestCapi_Init_Codec(PyObject *module);
|
||||
int _PyTestCapi_Init_Immortal(PyObject *module);
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
#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;
|
||||
}
|
|
@ -3904,6 +3904,16 @@ PyInit__testcapi(void)
|
|||
PyModule_AddIntConstant(m, "the_number_three", 3);
|
||||
PyModule_AddIntMacro(m, Py_C_RECURSION_LIMIT);
|
||||
|
||||
if (PyModule_AddIntMacro(m, Py_single_input)) {
|
||||
return NULL;
|
||||
}
|
||||
if (PyModule_AddIntMacro(m, Py_file_input)) {
|
||||
return NULL;
|
||||
}
|
||||
if (PyModule_AddIntMacro(m, Py_eval_input)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
testcapistate_t *state = get_testcapi_state(m);
|
||||
state->error = PyErr_NewException("_testcapi.error", NULL, NULL);
|
||||
PyModule_AddObject(m, "error", state->error);
|
||||
|
@ -3998,6 +4008,9 @@ PyInit__testcapi(void)
|
|||
if (_PyTestCapi_Init_PyAtomic(m) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (_PyTestCapi_Init_Run(m) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (_PyTestCapi_Init_Hash(m) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -124,6 +124,7 @@
|
|||
<ClCompile Include="..\Modules\_testcapi\object.c" />
|
||||
<ClCompile Include="..\Modules\_testcapi\immortal.c" />
|
||||
<ClCompile Include="..\Modules\_testcapi\gc.c" />
|
||||
<ClCompile Include="..\Modules\_testcapi\run.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="..\PC\python_nt.rc" />
|
||||
|
|
|
@ -105,6 +105,9 @@
|
|||
<ClCompile Include="..\Modules\_testcapi\gc.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Modules\_testcapi\run.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="..\PC\python_nt.rc">
|
||||
|
|
Loading…
Reference in New Issue