summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2018-10-30 11:16:02 (GMT)
committerGitHub <noreply@github.com>2018-10-30 11:16:02 (GMT)
commit95b6acf951fa7f503a3cc5ce7d969d7bcf2f95c9 (patch)
tree8f6c2e5a36ae932599e81ba3a226360ca59b19d7 /Lib
parentb83d917fafd87e4130f9c7d5209ad2debc7219cd (diff)
downloadcpython-95b6acf951fa7f503a3cc5ce7d969d7bcf2f95c9.zip
cpython-95b6acf951fa7f503a3cc5ce7d969d7bcf2f95c9.tar.gz
cpython-95b6acf951fa7f503a3cc5ce7d969d7bcf2f95c9.tar.bz2
bpo-34876: Change the lineno of the AST for decorated function and class. (GH-9731)
It was overridden by the lineno of the first decorator. Now it is the lineno of 'def' or 'class'.
Diffstat (limited to 'Lib')
-rw-r--r--Lib/test/test_ast.py16
-rw-r--r--Lib/test/test_trace.py44
2 files changed, 58 insertions, 2 deletions
diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py
index 4bbdc3b..0d51b11 100644
--- a/Lib/test/test_ast.py
+++ b/Lib/test/test_ast.py
@@ -124,6 +124,12 @@ exec_tests = [
"{*{1, 2}, 3}",
# Asynchronous comprehensions
"async def f():\n [i async for b in c]",
+ # Decorated FunctionDef
+ "@deco1\n@deco2()\ndef f(): pass",
+ # Decorated AsyncFunctionDef
+ "@deco1\n@deco2()\nasync def f(): pass",
+ # Decorated ClassDef
+ "@deco1\n@deco2()\nclass C: pass",
]
# These are compiled through "single"
@@ -203,13 +209,16 @@ class AST_Tests(unittest.TestCase):
return
if isinstance(ast_node, (ast.expr, ast.stmt, ast.excepthandler)):
node_pos = (ast_node.lineno, ast_node.col_offset)
- self.assertTrue(node_pos >= parent_pos)
+ self.assertGreaterEqual(node_pos, parent_pos)
parent_pos = (ast_node.lineno, ast_node.col_offset)
for name in ast_node._fields:
value = getattr(ast_node, name)
if isinstance(value, list):
+ first_pos = parent_pos
+ if value and name == 'decorator_list':
+ first_pos = (value[0].lineno, value[0].col_offset)
for child in value:
- self._assertTrueorder(child, parent_pos)
+ self._assertTrueorder(child, first_pos)
elif value is not None:
self._assertTrueorder(value, parent_pos)
@@ -1289,6 +1298,9 @@ exec_results = [
('Module', [('Expr', (1, 0), ('Dict', (1, 0), [None, ('Constant', (1, 10), 2)], [('Dict', (1, 3), [('Constant', (1, 4), 1)], [('Constant', (1, 6), 2)]), ('Constant', (1, 12), 3)]))]),
('Module', [('Expr', (1, 0), ('Set', (1, 0), [('Starred', (1, 1), ('Set', (1, 2), [('Constant', (1, 3), 1), ('Constant', (1, 6), 2)]), ('Load',)), ('Constant', (1, 10), 3)]))]),
('Module', [('AsyncFunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('Expr', (2, 1), ('ListComp', (2, 2), ('Name', (2, 2), 'i', ('Load',)), [('comprehension', ('Name', (2, 14), 'b', ('Store',)), ('Name', (2, 19), 'c', ('Load',)), [], 1)]))], [], None)]),
+('Module', [('FunctionDef', (3, 0), 'f', ('arguments', [], None, [], [], None, []), [('Pass', (3, 9))], [('Name', (1, 1), 'deco1', ('Load',)), ('Call', (2, 0), ('Name', (2, 1), 'deco2', ('Load',)), [], [])], None)]),
+('Module', [('AsyncFunctionDef', (3, 0), 'f', ('arguments', [], None, [], [], None, []), [('Pass', (3, 15))], [('Name', (1, 1), 'deco1', ('Load',)), ('Call', (2, 0), ('Name', (2, 1), 'deco2', ('Load',)), [], [])], None)]),
+('Module', [('ClassDef', (3, 0), 'C', [], [], [('Pass', (3, 9))], [('Name', (1, 1), 'deco1', ('Load',)), ('Call', (2, 0), ('Name', (2, 1), 'deco2', ('Load',)), [], [])])]),
]
single_results = [
('Interactive', [('Expr', (1, 0), ('BinOp', (1, 0), ('Constant', (1, 0), 1), ('Add',), ('Constant', (1, 2), 2)))]),
diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py
index 63f4741..5c333b7 100644
--- a/Lib/test/test_trace.py
+++ b/Lib/test/test_trace.py
@@ -75,6 +75,19 @@ def traced_caller_list_comprehension():
mylist = [traced_doubler(i) for i in range(k)]
return mylist
+def traced_decorated_function():
+ def decorator1(f):
+ return f
+ def decorator_fabric():
+ def decorator2(f):
+ return f
+ return decorator2
+ @decorator1
+ @decorator_fabric()
+ def func():
+ pass
+ func()
+
class TracedClass(object):
def __init__(self, x):
@@ -172,6 +185,24 @@ class TestLineCounts(unittest.TestCase):
}
self.assertEqual(self.tracer.results().counts, expected)
+ def test_traced_decorated_function(self):
+ self.tracer.runfunc(traced_decorated_function)
+
+ firstlineno = get_firstlineno(traced_decorated_function)
+ expected = {
+ (self.my_py_filename, firstlineno + 1): 1,
+ (self.my_py_filename, firstlineno + 2): 1,
+ (self.my_py_filename, firstlineno + 3): 1,
+ (self.my_py_filename, firstlineno + 4): 1,
+ (self.my_py_filename, firstlineno + 5): 1,
+ (self.my_py_filename, firstlineno + 6): 1,
+ (self.my_py_filename, firstlineno + 7): 1,
+ (self.my_py_filename, firstlineno + 8): 1,
+ (self.my_py_filename, firstlineno + 9): 1,
+ (self.my_py_filename, firstlineno + 10): 1,
+ (self.my_py_filename, firstlineno + 11): 1,
+ }
+ self.assertEqual(self.tracer.results().counts, expected)
def test_linear_methods(self):
# XXX todo: later add 'static_method_linear' and 'class_method_linear'
@@ -189,6 +220,7 @@ class TestLineCounts(unittest.TestCase):
}
self.assertEqual(tracer.results().counts, expected)
+
class TestRunExecCounts(unittest.TestCase):
"""A simple sanity test of line-counting, via runctx (exec)"""
def setUp(self):
@@ -263,6 +295,18 @@ class TestFuncs(unittest.TestCase):
}
self.assertEqual(self.tracer.results().calledfuncs, expected)
+ def test_traced_decorated_function(self):
+ self.tracer.runfunc(traced_decorated_function)
+
+ expected = {
+ self.filemod + ('traced_decorated_function',): 1,
+ self.filemod + ('decorator_fabric',): 1,
+ self.filemod + ('decorator2',): 1,
+ self.filemod + ('decorator1',): 1,
+ self.filemod + ('func',): 1,
+ }
+ self.assertEqual(self.tracer.results().calledfuncs, expected)
+
class TestCallers(unittest.TestCase):
"""White-box testing of callers tracing"""