Skip to content

Commit 02c1abf

Browse files
pythongh-69528: Distinguish between file modes "wb+" and "rb+" (pythonGH-137834)
Co-authored-by: Xiang Zhang <[email protected]>
1 parent 60df1d7 commit 02c1abf

File tree

7 files changed

+27
-10
lines changed

7 files changed

+27
-10
lines changed

Lib/_pyio.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1498,6 +1498,7 @@ class FileIO(RawIOBase):
14981498
_writable = False
14991499
_appending = False
15001500
_seekable = None
1501+
_truncate = False
15011502
_closefd = True
15021503

15031504
def __init__(self, file, mode='r', closefd=True, opener=None):
@@ -1553,6 +1554,7 @@ def __init__(self, file, mode='r', closefd=True, opener=None):
15531554
flags = 0
15541555
elif 'w' in mode:
15551556
self._writable = True
1557+
self._truncate = True
15561558
flags = os.O_CREAT | os.O_TRUNC
15571559
elif 'a' in mode:
15581560
self._writable = True
@@ -1877,7 +1879,10 @@ def mode(self):
18771879
return 'ab'
18781880
elif self._readable:
18791881
if self._writable:
1880-
return 'rb+'
1882+
if self._truncate:
1883+
return 'wb+'
1884+
else:
1885+
return 'rb+'
18811886
else:
18821887
return 'rb'
18831888
else:

Lib/test/test_gzip.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ def test_fileobj_mode(self):
639639
with open(self.filename, mode) as f:
640640
with gzip.GzipFile(fileobj=f) as g:
641641
self.assertEqual(g.mode, gzip.READ)
642-
for mode in "wb", "ab", "xb":
642+
for mode in "wb", "ab", "xb", "wb+", "ab+", "xb+":
643643
if "x" in mode:
644644
os_helper.unlink(self.filename)
645645
with open(self.filename, mode) as f:

Lib/test/test_io/test_fileio.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -567,8 +567,8 @@ def testModeStrings(self):
567567
# test that the mode attribute is correct for various mode strings
568568
# given as init args
569569
try:
570-
for modes in [('w', 'wb'), ('wb', 'wb'), ('wb+', 'rb+'),
571-
('w+b', 'rb+'), ('a', 'ab'), ('ab', 'ab'),
570+
for modes in [('w', 'wb'), ('wb', 'wb'), ('wb+', 'wb+'),
571+
('w+b', 'wb+'), ('a', 'ab'), ('ab', 'ab'),
572572
('ab+', 'ab+'), ('a+b', 'ab+'), ('r', 'rb'),
573573
('rb', 'rb'), ('rb+', 'rb+'), ('r+b', 'rb+')]:
574574
# read modes are last so that TESTFN will exist first

Lib/test/test_io/test_general.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -960,8 +960,8 @@ def test_attributes(self):
960960

961961
f = self.open(os_helper.TESTFN, "w+", encoding="utf-8")
962962
self.assertEqual(f.mode, "w+")
963-
self.assertEqual(f.buffer.mode, "rb+") # Does it really matter?
964-
self.assertEqual(f.buffer.raw.mode, "rb+")
963+
self.assertEqual(f.buffer.mode, "wb+")
964+
self.assertEqual(f.buffer.raw.mode, "wb+")
965965

966966
g = self.open(f.fileno(), "wb", closefd=False)
967967
self.assertEqual(g.mode, "wb")

Lib/test/test_tempfile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1386,7 +1386,7 @@ def test_properties(self):
13861386

13871387
f.write(b'x')
13881388
self.assertTrue(f._rolled)
1389-
self.assertEqual(f.mode, 'rb+')
1389+
self.assertEqual(f.mode, 'wb+')
13901390
self.assertIsNotNone(f.name)
13911391
with self.assertRaises(AttributeError):
13921392
f.newlines
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The :attr:`~io.FileIO.mode` attribute of files opened in the ``'wb+'`` mode is
2+
now ``'wb+'`` instead of ``'rb+'``.

Modules/_io/fileio.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ typedef struct {
7070
unsigned int writable : 1;
7171
unsigned int appending : 1;
7272
signed int seekable : 2; /* -1 means unknown */
73+
unsigned int truncate : 1;
7374
unsigned int closefd : 1;
7475
char finalizing;
7576
/* Stat result which was grabbed at file open, useful for optimizing common
@@ -209,6 +210,7 @@ fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
209210
self->writable = 0;
210211
self->appending = 0;
211212
self->seekable = -1;
213+
self->truncate = 0;
212214
self->stat_atopen = NULL;
213215
self->closefd = 1;
214216
self->weakreflist = NULL;
@@ -341,6 +343,7 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode,
341343
goto bad_mode;
342344
rwa = 1;
343345
self->writable = 1;
346+
self->truncate = 1;
344347
flags |= O_CREAT | O_TRUNC;
345348
break;
346349
case 'a':
@@ -1145,10 +1148,17 @@ mode_string(fileio *self)
11451148
return "ab";
11461149
}
11471150
else if (self->readable) {
1148-
if (self->writable)
1149-
return "rb+";
1150-
else
1151+
if (self->writable) {
1152+
if (self->truncate) {
1153+
return "wb+";
1154+
}
1155+
else {
1156+
return "rb+";
1157+
}
1158+
}
1159+
else {
11511160
return "rb";
1161+
}
11521162
}
11531163
else
11541164
return "wb";

0 commit comments

Comments
 (0)