-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Formatter: Uses space indentation in docstrings when using indent-style = "tab"
#8430
Comments
@konstin you implemented the docstring formatting and have a lot of context on the Python interop. Could you take this on as a formatter goal sometime this month? The solution doesn't have to be to fix the docstring formatting, we could also warn about using I think what the formatter should support is that it preserves tabs when using The alternative is that we always use |
Depends on #8445 |
Is there any way to circumvent this behaviour right now? Like telling the formatter to not format docstrings at all? |
@Mutantpenguin What is the formatting that you want for docstring when using tab indentation? And if you use some documentation generator (like sphinx), do you know how they react to tab indent vs. space indent, and if indent size matters? |
We use handwritten docstrings so I can't say anything about sphinx and so on. I want the same as MichaReiser:
E114 (which is in preview) should be taken into account too https://docs.astral.sh/ruff/rules/indentation-with-invalid-multiple-comment/ |
I now have a nice testcase. ruff.toml: [format]
indent-style="tab" This stays the same after formatting and doesn't get changed at all: def test(arg1: str) -> None:
"""
Arguments:
arg1: super duper arg with a tab in front
""" This stays the same after formatting and doesn't get changed at all too: def test(arg1: str) -> None:
"""
Arguments:
arg1: super duper arg with a tab and 4 spaces in front
""" This def test(arg1: str) -> None:
"""
Arguments:
arg1: super duper arg with 2 tabs in front
""" gets butchered and formatted to this: def test(arg1: str) -> None:
"""
Arguments:
arg1: super duper arg with 1 tab and 8 spaces in front
""" What the GitHub code formatting hides: we use a tab size of 4 in our editor so the last case looks expecially weird since the 8 spaces look like 2 indentation levels with tabs. |
@konstin are you still working on this? If not, feel free to un-assign. |
No, i don't have a good solution for this. I'd probably go with replacing every 8 spaces in the beginning of a docstring with tabs, implementation-wise, even if whatever we do is bad because it we either mismatch with cpython (always unconditionally assumes tab width 8 and always converts to spaces) or we mismatch our configuration. |
Would it be possible to add a new config knob to the formatter that lets an end user specify their tabwidth? Then it seems like we might be able to get things right here. |
We do have |
I think I probably don't have the full problem paged into context here. From above:
I think this is the part I was reacting to. So maybe it would be better to ask what it means when Python assumes a tabwidth of 8 inside of a docstring. Do we also have to follow that assumption? That's the part where I have gaps in my understanding I think. |
That's fair. For reference, there's some more explanation in #8445 about where Python assumes a tab width of 8 (used when inspecting a docstring |
I think it would be safe to change any leading groups of |
TLDRIMO, the current behavior that doesn't convert alignment spaces to tabs is correct because it ensures that docstrings are displayed correctly regardless of the used tab width. I agree that the behavior is unexpected when using a docstring-convention like def test(arg1: str) -> None:
"""
Arguments:
arg1: super duper arg with 2 tabs in front
""" But that's something the formatter doesn't understand understand today. The solution is to support docstring formatting (with the common convention) so that the formatter can convert spaces in front of That said, I agree that the formatter should not convert tabs to 8 spaces. It should either convert the tab to I keep the issue open to track this work but remove it from the stable formatter milestone because we want to keep the existing behavior. ReasoningThe only a difference between Python <= 2.12 and 3.13 is when inspecting
While not explicitly documented, the formatter’s I believe the expectation mismatch comes from that the whitespace before def test(arg1: str) -> None:
"""
Arguments:
arg1: super duper arg with 2 tabs in front
""" Distinguishing the whitespace before That’s why I recommend keeping the behaviour as is and the proper fix for it is to support docstring formatting (darglint). A possible alternative is to add a configuration option that allows enforcing hard-tabs over soft tabs. I’m reluctant of doing this because it may limit our formatting options in the future, e.g. it would no longer be possible to align the closing a
+ b
+ len([
1,
2,
]) Instead of a
+ b
+ len([
1,
2,
]) ResearchThe following program uses tabs for indenting the doctoring as well as alignment inside of the doctoring: def f():
"""Computes the value.
x = 1
y = x + 1
z = 2
""" A more realistic example: def test(arg1: str) -> None:
"""
Arguments:
arg1: super duper arg with 2 tabs in front
""" Where <= 3.12
|
If I have indent-style set to "tab", when I run ruff format it is still converting indentation within docstring comments to be spaces, which then causes ruff check to fail on rule E101: Indentation contains mixed spaces and tabs
Before:
After:
Is there a format setting I can add to fix this? or is this a bug/intentional?
Originally posted by @1024Adam in #7310 (comment)
We can reproduce this behavior in our own tests:
ruff/crates/ruff_python_formatter/tests/snapshots/format@docstring.py.snap
Lines 538 to 544 in 3076d76
ruff/crates/ruff_python_formatter/tests/snapshots/format@docstring.py.snap
Lines 569 to 577 in 3076d76
E101 has no special handling for docstring. It simply looks at every line and tests if the leading whitespace contains mixed spaces and tabs. Meaning, there can also be false positives if a multiline string contains leading whitespace that is a mix of spaces and tabs.
The text was updated successfully, but these errors were encountered: