8000 gh-127350: Add Py_fopen() function by vstinner · Pull Request #127821 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

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

Merged
merged 20 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Support __fspath__() protocol on Windows
Test also non-ASCII mode.
  • Loading branch information
vstinner committed Jan 6, 2025
commit 5b52a4e63dcd5ca445bdb721feb44f3d622c6e70
33 changes: 23 additions & 10 deletions Lib/test/test_capi/test_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,23 @@
class CAPIFileTest(unittest.TestCase):
def test_py_fopen(self):
# Test Py_fopen() and Py_fclose()
class FSPath:
def __init__(self, path):
self.path = path
def __fspath__(self):
return self.path

with open(__file__, "rb") as fp:
source = fp.read()

for filename in (__file__, os.fsencode(__file__)):
with self.subTest(filename=filename):
content = _testcapi.py_fopen(filename, "rb")
with open(filename, "rb") as fp:
self.assertEqual(fp.read(256), content)
data = _testcapi.py_fopen(filename, "rb")
self.assertEqual(data, source[:256])

data = _testcapi.py_fopen(FSPath(filename), "rb")
self.assertEqual(data, source[:256])

with open(__file__, "rb") as fp:
content = fp.read()
for filename in (
os_helper.TESTFN,
os.fsencode(os_helper.TESTFN),
Expand All @@ -26,11 +35,10 @@ def test_py_fopen(self):
with self.subTest(filename=filename):
try:
with open(filename, "wb") as fp:
fp.write(content)
fp.write(source)

content = _testcapi.py_fopen(filename, "rb")
with open(filename, "rb") as fp:
self.assertEqual(fp.read(256), content[:256])
data = _testcapi.py_fopen(filename, "rb")
self.assertEqual(data, source[:256])
finally:
os_helper.unlink(filename)

Expand All @@ -40,10 +48,15 @@ def test_py_fopen(self):
with self.assertRaises(ValueError):
_testcapi.py_fopen(b"a\x00b", "rb")

# non-ASCII mode failing with "Invalid argument"
with self.assertRaises(OSError):
_testcapi.py_fopen(__file__, "\xe9")
Copy link
Member
@serhiy-storchaka serhiy-storchaka Jan 6, 2025

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.

Copy link
Member Author

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().


# invalid filename type
for invalid_type in (123, object()):
with self.subTest(filename=invalid_type):
with self.assertRaises(TypeError):
_testcapi.py_fopen(invalid_type, "r")
_testcapi.py_fopen(invalid_type, "rb")

if support.MS_WINDOWS:
with self.assertRaises(OSError):
Expand Down
6 changes: 6 additions & 0 deletions Python/fileutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -1776,6 +1776,12 @@ Py_fopen(PyObject *path, const char *mode)
int async_err = 0;
int saved_errno;
#ifdef MS_WINDOWS
PyObject *fspath = PyOS_FSPath(arg);
if (fspath == NULL) {
return NULL;
}
Py_SETREF(path, fspath);

if (PyUnicode_Check(path)) {
wchar_t *wpath = PyUnicode_AsWideCharString(path, NULL);
if (wpath == NULL) {
Expand Down
Loading
0