gh-69528: Distinguish between file modes "wb+" and "rb+" (GH-137834)

Co-authored-by: Xiang Zhang <angwerzx@126.com>
This commit is contained in:
Stan Ulbrych 2025-10-21 18:33:30 +01:00 committed by GitHub
parent 60df1d7e0c
commit 02c1abfc54
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 27 additions and 10 deletions

View file

@ -1498,6 +1498,7 @@ class FileIO(RawIOBase):
_writable = False
_appending = False
_seekable = None
_truncate = False
_closefd = True
def __init__(self, file, mode='r', closefd=True, opener=None):
@ -1553,6 +1554,7 @@ def __init__(self, file, mode='r', closefd=True, opener=None):
flags = 0
elif 'w' in mode:
self._writable = True
self._truncate = True
flags = os.O_CREAT | os.O_TRUNC
elif 'a' in mode:
self._writable = True
@ -1877,6 +1879,9 @@ def mode(self):
return 'ab'
elif self._readable:
if self._writable:
if self._truncate:
return 'wb+'
else:
return 'rb+'
else:
return 'rb'

View file

@ -639,7 +639,7 @@ def test_fileobj_mode(self):
with open(self.filename, mode) as f:
with gzip.GzipFile(fileobj=f) as g:
self.assertEqual(g.mode, gzip.READ)
for mode in "wb", "ab", "xb":
for mode in "wb", "ab", "xb", "wb+", "ab+", "xb+":
if "x" in mode:
os_helper.unlink(self.filename)
with open(self.filename, mode) as f:

View file

@ -567,8 +567,8 @@ def testModeStrings(self):
# test that the mode attribute is correct for various mode strings
# given as init args
try:
for modes in [('w', 'wb'), ('wb', 'wb'), ('wb+', 'rb+'),
('w+b', 'rb+'), ('a', 'ab'), ('ab', 'ab'),
for modes in [('w', 'wb'), ('wb', 'wb'), ('wb+', 'wb+'),
('w+b', 'wb+'), ('a', 'ab'), ('ab', 'ab'),
('ab+', 'ab+'), ('a+b', 'ab+'), ('r', 'rb'),
('rb', 'rb'), ('rb+', 'rb+'), ('r+b', 'rb+')]:
# read modes are last so that TESTFN will exist first

View file

@ -960,8 +960,8 @@ def test_attributes(self):
f = self.open(os_helper.TESTFN, "w+", encoding="utf-8")
self.assertEqual(f.mode, "w+")
self.assertEqual(f.buffer.mode, "rb+") # Does it really matter?
self.assertEqual(f.buffer.raw.mode, "rb+")
self.assertEqual(f.buffer.mode, "wb+")
self.assertEqual(f.buffer.raw.mode, "wb+")
g = self.open(f.fileno(), "wb", closefd=False)
self.assertEqual(g.mode, "wb")

View file

@ -1386,7 +1386,7 @@ def test_properties(self):
f.write(b'x')
self.assertTrue(f._rolled)
self.assertEqual(f.mode, 'rb+')
self.assertEqual(f.mode, 'wb+')
self.assertIsNotNone(f.name)
with self.assertRaises(AttributeError):
f.newlines

View file

@ -0,0 +1,2 @@
The :attr:`~io.FileIO.mode` attribute of files opened in the ``'wb+'`` mode is
now ``'wb+'`` instead of ``'rb+'``.

View file

@ -70,6 +70,7 @@ typedef struct {
unsigned int writable : 1;
unsigned int appending : 1;
signed int seekable : 2; /* -1 means unknown */
unsigned int truncate : 1;
unsigned int closefd : 1;
char finalizing;
/* Stat result which was grabbed at file open, useful for optimizing common
@ -209,6 +210,7 @@ fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self->writable = 0;
self->appending = 0;
self->seekable = -1;
self->truncate = 0;
self->stat_atopen = NULL;
self->closefd = 1;
self->weakreflist = NULL;
@ -341,6 +343,7 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode,
goto bad_mode;
rwa = 1;
self->writable = 1;
self->truncate = 1;
flags |= O_CREAT | O_TRUNC;
break;
case 'a':
@ -1145,11 +1148,18 @@ mode_string(fileio *self)
return "ab";
}
else if (self->readable) {
if (self->writable)
if (self->writable) {
if (self->truncate) {
return "wb+";
}
else {
return "rb+";
else
}
}
else {
return "rb";
}
}
else
return "wb";
}