classification
Title: io's r+ mode truncate(0)
Type: enhancement Stage: resolved
Components: IO Versions: Python 3.10
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: Nosy List: ke265379ke, steven.daprano, terry.reedy
Priority: normal Keywords:

Created on 2020-12-25 03:23 by ke265379ke, last changed 2020-12-28 09:32 by ke265379ke. This issue is now closed.

Files
File name Uploaded Description Edit
Screenshot from 2020-12-25 10-46-42.png ke265379ke, 2020-12-25 03:23 if you didn't use seek(0) after truncate(0), file will like this
Repositories containing patches
https://github.com/841020/open_source
Messages (7)
msg383710 - (view) Author: 施文峰 (ke265379ke) * Date: 2020-12-25 03:23
happen at io's reading & updating(r+) mode
after read,
file object's postion stay in last if you remove whole content ( truncate(0) ),
postion wil not back to 0 still stay in the last
then you start writing from last position(not 0)
that's why problem happen

test case can check here https://github.com/841020/open_source
msg383742 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2020-12-25 13:05
You say:

>  after process python3 test_case.py
>  json file's content like this
>  
>  @@@@@@@@@@{"how_dare_you": "how_dare_you"}


I cannot replicate that result.


I created a "data.json" with the following content:


```
>>> with open(FILE_PATH, 'w') as f:
...     f.write('{"how_dare_you": "how_dare_you"}\n')
... 
33
>>> with open(FILE_PATH, 'r') as f:
...     print(f.read())
... 
{"how_dare_you": "how_dare_you"}

```


Then I ran your `test` function and the only result was to delete the newline at the end of the file:


```
>>> test()
beginning tell 0
tell after read 33
tell after delete content 33
content 0 
tell after write 65
content 0 
>>> 
>>> with open(FILE_PATH, 'r') as f:
...     print(f.read())
... 
{"how_dare_you": "how_dare_you"}
>>> 
```

So I cannot replicate your reported bug. There is no sign of any garbage characters inserted at the start of the file as you state.
msg383743 - (view) Author: 施文峰 (ke265379ke) * Date: 2020-12-25 13:31
hi Steven

thanks for check my post

first test have a problem,you didn’t use r+ mode
step is 
1. f.read
2. f.truncate(0)
3. f.write(something)

my test msg tell you

“
tell after delete content 33
content 0 
tell after write 65
“

so you know after truncate f.tell =33
but no content in file
and after you write something into file
msg tell you f.tell =65
but “ {"how_dare_you": "how_dare_you"}” is only 33 length 
so you know must have someing in file

please use editor open the file
you will find it
msg383791 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2020-12-26 02:19
"Enhancements" (non-bugfix feature changes) can only be applied to future versions.  However, you are asking for the reversion of an intentional feature change made in a 'bugfix' release# for (I believe) 3.1.  Before the change, as I remember, truncating to 0 *did* move the file pointer back to 0.  As I remember, Guide von Rossum requested the change and Antoine Pitrou made it.  

https://docs.python.org/3/library/io.html#io.IOBase.seek
new says "The current stream position isn’t changed."

If you also want to change the stream position, do it with seek(), perhaps before the truncate.

# This change in a bugfix release, a violation the rule stated above, broke the code of multiple people.  (We thereafter strengthened the  policy.)  To fix my code, I had to add a seek(0).  I put it before truncate(0), so I know that this works.
msg383792 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2020-12-26 02:20
On Fri, Dec 25, 2020 at 01:31:51PM +0000, 施文峰 wrote:

> first test have a problem,you didn’t use r+ mode

I did, I copied your `test()` function exactly, however I did make a 
mistake. I tried again with this:

>>> with open(FILE_PATH, 'r') as f:
...     print(repr(f.read()))
... 
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00{"how_dare_you": 
"how_dare_you"}'

so you are correct, the file is padded with NUL characters.

Here is a simpler demonstration of the behaviour.

```
FILE_PATH = 'example.data'

# create a file
with open(FILE_PATH, 'w') as f:
    f.write('abc\n')

# Truncate using r+ mode.
with open(FILE_PATH, 'r+') as f:
    assert f.tell() == 0
    assert f.read() == 'abc\n'
    assert f.tell() == 4  # File position is now at end of file.
    f.truncate(0)
    assert f.tell() == 4  # File position has not changed.
    assert f.read() == ''  # Nothing remaining to read.
    f.write('xyz\n')
    f.flush()
    assert f.tell() == 8
    assert f.read() == ''  # Nothing remaining to read.
    # Return the file position to start of file.
    f.seek(0)
    assert f.read() == '\0\0\0\0xyz\n'

```

All the assertions pass.

I think this is standard and correct behaviour. Do you have examples of 
other programming languages that behave differently? PHP seems to do the 
same thing:

https://www.php.net/manual/en/function.ftruncate.php
msg383793 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2020-12-26 02:55
On Sat, Dec 26, 2020 at 02:19:55AM +0000, Terry J. Reedy wrote:

> "Enhancements" (non-bugfix feature changes) can only be applied to 
> future versions.  However, you are asking for the reversion of an 
> intentional feature change made in a 'bugfix' release# for (I believe) 
> 3.1.  Before the change, as I remember, truncating to 0 *did* move the 
> file pointer back to 0.  As I remember, Guide von Rossum requested the 
> change and Antoine Pitrou made it.

Thanks for that insight Terry. I think the current behaviour is correct. 
Sparse files can have holes in them, and non-sparse files have to be 
filled with NUL bytes, so this has to work:

    f.truncate(0)
    f.seek(10)
    f.write('x')
    # File is now ten NUL bytes and a single 'x'

If you swap the order of the truncate and the seek, the behaviour 
shouldn't change: truncate is documented as not moving the file 
position, so changing this will be backwards incompatible and will 
probably break a lot of code that expects the current behaviour.

https://docs.python.org/3/library/io.html#io.IOBase.truncate

I agree with Terry rejecting this feature request. If 施文峰 (Shīwén 
Fēng according to Google translate) wishes to disagree, this will have 
to be discussed on the Python-Ideas mailing list first.
msg383888 - (view) Author: 施文峰 (ke265379ke) * Date: 2020-12-28 09:32
hi Terry

you are right
i download python version 3.0.1 to check this case

'''
Python 3.0.1 (r301:69556, Dec 28 2020, 14:14:02) 
[GCC 7.5.0] on linux5
Type "help", "copyright", "credits" or "license" for more information.
>>> from test_case import test
[43552 refs]
>>> test()
beginning tell 0
tell after read 32
tell after delete content 0
tell after write 32
[52665 refs]
>>> 

'''
History
Date User Action Args
2020-12-28 09:32:37ke265379kesetmessages: + msg383888
2020-12-26 02:55:28steven.dapranosetmessages: + msg383793
title: [issue] io's r+ mode truncate(0) -> io's r+ mode truncate(0)
2020-12-26 02:20:18steven.dapranosetmessages: + msg383792
title: io's r+ mode truncate(0) -> [issue] io's r+ mode truncate(0)
2020-12-26 02:19:55terry.reedysetstatus: open -> closed


title: [issue] io's r+ mode truncate(0) -> io's r+ mode truncate(0)
nosy: + terry.reedy
versions: + Python 3.10, - Python 3.6, Python 3.7, Python 3.8, Python 3.9
messages: + msg383791
resolution: rejected
stage: resolved
2020-12-25 13:31:51ke265379kesetmessages: + msg383743
2020-12-25 13:05:18steven.dapranosetnosy: + steven.daprano
messages: + msg383742
2020-12-25 03:23:11ke265379kecreate