• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2021 The TensorFlow Authors. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ==============================================================================
15"""Utilities related to TensorFlow exception stack trace prettifying."""
16
17import os
18import sys
19import threading
20import traceback
21import types
22from tensorflow.python.util import tf_decorator
23from tensorflow.python.util.tf_export import tf_export
24
25
26_ENABLE_TRACEBACK_FILTERING = threading.local()
27_EXCLUDED_PATHS = (
28    os.path.abspath(os.path.join(__file__, '..', '..')),
29)
30
31
32@tf_export('debugging.is_traceback_filtering_enabled')
33def is_traceback_filtering_enabled():
34  """Check whether traceback filtering is currently enabled.
35
36  See also `tf.debugging.enable_traceback_filtering()` and
37  `tf.debugging.disable_traceback_filtering()`. Note that filtering out
38  internal frames from the tracebacks of exceptions raised by TensorFlow code
39  is the default behavior.
40
41  Returns:
42    True if traceback filtering is enabled
43    (e.g. if `tf.debugging.enable_traceback_filtering()` was called),
44    and False otherwise (e.g. if `tf.debugging.disable_traceback_filtering()`
45    was called).
46  """
47  value = getattr(_ENABLE_TRACEBACK_FILTERING, 'value', True)
48  return value
49
50
51@tf_export('debugging.enable_traceback_filtering')
52def enable_traceback_filtering():
53  """Enable filtering out TensorFlow-internal frames in exception stack traces.
54
55  Raw TensorFlow stack traces involve many internal frames, which can be
56  challenging to read through, while not being actionable for end users.
57  By default, TensorFlow filters internal frames in most exceptions that it
58  raises, to keep stack traces short, readable, and focused on what's
59  actionable for end users (their own code).
60
61  If you have previously disabled traceback filtering via
62  `tf.debugging.disable_traceback_filtering()`, you can re-enable it via
63  `tf.debugging.enable_traceback_filtering()`.
64
65  Raises:
66    RuntimeError: If Python version is not at least 3.7.
67  """
68  if sys.version_info.major != 3 or sys.version_info.minor < 7:
69    raise RuntimeError(
70        f'Traceback filtering is only available with Python 3.7 or higher. '
71        f'This Python version: {sys.version}')
72  global _ENABLE_TRACEBACK_FILTERING
73  _ENABLE_TRACEBACK_FILTERING.value = True
74
75
76@tf_export('debugging.disable_traceback_filtering')
77def disable_traceback_filtering():
78  """Disable filtering out TensorFlow-internal frames in exception stack traces.
79
80  Raw TensorFlow stack traces involve many internal frames, which can be
81  challenging to read through, while not being actionable for end users.
82  By default, TensorFlow filters internal frames in most exceptions that it
83  raises, to keep stack traces short, readable, and focused on what's
84  actionable for end users (their own code).
85
86  Calling `tf.debugging.disable_traceback_filtering` disables this filtering
87  mechanism, meaning that TensorFlow exceptions stack traces will include
88  all frames, in particular TensorFlow-internal ones.
89
90  **If you are debugging a TensorFlow-internal issue, you need to call
91  `tf.debugging.disable_traceback_filtering`**.
92  To re-enable traceback filtering afterwards, you can call
93  `tf.debugging.enable_traceback_filtering()`.
94  """
95  global _ENABLE_TRACEBACK_FILTERING
96  _ENABLE_TRACEBACK_FILTERING.value = False
97
98
99def include_frame(fname):
100  for exclusion in _EXCLUDED_PATHS:
101    if exclusion in fname:
102      return False
103  return True
104
105
106def _process_traceback_frames(tb):
107  new_tb = None
108  tb_list = list(traceback.walk_tb(tb))
109  for f, line_no in reversed(tb_list):
110    if include_frame(f.f_code.co_filename):
111      new_tb = types.TracebackType(new_tb, f, f.f_lasti, line_no)
112  if new_tb is None and tb_list:
113    f, line_no = tb_list[-1]
114    new_tb = types.TracebackType(new_tb, f, f.f_lasti, line_no)
115  return new_tb
116
117
118def filter_traceback(fn):
119  """Decorator to filter out TF-internal stack trace frames in exceptions.
120
121  Raw TensorFlow stack traces involve many internal frames, which can be
122  challenging to read through, while not being actionable for end users.
123  By default, TensorFlow filters internal frames in most exceptions that it
124  raises, to keep stack traces short, readable, and focused on what's
125  actionable for end users (their own code).
126
127  Arguments:
128    fn: The function or method to decorate. Any exception raised within the
129      function will be reraised with its internal stack trace frames filtered
130      out.
131
132  Returns:
133    Decorated function or method.
134  """
135  if sys.version_info.major != 3 or sys.version_info.minor < 7:
136    return fn
137
138  def error_handler(*args, **kwargs):
139    try:
140      if not is_traceback_filtering_enabled():
141        return fn(*args, **kwargs)
142    except NameError:
143      # In some very rare cases,
144      # `is_traceback_filtering_enabled` (from the outer scope) may not be
145      # accessible from inside this function
146      return fn(*args, **kwargs)
147
148    filtered_tb = None
149    try:
150      return fn(*args, **kwargs)
151    except Exception as e:
152      filtered_tb = _process_traceback_frames(e.__traceback__)
153      raise e.with_traceback(filtered_tb) from None
154    finally:
155      del filtered_tb
156
157  return tf_decorator.make_decorator(fn, error_handler)
158