gh-155907: Raise OSError and KeyboardInterrupt when reading marshal data - #155909
Conversation
…shal data Reading marshalled data from a FILE* did not check ferror() nor signals, so an I/O error or a Ctrl-C was reported as EOFError. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Documentation build overview
77 files changed ·
|
|
Failure on Windows is expected -- this is a bug #155905. |
PyMarshal_WriteObjectToFile() and PyMarshal_WriteLongToFile() now set the error indicator when writing to the underlying FILE* fails or is interrupted by a signal, instead of ignoring the failure. PyMarshal_WriteObjectToFile() now also sets the error indicator when the value cannot be marshalled. r_byte() no longer replaces an exception raised by a signal handler with EOFError. Document which exceptions the marshalling functions can raise. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| Previously, in functions taking a :c:expr:`FILE*`, | ||
| the reading functions raised :exc:`EOFError` | ||
| instead of :exc:`OSError` and :exc:`KeyboardInterrupt`, | ||
| and the writing functions ignored I/O errors and interruptions. |
There was a problem hiding this comment.
marshal.load() and marshal.dump() error handling also changes and should be documented in Doc/library/marshal.rst.
There was a problem hiding this comment.
No, they are not affected. They do not use FILE* based C API.
There was a problem hiding this comment.
I checked again your change, you're right and I'm wrong. marshal.load() and marshal.dump() are not affected.
Preserve errno across PyErr_CheckSignals() when reporting a read error, rename w_set_error() to w_set_exception(), handle a PyNumber_AsSsize_t() failure explicitly, and check the error message in the test. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vstinner
left a comment
There was a problem hiding this comment.
LGTM. I just suggest renaming WFERR_ERROR_SET to WFERR_EXCEPTION_SET.
| #define WFERR_NESTEDTOODEEP 2 | ||
| #define WFERR_NOMEMORY 3 | ||
| #define WFERR_CODE_NOT_ALLOWED 4 | ||
| #define WFERR_ERROR_SET 5 /* An exception has already been raised. */ |
There was a problem hiding this comment.
You should rename the constant WFERR_EXCEPTION_SET since it's an "exception set", not an "error set".
| Previously, in functions taking a :c:expr:`FILE*`, | ||
| the reading functions raised :exc:`EOFError` | ||
| instead of :exc:`OSError` and :exc:`KeyboardInterrupt`, | ||
| and the writing functions ignored I/O errors and interruptions. |
There was a problem hiding this comment.
I checked again your change, you're right and I'm wrong. marshal.load() and marshal.dump() are not affected.
I don't think that it's a good idea to backport this change. Some projects can rely on the current exact exception raised by these C functions. It's ok to change them in Python 3.16, but IMO it's too risky to backport the change to stable versions. |
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Thank you for your review. Initially it was smaller change, but it grew more after discovering more silenced/overridden errors. I plannet to fix the writing part in a separate issue, but is better for documentation if fix them together. |
r_string(),r_byte()andPyMarshal_ReadLastObjectFromFile()did not checkferror()nor callPyErr_CheckSignals(), so a genuine I/O error or a read interrupted by Ctrl-C was reported asEOFError, as if the file had simply ended.The writing side had the same gap:
w_flush()andw_string()discarded the result offwrite(), so a failed or interrupted write to aFILE*was ignored.PyMarshal_WriteObjectToFile()also never turned the recordedWFERR_*code into an exception, so even an unmarshallable value was ignored.The
WFERR_*to exception mapping is factored out ofPyMarshal_WriteObjectToString()intow_set_error(), and all three writers now use it. This removes the note abovePyMarshal_WriteLongToFile()saying that it never sets an exception and that doing so "should be regarded as an API-breaking change": detecting write errors makes it set one, which is what its documentation has promised since gh-105184.The documentation of these functions listed only
EOFError,ValueErrorandTypeError, which was inaccurate even before this change (MemoryErroris raised when the read buffer cannot be allocated), so the per-function lists are replaced by one list for the whole section.