diff --git a/AUTHORS b/AUTHORS index c57502ac2..25b55e5ae 100644 --- a/AUTHORS +++ b/AUTHORS @@ -348,6 +348,7 @@ Xixi Zhao Xuan Luong Xuecong Liao Yoav Caspi +Yuval Shimon Zac Hatfield-Dodds Zachary Kneupper Zoltán Máté diff --git a/changelog/9355.bugfix.rst b/changelog/9355.bugfix.rst new file mode 100644 index 000000000..62ea79ea4 --- /dev/null +++ b/changelog/9355.bugfix.rst @@ -0,0 +1 @@ +Fixed error message prints function decorators when using assert in Python 3.9 and above. diff --git a/src/_pytest/_code/source.py b/src/_pytest/_code/source.py index 6f54057c0..208cfb800 100644 --- a/src/_pytest/_code/source.py +++ b/src/_pytest/_code/source.py @@ -149,6 +149,11 @@ def get_statement_startend2(lineno: int, node: ast.AST) -> Tuple[int, Optional[i values: List[int] = [] for x in ast.walk(node): if isinstance(x, (ast.stmt, ast.ExceptHandler)): + # Before Python 3.8, the lineno of a decorated class or function pointed at the decorator. + # Since Python 3.8, the lineno points to the class/def, so need to include the decorators. + if isinstance(x, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)): + for d in x.decorator_list: + values.append(d.lineno - 1) values.append(x.lineno - 1) for name in ("finalbody", "orelse"): val: Optional[List[ast.stmt]] = getattr(x, name, None) diff --git a/testing/code/test_source.py b/testing/code/test_source.py index 53e1bb985..53202ee27 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -618,6 +618,19 @@ def something(): assert str(source) == "def func(): raise ValueError(42)" +def test_decorator() -> None: + s = """\ +def foo(f): + pass + +@foo +def bar(): + pass + """ + source = getstatement(3, s) + assert "@foo" in str(source) + + def XXX_test_expression_multiline() -> None: source = """\ something