Skip to content

Commit

Permalink
Correctly handle inline tabs in docstrings (psf#1810)
Browse files Browse the repository at this point in the history
The `fix_docstring` function expanded all tabs, which caused a
difference in the AST representation when those tabs were inline and not
leading. This changes the function to only expand leading tabs so inline
tabs are preserved.

Fixes psf#1601.
  • Loading branch information
density authored and noxan committed Jun 6, 2021
1 parent db82569 commit 93d0c1c
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 4 deletions.
26 changes: 23 additions & 3 deletions src/black/__init__.py
Expand Up @@ -6744,13 +6744,33 @@ def is_docstring(leaf: Leaf) -> bool:
return False


def lines_with_leading_tabs_expanded(s: str) -> List[str]:
"""
Splits string into lines and expands only leading tabs (following the normal
Python rules)
"""
lines = []
for line in s.splitlines():
# Find the index of the first non-whitespace character after a string of
# whitespace that includes at least one tab
match = re.match(r"\s*\t+\s*(\S)", line)
if match:
first_non_whitespace_idx = match.start(1)

lines.append(
line[:first_non_whitespace_idx].expandtabs()
+ line[first_non_whitespace_idx:]
)
else:
lines.append(line)
return lines


def fix_docstring(docstring: str, prefix: str) -> str:
# https://www.python.org/dev/peps/pep-0257/#handling-docstring-indentation
if not docstring:
return ""
# Convert tabs to spaces (following the normal Python rules)
# and split into a list of lines:
lines = docstring.expandtabs().splitlines()
lines = lines_with_leading_tabs_expanded(docstring)
# Determine minimum indentation (first line doesn't count):
indent = sys.maxsize
for line in lines[1:]:
Expand Down
53 changes: 52 additions & 1 deletion tests/data/docstring.py
Expand Up @@ -110,6 +110,32 @@ def ignored_docstring():
"""a => \
b"""


def docstring_with_inline_tabs_and_space_indentation():
"""hey
tab separated value
tab at start of line and then a tab separated value
multiple tabs at the beginning and inline
mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
line ends with some tabs
"""


def docstring_with_inline_tabs_and_tab_indentation():
"""hey
tab separated value
tab at start of line and then a tab separated value
multiple tabs at the beginning and inline
mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
line ends with some tabs
"""
pass


# output

class MyClass:
Expand Down Expand Up @@ -222,4 +248,29 @@ def believe_it_or_not_this_is_in_the_py_stdlib():

def ignored_docstring():
"""a => \
b"""
b"""


def docstring_with_inline_tabs_and_space_indentation():
"""hey
tab separated value
tab at start of line and then a tab separated value
multiple tabs at the beginning and inline
mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
line ends with some tabs
"""


def docstring_with_inline_tabs_and_tab_indentation():
"""hey
tab separated value
tab at start of line and then a tab separated value
multiple tabs at the beginning and inline
mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
line ends with some tabs
"""
pass

0 comments on commit 93d0c1c

Please sign in to comment.