Coverage for python/lsst/pex/config/callStack.py: 79%
46 statements
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-12 01:30 -0800
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-12 01:30 -0800
1# This file is part of pex_config.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This software is dual licensed under the GNU General Public License and also
10# under a 3-clause BSD license. Recipients may choose which of these licenses
11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
12# respectively. If you choose the GPL option then the following text applies
13# (but note that there is still no warranty even if you opt for BSD instead):
14#
15# This program is free software: you can redistribute it and/or modify
16# it under the terms of the GNU General Public License as published by
17# the Free Software Foundation, either version 3 of the License, or
18# (at your option) any later version.
19#
20# This program is distributed in the hope that it will be useful,
21# but WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23# GNU General Public License for more details.
24#
25# You should have received a copy of the GNU General Public License
26# along with this program. If not, see <http://www.gnu.org/licenses/>.
28__all__ = ['getCallerFrame', 'getStackFrame', 'StackFrame', 'getCallStack']
30import inspect
31import linecache
34def getCallerFrame(relative=0):
35 """Get the frame for the user's caller.
37 Parameters
38 ----------
39 relative : `int`, optional
40 Number of frames (0 or more) above the caller to retrieve.
41 Default is 0.
43 Returns
44 -------
45 frame : `__builtin__.Frame`
46 Frame for the caller.
48 Notes
49 -----
50 This function is excluded from the frame.
51 """
52 frame = inspect.currentframe().f_back.f_back # Our caller's caller
53 for ii in range(relative):
54 frame = frame.f_back
55 return frame
58def getStackFrame(relative=0):
59 """Get the `StackFrame` for the user's caller.
61 Parameters
62 ----------
63 relative : `int`, optional
64 Number of frames (0 or more) above the caller to retrieve.
66 Returns
67 -------
68 frame : `StackFrame`
69 Stack frame for the caller.
70 """
71 frame = getCallerFrame(relative + 1)
72 return StackFrame.fromFrame(frame)
75class StackFrame:
76 """A single element of the stack trace.
78 This differs slightly from the standard system mechanisms for getting a
79 stack trace by the fact that it does not look up the source code until it
80 is absolutely necessary, reducing the I/O.
82 Parameters
83 ----------
84 filename : `str`
85 Name of file containing the code being executed.
86 lineno : `int`
87 Line number of file being executed.
88 function : `str`
89 Function name being executed.
90 content : `str`, optional
91 The actual content being executed. If not provided, it will be loaded
92 from the file.
94 Notes
95 -----
96 This differs slightly from the standard system mechanisms for getting a
97 stack trace by the fact that it does not look up the source code until it
98 is absolutely necessary, reducing the I/O.
100 See also
101 --------
102 getStackFrame
103 """
105 _STRIP = "/python/lsst/"
106 """String to strip from the ``filename`` in the constructor."""
108 def __init__(self, filename, lineno, function, content=None):
109 loc = filename.rfind(self._STRIP)
110 if loc > 0:
111 filename = filename[loc + len(self._STRIP):]
112 self.filename = filename
113 self.lineno = lineno
114 self.function = function
115 self._content = content
117 @property
118 def content(self):
119 """Content being executed (loaded on demand) (`str`).
120 """
121 if self._content is None:
122 self._content = linecache.getline(self.filename, self.lineno).strip()
123 return self._content
125 @classmethod
126 def fromFrame(cls, frame):
127 """Construct from a Frame object.
129 Parameters
130 ----------
131 frame : `Frame`
132 Frame object to interpret, such as from `inspect.currentframe`.
134 Returns
135 -------
136 stackFrame : `StackFrame`
137 A `StackFrame` instance.
139 Examples
140 --------
141 `inspect.currentframe` provides a Frame object. This is a convenience
142 constructor to interpret that Frame object:
144 >>> import inspect
145 >>> stackFrame = StackFrame.fromFrame(inspect.currentframe())
146 """
147 filename = frame.f_code.co_filename
148 lineno = frame.f_lineno
149 function = frame.f_code.co_name
150 return cls(filename, lineno, function)
152 def __repr__(self):
153 return "%s(%s, %s, %s)" % (self.__class__.__name__, self.filename, self.lineno, self.function)
155 def format(self, full=False):
156 """Format for printing.
158 Parameters
159 ----------
160 full : `bool`, optional
161 If `True`, output includes the conentent (`StackFrame.content`)
162 being executed. Default is `False`.
164 Returns
165 -------
166 result : `str`
167 Formatted string.
168 """
169 result = " File %s:%s (%s)" % (self.filename, self.lineno, self.function)
170 if full:
171 result += "\n %s" % (self.content,)
172 return result
175def getCallStack(skip=0):
176 """Retrieve the call stack for the caller.
178 Parameters
179 ----------
180 skip : `int`, non-negative
181 Number of stack frames above caller to skip.
183 Returns
184 -------
185 output : `list` of `StackFrame`
186 The call stack. The `list` is ordered with the most recent frame to
187 last.
189 Notes
190 -----
191 This function is excluded from the call stack.
192 """
193 frame = getCallerFrame(skip + 1)
194 stack = []
195 while frame:
196 stack.append(StackFrame.fromFrame(frame))
197 frame = frame.f_back
198 return list(reversed(stack))