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 21from tensorflow.python.util import tf_stack 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 # Offset is defined in "Args" as relative to the caller. We are one frame 55 # beyond the caller. 56 local_offset = offset + 1 57 58 frame_records = tf_stack.extract_stack() 59 if not frame_records: 60 return self.FAILURE 61 if len(frame_records) > local_offset: 62 # Negative indexing is one-indexed instead of zero-indexed. 63 negative_offset = -(local_offset + 1) 64 self.filename, self.lineno = frame_records[negative_offset][:2] 65 return self.SUCCESS 66 else: 67 # If the offset is too large then we use the largest offset possible, 68 # meaning we use the outermost stack frame at index 0. 69 self.filename, self.lineno = frame_records[0][:2] 70 return self.HEURISTIC_USED 71 72 def copy_metadata(self): 73 """Return a TraceableObject like this one, but without the object.""" 74 return self.__class__(None, filename=self.filename, lineno=self.lineno) 75 76 77class TraceableStack(object): 78 """A stack of TraceableObjects.""" 79 80 def __init__(self, existing_stack=None): 81 """Constructor. 82 83 Args: 84 existing_stack: [TraceableObject, ...] If provided, this object will 85 set its new stack to a SHALLOW COPY of existing_stack. 86 """ 87 self._stack = existing_stack[:] if existing_stack else [] 88 89 def push_obj(self, obj, offset=0): 90 """Add object to the stack and record its filename and line information. 91 92 Args: 93 obj: An object to store on the stack. 94 offset: Integer. If 0, the caller's stack frame is used. If 1, 95 the caller's caller's stack frame is used. 96 97 Returns: 98 TraceableObject.SUCCESS if appropriate stack information was found, 99 TraceableObject.HEURISTIC_USED if the stack was smaller than expected, 100 and TraceableObject.FAILURE if the stack was empty. 101 """ 102 traceable_obj = TraceableObject(obj) 103 self._stack.append(traceable_obj) 104 # Offset is defined in "Args" as relative to the caller. We are 1 frame 105 # beyond the caller and need to compensate. 106 return traceable_obj.set_filename_and_line_from_caller(offset + 1) 107 108 def pop_obj(self): 109 """Remove last-inserted object and return it, without filename/line info.""" 110 return self._stack.pop().obj 111 112 def peek_objs(self): 113 """Return list of stored objects ordered newest to oldest.""" 114 return [t_obj.obj for t_obj in reversed(self._stack)] 115 116 def peek_traceable_objs(self): 117 """Return list of stored TraceableObjects ordered newest to oldest.""" 118 return list(reversed(self._stack)) 119 120 def __len__(self): 121 """Return number of items on the stack, and used for truth-value testing.""" 122 return len(self._stack) 123 124 def copy(self): 125 """Return a copy of self referencing the same objects but in a new list. 126 127 This method is implemented to support thread-local stacks. 128 129 Returns: 130 TraceableStack with a new list that holds existing objects. 131 """ 132 return TraceableStack(self._stack) 133