mirror of https://github.com/python/cpython
gh-94808:Improve coverage of PyObject_Print (GH-98749)
This commit is contained in:
parent
348cf6e007
commit
90c3c68a65
|
@ -1,8 +1,10 @@
|
|||
import enum
|
||||
import unittest
|
||||
from test.support import import_helper
|
||||
from test.support import os_helper
|
||||
|
||||
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
|
||||
_testcapi = import_helper.import_module('_testcapi')
|
||||
|
||||
|
||||
class Constant(enum.IntEnum):
|
||||
|
@ -20,7 +22,7 @@ class Constant(enum.IntEnum):
|
|||
INVALID_CONSTANT = Py_CONSTANT_EMPTY_TUPLE + 1
|
||||
|
||||
|
||||
class CAPITest(unittest.TestCase):
|
||||
class GetConstantTest(unittest.TestCase):
|
||||
def check_get_constant(self, get_constant):
|
||||
self.assertIs(get_constant(Constant.Py_CONSTANT_NONE), None)
|
||||
self.assertIs(get_constant(Constant.Py_CONSTANT_FALSE), False)
|
||||
|
@ -50,5 +52,56 @@ class CAPITest(unittest.TestCase):
|
|||
self.check_get_constant(_testlimitedcapi.get_constant_borrowed)
|
||||
|
||||
|
||||
class PrintTest(unittest.TestCase):
|
||||
def testPyObjectPrintObject(self):
|
||||
|
||||
class PrintableObject:
|
||||
|
||||
def __repr__(self):
|
||||
return "spam spam spam"
|
||||
|
||||
def __str__(self):
|
||||
return "egg egg egg"
|
||||
|
||||
obj = PrintableObject()
|
||||
output_filename = os_helper.TESTFN
|
||||
self.addCleanup(os_helper.unlink, output_filename)
|
||||
|
||||
# Test repr printing
|
||||
_testcapi.call_pyobject_print(obj, output_filename, False)
|
||||
with open(output_filename, 'r') as output_file:
|
||||
self.assertEqual(output_file.read(), repr(obj))
|
||||
|
||||
# Test str printing
|
||||
_testcapi.call_pyobject_print(obj, output_filename, True)
|
||||
with open(output_filename, 'r') as output_file:
|
||||
self.assertEqual(output_file.read(), str(obj))
|
||||
|
||||
def testPyObjectPrintNULL(self):
|
||||
output_filename = os_helper.TESTFN
|
||||
self.addCleanup(os_helper.unlink, output_filename)
|
||||
|
||||
# Test repr printing
|
||||
_testcapi.pyobject_print_null(output_filename)
|
||||
with open(output_filename, 'r') as output_file:
|
||||
self.assertEqual(output_file.read(), '<nil>')
|
||||
|
||||
def testPyObjectPrintNoRefObject(self):
|
||||
output_filename = os_helper.TESTFN
|
||||
self.addCleanup(os_helper.unlink, output_filename)
|
||||
|
||||
# Test repr printing
|
||||
correct_output = _testcapi.pyobject_print_noref_object(output_filename)
|
||||
with open(output_filename, 'r') as output_file:
|
||||
self.assertEqual(output_file.read(), correct_output)
|
||||
|
||||
def testPyObjectPrintOSError(self):
|
||||
output_filename = os_helper.TESTFN
|
||||
self.addCleanup(os_helper.unlink, output_filename)
|
||||
|
||||
open(output_filename, "w+").close()
|
||||
with self.assertRaises(OSError):
|
||||
_testcapi.pyobject_print_os_error(output_filename)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
@ -788,5 +788,6 @@ class ClassTests(unittest.TestCase):
|
|||
Type(i)
|
||||
self.assertEqual(calls, 100)
|
||||
|
||||
|
||||
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
|
||||
@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__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
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
#include "parts.h"
|
||||
#include "util.h"
|
||||
|
||||
static PyObject *
|
||||
call_pyobject_print(PyObject *self, PyObject * args)
|
||||
{
|
||||
PyObject *object;
|
||||
PyObject *filename;
|
||||
PyObject *print_raw;
|
||||
FILE *fp;
|
||||
int flags = 0;
|
||||
|
||||
if (!PyArg_UnpackTuple(args, "call_pyobject_print", 3, 3,
|
||||
&object, &filename, &print_raw)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fp = _Py_fopen_obj(filename, "w+");
|
||||
|
||||
if (Py_IsTrue(print_raw)) {
|
||||
flags = Py_PRINT_RAW;
|
||||
}
|
||||
|
||||
if (PyObject_Print(object, fp, flags) < 0) {
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
pyobject_print_null(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *filename;
|
||||
FILE *fp;
|
||||
|
||||
if (!PyArg_UnpackTuple(args, "call_pyobject_print", 1, 1, &filename)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fp = _Py_fopen_obj(filename, "w+");
|
||||
|
||||
if (PyObject_Print(NULL, fp, 0) < 0) {
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
pyobject_print_noref_object(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *test_string;
|
||||
PyObject *filename;
|
||||
FILE *fp;
|
||||
char correct_string[100];
|
||||
|
||||
test_string = PyUnicode_FromString("Spam spam spam");
|
||||
|
||||
Py_SET_REFCNT(test_string, 0);
|
||||
|
||||
PyOS_snprintf(correct_string, 100, "<refcnt %zd at %p>",
|
||||
Py_REFCNT(test_string), (void *)test_string);
|
||||
|
||||
if (!PyArg_UnpackTuple(args, "call_pyobject_print", 1, 1, &filename)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fp = _Py_fopen_obj(filename, "w+");
|
||||
|
||||
if (PyObject_Print(test_string, fp, 0) < 0){
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
Py_SET_REFCNT(test_string, 1);
|
||||
Py_DECREF(test_string);
|
||||
|
||||
return PyUnicode_FromString(correct_string);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
pyobject_print_os_error(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *test_string;
|
||||
PyObject *filename;
|
||||
FILE *fp;
|
||||
|
||||
test_string = PyUnicode_FromString("Spam spam spam");
|
||||
|
||||
if (!PyArg_UnpackTuple(args, "call_pyobject_print", 1, 1, &filename)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// open file in read mode to induce OSError
|
||||
fp = _Py_fopen_obj(filename, "r");
|
||||
|
||||
if (PyObject_Print(test_string, fp, 0) < 0) {
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyMethodDef test_methods[] = {
|
||||
{"call_pyobject_print", call_pyobject_print, METH_VARARGS},
|
||||
{"pyobject_print_null", pyobject_print_null, METH_VARARGS},
|
||||
{"pyobject_print_noref_object", pyobject_print_noref_object, METH_VARARGS},
|
||||
{"pyobject_print_os_error", pyobject_print_os_error, METH_VARARGS},
|
||||
|
||||
{NULL},
|
||||
};
|
||||
|
||||
int
|
||||
_PyTestCapi_Init_Object(PyObject *m)
|
||||
{
|
||||
if (PyModule_AddFunctions(m, test_methods) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -57,5 +57,6 @@ int _PyTestCapi_Init_Immortal(PyObject *module);
|
|||
int _PyTestCapi_Init_GC(PyObject *module);
|
||||
int _PyTestCapi_Init_Hash(PyObject *module);
|
||||
int _PyTestCapi_Init_Time(PyObject *module);
|
||||
int _PyTestCapi_Init_Object(PyObject *module);
|
||||
|
||||
#endif // Py_TESTCAPI_PARTS_H
|
||||
|
|
|
@ -4049,6 +4049,9 @@ PyInit__testcapi(void)
|
|||
if (_PyTestCapi_Init_Time(m) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (_PyTestCapi_Init_Object(m) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyState_AddModule(m, &_testcapimodule);
|
||||
return m;
|
||||
|
|
|
@ -121,6 +121,7 @@
|
|||
<ClCompile Include="..\Modules\_testcapi\codec.c" />
|
||||
<ClCompile Include="..\Modules\_testcapi\hash.c" />
|
||||
<ClCompile Include="..\Modules\_testcapi\time.c" />
|
||||
<ClCompile Include="..\Modules\_testcapi\object.c" />
|
||||
<ClCompile Include="..\Modules\_testcapi\immortal.c" />
|
||||
<ClCompile Include="..\Modules\_testcapi\gc.c" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -99,6 +99,9 @@
|
|||
<ClCompile Include="..\Modules\_testcapi\time.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Modules\_testcapi\object.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Modules\_testcapi\gc.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
|
Loading…
Reference in New Issue