Repository navigation
Preserve line endings when formatting a file in place - #288
Conversation
|
I'll wait with landing this until I fix the tests on Travis. |
|
In the meanwhile I'll figure out how to install precommit on windows and fix everything I've broken |
|
|
||
| if write_back == write_back.YES: | ||
| with open(src, "w", encoding=src_buffer.encoding) as f: | ||
| with open(src, encoding=src_buffer.encoding, newline="") as src_buffer: |
There was a problem hiding this comment.
You don't have to open it again, that's slow. You already have src_contents that you can check. Suggestion:
windows_newlines = src_contents.split("\n", 1)[0][-1:] == "\r"
newline = "\r\n" if windows_newlines else "\n"There was a problem hiding this comment.
src_contents uses universal newlines so "\r" is never in it :(
There was a problem hiding this comment.
I'm not sure if opening a file for reading one more time right after reading through it is a huge performance hit, but if it turns out to be the case, we can reproduce the 10 or so lines of code in tokenize.open() to make sure we only do it once.
There was a problem hiding this comment.
Right. OK, leave as is but open it in "rb" so it doesn't need to decode anything. The first line must be readable anyway since if it's not UTF8 (or ASCII on Python 2) it needs the # -*- coding: X -*- comment.
And as a total nit: instead of b"\r\n" in readline you can do readline[-2:] == b"\r\n" which is marginally faster still.
| test_file.write_bytes(contents.encode()) | ||
| ff(test_file, write_back=black.WriteBack.YES) | ||
| updated_contents: bytes = test_file.read_bytes() | ||
| self.assertIn(nl.encode(), updated_contents) # type: ignore |
There was a problem hiding this comment.
mypy complains about this line like so:
Cannot infer type argument 1 of "assertIn" of "TestCase"
I think it's confused because both arguments are bytes. Seems like a bug in mypy to me
There was a problem hiding this comment.
I think the typeshed stub is wrong. It has:
def assertIn(self, first: _T, second: Iterable[_T],
msg: Any = ...) -> None: ...
def assertNotIn(self, first: _T, second: Iterable[_T],
msg: Any = ...) -> None: ...
This should actually be Container, not Iterable.
Pull Request Test Coverage Report for Build 476
💛 - Coveralls |
I think It makes things very "compromising" and I thought it wasn't the purpose of black. I think it would be more consistant that black preserves line ending with Anyway If you disagree It should be mentioned in the readme |
|
@zsol, I think @jgirardet is right. Especially on piping stdin to stdout we should be preserving existing newline style. But also on --diff, not taking existing format into account will lead to bugs in intergrations (like sublack). I don't understand @jgirardet's comment about compromise. I just think the current behavior is still effectively buggy. |
|
Just a joke, but that was probably bad English speaking 😊 |
|
fair enough, let me iterate on this |
|
Hi guys, >> p = subprocess.Popen(
['black', '-'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,)
>>> p.communicate(b"hgello\n")
(b'hgello\r\n', b'- already well formatted, good job.\r\n')
>>> p = subprocess.Popen(
['black', '-'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
>>> p.communicate('hello\n')
('hello\n', '- already well formatted, good job.\nMaybe this is just the popen standard behaviour ? |
|
@jgirardet, wow, this is strange. I don't work with Windows so I don't really understand what the expected behavior should be with regards to stdin/stdout. Maybe what @zsol is already doing is valid after all on Windows? |
|
@jgirardet what you're seeing here is just the effect of There is a bug in black in that it doesn't preserve newline styles as you can see in your first |
|
Alright this is ready for review @ambv. I had to rewrite most of the code because we can't afford the luxury of consuming stdin twice |
|
Thanks! ✨ 🍰 ✨ |
|
thanks @zsol. I added the quotes to show you the "<0x0d>" added at end of line. |
|
@jgirardet, stderr is user-facing messages. It should have \r\n on Windows, this is how people are going to see them in the console. |
|
ok |
|
If I understand this correctly, black should preserve line endings when used with - (stdin). But I think 18.6b4 (the version that is currently supplied by pip) won't. I input code over stdin with \r\n line endings, I get output with \n. [am I wrong or missing something?] |
|
@blkpanther Can you share how you're calling black and how you're piping code to it? Also, what environment (OS, locale) are you running this in? In my case, black behaves as advertised (your understanding is correct): And my environment: |
|
@zsol, can you try it directly from PowerShell on Windows (without WSL)? |
|
I have no idea how to verify this in PowerShell (or cmd.exe for that matter). I do know our test passes, but that doesn't use |
|
OK I wrote a new test for standardin and it fails. Investigating edit: nevermind this was just an artifact of the test runner. Black behaves correctly: produces |
|
Works correct for me, too (sorry I caused a fuss). |
Fixes #258
Note that this only changes behavior when black writes back to the file. In other cases like when using
--diffor writing to stdout, I think it's reasonable to expect the caller to handle line endings.