Skip to content

Commit ad88086

Browse files
Handle non-JSON keys in toolbar storage
Includes, cache panel regression test for skipped JSON keys.
1 parent 5ff52c7 commit ad88086

6 files changed

Lines changed: 34 additions & 1 deletion

File tree

debug_toolbar/store.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def serialize(data: Any) -> str:
2727
# If this starts throwing an exceptions, consider
2828
# Subclassing DjangoJSONEncoder and using force_str to
2929
# make it JSON serializable.
30-
return json.dumps(data, cls=DebugToolbarJSONEncoder)
30+
return json.dumps(data, cls=DebugToolbarJSONEncoder, skipkeys=True)
3131

3232

3333
def deserialize(data: str) -> Any:

docs/changes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ Pending
55
-------
66

77
* Prevent check from failing when ``ROOT_URLCONF`` is not defined.
8+
* Prevent toolbar storage from failing when serialized panel data contains
9+
mapping keys that are not JSON-compatible.
810
* Prevent debounce race conditions in the history panel for rapid
911
fetch requests.
1012
* Added a note to the prerequisites section of the installation docs

tests/test_integration.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
import re
33
import time
44
import unittest
5+
import warnings
56
from unittest.mock import patch
67

78
import html5lib
89
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
910
from django.core import signing
1011
from django.core.cache import cache
12+
from django.core.cache.backends.base import CacheKeyWarning
1113
from django.db import connection
1214
from django.http import HttpResponse
1315
from django.template.loader import get_template
@@ -209,6 +211,23 @@ def test_low_level_cache_view(self):
209211
len(response.toolbar.get_panel_by_id(CachePanel.panel_id).calls), 1
210212
)
211213

214+
def test_cache_panel_store_skips_non_json_keys(self):
215+
cache.clear()
216+
with warnings.catch_warnings():
217+
warnings.simplefilter("ignore", CacheKeyWarning)
218+
response = self.client.get("/cache_with_non_json_key/")
219+
220+
self.assertEqual(response.status_code, 200)
221+
self.assertEqual(
222+
len(response.toolbar.get_panel_by_id(CachePanel.panel_id).calls), 1
223+
)
224+
225+
request_id = list(get_store().request_ids())[-1]
226+
toolbar = DebugToolbar.fetch(request_id, CachePanel.panel_id)
227+
stats = toolbar.get_panel_by_id(CachePanel.panel_id).get_stats()
228+
self.assertEqual(stats["calls"][0]["name"], "set_many")
229+
self.assertEqual(stats["calls"][0]["args"], [{"foo": "bar"}])
230+
212231
def test_cache_disable_instrumentation(self):
213232
"""
214233
Verify that middleware cache usages before and after

tests/test_store.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ def test_serialize_logs_on_failure(self):
2424
'{"hello": {"foo": "bar"}}',
2525
)
2626

27+
def test_serialize_unexpected(self):
28+
self.assertEqual(
29+
store.serialize({"hello": {str: "this-is-a-string", "foo": "bar"}}),
30+
'{"hello": {"foo": "bar"}}',
31+
)
32+
2733
def test_deserialize(self):
2834
self.assertEqual(
2935
store.deserialize('{"hello": {"foo": "bar"}}'),

tests/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
path("async_execute_sql_concurrently/", views.async_execute_sql_concurrently),
2626
path("cached_view/", views.cached_view),
2727
path("cached_low_level_view/", views.cached_low_level_view),
28+
path("cache_with_non_json_key/", views.cache_with_non_json_key_view),
2829
path("json_view/", views.json_view),
2930
path("redirect/", views.redirect_view),
3031
path("ajax/", views.ajax_view),

tests/views.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ def cached_low_level_view(request):
106106
return render(request, "base.html")
107107

108108

109+
def cache_with_non_json_key_view(request):
110+
cache.set_many({str: "this-is-a-string", "foo": "bar"})
111+
return render(request, "base.html")
112+
113+
109114
def json_view(request):
110115
return JsonResponse({"foo": "bar"})
111116

0 commit comments

Comments
 (0)