-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
gh-127350: Add Py_fopen() function #127821
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Rename _Py_fopen_obj() to Py_fopen(). The function now also accepts bytes path on Windows. Remove the private, undocumented, and untested function _Py_fopen_obj().
Do we need |
|
To be honest, I don't understand well the Windows DLL issue. From what I understood, to use a |
PyRun_AnyFileExFlags has "closeit" parameter which I always used. Thank you guys for doing all this. |
Is Py_fclose() needed because of the DLL issue? |
Yes. When you call |
Ok, I wanted to make sure that I get the rationale correctly. I added |
Your updated documentation seems a bit snarky. The I see no reason not to just document it as "should be used to close files opened by |
It's not my intent to be snarky. I just tried to explain why calling fclose() directly can cause issues in practice.
Ok, let's do that. |
Co-authored-by: Steve Dower <[email protected]>
I created capi-workgroup/decisions#51: "Add public Py_fopen() and Py_fclose() functions, and remove private _Py_fopen_obj() function". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM in general. I would like to see more tests. The support of the path-like protocol is inconsistent.
Python/fileutils.c
Outdated
wmode, Py_ARRAY_LENGTH(wmode)); | ||
if (usize == 0) { | ||
PyErr_SetFromWindowsErr(0); | ||
wchar_t wmode[10]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: Add tests for mode: NULL, non-decodable (non-ASCII) mode, too long mode (e.g. "rt+, ccs=UTF-8").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a test for "too long mode" and non-ASCII mode.
Passing NULL does crash. I don't know how to modify the Argument Clinic code to accept NULL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use "z#" to pass arbitrary sequence of bytes or NULL. In Argument Clinic it is expressed as str(zeroes=True, accept={robuffer, str, NoneType})
.
Use the NULLABLE
macro for path.
If it crashes, just add a comment # CRASHES
with specific call as in other tests as indication that we did not miss such case, but cannot test it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it crashes, just add a comment # CRASHES with specific call as in other tests as indication that we did not miss such case, but cannot test it.
Ok, I added a comment.
Test also non-ASCII mode.
* Skip TESTFN_UNENCODABLE if it's None. * Remove TESTFN_UNDECODABLE test.
|
||
# non-ASCII mode failing with "Invalid argument" | ||
with self.assertRaises(OSError): | ||
_testcapi.py_fopen(__file__, "\xe9") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"\xe9"
is encoded to b'\xc3\xa9'
. Please test also with non-UTF-8 bytes. You may get different error on Windows. Actually, it may depend on the locale.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not possible to pass non-UTF-8 bytes, PySys_Audit()
decodes the mode from UTF-8 in strict mode:
if (PySys_Audit("open", "Osi", path, mode, 0) < 0) {
return NULL;
}
I don't think that it's worth to "support" non-UTF-8 just for the test, whereas it's rejected anyway by fopen()
.
Python/fileutils.c
Outdated
wmode, Py_ARRAY_LENGTH(wmode)); | ||
if (usize == 0) { | ||
PyErr_SetFromWindowsErr(0); | ||
wchar_t wmode[10]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use "z#" to pass arbitrary sequence of bytes or NULL. In Argument Clinic it is expressed as str(zeroes=True, accept={robuffer, str, NoneType})
.
Use the NULLABLE
macro for path.
If it crashes, just add a comment # CRASHES
with specific call as in other tests as indication that we did not miss such case, but cannot test it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yet more test suggestions.
@serhiy-storchaka: I tried to implement all requested tests, but I skipped some of them for practical reasons. On Windows, the file mode is limited to 10 characters. If it's a problem later, I can allocate memory on the heap and write more complicated code to handle longer modes. In practice, we should be fine with this arbitrary limit. |
PR merged, thanks for your reviews! @serhiy-storchaka: You can write a follow-up PR if you want to extend the code coverage (add more tests). |
Rename _Py_fopen_obj() to Py_fopen(). The function now also accepts bytes path on Windows.
Remove the private, undocumented, and untested function _Py_fopen_obj().
📚 Documentation preview 📚: https://cpython-previews--127821.org.readthedocs.build/