diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0ac25c776..172c4ddeb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,7 +43,7 @@ repos: - id: prettier types_or: [javascript, css] - repo: https://github.com/pre-commit/mirrors-eslint - rev: v8.13.0 + rev: v8.14.0 hooks: - id: eslint files: \.js?$ diff --git a/README.rst b/README.rst index 66cca214b..d146726d5 100644 --- a/README.rst +++ b/README.rst @@ -44,7 +44,7 @@ Here's a screenshot of the toolbar in action: In addition to the built-in panels, a number of third-party panels are contributed by the community. -The current stable version of the Debug Toolbar is 3.3.0. It works on +The current stable version of the Debug Toolbar is 3.4.0. It works on Django ≥ 3.2. Documentation, including installation and configuration instructions, is diff --git a/debug_toolbar/__init__.py b/debug_toolbar/__init__.py index 0e749bdc0..e085bea73 100644 --- a/debug_toolbar/__init__.py +++ b/debug_toolbar/__init__.py @@ -4,7 +4,7 @@ # Do not use pkg_resources to find the version but set it here directly! # see issue #1446 -VERSION = "3.3.0" +VERSION = "3.4.0" # Code that discovers files or modules in INSTALLED_APPS imports this module. urls = "debug_toolbar.urls", APP_NAME diff --git a/debug_toolbar/panels/sql/tracking.py b/debug_toolbar/panels/sql/tracking.py index e3b225e9a..93304b21f 100644 --- a/debug_toolbar/panels/sql/tracking.py +++ b/debug_toolbar/panels/sql/tracking.py @@ -13,7 +13,10 @@ except ImportError: PostgresJson = None -recording = contextvars.ContextVar("debug-toolbar-recording", default=True) +# Prevents SQL queries from being sent to the DB. It's used +# by the TemplatePanel to prevent the toolbar from issuing +# additional queries. +allow_sql = contextvars.ContextVar("debug-toolbar-allow-sql", default=True) class SQLQueryTriggered(Exception): @@ -32,7 +35,7 @@ def cursor(*args, **kwargs): # See: # https://github.com/jazzband/django-debug-toolbar/pull/615 # https://github.com/jazzband/django-debug-toolbar/pull/896 - if recording.get(): + if allow_sql.get(): wrapper = NormalCursorWrapper else: wrapper = ExceptionCursorWrapper @@ -43,7 +46,7 @@ def chunked_cursor(*args, **kwargs): # solves https://github.com/jazzband/django-debug-toolbar/issues/1239 cursor = connection._djdt_chunked_cursor(*args, **kwargs) if not isinstance(cursor, BaseCursorWrapper): - if recording.get(): + if allow_sql.get(): wrapper = NormalCursorWrapper else: wrapper = ExceptionCursorWrapper diff --git a/debug_toolbar/panels/templates/panel.py b/debug_toolbar/panels/templates/panel.py index 0615a7601..1c2c96e09 100644 --- a/debug_toolbar/panels/templates/panel.py +++ b/debug_toolbar/panels/templates/panel.py @@ -13,7 +13,7 @@ from django.utils.translation import gettext_lazy as _ from debug_toolbar.panels import Panel -from debug_toolbar.panels.sql.tracking import SQLQueryTriggered, recording +from debug_toolbar.panels.sql.tracking import SQLQueryTriggered, allow_sql from debug_toolbar.panels.templates import views # Monkey-patch to enable the template_rendered signal. The receiver returns @@ -118,7 +118,7 @@ def _store_template_info(self, sender, **kwargs): value.model._meta.label, ) else: - token = recording.set(False) + token = allow_sql.set(False) try: saferepr(value) # this MAY trigger a db query except SQLQueryTriggered: @@ -130,7 +130,7 @@ def _store_template_info(self, sender, **kwargs): else: temp_layer[key] = value finally: - recording.reset(token) + allow_sql.reset(token) pformatted = pformat(temp_layer) self.pformat_layers.append((context_layer, pformatted)) context_list.append(pformatted) diff --git a/debug_toolbar/utils.py b/debug_toolbar/utils.py index ae4d49168..6b80c5af0 100644 --- a/debug_toolbar/utils.py +++ b/debug_toolbar/utils.py @@ -72,10 +72,17 @@ def render_stacktrace(trace): show_locals = dt_settings.get_config()["ENABLE_STACKTRACES_LOCALS"] html = "" for abspath, lineno, func, code, locals_ in trace: - directory, filename = abspath.rsplit(os.path.sep, 1) + if os.path.sep in abspath: + directory, filename = abspath.rsplit(os.path.sep, 1) + # We want the separator to appear in the UI so add it back. + directory += os.path.sep + else: + # abspath could be something like "" + directory = "" + filename = abspath html += format_html( ( - '{}/' + '{}' + '{} in' + ' {}' + '({})\n' diff --git a/docs/changes.rst b/docs/changes.rst index 0f1b37ae6..bad3bc033 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -1,8 +1,14 @@ Change log ========== -Next version ------------- +3.4.0 (2022-05-03) +------------------ + +* Fixed issue of stacktrace having frames that have no path to the file, + but are instead a string of the code such as + ``''``. +* Renamed internal SQL tracking context var from ``recording`` to + ``allow_sql``. 3.3.0 (2022-04-28) ------------------ diff --git a/docs/conf.py b/docs/conf.py index 7a49fd3fb..6bf4770dc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -25,7 +25,7 @@ copyright = copyright.format(datetime.date.today().year) # The full version, including alpha/beta/rc tags -release = "3.3.0" +release = "3.4.0" # -- General configuration --------------------------------------------------- diff --git a/setup.cfg b/setup.cfg index 66f2bc47f..b984e23cc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = django-debug-toolbar -version = 3.3.0 +version = 3.4.0 description = A configurable set of panels that display various debug information about the current request/response. long_description = file: README.rst long_description_content_type = text/x-rst diff --git a/tests/panels/test_sql.py b/tests/panels/test_sql.py index 2445827c7..9824a1bec 100644 --- a/tests/panels/test_sql.py +++ b/tests/panels/test_sql.py @@ -18,7 +18,6 @@ from ..base import BaseTestCase from ..models import PostgresJSON -from ..sync import database_sync_to_async def sql_call(use_iterator=False): @@ -98,19 +97,21 @@ async def test_cursor_wrapper_async(self, mock_wrapper): wraps=sql_tracking.NormalCursorWrapper, ) async def test_cursor_wrapper_asyncio_ctx(self, mock_wrapper): - self.assertTrue(sql_tracking.recording.get()) + self.assertTrue(sql_tracking.allow_sql.get()) await sync_to_async(sql_call)() async def task(): - sql_tracking.recording.set(False) - # Calling this in another context requires the db connections - # to be closed properly. - await database_sync_to_async(sql_call)() + sql_tracking.allow_sql.set(False) + # By disabling sql_tracking.allow_sql, we are indicating that any + # future SQL queries should be stopped. If SQL query occurs, + # it raises an exception. + with self.assertRaises(sql_tracking.SQLQueryTriggered): + await sync_to_async(sql_call)() # Ensure this is called in another context await asyncio.create_task(task()) # Because it was called in another context, it should not have affected ours - self.assertTrue(sql_tracking.recording.get()) + self.assertTrue(sql_tracking.allow_sql.get()) self.assertEqual(mock_wrapper.call_count, 1) def test_generate_server_timing(self): diff --git a/tests/test_utils.py b/tests/test_utils.py index fa1312482..9cfc33bc7 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,6 @@ import unittest -from debug_toolbar.utils import get_name_from_obj +from debug_toolbar.utils import get_name_from_obj, render_stacktrace class GetNameFromObjTestCase(unittest.TestCase): @@ -21,3 +21,29 @@ class A: res = get_name_from_obj(A) self.assertEqual(res, "tests.test_utils.A") + + +class RenderStacktraceTestCase(unittest.TestCase): + def test_importlib_path_issue_1612(self): + trace = [ + ("/server/app.py", 1, "foo", ["code line 1", "code line 2"], {"foo": "bar"}) + ] + result = render_stacktrace(trace) + self.assertIn('/server/', result) + self.assertIn('app.py in', result) + + trace = [ + ( + "", + 1, + "foo", + ["code line 1", "code line 2"], + {"foo": "bar"}, + ) + ] + result = render_stacktrace(trace) + self.assertIn('', result) + self.assertIn( + '<frozen importlib._bootstrap> in', + result, + )