Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Tweak the get_stack_trace() API
Rename the `depth` argument to `skip` and change the semantics so that
now `skip=0` has the same meaning as `depth=1`.
  • Loading branch information
living180 committed Jun 1, 2022
commit dc8427800a58fcca17b869e8159892824fad7a92
31 changes: 24 additions & 7 deletions debug_toolbar/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,11 +246,12 @@ def get_stack(context=1):
return framelist


def _stack_frames(depth=1):
def _stack_frames(*, skip=0):
skip += 1 # Skip the frame for this generator.
frame = inspect.currentframe()
while frame is not None:
if depth > 0:
depth -= 1
if skip > 0:
skip -= 1
else:
yield frame
frame = frame.f_back
Expand Down Expand Up @@ -279,9 +280,10 @@ def get_source_file(self, frame):

return value

def get_stack_trace(self, *, excluded_modules=None, include_locals=False, depth=1):
def get_stack_trace(self, *, excluded_modules=None, include_locals=False, skip=0):
trace = []
for frame in _stack_frames(depth=depth + 1):
skip += 1 # Skip the frame for this method.
for frame in _stack_frames(skip=skip):
if _is_excluded_frame(frame, excluded_modules):
continue

Expand All @@ -306,18 +308,33 @@ def get_stack_trace(self, *, excluded_modules=None, include_locals=False, depth=
return trace


def get_stack_trace(*, depth=1):
def get_stack_trace(*, skip=0):
"""
Return a processed stack trace for the current call stack.

If the ``ENABLE_STACKTRACES`` setting is False, return an empty :class:`list`.
Otherwise return a :class:`list` of processed stack frame tuples (file name, line
number, function name, source line, frame locals) for the current call stack. The
first entry in the list will be for the bottom of the stack and the last entry will
be for the top of the stack.

``skip`` is an :class:`int` indicating the number of stack frames above the frame
for this function to omit from the stack trace. The default value of ``0`` means
that the entry for the caller of this function will be the last entry in the
returned stack trace.
"""
config = dt_settings.get_config()
if not config["ENABLE_STACKTRACES"]:
return []
skip += 1 # Skip the frame for this function.
stack_trace_recorder = getattr(_local_data, "stack_trace_recorder", None)
if stack_trace_recorder is None:
stack_trace_recorder = _StackTraceRecorder()
_local_data.stack_trace_recorder = stack_trace_recorder
return stack_trace_recorder.get_stack_trace(
excluded_modules=config["HIDE_IN_STACKTRACES"],
include_locals=config["ENABLE_STACKTRACES_LOCALS"],
depth=depth,
skip=skip,
)


Expand Down
18 changes: 18 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import unittest

from django.test import override_settings

import debug_toolbar.utils
from debug_toolbar.utils import (
get_name_from_obj,
get_stack,
get_stack_trace,
render_stacktrace,
tidy_stacktrace,
)
Expand Down Expand Up @@ -55,6 +59,20 @@ def test_importlib_path_issue_1612(self):


class StackTraceTestCase(unittest.TestCase):
@override_settings(DEBUG_TOOLBAR_CONFIG={"HIDE_IN_STACKTRACES": []})
def test_get_stack_trace_skip(self):
stack_trace = get_stack_trace(skip=-1)
self.assertTrue(len(stack_trace) > 2)
self.assertEqual(stack_trace[-1][0], debug_toolbar.utils.__file__)
self.assertEqual(stack_trace[-1][2], "get_stack_trace")
self.assertEqual(stack_trace[-2][0], __file__)
self.assertEqual(stack_trace[-2][2], "test_get_stack_trace_skip")

stack_trace = get_stack_trace()
self.assertTrue(len(stack_trace) > 1)
self.assertEqual(stack_trace[-1][0], __file__)
self.assertEqual(stack_trace[-1][2], "test_get_stack_trace_skip")

def test_deprecated_functions(self):
with self.assertWarns(DeprecationWarning):
stack = get_stack()
Expand Down