Skip to content

Commit 6bb5102

Browse files
committed
Custom implementation of inspect.stack() which safely handles errors with findsource
1 parent f9679aa commit 6bb5102

File tree

1 file changed

+59
-3
lines changed

1 file changed

+59
-3
lines changed

debug_toolbar/utils/__init__.py

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import inspect
12
import os.path
23
import django
34
import SocketServer
5+
import sys
46

57
from django.conf import settings
68
from django.views.debug import linebreak_iter
@@ -21,7 +23,7 @@ def tidy_stacktrace(stack):
2123
1. Are part of Django (except contrib apps)
2224
2. Are part of SocketServer (used by Django's dev server)
2325
3. Are the last entry (which is part of our stacktracing code)
24-
26+
2527
``stack`` should be a list of frame tuples from ``inspect.stack()``
2628
"""
2729
trace = []
@@ -84,9 +86,63 @@ def get_name_from_obj(obj):
8486
name = obj.__class__.__name__
8587
else:
8688
name = '<unknown>'
87-
89+
8890
if hasattr(obj, '__module__'):
8991
module = obj.__module__
9092
name = '%s.%s' % (module, name)
9193

92-
return name
94+
return name
95+
96+
def getframeinfo(frame, context=1):
97+
"""
98+
Get information about a frame or traceback object.
99+
100+
A tuple of five things is returned: the filename, the line number of
101+
the current line, the function name, a list of lines of context from
102+
the source code, and the index of the current line within that list.
103+
The optional second argument specifies the number of lines of context
104+
to return, which are centered around the current line.
105+
106+
This originally comes from ``inspect`` but is modified to handle issues
107+
with ``findsource()``.
108+
"""
109+
if inspect.istraceback(frame):
110+
lineno = frame.tb_lineno
111+
frame = frame.tb_frame
112+
else:
113+
lineno = frame.f_lineno
114+
if not inspect.isframe(frame):
115+
raise TypeError('arg is not a frame or traceback object')
116+
117+
filename = inspect.getsourcefile(frame) or inspect.getfile(frame)
118+
if context > 0:
119+
start = lineno - 1 - context//2
120+
try:
121+
lines, lnum = inspect.findsource(frame)
122+
except (IOError, IndexError):
123+
lines = index = None
124+
else:
125+
start = max(start, 1)
126+
start = max(0, min(start, len(lines) - context))
127+
lines = lines[start:start+context]
128+
index = lineno - 1 - start
129+
else:
130+
lines = index = None
131+
132+
return inspect.Traceback(filename, lineno, frame.f_code.co_name, lines, index)
133+
134+
def get_stack(context=1):
135+
"""
136+
Get a list of records for a frame and all higher (calling) frames.
137+
138+
Each record contains a frame object, filename, line number, function
139+
name, a list of lines of context, and index within the context.
140+
141+
Modified version of ``inspect.stack()`` which calls our own ``getframeinfo()``
142+
"""
143+
frame = sys._getframe(1)
144+
framelist = []
145+
while frame:
146+
framelist.append((frame,) + getframeinfo(frame, context))
147+
frame = frame.f_back
148+
return framelist

0 commit comments

Comments
 (0)