diff --git a/Doc/c-api/marshal.rst b/Doc/c-api/marshal.rst index 489f1580a414b2..dedf33b816b65e 100644 --- a/Doc/c-api/marshal.rst +++ b/Doc/c-api/marshal.rst @@ -19,22 +19,26 @@ unmarshalling. Version 2 uses a binary format for floating point numbers. ``Py_MARSHAL_VERSION`` indicates the current file format (currently 2). -.. c:function:: void PyMarshal_WriteLongToFile(long value, FILE *file, int version) +.. c:function:: int PyMarshal_WriteLongToFile(long value, FILE *file, int version) Marshal a :c:expr:`long` integer, *value*, to *file*. This will only write the least-significant 32 bits of *value*; regardless of the size of the native :c:expr:`long` type. *version* indicates the file format. - This function can fail, in which case it sets the error indicator. - Use :c:func:`PyErr_Occurred` to check for that. + Return 0 on success. Return -1 and set an exception on error. -.. c:function:: void PyMarshal_WriteObjectToFile(PyObject *value, FILE *file, int version) + .. versionchanged:: 3.13 + Added return value. + +.. c:function:: int PyMarshal_WriteObjectToFile(PyObject *value, FILE *file, int version) Marshal a Python object, *value*, to *file*. *version* indicates the file format. - This function can fail, in which case it sets the error indicator. - Use :c:func:`PyErr_Occurred` to check for that. + Return 0 on success. Return -1 and set an exception on error. + + .. versionchanged:: 3.13 + Added return value. .. c:function:: PyObject* PyMarshal_WriteObjectToString(PyObject *value, int version) diff --git a/Include/marshal.h b/Include/marshal.h index f8b0de80cfc38d..4ea850cc85f366 100644 --- a/Include/marshal.h +++ b/Include/marshal.h @@ -20,8 +20,8 @@ PyAPI_FUNC(int) PyMarshal_ReadShortFromFile(FILE *); PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromFile(FILE *); PyAPI_FUNC(PyObject *) PyMarshal_ReadLastObjectFromFile(FILE *); -PyAPI_FUNC(void) PyMarshal_WriteLongToFile(long, FILE *, int); -PyAPI_FUNC(void) PyMarshal_WriteObjectToFile(PyObject *, FILE *, int); +PyAPI_FUNC(int) PyMarshal_WriteLongToFile(long, FILE *, int); +PyAPI_FUNC(int) PyMarshal_WriteObjectToFile(PyObject *, FILE *, int); #ifdef __cplusplus } diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-06-02-12-02-42.gh-issue-105184.M4yTzg.rst b/Misc/NEWS.d/next/Core and Builtins/2023-06-02-12-02-42.gh-issue-105184.M4yTzg.rst new file mode 100644 index 00000000000000..fe3e929b99de8b --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-06-02-12-02-42.gh-issue-105184.M4yTzg.rst @@ -0,0 +1,3 @@ +Add return value indicating success/failure to +:c:func:`PyMarshal_WriteLongToFile` and +:c:func:`PyMarshal_WriteObjectToFile`. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 38c2d1a91af31c..3c48bd7d8c674c 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1807,7 +1807,8 @@ pymarshal_write_long_to_file(PyObject* self, PyObject *args) return NULL; } - PyMarshal_WriteLongToFile(value, fp, version); + int ret = PyMarshal_WriteLongToFile(value, fp, version); + assert(ret == 0); assert(!PyErr_Occurred()); fclose(fp); @@ -1832,7 +1833,8 @@ pymarshal_write_object_to_file(PyObject* self, PyObject *args) return NULL; } - PyMarshal_WriteObjectToFile(obj, fp, version); + int ret = PyMarshal_WriteObjectToFile(obj, fp, version); + assert(ret == 0); assert(!PyErr_Occurred()); fclose(fp); diff --git a/Python/marshal.c b/Python/marshal.c index 6439503d2c6879..c6a1b0564e405b 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -625,11 +625,7 @@ w_clear_refs(WFILE *wf) } /* version currently has no effect for writing ints. */ -/* Note that while the documentation states that this function - * can error, currently it never does. Setting an exception in - * this function should be regarded as an API-breaking change. - */ -void +int PyMarshal_WriteLongToFile(long x, FILE *fp, int version) { char buf[4]; @@ -642,15 +638,23 @@ PyMarshal_WriteLongToFile(long x, FILE *fp, int version) wf.version = version; w_long(x, &wf); w_flush(&wf); + + /* While the documentation states that this function can error, + * currently it never does. Setting an exception in this function + * should be regarded as an API-breaking change. + */ + assert(!PyErr_Occurred()); + + return 0; } -void +int PyMarshal_WriteObjectToFile(PyObject *x, FILE *fp, int version) { char buf[BUFSIZ]; WFILE wf; if (PySys_Audit("marshal.dumps", "Oi", x, version) < 0) { - return; /* caller must check PyErr_Occurred() */ + return -1; } memset(&wf, 0, sizeof(wf)); wf.fp = fp; @@ -658,12 +662,13 @@ PyMarshal_WriteObjectToFile(PyObject *x, FILE *fp, int version) wf.end = wf.ptr + sizeof(buf); wf.error = WFERR_OK; wf.version = version; - if (w_init_refs(&wf, version)) { - return; /* caller must check PyErr_Occurred() */ + if (w_init_refs(&wf, version) < 0) { + return -1; } w_object(x, &wf); w_clear_refs(&wf); w_flush(&wf); + return 0; } typedef struct { @@ -1612,6 +1617,7 @@ PyMarshal_ReadObjectFromFile(FILE *fp) Py_DECREF(rf.refs); if (rf.buf != NULL) PyMem_Free(rf.buf); + assert((result != NULL) == !PyErr_Occurred()); return result; } @@ -1652,7 +1658,7 @@ PyMarshal_WriteObjectToString(PyObject *x, int version) wf.end = wf.ptr + PyBytes_GET_SIZE(wf.str); wf.error = WFERR_OK; wf.version = version; - if (w_init_refs(&wf, version)) { + if (w_init_refs(&wf, version) < 0) { Py_DECREF(wf.str); return NULL; }