• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2015 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"""A simple stack that associates filename and line numbers with each object."""
16
17from __future__ import absolute_import
18from __future__ import division
19from __future__ import print_function
20
21import inspect
22
23
24class TraceableObject(object):
25  """Wrap an object together with its the code definition location."""
26
27  # Return codes for the set_filename_and_line_from_caller() method.
28  SUCCESS, HEURISTIC_USED, FAILURE = (0, 1, 2)
29
30  def __init__(self, obj, filename=None, lineno=None):
31    self.obj = obj
32    self.filename = filename
33    self.lineno = lineno
34
35  def set_filename_and_line_from_caller(self, offset=0):
36    """Set filename and line using the caller's stack frame.
37
38    If the requested stack information is not available, a heuristic may
39    be applied and self.HEURISTIC USED will be returned.  If the heuristic
40    fails then no change will be made to the filename and lineno members
41    (None by default) and self.FAILURE will be returned.
42
43    Args:
44      offset: Integer.  If 0, the caller's stack frame is used.  If 1,
45          the caller's caller's stack frame is used.  Larger values are
46          permissible but if out-of-range (larger than the number of stack
47          frames available) the outermost stack frame will be used.
48
49    Returns:
50      TraceableObject.SUCCESS if appropriate stack information was found,
51      TraceableObject.HEURISTIC_USED if the offset was larger than the stack,
52      and TraceableObject.FAILURE if the stack was empty.
53    """
54    retcode = self.SUCCESS
55    frame = inspect.currentframe()
56    # Offset is defined in "Args" as relative to the caller. We are one frame
57    # beyond the caller.
58    for _ in range(offset + 1):
59      parent = frame.f_back
60      if parent is None:
61        # If the offset is too large then we use the largest offset possible.
62        retcode = self.HEURISTIC_USED
63        break
64      frame = parent
65    self.filename = frame.f_code.co_filename
66    self.lineno = frame.f_lineno
67    return retcode
68
69  def copy_metadata(self):
70    """Return a TraceableObject like this one, but without the object."""
71    return self.__class__(None, filename=self.filename, lineno=self.lineno)
72
73
74class TraceableStack(object):
75  """A stack of TraceableObjects."""
76
77  def __init__(self, existing_stack=None):
78    """Constructor.
79
80    Args:
81      existing_stack: [TraceableObject, ...] If provided, this object will
82        set its new stack to a SHALLOW COPY of existing_stack.
83    """
84    self._stack = existing_stack[:] if existing_stack else []
85
86  def push_obj(self, obj, offset=0):
87    """Add object to the stack and record its filename and line information.
88
89    Args:
90      obj: An object to store on the stack.
91      offset: Integer.  If 0, the caller's stack frame is used.  If 1,
92          the caller's caller's stack frame is used.
93
94    Returns:
95      TraceableObject.SUCCESS if appropriate stack information was found,
96      TraceableObject.HEURISTIC_USED if the stack was smaller than expected,
97      and TraceableObject.FAILURE if the stack was empty.
98    """
99    traceable_obj = TraceableObject(obj)
100    self._stack.append(traceable_obj)
101    # Offset is defined in "Args" as relative to the caller.  We are 1 frame
102    # beyond the caller and need to compensate.
103    return traceable_obj.set_filename_and_line_from_caller(offset + 1)
104
105  def pop_obj(self):
106    """Remove last-inserted object and return it, without filename/line info."""
107    return self._stack.pop().obj
108
109  def peek_top_obj(self):
110    """Return the most recent stored object."""
111    return self._stack[-1].obj
112
113  def peek_objs(self):
114    """Return iterator over stored objects ordered newest to oldest."""
115    return (t_obj.obj for t_obj in reversed(self._stack))
116
117  def peek_traceable_objs(self):
118    """Return iterator over stored TraceableObjects ordered newest to oldest."""
119    return reversed(self._stack)
120
121  def __len__(self):
122    """Return number of items on the stack, and used for truth-value testing."""
123    return len(self._stack)
124
125  def copy(self):
126    """Return a copy of self referencing the same objects but in a new list.
127
128    This method is implemented to support thread-local stacks.
129
130    Returns:
131      TraceableStack with a new list that holds existing objects.
132    """
133    return TraceableStack(self._stack)
134