cpython icon indicating copy to clipboard operation
cpython copied to clipboard

`Path.write_text` does not check whether LF is preceded by CR before converting it to CRLF on windows, resulting in CRCRLF

Open DetachHead opened this issue 1 week ago • 3 comments

Bug report

Bug description:

from pathlib import Path

path = Path("foo.txt")
path.write_text("foo\r\nbar")
print(path.read_bytes())  # b'foo\r\r\nbar'

i know i can workaround this behavior by passing newline="" to write_text or using write_bytes instead, but this should not be necessary. it should check whether the text is already using CRLF.

(sidenote: windows has supported LF for many years now. the automatic conversion many tools do by default is not only no longer necessary, but nowadays causes far more problems than it solves IMO. https://old.reddit.com/r/git/comments/7mg1q1/gits_crlf_handling_is_the_source_of_all_modern/)

CPython versions tested on:

3.14

Operating systems tested on:

Windows

DetachHead avatar Jan 05 '26 05:01 DetachHead

pathlib.Path is just forwarding to the standard Python I/O stack and not adding any new behaviors here (source code, I/O Docs: https://docs.python.org/3/library/io.html#io.TextIOWrapper). The newline behavior has been around since the beginning of Python 3 and changing the default here would likely break existing programs.

cmaloney avatar Jan 05 '26 05:01 cmaloney

this seems like an obvious bug to me though, regardless of whether fixing it would be a breaking change. does this mean a PEP would be required to change the behavior?

DetachHead avatar Jan 05 '26 05:01 DetachHead

CPython follows a backwards compatibility policy outlined in PEP-387. That also has a section on how to propose and make backwards incompatible changes.

cmaloney avatar Jan 05 '26 06:01 cmaloney

I’d like to propose a small documentation clarification for Path.write_text() on Windows:

When writing text containing CRLF sequences (\r\n), Python’s text mode automatically translates \n to \r\n. If the text already contains CRLF, this can lead to duplication (\r\n\r\r\n):

from pathlib import Path

path = Path("foo.txt")
path.write_text("foo\r\nbar")
print(path.read_bytes())  # b'foo\r\r\nbar'

Smchavan491 avatar Jan 06 '26 07:01 Smchavan491

I understand and acknowledge the problem--I've actually had to debug this exact thing, as \r\r\n between lines of code somehow completely messed up line numbers in tracebacks on Windows :-)

However, I don't think there's anything we can do to address this at the moment, so I'll close. The Path.write_text documentation refers to the open built-in, and open (as well as TextIOWrapper) very clearly documents this behavior.

Maybe try to raise this issue at https://discuss.python.org/? Maybe suggest a rule to Ruff or other linters to detect uses of os.linesep / \r\n in arguments to Path.write_text() / open(..., "w").write()?

johnslavik avatar Jan 07 '26 13:01 johnslavik