Comparing versions: export PY38=/usr/local/pyenv/pyenv/versions/3.8.12/bin/python3.8 export PY39=/usr/local/pyenv/pyenv/versions/3.9.10/bin/python3.9 export PP38old=/usr/local/pypy/pypy3.8-v7.3.7-osx64/bin/pypy3 export PP38=/usr/local/pypy/pypy3.8-v7.3.8rc1-osx64/bin/pypy3 export PP39=/usr/local/pypy/pypy3.9-v7.3.8rc1-osx64/bin/pypy3 $ for py in $PY38 $PY39 $PP38old $PP38 $PP39; do $py -m coverage run --debug=pybehave igor.py; done 2>&1 | grep trace trace_decorated_def: True trace_decorator_line_again: False trace_decorated_def: True trace_decorator_line_again: False trace_decorated_def: False trace_decorator_line_again: False trace_decorated_def: False trace_decorator_line_again: False trace_decorated_def: False trace_decorator_line_again: False # t466a_ast.py: import ast import sys def find_function(node, name): if node.__class__.__name__ == "FunctionDef" and node.name == name: return node for node in getattr(node, "body", ()): fnode = find_function(node, name) if fnode is not None: return fnode root_node = ast.parse(open(__file__).read()) func_node = find_function(root_node, "parse") print(func_node.name, func_node.lineno, func_node.end_lineno, tuple(sys.version_info), tuple(getattr(sys, "pypy_version_info", ()))) class Parser(object): @classmethod def parse(cls): formats = [ 5 ] return None Parser.parse() $ for py in $PY38 $PY39 $PP38old $PP38 $PP39; do $py t466a_ast.py; done parse 20 24 (3, 8, 12, 'final', 0) () parse 20 24 (3, 9, 10, 'final', 0) () parse 19 -1 (3, 8, 12, 'final', 0) (7, 3, 7, 'final', 0) parse 19 -1 (3, 8, 12, 'final', 0) (7, 3, 8, 'final', 0) parse 20 24 (3, 9, 10, 'final', 0) (7, 3, 8, 'final', 0) PyPy <=3.8 includes the decorator line in the FunctionDef node PyPy >=3.9 does not include the decorator line in the node PyPy traces the decorator line, but not the def: $ $PP38 -m trace --trace t466a_plain.py --- modulename: t466a_plain, funcname: t466a_plain.py(1): class Parser(object): --- modulename: t466a_plain, funcname: Parser t466a_plain.py(1): class Parser(object): t466a_plain.py(3): @classmethod t466a_plain.py(10): Parser.parse() --- modulename: t466a_plain, funcname: parse t466a_plain.py(5): formats = [ 5 ] t466a_plain.py(8): return None $ $PP39 -m trace --trace t466a_plain.py --- modulename: t466a_plain, funcname: t466a_plain.py(1): class Parser(object): --- modulename: t466a_plain, funcname: Parser t466a_plain.py(1): class Parser(object): t466a_plain.py(3): @classmethod t466a_plain.py(10): Parser.parse() --- modulename: t466a_plain, funcname: parse t466a_plain.py(5): formats = [ 5 ] t466a_plain.py(8): return None CPython traces the decorator and the def: $ $PY39 -m trace --trace t466a_plain.py --- modulename: t466a_plain, funcname: t466a_plain.py(1): class Parser(object): --- modulename: t466a_plain, funcname: Parser t466a_plain.py(1): class Parser(object): t466a_plain.py(3): @classmethod t466a_plain.py(4): def parse(cls): t466a_plain.py(10): Parser.parse() --- modulename: t466a_plain, funcname: parse t466a_plain.py(5): formats = [ 5 ] t466a_plain.py(8): return None