Skip to content

Commit 49face6

Browse files
authored
Fixed indentation of multiline strings (#166)
Closes #127. Closes #129.
1 parent c95c959 commit 49face6

File tree

3 files changed

+55
-1
lines changed

3 files changed

+55
-1
lines changed

sphinx_autodoc_typehints.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,45 @@ def format_annotation(annotation,
148148
role=role, prefix=prefix, full_name=full_name, formatted_args=formatted_args)
149149

150150

151+
# reference: https://github.com/pytorch/pytorch/pull/46548/files
152+
def normalize_source_lines(sourcelines: str) -> str:
153+
"""
154+
This helper function accepts a list of source lines. It finds the
155+
indentation level of the function definition (`def`), then it indents
156+
all lines in the function body to a point at or greater than that
157+
level. This allows for comments and continued string literals that
158+
are at a lower indentation than the rest of the code.
159+
Arguments:
160+
sourcelines: source code
161+
Returns:
162+
source lines that have been correctly aligned
163+
"""
164+
sourcelines = sourcelines.split("\n")
165+
166+
def remove_prefix(text, prefix):
167+
return text[text.startswith(prefix) and len(prefix):]
168+
169+
# Find the line and line number containing the function definition
170+
for i, l in enumerate(sourcelines):
171+
if l.lstrip().startswith("def"):
172+
idx = i
173+
break
174+
else:
175+
return "\n".join(sourcelines)
176+
fn_def = sourcelines[idx]
177+
178+
# Get a string representing the amount of leading whitespace
179+
whitespace = fn_def.split("def")[0]
180+
181+
# Add this leading whitespace to all lines before and after the `def`
182+
aligned_prefix = [whitespace + remove_prefix(s, whitespace) for s in sourcelines[:idx]]
183+
aligned_suffix = [whitespace + remove_prefix(s, whitespace) for s in sourcelines[idx + 1:]]
184+
185+
# Put it together again
186+
aligned_prefix.append(fn_def)
187+
return "\n".join(aligned_prefix + aligned_suffix)
188+
189+
151190
def process_signature(app, what: str, name: str, obj, options, signature, return_annotation):
152191
if not callable(obj):
153192
return
@@ -270,7 +309,8 @@ def _one_child(module):
270309
return children[0]
271310

272311
try:
273-
obj_ast = ast.parse(textwrap.dedent(inspect.getsource(obj)), **parse_kwargs)
312+
obj_ast = ast.parse(textwrap.dedent(
313+
normalize_source_lines(inspect.getsource(obj))), **parse_kwargs)
274314
except (OSError, TypeError):
275315
return {}
276316

tests/roots/test-dummy/dummy_module.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,16 @@ def foo(
180180
"""
181181
return 42
182182

183+
def method_without_typehint(self, x):
184+
"""
185+
Method docstring.
186+
"""
187+
# test that multiline str can be correctly indented
188+
multiline_str = """
189+
test
190+
"""
191+
return multiline_str
192+
183193

184194
def function_with_typehint_comment_not_inline(x=None, *y, z, **kwargs):
185195
# type: (Union[str, bytes], *str, bytes, **int) -> None

tests/test_sphinx_autodoc_typehints.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,10 @@ class dummy_module.ClassWithTypehints(x)
447447
Return type:
448448
"int"
449449
450+
method_without_typehint(x)
451+
452+
Method docstring.
453+
450454
dummy_module.function_with_typehint_comment_not_inline(x=None, *y, z, **kwargs)
451455
452456
Function docstring.

0 commit comments

Comments
 (0)