Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
87 changes: 87 additions & 0 deletions debug_toolbar/panels/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from django.utils.translation import gettext_lazy as _, ngettext

from debug_toolbar.panels import Panel


class TasksPanel(Panel):
"""
Panel that displays Django tasks queued or executed during the
processing of the request.
"""

title = _("Tasks")
template = "debug_toolbar/panels/tasks.html"

is_async = True

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.queued_tasks = []

@property
def nav_subtitle(self):
num_tasks = self.get_stats()["total_tasks"]
return ngettext(
"%(num_tasks)d task enqueued",
"%(num_tasks)d tasks enqueued",
num_tasks,
) % {"num_tasks": num_tasks}

def generate_stats(self, request, response):
stats = {"tasks": self.queued_tasks, "total_tasks": len(self.queued_tasks)}

self.record_stats(stats)

def enable_instrumentation(self):
"""Hook into task system to collect queued tasks"""
try:
import django

if django.VERSION < (6, 0):
return
from django.tasks import Task

print("[TasksPanel] instrumentation enabled:", hasattr(Task, "enqueue"))

# Store original enqueue method
if hasattr(Task, "enqueue"):
self._original_enqueue = Task.enqueue

def wrapped_enqueue(task, *args, **kwargs):
result = self._original_enqueue(task, *args, **kwargs).return_value
self._record_task(task, args, kwargs, result)
return result

Task.enqueue = wrapped_enqueue
except (ImportError, AttributeError):
pass

def _record_task(self, task, args, kwargs, result):
"""Record a task that was queued"""
task_info = {
"name": getattr(task, "__name__", str(task)),
"args": repr(args) if args else "",
"kwargs": repr(kwargs) if kwargs else "",
}
self.queued_tasks.append(task_info)

def disable_instrumentation(self):
"""Restore original methods"""
try:
from django.tasks import Task

if hasattr(self, "_original_enqueue"):
Task.enqueue = self._original_enqueue
except (ImportError, AttributeError):
pass

def _check_tasks_available(self):
"""Check if Django tasks system is available"""
try:
import django

if django.VERSION < (6, 0):
return False
return True
except (ImportError, AttributeError):
return False
1 change: 1 addition & 0 deletions debug_toolbar/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def get_config():
"debug_toolbar.panels.cache.CachePanel",
"debug_toolbar.panels.signals.SignalsPanel",
"debug_toolbar.panels.community.CommunityPanel",
"debug_toolbar.panels.tasks.TasksPanel",
"debug_toolbar.panels.redirects.RedirectsPanel",
"debug_toolbar.panels.profiling.ProfilingPanel",
]
Expand Down
14 changes: 14 additions & 0 deletions debug_toolbar/templates/debug_toolbar/panels/tasks.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<table>
<thead>
<tr>
<th>Task Info</th>
</tr>
</thead>
<tbody>
{% for task in tasks %}
<tr>
<td>{{ task }}</td>
</tr>
{% endfor %}
</tbody>
</table>
19 changes: 19 additions & 0 deletions example/async_/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
try:
from django.tasks import task
except ImportError:
# Define a fallback decorator
def task(func=None, **kwargs):
def decorator(f):
return f

return decorator if func is None else decorator(func)


@task
def send_welcome_message(message):
return f"Sent message: {message}"


@task
def generate_report(report_id):
return f"Report {report_id} generated"
8 changes: 8 additions & 0 deletions example/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import asyncio

import django
from asgiref.sync import sync_to_async
from django.contrib.auth.models import User
from django.http import JsonResponse
Expand All @@ -20,6 +21,13 @@ def jinja2_view(request):


async def async_home(request):
if django.VERSION >= (6, 0):
from .async_.tasks import generate_report, send_welcome_message

# Queue some tasks
send_welcome_message.enqueue(message="hi there")
generate_report.enqueue(report_id=456)

return await sync_to_async(render)(request, "index.html")


Expand Down
Loading