I wrote Django commands to make it easier to debug Celery tasks

I wanted to call tasks synchronously (not asynchronously) in a Django + Celery project to see how they work. I thought it would be easier to call it by specifying a task from Django's management command, so I wrote it.

Place the following files under management / commands / of the application.

run_task.py



import importlib
import inspect
import json
import pdb

from django.core.management.base import BaseCommand


class Command(BaseCommand):
    help = "Django commands that make debugging celery tasks easier"

    def add_arguments(self, parser):
        parser.add_argument(
            "dottedname", help="The dotted name of the callable object."
        )
        parser.add_argument(
            "--task-args",
            default="[]",
            type=json.loads,
            help="Arguments passed to the task. (default: '[]')",
        )
        parser.add_argument(
            "--task-kwargs",
            default="{}",
            type=json.loads,
            help="Keyword arguments passed to the task. (default: '{}')",
        )
        parser.add_argument(
            "--pdb", action="store_true", help="Stop execution by debugger."
        )
        parser.add_argument(
            "--pdb-offset",
            default=0,
            type=int,
            help="Offset for debugger to create breakpoint. (default: 0)",
        )

    def handle(self, **options):
        dotted_list = options["dottedname"].strip().split(".")
        module_name = ".".join(dotted_list[:-1])
        func_name = dotted_list[-1]

        try:
            module = importlib.import_module(module_name)
        except ModuleNotFoundError:
            self.stderr.write(f"No module: {module_name}")
            return

        try:
            func = getattr(module, func_name)
        except AttributeError:
            self.stderr.write(f"No attribute: {func_name} not in {module_name}")
            return

        if not callable(func):
            self.stderr.write(f"Not function: {module_name}.{func_name}")
            return

        if options["pdb"]:
            lineno = inspect.getsourcelines(func)[1] + options["pdb_offset"]
            debugger = pdb.Pdb()
            debugger.set_break(module.__file__, lineno=lineno, funcname=func_name)
            debugger.set_trace()

        result = func(*options["task_args"], **options["task_kwargs"])
        self.stdout.write(f"Return: {json.dumps(result)}")

If you call it like this

$ python manage.py run_task myproject.tasks.example_task --task-args '[1, 2, 3]'

It behaves the same as calling it like this.

  >>> from myproject.tasks import example_task
  >>> example_task(1, 2, 3)

I also tried to start the debugger by specifying --pdb.

$ python manage.py run_task myproject.tasks.example_task --pdb --task-args '[1, 2, 3]'

Then, the debugger starts with the breakpoint set in myproject.tasks.example_task. I wrote it for the title, but Celery didn't really matter.

Put it in gist. https://gist.github.com/TakesxiSximada/d986ef7d8fbf5555d8e12586226dc389

Recommended Posts

I wrote Django commands to make it easier to debug Celery tasks
Expand devicetree source include to make it easier to read
I tried to debug.
How to use Decorator in Django and how to make it
[Git] I tried to make it easier to understand how to use git stash using a concrete example
You who color the log to make it easier to see
I want to make a blog editor with django admin
One liner that formats JSON to make it easier to see
When I try to make Apache SSL, it doesn't start.
I wrote it in Go to understand the SOLID principle
Join csv normalized by Python pandas to make it easier to check
Make it easier to test programs that work with APIs with vcrpy
I want to debug with Python
I want to do it with Python lambda Django, but I will stop
I tried to make a calculator with Tkinter so I will write it
I want to use the Django Debug Toolbar in my Ajax application
[Zaif] I tried to make it easy to trade virtual currencies with Python