Repository: jazzband/django-debug-toolbar

Issue:
ValueError: not enough values to unpack (expected 2, got 1)
Hi,

Thank you very much for this essential library ❤️ 

I just upgraded my project to Django 4.0.4 and now I frequently get the following exception when refreshing the page:

```
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/usr/local/lib/python3.9/site-packages/debug_toolbar/middleware.py", line 67, in __call__
    panel.generate_stats(request, response)
  File "/usr/local/lib/python3.9/site-packages/debug_toolbar/panels/sql/panel.py", line 230, in generate_stats
    query["stacktrace"] = render_stacktrace(query["stacktrace"])
  File "/usr/local/lib/python3.9/site-packages/debug_toolbar/utils.py", line 75, in render_stacktrace
    directory, filename = abspath.rsplit(os.path.sep, 1)
ValueError: not enough values to unpack (expected 2, got 1)
```

The value of `abspath` is `'<frozen importlib._bootstrap>'`.

Let me know if you need further information.

Best regards,
Benoit



Discussion:
I'm getting the same kind of errors during tests after upgrading to 3.3.0, so the problem was likely introduced in this release.
Indeed, I'm using django-debug-toolbar 3.3.0.
Thanks for the report! I'm unable to reproduce this (I tried with Python 3.9 and 3.10 in Linux/WSL)

Do you have a way to print the stack which fails, e.g. by printing the value of `query["stacktrace"]`? That may prove useful.
It's a little hard to reproduce on my end. It only happens during tests (that I've noticed so far, anyway) and only when running pytest with xdist - so the tests running in parallel - and you can't use --pdb when running in parallel. Maybe that information is useful somehow.
The exception occurs every time I hit refresh after a status code 500.

To reproduce:

1. Edit a view to insert a bug (for example, an undefined symbol).
2. Open the corresponding URL in your browser.
3. Wait for the Django error page to show.
4. Remove the bug from the view
5. Refresh the page in the browser
6. No error is expected, yet you can see "not enough values to unpack (expected 2, got 1)"
~So what I think is happening is that `render_stacktrace` is being passed a list of frame data and expects the first ~frame~ element to be the path, but it's actually the frame.~
@bblanchon could you get the other values for `lineno, func, code, locals_` (remove sensitive data from locals_) for the iteration of the loop that causes the error?
@matthiask I don't think we're going to figure out what path and file to show when abspath is set to `<frozen importlib._bootstrap>`. This was probably always an issue but hidden by the Django template rendering system that was removed in #1571.

I think a reasonable solution would be:

```python
if os.path.sep in abspath:
    directory, filename = abspath.rsplit(os.path.sep, 1)
else:
    directory = ""
    filename = abspath
```

I'm working on the PR to add the test to prevent regressions.

Resolving patch:
diff --git a/debug_toolbar/utils.py b/debug_toolbar/utils.py
--- 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 "<frozen importlib._bootstrap>"
+            directory = ""
+            filename = abspath
         html += format_html(
             (
-                '<span class="djdt-path">{}/</span>'
+                '<span class="djdt-path">{}</span>'
                 + '<span class="djdt-file">{}</span> in'
                 + ' <span class="djdt-func">{}</span>'
                 + '(<span class="djdt-lineno">{}</span>)\n'


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- 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('<span class="djdt-path">/server/</span>', result)
+        self.assertIn('<span class="djdt-file">app.py</span> in', result)
+
+        trace = [
+            (
+                "<frozen importlib._bootstrap>",
+                1,
+                "foo",
+                ["code line 1", "code line 2"],
+                {"foo": "bar"},
+            )
+        ]
+        result = render_stacktrace(trace)
+        self.assertIn('<span class="djdt-path"></span>', result)
+        self.assertIn(
+            '<span class="djdt-file">&lt;frozen importlib._bootstrap&gt;</span> in',
+            result,
+        )