From fe6e55db50a54ba3c5a83c21ee22174659291c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikul=C3=A1=C5=A1=20Poul?= Date: Mon, 30 Sep 2024 18:12:47 +0100 Subject: [PATCH] Add management commands from MODULES and SUBMODULES to help --- src/management_commands/core.py | 46 +++++++++++++++++++++++++++ src/management_commands/management.py | 17 +++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/management_commands/core.py b/src/management_commands/core.py index 86e0b05..ef30f9a 100644 --- a/src/management_commands/core.py +++ b/src/management_commands/core.py @@ -1,5 +1,8 @@ from __future__ import annotations +import contextlib +import importlib +import pkgutil from contextlib import suppress from django.apps.registry import apps @@ -27,6 +30,49 @@ def import_command_class(dotted_path: str) -> type[BaseCommand]: return command_class +def _discover_commands_in_module(module: str) -> list[str]: + commands: list[str] = [] + try: + files_in_dir = [ + name + for _, name, is_pkg in pkgutil.iter_modules( + importlib.import_module(module).__path__, + ) + if not is_pkg and not name.startswith("_") + ] + except ImportError: # module doesn't exist + return commands + + for file in files_in_dir: + with ( + contextlib.suppress(CommandImportError), + contextlib.suppress(CommandTypeError), + ): + import_command_class(f"{module}.{file}.Command") + commands.append(file) + + return commands + + +def get_commands_from_modules_and_submodules() -> dict[str, list[str]]: + commands = {} + for module in settings.MODULES: + if module_commands := _discover_commands_in_module(module): + commands[module] = module_commands + + for app in apps.get_app_configs(): + for submodule in settings.SUBMODULES: + if app.name == "django.core" or submodule == "management.commands": + continue + + if module_commands := _discover_commands_in_module( + f"{app.name}.{submodule}", + ): + commands[app.name] = module_commands + + return commands + + def get_command_paths(name: str, app_label: str | None = None) -> list[str]: if not app_label: app_names = [ diff --git a/src/management_commands/management.py b/src/management_commands/management.py index c046422..0a10a60 100644 --- a/src/management_commands/management.py +++ b/src/management_commands/management.py @@ -1,5 +1,6 @@ from __future__ import annotations +import itertools import sys from typing import TYPE_CHECKING @@ -7,7 +8,11 @@ from django.core.management.color import color_style from .conf import settings -from .core import import_command_class, load_command_class +from .core import ( + get_commands_from_modules_and_submodules, + import_command_class, + load_command_class, +) if TYPE_CHECKING: from django.core.management.base import BaseCommand @@ -43,11 +48,21 @@ def main_help_text(self, commands_only: bool = False) -> str: if (aliases := settings.ALIASES) else [] ) + modules = get_commands_from_modules_and_submodules() + modules_usage = ( + [ + style.NOTICE(f"[django-management-commands: {module}]"), + *[f" {file}" for file in modules[module]], + "", + ] + for module in modules + ) usage_list = usage.split("\n") usage_list.append("") usage_list.extend(commands_usage) usage_list.extend(aliases_usage) + usage_list.extend(itertools.chain(*modules_usage)) return "\n".join(usage_list)