mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
[3.12] gh-111065: Add more tests for the C API with the PySys_ prefix (GH-111067) (GH-111305)
* Move existing tests for PySys_GetObject() and PySys_SetObject() into
specialized files.
* Add test for PySys_GetXOptions() using _testcapi.
* Add tests for PySys_FormatStdout(), PySys_FormatStderr(),
PySys_WriteStdout() and PySys_WriteStderr() using ctypes.
(cherry picked from commit b2ba298527
)
This commit is contained in:
parent
6f130f231b
commit
54e93d36d0
8 changed files with 215 additions and 72 deletions
|
@ -1090,46 +1090,6 @@ class CAPITest(unittest.TestCase):
|
||||||
del d.extra
|
del d.extra
|
||||||
self.assertIsNone(d.extra)
|
self.assertIsNone(d.extra)
|
||||||
|
|
||||||
def test_sys_getobject(self):
|
|
||||||
getobject = _testcapi.sys_getobject
|
|
||||||
|
|
||||||
self.assertIs(getobject(b'stdout'), sys.stdout)
|
|
||||||
with support.swap_attr(sys, '\U0001f40d', 42):
|
|
||||||
self.assertEqual(getobject('\U0001f40d'.encode()), 42)
|
|
||||||
|
|
||||||
self.assertIs(getobject(b'nonexisting'), AttributeError)
|
|
||||||
self.assertIs(getobject(b'\xff'), AttributeError)
|
|
||||||
# CRASHES getobject(NULL)
|
|
||||||
|
|
||||||
def test_sys_setobject(self):
|
|
||||||
setobject = _testcapi.sys_setobject
|
|
||||||
|
|
||||||
value = ['value']
|
|
||||||
value2 = ['value2']
|
|
||||||
try:
|
|
||||||
self.assertEqual(setobject(b'newattr', value), 0)
|
|
||||||
self.assertIs(sys.newattr, value)
|
|
||||||
self.assertEqual(setobject(b'newattr', value2), 0)
|
|
||||||
self.assertIs(sys.newattr, value2)
|
|
||||||
self.assertEqual(setobject(b'newattr', NULL), 0)
|
|
||||||
self.assertFalse(hasattr(sys, 'newattr'))
|
|
||||||
self.assertEqual(setobject(b'newattr', NULL), 0)
|
|
||||||
finally:
|
|
||||||
with contextlib.suppress(AttributeError):
|
|
||||||
del sys.newattr
|
|
||||||
try:
|
|
||||||
self.assertEqual(setobject('\U0001f40d'.encode(), value), 0)
|
|
||||||
self.assertIs(getattr(sys, '\U0001f40d'), value)
|
|
||||||
self.assertEqual(setobject('\U0001f40d'.encode(), NULL), 0)
|
|
||||||
self.assertFalse(hasattr(sys, '\U0001f40d'))
|
|
||||||
finally:
|
|
||||||
with contextlib.suppress(AttributeError):
|
|
||||||
delattr(sys, '\U0001f40d')
|
|
||||||
|
|
||||||
with self.assertRaises(UnicodeDecodeError):
|
|
||||||
setobject(b'\xff', value)
|
|
||||||
# CRASHES setobject(NULL, value)
|
|
||||||
|
|
||||||
|
|
||||||
@requires_limited_api
|
@requires_limited_api
|
||||||
class TestHeapTypeRelative(unittest.TestCase):
|
class TestHeapTypeRelative(unittest.TestCase):
|
||||||
|
|
149
Lib/test/test_capi/test_sys.py
Normal file
149
Lib/test/test_capi/test_sys.py
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
import unittest
|
||||||
|
import contextlib
|
||||||
|
import sys
|
||||||
|
from test import support
|
||||||
|
from test.support import import_helper
|
||||||
|
|
||||||
|
try:
|
||||||
|
import _testcapi
|
||||||
|
except ImportError:
|
||||||
|
_testcapi = None
|
||||||
|
|
||||||
|
NULL = None
|
||||||
|
|
||||||
|
class CAPITest(unittest.TestCase):
|
||||||
|
# TODO: Test the following functions:
|
||||||
|
#
|
||||||
|
# PySys_Audit()
|
||||||
|
|
||||||
|
maxDiff = None
|
||||||
|
|
||||||
|
@support.cpython_only
|
||||||
|
@unittest.skipIf(_testcapi is None, 'need _testcapi module')
|
||||||
|
def test_sys_getobject(self):
|
||||||
|
# Test PySys_GetObject()
|
||||||
|
getobject = _testcapi.sys_getobject
|
||||||
|
|
||||||
|
self.assertIs(getobject(b'stdout'), sys.stdout)
|
||||||
|
with support.swap_attr(sys, '\U0001f40d', 42):
|
||||||
|
self.assertEqual(getobject('\U0001f40d'.encode()), 42)
|
||||||
|
|
||||||
|
self.assertIs(getobject(b'nonexisting'), AttributeError)
|
||||||
|
self.assertIs(getobject(b'\xff'), AttributeError)
|
||||||
|
# CRASHES getobject(NULL)
|
||||||
|
|
||||||
|
@support.cpython_only
|
||||||
|
@unittest.skipIf(_testcapi is None, 'need _testcapi module')
|
||||||
|
def test_sys_setobject(self):
|
||||||
|
# Test PySys_SetObject()
|
||||||
|
setobject = _testcapi.sys_setobject
|
||||||
|
|
||||||
|
value = ['value']
|
||||||
|
value2 = ['value2']
|
||||||
|
try:
|
||||||
|
self.assertEqual(setobject(b'newattr', value), 0)
|
||||||
|
self.assertIs(sys.newattr, value)
|
||||||
|
self.assertEqual(setobject(b'newattr', value2), 0)
|
||||||
|
self.assertIs(sys.newattr, value2)
|
||||||
|
self.assertEqual(setobject(b'newattr', NULL), 0)
|
||||||
|
self.assertFalse(hasattr(sys, 'newattr'))
|
||||||
|
self.assertEqual(setobject(b'newattr', NULL), 0)
|
||||||
|
finally:
|
||||||
|
with contextlib.suppress(AttributeError):
|
||||||
|
del sys.newattr
|
||||||
|
try:
|
||||||
|
self.assertEqual(setobject('\U0001f40d'.encode(), value), 0)
|
||||||
|
self.assertIs(getattr(sys, '\U0001f40d'), value)
|
||||||
|
self.assertEqual(setobject('\U0001f40d'.encode(), NULL), 0)
|
||||||
|
self.assertFalse(hasattr(sys, '\U0001f40d'))
|
||||||
|
finally:
|
||||||
|
with contextlib.suppress(AttributeError):
|
||||||
|
delattr(sys, '\U0001f40d')
|
||||||
|
|
||||||
|
with self.assertRaises(UnicodeDecodeError):
|
||||||
|
setobject(b'\xff', value)
|
||||||
|
# CRASHES setobject(NULL, value)
|
||||||
|
|
||||||
|
@support.cpython_only
|
||||||
|
@unittest.skipIf(_testcapi is None, 'need _testcapi module')
|
||||||
|
def test_sys_getxoptions(self):
|
||||||
|
# Test PySys_GetXOptions()
|
||||||
|
getxoptions = _testcapi.sys_getxoptions
|
||||||
|
|
||||||
|
self.assertIs(getxoptions(), sys._xoptions)
|
||||||
|
|
||||||
|
xoptions = sys._xoptions
|
||||||
|
try:
|
||||||
|
sys._xoptions = 'non-dict'
|
||||||
|
self.assertEqual(getxoptions(), {})
|
||||||
|
self.assertIs(getxoptions(), sys._xoptions)
|
||||||
|
|
||||||
|
del sys._xoptions
|
||||||
|
self.assertEqual(getxoptions(), {})
|
||||||
|
self.assertIs(getxoptions(), sys._xoptions)
|
||||||
|
finally:
|
||||||
|
sys._xoptions = xoptions
|
||||||
|
self.assertIs(getxoptions(), sys._xoptions)
|
||||||
|
|
||||||
|
def _test_sys_formatstream(self, funname, streamname):
|
||||||
|
import_helper.import_module('ctypes')
|
||||||
|
from ctypes import pythonapi, c_char_p, py_object
|
||||||
|
func = getattr(pythonapi, funname)
|
||||||
|
func.argtypes = (c_char_p,)
|
||||||
|
|
||||||
|
# Supports plain C types.
|
||||||
|
with support.captured_output(streamname) as stream:
|
||||||
|
func(b'Hello, %s!', c_char_p(b'world'))
|
||||||
|
self.assertEqual(stream.getvalue(), 'Hello, world!')
|
||||||
|
|
||||||
|
# Supports Python objects.
|
||||||
|
with support.captured_output(streamname) as stream:
|
||||||
|
func(b'Hello, %R!', py_object('world'))
|
||||||
|
self.assertEqual(stream.getvalue(), "Hello, 'world'!")
|
||||||
|
|
||||||
|
# The total length is not limited.
|
||||||
|
with support.captured_output(streamname) as stream:
|
||||||
|
func(b'Hello, %s!', c_char_p(b'world'*200))
|
||||||
|
self.assertEqual(stream.getvalue(), 'Hello, ' + 'world'*200 + '!')
|
||||||
|
|
||||||
|
def test_sys_formatstdout(self):
|
||||||
|
# Test PySys_FormatStdout()
|
||||||
|
self._test_sys_formatstream('PySys_FormatStdout', 'stdout')
|
||||||
|
|
||||||
|
def test_sys_formatstderr(self):
|
||||||
|
# Test PySys_FormatStderr()
|
||||||
|
self._test_sys_formatstream('PySys_FormatStderr', 'stderr')
|
||||||
|
|
||||||
|
def _test_sys_writestream(self, funname, streamname):
|
||||||
|
import_helper.import_module('ctypes')
|
||||||
|
from ctypes import pythonapi, c_char_p
|
||||||
|
func = getattr(pythonapi, funname)
|
||||||
|
func.argtypes = (c_char_p,)
|
||||||
|
|
||||||
|
# Supports plain C types.
|
||||||
|
with support.captured_output(streamname) as stream:
|
||||||
|
func(b'Hello, %s!', c_char_p(b'world'))
|
||||||
|
self.assertEqual(stream.getvalue(), 'Hello, world!')
|
||||||
|
|
||||||
|
# There is a limit on the total length.
|
||||||
|
with support.captured_output(streamname) as stream:
|
||||||
|
func(b'Hello, %s!', c_char_p(b'world'*100))
|
||||||
|
self.assertEqual(stream.getvalue(), 'Hello, ' + 'world'*100 + '!')
|
||||||
|
with support.captured_output(streamname) as stream:
|
||||||
|
func(b'Hello, %s!', c_char_p(b'world'*200))
|
||||||
|
out = stream.getvalue()
|
||||||
|
self.assertEqual(out[:20], 'Hello, worldworldwor')
|
||||||
|
self.assertEqual(out[-13:], '... truncated')
|
||||||
|
self.assertGreater(len(out), 1000)
|
||||||
|
|
||||||
|
def test_sys_writestdout(self):
|
||||||
|
# Test PySys_WriteStdout()
|
||||||
|
self._test_sys_writestream('PySys_WriteStdout', 'stdout')
|
||||||
|
|
||||||
|
def test_sys_writestderr(self):
|
||||||
|
# Test PySys_WriteStderr()
|
||||||
|
self._test_sys_writestream('PySys_WriteStderr', 'stderr')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
|
@ -168,7 +168,7 @@
|
||||||
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
|
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
|
||||||
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
|
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
|
||||||
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c
|
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c
|
||||||
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyos.c _testcapi/immortal.c _testcapi/heaptype_relative.c _testcapi/gc.c
|
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyos.c _testcapi/immortal.c _testcapi/heaptype_relative.c _testcapi/gc.c _testcapi/sys.c
|
||||||
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
|
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
|
||||||
|
|
||||||
# Some testing modules MUST be built as shared libraries.
|
# Some testing modules MUST be built as shared libraries.
|
||||||
|
|
|
@ -45,6 +45,7 @@ int _PyTestCapi_Init_Buffer(PyObject *module);
|
||||||
int _PyTestCapi_Init_PyOS(PyObject *module);
|
int _PyTestCapi_Init_PyOS(PyObject *module);
|
||||||
int _PyTestCapi_Init_Immortal(PyObject *module);
|
int _PyTestCapi_Init_Immortal(PyObject *module);
|
||||||
int _PyTestCapi_Init_GC(PyObject *mod);
|
int _PyTestCapi_Init_GC(PyObject *mod);
|
||||||
|
int _PyTestCapi_Init_Sys(PyObject *);
|
||||||
|
|
||||||
#ifdef LIMITED_API_AVAILABLE
|
#ifdef LIMITED_API_AVAILABLE
|
||||||
int _PyTestCapi_Init_VectorcallLimited(PyObject *module);
|
int _PyTestCapi_Init_VectorcallLimited(PyObject *module);
|
||||||
|
|
57
Modules/_testcapi/sys.c
Normal file
57
Modules/_testcapi/sys.c
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
|
#include "parts.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
sys_getobject(PyObject *Py_UNUSED(module), PyObject *arg)
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
Py_ssize_t size;
|
||||||
|
if (!PyArg_Parse(arg, "z#", &name, &size)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyObject *result = PySys_GetObject(name);
|
||||||
|
if (result == NULL) {
|
||||||
|
result = PyExc_AttributeError;
|
||||||
|
}
|
||||||
|
return Py_NewRef(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
sys_setobject(PyObject *Py_UNUSED(module), PyObject *args)
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
Py_ssize_t size;
|
||||||
|
PyObject *value;
|
||||||
|
if (!PyArg_ParseTuple(args, "z#O", &name, &size, &value)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
NULLABLE(value);
|
||||||
|
RETURN_INT(PySys_SetObject(name, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
sys_getxoptions(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
PyObject *result = PySys_GetXOptions();
|
||||||
|
return Py_XNewRef(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyMethodDef test_methods[] = {
|
||||||
|
{"sys_getobject", sys_getobject, METH_O},
|
||||||
|
{"sys_setobject", sys_setobject, METH_VARARGS},
|
||||||
|
{"sys_getxoptions", sys_getxoptions, METH_NOARGS},
|
||||||
|
{NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyTestCapi_Init_Sys(PyObject *m)
|
||||||
|
{
|
||||||
|
if (PyModule_AddFunctions(m, test_methods) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -3243,35 +3243,6 @@ test_atexit(PyObject *self, PyObject *Py_UNUSED(args))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
sys_getobject(PyObject *Py_UNUSED(module), PyObject *arg)
|
|
||||||
{
|
|
||||||
const char *name;
|
|
||||||
Py_ssize_t size;
|
|
||||||
if (!PyArg_Parse(arg, "z#", &name, &size)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
PyObject *result = PySys_GetObject(name);
|
|
||||||
if (result == NULL) {
|
|
||||||
result = PyExc_AttributeError;
|
|
||||||
}
|
|
||||||
return Py_NewRef(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
sys_setobject(PyObject *Py_UNUSED(module), PyObject *args)
|
|
||||||
{
|
|
||||||
const char *name;
|
|
||||||
Py_ssize_t size;
|
|
||||||
PyObject *value;
|
|
||||||
if (!PyArg_ParseTuple(args, "z#O", &name, &size, &value)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
NULLABLE(value);
|
|
||||||
RETURN_INT(PySys_SetObject(name, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *);
|
static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *);
|
||||||
|
|
||||||
static PyMethodDef TestMethods[] = {
|
static PyMethodDef TestMethods[] = {
|
||||||
|
@ -3410,8 +3381,6 @@ static PyMethodDef TestMethods[] = {
|
||||||
{"function_get_kw_defaults", function_get_kw_defaults, METH_O, NULL},
|
{"function_get_kw_defaults", function_get_kw_defaults, METH_O, NULL},
|
||||||
{"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL},
|
{"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL},
|
||||||
{"test_atexit", test_atexit, METH_NOARGS},
|
{"test_atexit", test_atexit, METH_NOARGS},
|
||||||
{"sys_getobject", sys_getobject, METH_O},
|
|
||||||
{"sys_setobject", sys_setobject, METH_VARARGS},
|
|
||||||
{NULL, NULL} /* sentinel */
|
{NULL, NULL} /* sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4058,6 +4027,9 @@ PyInit__testcapi(void)
|
||||||
if (_PyTestCapi_Init_PyOS(m) < 0) {
|
if (_PyTestCapi_Init_PyOS(m) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (_PyTestCapi_Init_Sys(m) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
if (_PyTestCapi_Init_Immortal(m) < 0) {
|
if (_PyTestCapi_Init_Immortal(m) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,7 @@
|
||||||
<ClCompile Include="..\Modules\_testcapi\code.c" />
|
<ClCompile Include="..\Modules\_testcapi\code.c" />
|
||||||
<ClCompile Include="..\Modules\_testcapi\buffer.c" />
|
<ClCompile Include="..\Modules\_testcapi\buffer.c" />
|
||||||
<ClCompile Include="..\Modules\_testcapi\pyos.c" />
|
<ClCompile Include="..\Modules\_testcapi\pyos.c" />
|
||||||
|
<ClCompile Include="..\Modules\_testcapi\sys.c" />
|
||||||
<ClCompile Include="..\Modules\_testcapi\immortal.c" />
|
<ClCompile Include="..\Modules\_testcapi\immortal.c" />
|
||||||
<ClCompile Include="..\Modules\_testcapi\gc.c" />
|
<ClCompile Include="..\Modules\_testcapi\gc.c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -72,6 +72,9 @@
|
||||||
<ClCompile Include="..\Modules\_testcapi\pyos.c">
|
<ClCompile Include="..\Modules\_testcapi\pyos.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\Modules\_testcapi\sys.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\Modules\_testcapi\gc.c">
|
<ClCompile Include="..\Modules\_testcapi\gc.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue