[tfdbg] Fix source_utils_test in Python 3.8+

This is related to https://bugs.python.org/issue12458

In python 3.8, traceback reports the first instead of last line in
a multi-line continuation block.

Certain parts of source_utils_test.py assume that traceback always
returns the last line, which is true all the way up to 3.7.

In order to fix this, we use the `ast` module to extract the lineno
of the first line in a multi-line continuation block.

PiperOrigin-RevId: 312067389
Change-Id: I8a3ac129b3d75230a3eedd64c3605779dcab5336
This commit is contained in:
Shanqing Cai 2020-05-18 06:28:20 -07:00 committed by TensorFlower Gardener
parent b2f3e8f563
commit fb416f16e2
2 changed files with 37 additions and 2 deletions

View File

@ -840,7 +840,6 @@ py_test(
python_version = "PY3",
srcs_version = "PY2AND3",
tags = [
"no_oss_py38", #TODO(b/151449908)
"no_windows",
],
deps = [

View File

@ -18,7 +18,9 @@ from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import ast
import os
import sys
import tempfile
import zipfile
@ -43,7 +45,41 @@ from tensorflow.python.util import tf_inspect
def line_number_above():
return tf_inspect.stack()[1][2] - 1
"""Get lineno of the AST node immediately above this function's call site.
It is assumed that there is no empty line(s) between the call site and the
preceding AST node.
Returns:
The lineno of the preceding AST node, at the same level of the AST.
If the preceding AST spans multiple lines:
- In Python 3.8+, the lineno of the first line is returned.
- In older Python versions, the lineno of the last line is returned.
"""
# https://bugs.python.org/issue12458: In Python 3.8, traceback started
# to return the lineno of the first line of a multi-line continuation block,
# instead of that of the last line. Therefore, in Python 3.8+, we use `ast` to
# get the lineno of the first line.
call_site_lineno = tf_inspect.stack()[1][2]
if sys.version_info < (3, 8):
return call_site_lineno - 1
else:
with open(__file__, "rb") as f:
source_text = f.read().decode("utf-8")
source_tree = ast.parse(source_text)
prev_node = _find_preceding_ast_node(source_tree, call_site_lineno)
return prev_node.lineno
def _find_preceding_ast_node(node, lineno):
"""Find the ast node immediately before and not including lineno."""
for i, child_node in enumerate(node.body):
if child_node.lineno == lineno:
return node.body[i - 1]
if hasattr(child_node, "body"):
found_node = _find_preceding_ast_node(child_node, lineno)
if found_node:
return found_node
class GuessIsTensorFlowLibraryTest(test_util.TensorFlowTestCase):