import platform import sys from argparse import Namespace from multiprocessing import set_start_method from typing import Any from django.apps.registry import apps from django.core.management.base import BaseCommand, CommandParser from django.db import connections from django.utils.module_loading import import_string, module_has_submodule from dramatiq.cli import main from django_dramatiq_postgres.conf import Conf class Command(BaseCommand): """Run worker""" def add_arguments(self, parser: CommandParser) -> None: parser.add_argument( "--pid-file", action="store", default=None, dest="pid_file", help="PID file", ) parser.add_argument( "--watch", action="store_true", default=False, dest="watch", help="Watch for file changes", ) def handle( self, pid_file: str, watch: bool, verbosity: int, **options: Any, ) -> None: worker = Conf().worker setup, modules = self._discover_tasks_modules() args = Namespace( broker=setup, modules=modules, path=["."], queues=None, log_file=None, skip_logging=True, use_spawn=False, forks=[], worker_shutdown_timeout=600000, watch=None, watch_use_polling=False, include_patterns=["**.py"], exclude_patterns=None, verbose=0, ) if watch: args.watch = worker["watch_folder"] if worker["watch_use_polling"]: args.watch_use_polling = True if processes := worker["processes"]: args.processes = processes if threads := worker["threads"]: args.threads = threads if pid_file is not None: args.pid_file = pid_file args.verbose = verbosity - 1 # > On macOS [...] the fork start method should be considered unsafe # https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods if not platform.system() == "Darwin": set_start_method("fork") connections.close_all() sys.exit(main(args)) # type: ignore[no-untyped-call] def _discover_tasks_modules(self) -> tuple[str, list[str]]: # Does not support a tasks directory autodiscovery = Conf().autodiscovery modules = [] if autodiscovery["enabled"]: for app in apps.get_app_configs(): if autodiscovery["apps_prefix"] and not app.name.startswith( autodiscovery["apps_prefix"] ): continue if module_has_submodule(app.module, autodiscovery["actors_module_name"]): modules.append(f"{app.name}.{autodiscovery['actors_module_name']}") else: modules_callback = autodiscovery["modules_callback"] callback = ( modules_callback if not isinstance(modules_callback, str) else import_string(modules_callback) ) modules.extend(callback()) return autodiscovery["setup_module"], modules