Skip to content

Commit b518134

Browse files
Issue #23392: Added tests for marshal C API that works with FILE*.
1 parent 17d337b commit b518134

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
@@ -326,6 +326,8 @@ IDLE
326326
Tests
327327
-----
328328

329+
- Issue #23392: Added tests for marshal C API that works with FILE*.
330+
329331
- Issue #18982: Add tests for CLI of the calendar module.
330332

331333
- Issue #19548: Added some additional checks to test_codecs to ensure that

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

1516
#ifdef WITH_THREAD
1617
#include "pythread.h"
@@ -3050,6 +3051,159 @@ call_in_temporary_c_thread(PyObject *self, PyObject *callback)
30503051
}
30513052
#endif /* WITH_THREAD */
30523053

3054+
/* marshal */
3055+
3056+
static PyObject*
3057+
pymarshal_write_long_to_file(PyObject* self, PyObject *args)
3058+
{
3059+
long value;
3060+
char *filename;
3061+
int version;
3062+
FILE *fp;
3063+
3064+
if (!PyArg_ParseTuple(args, "lsi:pymarshal_write_long_to_file",
3065+
&value, &filename, &version))
3066+
return NULL;
3067+
3068+
fp = fopen(filename, "wb");
3069+
if (fp == NULL) {
3070+
PyErr_SetFromErrno(PyExc_OSError);
3071+
return NULL;
3072+
}
3073+
3074+
PyMarshal_WriteLongToFile(value, fp, version);
3075+
3076+
fclose(fp);
3077+
if (PyErr_Occurred())
3078+
return NULL;
3079+
Py_RETURN_NONE;
3080+
}
3081+
3082+
static PyObject*
3083+
pymarshal_write_object_to_file(PyObject* self, PyObject *args)
3084+
{
3085+
PyObject *obj;
3086+
char *filename;
3087+
int version;
3088+
FILE *fp;
3089+
3090+
if (!PyArg_ParseTuple(args, "Osi:pymarshal_write_object_to_file",
3091+
&obj, &filename, &version))
3092+
return NULL;
3093+
3094+
fp = fopen(filename, "wb");
3095+
if (fp == NULL) {
3096+
PyErr_SetFromErrno(PyExc_OSError);
3097+
return NULL;
3098+
}
3099+
3100+
PyMarshal_WriteObjectToFile(obj, fp, version);
3101+
3102+
fclose(fp);
3103+
if (PyErr_Occurred())
3104+
return NULL;
3105+
Py_RETURN_NONE;
3106+
}
3107+
3108+
static PyObject*
3109+
pymarshal_read_short_from_file(PyObject* self, PyObject *args)
3110+
{
3111+
int value;
3112+
long pos;
3113+
char *filename;
3114+
FILE *fp;
3115+
3116+
if (!PyArg_ParseTuple(args, "s:pymarshal_read_short_from_file", &filename))
3117+
return NULL;
3118+
3119+
fp = fopen(filename, "rb");
3120+
if (fp == NULL) {
3121+
PyErr_SetFromErrno(PyExc_OSError);
3122+
return NULL;
3123+
}
3124+
3125+
value = PyMarshal_ReadShortFromFile(fp);
3126+
pos = ftell(fp);
3127+
3128+
fclose(fp);
3129+
if (PyErr_Occurred())
3130+
return NULL;
3131+
return Py_BuildValue("il", value, pos);
3132+
}
3133+
3134+
static PyObject*
3135+
pymarshal_read_long_from_file(PyObject* self, PyObject *args)
3136+
{
3137+
long value, pos;
3138+
char *filename;
3139+
FILE *fp;
3140+
3141+
if (!PyArg_ParseTuple(args, "s:pymarshal_read_long_from_file", &filename))
3142+
return NULL;
3143+
3144+
fp = fopen(filename, "rb");
3145+
if (fp == NULL) {
3146+
PyErr_SetFromErrno(PyExc_OSError);
3147+
return NULL;
3148+
}
3149+
3150+
value = PyMarshal_ReadLongFromFile(fp);
3151+
pos = ftell(fp);
3152+
3153+
fclose(fp);
3154+
if (PyErr_Occurred())
3155+
return NULL;
3156+
return Py_BuildValue("ll", value, pos);
3157+
}
3158+
3159+
static PyObject*
3160+
pymarshal_read_last_object_from_file(PyObject* self, PyObject *args)
3161+
{
3162+
PyObject *obj;
3163+
long pos;
3164+
char *filename;
3165+
FILE *fp;
3166+
3167+
if (!PyArg_ParseTuple(args, "s:pymarshal_read_last_object_from_file", &filename))
3168+
return NULL;
3169+
3170+
fp = fopen(filename, "rb");
3171+
if (fp == NULL) {
3172+
PyErr_SetFromErrno(PyExc_OSError);
3173+
return NULL;
3174+
}
3175+
3176+
obj = PyMarshal_ReadLastObjectFromFile(fp);
3177+
pos = ftell(fp);
3178+
3179+
fclose(fp);
3180+
return Py_BuildValue("Nl", obj, pos);
3181+
}
3182+
3183+
static PyObject*
3184+
pymarshal_read_object_from_file(PyObject* self, PyObject *args)
3185+
{
3186+
PyObject *obj;
3187+
long pos;
3188+
char *filename;
3189+
FILE *fp;
3190+
3191+
if (!PyArg_ParseTuple(args, "s:pymarshal_read_object_from_file", &filename))
3192+
return NULL;
3193+
3194+
fp = fopen(filename, "rb");
3195+
if (fp == NULL) {
3196+
PyErr_SetFromErrno(PyExc_OSError);
3197+
return NULL;
3198+
}
3199+
3200+
obj = PyMarshal_ReadObjectFromFile(fp);
3201+
pos = ftell(fp);
3202+
3203+
fclose(fp);
3204+
return Py_BuildValue("Nl", obj, pos);
3205+
}
3206+
30533207

30543208
static PyMethodDef TestMethods[] = {
30553209
{"raise_exception", raise_exception, METH_VARARGS},
@@ -3190,6 +3344,18 @@ static PyMethodDef TestMethods[] = {
31903344
{"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_O,
31913345
PyDoc_STR("set_error_class(error_class) -> None")},
31923346
#endif
3347+
{"pymarshal_write_long_to_file",
3348+
pymarshal_write_long_to_file, METH_VARARGS},
3349+
{"pymarshal_write_object_to_file",
3350+
pymarshal_write_object_to_file, METH_VARARGS},
3351+
{"pymarshal_read_short_from_file",
3352+
pymarshal_read_short_from_file, METH_VARARGS},
3353+
{"pymarshal_read_long_from_file",
3354+
pymarshal_read_long_from_file, METH_VARARGS},
3355+
{"pymarshal_read_last_object_from_file",
3356+
pymarshal_read_last_object_from_file, METH_VARARGS},
3357+
{"pymarshal_read_object_from_file",
3358+
pymarshal_read_object_from_file, METH_VARARGS},
31933359
{NULL, NULL} /* sentinel */
31943360
};
31953361

0 commit comments

Comments
 (0)