This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author Mark.Williams
Recipients Mark.Williams
Date 2015-10-08.07:42:57
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1444290178.48.0.0607111061258.issue25341@psf.upfronthosting.co.za>
In-reply-to
Content
There is at least one mode in which a file can be opened that cannot be represented in its mode attribute: wb+.  This mode instead appears as 'rb+' in the mode attribute:

Python 3.5.0 (default, Oct  3 2015, 10:40:38)
[GCC 4.2.1 Compatible FreeBSD Clang 3.4.1 (tags/RELEASE_34/dot1-final 208032)] on freebsd10
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> if os.path.exists('some_file'): os.unlink('some_file')
...
>>> with open('some_file', 'r+b') as f: print(f.mode)
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'some_file'
>>> with open('some_file', 'w+b') as f: print(f.mode)
...
rb+
>>> with open('some_file', 'r+b') as f: print(f.mode)
rb+


This means code that interacts with file objects cannot trust the mode of binary files.  For example, you can't use tempfile.TemporaryFile (the mode argument of which defaults to 'wb+') and GzipFile:


>>> import gzip
>>> from tempfile import TemporaryFile
>>> with TemporaryFile() as f:
...     gzip.GzipFile(fileobj=f).write(b'test')
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "/usr/local/lib/python3.5/gzip.py", line 249, in write
    raise OSError(errno.EBADF, "write() on read-only GzipFile object")
OSError: [Errno 9] write() on read-only GzipFile object


This occurs because without a mode argument passed to its initializer, GzipFile checks that the fp object's mode starts with 'w', 'a', or 'x'.

For the sake of completeness/searchability: w+ and r+ are different modes, so rb+ and wb+ must be different modes.  Per https://docs.python.org/3/library/functions.html#open :

"""
For binary read-write access, the mode 'w+b' opens and truncates the file to 0 bytes. 'r+b' opens the file without truncation.
"""


I haven't been able to test this on Windows, but I expect precisely the same behavior given my understanding of the relevant source.

_io_FileIO___init___impl in _io/fileio.c does the right thing and includes O_CREAT and O_TRUNC in the open(2) flags upon seeing 'w' in the mode:

https://hg.python.org/cpython/file/3.5/Modules/_io/fileio.c#l324

this ensures correct interaction with the file system.  But it also sets self->readable and self->writable upon seeing '+' in the mode:

https://hg.python.org/cpython/file/3.5/Modules/_io/fileio.c#l341

The open flags are not retained.  Consequently, when the mode attribute is accessed and the get_mode calls the mode_string function, the instance has insufficient information to differentiate between 'rb+' and 'wb+':

https://hg.python.org/cpython/file/3.5/Modules/_io/fileio.c#l1043

If the FileIO instance did retain the 'flags' variable that's declared and set in its initializer, then mode_string could use it to determine the difference between wb+ and rb+.

I would be happy to write a patch for this.
History
Date User Action Args
2015-10-08 07:42:58Mark.Williamssetrecipients: + Mark.Williams
2015-10-08 07:42:58Mark.Williamssetmessageid: <1444290178.48.0.0607111061258.issue25341@psf.upfronthosting.co.za>
2015-10-08 07:42:58Mark.Williamslinkissue25341 messages
2015-10-08 07:42:57Mark.Williamscreate