Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## Version 1.3.2 - Unreleased

### Fixed
* Correctly find line numbers for decorated `async def` functions (without crashing).


## Version 1.3.1 - Released 2026-03-25

Expand Down
36 changes: 1 addition & 35 deletions src/xdoctest/static_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,7 @@ def _visit_generic_FunctionDef(
# callname = callname + '.fset'
return

# TODO: Is this still necessary in modern Python versions?
lineno = self._workaround_func_lineno(node)
lineno = node.lineno
docstr, doclineno, doclineno_end = self._get_docstring(node)
calldef = CallDefNode(
callname, lineno, docstr, doclineno, doclineno_end, args=node.args
Expand Down Expand Up @@ -714,39 +713,6 @@ def foo():
doclineno_end = None
return (docstr, doclineno, doclineno_end)

def _workaround_func_lineno(self, node):
"""
Finds the correct line for the original function definition even when
decorators are involved.

Example:
>>> source = utils.codeblock(
'''
@bar
@baz
def foo():
'docstr'
''')
>>> self = TopLevelVisitor(source)
>>> node = self.syntax_tree().body[0]
>>> self._workaround_func_lineno(node)
3
"""
# Try and find the lineno of the function definition
# (maybe the fact that its on a decorator is actually right...)
if node.decorator_list:
# Decorators can throw off the line the function is declared on
linex = node.lineno - 1
pattern = r'\s*def\s*' + node.name
# I think this is actually robust
assert self.sourcelines is not None
while not re.match(pattern, self.sourcelines[linex]):
linex += 1
lineno = linex + 1
else:
lineno = node.lineno
return lineno


def parse_static_calldefs(
source: str | None = None, fpath: str | os.PathLike | None = None
Expand Down
Loading
Loading