Skip to content

Commit 6cfb61f

Browse files
Issue #23392: Added tests for marshal C API that works with FILE*.
2 parents 313ee59 + b518134 commit 6cfb61f

File tree

3 files changed

+255
-12
lines changed

3 files changed

+255
-12
lines changed

Lib/test/test_marshal.py

Lines changed: 87 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
import os
88
import types
99

10+
try:
11+
import _testcapi
12+
except ImportError:
13+
_testcapi = None
14+
1015
class HelperMixin:
1116
def helper(self, sample, *extra):
1217
new = marshal.loads(marshal.dumps(sample, *extra))
@@ -434,18 +439,88 @@ def testNoIntern(self):
434439
s2 = sys.intern(s)
435440
self.assertNotEqual(id(s2), id(s))
436441

442+
@support.cpython_only
443+
@unittest.skipUnless(_testcapi, 'requires _testcapi')
444+
class CAPI_TestCase(unittest.TestCase, HelperMixin):
445+
446+
def test_write_long_to_file(self):
447+
for v in range(marshal.version + 1):
448+
_testcapi.pymarshal_write_long_to_file(0x12345678, support.TESTFN, v)
449+
with open(support.TESTFN, 'rb') as f:
450+
data = f.read()
451+
support.unlink(support.TESTFN)
452+
self.assertEqual(data, b'\x78\x56\x34\x12')
453+
454+
def test_write_object_to_file(self):
455+
obj = ('\u20ac', b'abc', 123, 45.6, 7+8j, 'long line '*1000)
456+
for v in range(marshal.version + 1):
457+
_testcapi.pymarshal_write_object_to_file(obj, support.TESTFN, v)
458+
with open(support.TESTFN, 'rb') as f:
459+
data = f.read()
460+
support.unlink(support.TESTFN)
461+
self.assertEqual(marshal.loads(data), obj)
462+
463+
def test_read_short_from_file(self):
464+
with open(support.TESTFN, 'wb') as f:
465+
f.write(b'\x34\x12xxxx')
466+
r, p = _testcapi.pymarshal_read_short_from_file(support.TESTFN)
467+
support.unlink(support.TESTFN)
468+
self.assertEqual(r, 0x1234)
469+
self.assertEqual(p, 2)
470+
471+
with open(support.TESTFN, 'wb') as f:
472+
f.write(b'\x12')
473+
with self.assertRaises(EOFError):
474+
_testcapi.pymarshal_read_short_from_file(support.TESTFN)
475+
support.unlink(support.TESTFN)
476+
477+
def test_read_long_from_file(self):
478+
with open(support.TESTFN, 'wb') as f:
479+
f.write(b'\x78\x56\x34\x12xxxx')
480+
r, p = _testcapi.pymarshal_read_long_from_file(support.TESTFN)
481+
support.unlink(support.TESTFN)
482+
self.assertEqual(r, 0x12345678)
483+
self.assertEqual(p, 4)
484+
485+
with open(support.TESTFN, 'wb') as f:
486+
f.write(b'\x56\x34\x12')
487+
with self.assertRaises(EOFError):
488+
_testcapi.pymarshal_read_long_from_file(support.TESTFN)
489+
support.unlink(support.TESTFN)
490+
491+
def test_read_last_object_from_file(self):
492+
obj = ('\u20ac', b'abc', 123, 45.6, 7+8j)
493+
for v in range(marshal.version + 1):
494+
data = marshal.dumps(obj, v)
495+
with open(support.TESTFN, 'wb') as f:
496+
f.write(data + b'xxxx')
497+
r, p = _testcapi.pymarshal_read_last_object_from_file(support.TESTFN)
498+
support.unlink(support.TESTFN)
499+
self.assertEqual(r, obj)
500+
501+
with open(support.TESTFN, 'wb') as f:
502+
f.write(data[:1])
503+
with self.assertRaises(EOFError):
504+
_testcapi.pymarshal_read_last_object_from_file(support.TESTFN)
505+
support.unlink(support.TESTFN)
506+
507+
def test_read_object_from_file(self):
508+
obj = ('\u20ac', b'abc', 123, 45.6, 7+8j)
509+
for v in range(marshal.version + 1):
510+
data = marshal.dumps(obj, v)
511+
with open(support.TESTFN, 'wb') as f:
512+
f.write(data + b'xxxx')
513+
r, p = _testcapi.pymarshal_read_object_from_file(support.TESTFN)
514+
support.unlink(support.TESTFN)
515+
self.assertEqual(r, obj)
516+
self.assertEqual(p, len(data))
517+
518+
with open(support.TESTFN, 'wb') as f:
519+
f.write(data[:1])
520+
with self.assertRaises(EOFError):
521+
_testcapi.pymarshal_read_object_from_file(support.TESTFN)
522+
support.unlink(support.TESTFN)
437523

438-
def test_main():
439-
support.run_unittest(IntTestCase,
440-
FloatTestCase,
441-
StringTestCase,
442-
CodeTestCase,
443-
ContainerTestCase,
444-
ExceptionTestCase,
445-
BufferTestCase,
446-
BugsTestCase,
447-
LargeValuesTestCase,
448-
)
449524

450525
if __name__ == "__main__":
451-
test_main()
526+
unittest.main()

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,8 @@ Library
613613
- Issue #17442: InteractiveInterpreter now displays the full chained traceback
614614
in its showtraceback method, to match the built in interactive interpreter.
615615

616+
- Issue #23392: Added tests for marshal C API that works with FILE*.
617+
616618
- Issue #10510: distutils register and upload methods now use HTML standards
617619
compliant CRLF line endings.
618620

Modules/_testcapimodule.c

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <float.h>
1212
#include "structmember.h"
1313
#include "datetime.h"
14+
#include "marshal.h"
1415
#include <signal.h>
1516

1617
#ifdef WITH_THREAD
@@ -3199,6 +3200,159 @@ test_raise_signal(PyObject* self, PyObject *args)
31993200
Py_RETURN_NONE;
32003201
}
32013202

3203+
/* marshal */
3204+
3205+
static PyObject*
3206+
pymarshal_write_long_to_file(PyObject* self, PyObject *args)
3207+
{
3208+
long value;
3209+
char *filename;
3210+
int version;
3211+
FILE *fp;
3212+
3213+
if (!PyArg_ParseTuple(args, "lsi:pymarshal_write_long_to_file",
3214+
&value, &filename, &version))
3215+
return NULL;
3216+
3217+
fp = fopen(filename, "wb");
3218+
if (fp == NULL) {
3219+
PyErr_SetFromErrno(PyExc_OSError);
3220+
return NULL;
3221+
}
3222+
3223+
PyMarshal_WriteLongToFile(value, fp, version);
3224+
3225+
fclose(fp);
3226+
if (PyErr_Occurred())
3227+
return NULL;
3228+
Py_RETURN_NONE;
3229+
}
3230+
3231+
static PyObject*
3232+
pymarshal_write_object_to_file(PyObject* self, PyObject *args)
3233+
{
3234+
PyObject *obj;
3235+
char *filename;
3236+
int version;
3237+
FILE *fp;
3238+
3239+
if (!PyArg_ParseTuple(args, "Osi:pymarshal_write_object_to_file",
3240+
&obj, &filename, &version))
3241+
return NULL;
3242+
3243+
fp = fopen(filename, "wb");
3244+
if (fp == NULL) {
3245+
PyErr_SetFromErrno(PyExc_OSError);
3246+
return NULL;
3247+
}
3248+
3249+
PyMarshal_WriteObjectToFile(obj, fp, version);
3250+
3251+
fclose(fp);
3252+
if (PyErr_Occurred())
3253+
return NULL;
3254+
Py_RETURN_NONE;
3255+
}
3256+
3257+
static PyObject*
3258+
pymarshal_read_short_from_file(PyObject* self, PyObject *args)
3259+
{
3260+
int value;
3261+
long pos;
3262+
char *filename;
3263+
FILE *fp;
3264+
3265+
if (!PyArg_ParseTuple(args, "s:pymarshal_read_short_from_file", &filename))
3266+
return NULL;
3267+
3268+
fp = fopen(filename, "rb");
3269+
if (fp == NULL) {
3270+
PyErr_SetFromErrno(PyExc_OSError);
3271+
return NULL;
3272+
}
3273+
3274+
value = PyMarshal_ReadShortFromFile(fp);
3275+
pos = ftell(fp);
3276+
3277+
fclose(fp);
3278+
if (PyErr_Occurred())
3279+
return NULL;
3280+
return Py_BuildValue("il", value, pos);
3281+
}
3282+
3283+
static PyObject*
3284+
pymarshal_read_long_from_file(PyObject* self, PyObject *args)
3285+
{
3286+
long value, pos;
3287+
char *filename;
3288+
FILE *fp;
3289+
3290+
if (!PyArg_ParseTuple(args, "s:pymarshal_read_long_from_file", &filename))
3291+
return NULL;
3292+
3293+
fp = fopen(filename, "rb");
3294+
if (fp == NULL) {
3295+
PyErr_SetFromErrno(PyExc_OSError);
3296+
return NULL;
3297+
}
3298+
3299+
value = PyMarshal_ReadLongFromFile(fp);
3300+
pos = ftell(fp);
3301+
3302+
fclose(fp);
3303+
if (PyErr_Occurred())
3304+
return NULL;
3305+
return Py_BuildValue("ll", value, pos);
3306+
}
3307+
3308+
static PyObject*
3309+
pymarshal_read_last_object_from_file(PyObject* self, PyObject *args)
3310+
{
3311+
PyObject *obj;
3312+
long pos;
3313+
char *filename;
3314+
FILE *fp;
3315+
3316+
if (!PyArg_ParseTuple(args, "s:pymarshal_read_last_object_from_file", &filename))
3317+
return NULL;
3318+
3319+
fp = fopen(filename, "rb");
3320+
if (fp == NULL) {
3321+
PyErr_SetFromErrno(PyExc_OSError);
3322+
return NULL;
3323+
}
3324+
3325+
obj = PyMarshal_ReadLastObjectFromFile(fp);
3326+
pos = ftell(fp);
3327+
3328+
fclose(fp);
3329+
return Py_BuildValue("Nl", obj, pos);
3330+
}
3331+
3332+
static PyObject*
3333+
pymarshal_read_object_from_file(PyObject* self, PyObject *args)
3334+
{
3335+
PyObject *obj;
3336+
long pos;
3337+
char *filename;
3338+
FILE *fp;
3339+
3340+
if (!PyArg_ParseTuple(args, "s:pymarshal_read_object_from_file", &filename))
3341+
return NULL;
3342+
3343+
fp = fopen(filename, "rb");
3344+
if (fp == NULL) {
3345+
PyErr_SetFromErrno(PyExc_OSError);
3346+
return NULL;
3347+
}
3348+
3349+
obj = PyMarshal_ReadObjectFromFile(fp);
3350+
pos = ftell(fp);
3351+
3352+
fclose(fp);
3353+
return Py_BuildValue("Nl", obj, pos);
3354+
}
3355+
32023356

32033357
static PyMethodDef TestMethods[] = {
32043358
{"raise_exception", raise_exception, METH_VARARGS},
@@ -3346,6 +3500,18 @@ static PyMethodDef TestMethods[] = {
33463500
{"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_O,
33473501
PyDoc_STR("set_error_class(error_class) -> None")},
33483502
#endif
3503+
{"pymarshal_write_long_to_file",
3504+
pymarshal_write_long_to_file, METH_VARARGS},
3505+
{"pymarshal_write_object_to_file",
3506+
pymarshal_write_object_to_file, METH_VARARGS},
3507+
{"pymarshal_read_short_from_file",
3508+
pymarshal_read_short_from_file, METH_VARARGS},
3509+
{"pymarshal_read_long_from_file",
3510+
pymarshal_read_long_from_file, METH_VARARGS},
3511+
{"pymarshal_read_last_object_from_file",
3512+
pymarshal_read_last_object_from_file, METH_VARARGS},
3513+
{"pymarshal_read_object_from_file",
3514+
pymarshal_read_object_from_file, METH_VARARGS},
33493515
{NULL, NULL} /* sentinel */
33503516
};
33513517

0 commit comments

Comments
 (0)