Skip to content

Commit fe6e55d

Browse files
committed
Add management commands from MODULES and SUBMODULES to help
1 parent 9bf5ce4 commit fe6e55d

File tree

2 files changed

+62
-1
lines changed

2 files changed

+62
-1
lines changed

src/management_commands/core.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
from __future__ import annotations
22

3+
import contextlib
4+
import importlib
5+
import pkgutil
36
from contextlib import suppress
47

58
from django.apps.registry import apps
@@ -27,6 +30,49 @@ def import_command_class(dotted_path: str) -> type[BaseCommand]:
2730
return command_class
2831

2932

33+
def _discover_commands_in_module(module: str) -> list[str]:
34+
commands: list[str] = []
35+
try:
36+
files_in_dir = [
37+
name
38+
for _, name, is_pkg in pkgutil.iter_modules(
39+
importlib.import_module(module).__path__,
40+
)
41+
if not is_pkg and not name.startswith("_")
42+
]
43+
except ImportError: # module doesn't exist
44+
return commands
45+
46+
for file in files_in_dir:
47+
with (
48+
contextlib.suppress(CommandImportError),
49+
contextlib.suppress(CommandTypeError),
50+
):
51+
import_command_class(f"{module}.{file}.Command")
52+
commands.append(file)
53+
54+
return commands
55+
56+
57+
def get_commands_from_modules_and_submodules() -> dict[str, list[str]]:
58+
commands = {}
59+
for module in settings.MODULES:
60+
if module_commands := _discover_commands_in_module(module):
61+
commands[module] = module_commands
62+
63+
for app in apps.get_app_configs():
64+
for submodule in settings.SUBMODULES:
65+
if app.name == "django.core" or submodule == "management.commands":
66+
continue
67+
68+
if module_commands := _discover_commands_in_module(
69+
f"{app.name}.{submodule}",
70+
):
71+
commands[app.name] = module_commands
72+
73+
return commands
74+
75+
3076
def get_command_paths(name: str, app_label: str | None = None) -> list[str]:
3177
if not app_label:
3278
app_names = [

src/management_commands/management.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
from __future__ import annotations
22

3+
import itertools
34
import sys
45
from typing import TYPE_CHECKING
56

67
from django.core.management import ManagementUtility as BaseManagementUtility
78
from django.core.management.color import color_style
89

910
from .conf import settings
10-
from .core import import_command_class, load_command_class
11+
from .core import (
12+
get_commands_from_modules_and_submodules,
13+
import_command_class,
14+
load_command_class,
15+
)
1116

1217
if TYPE_CHECKING:
1318
from django.core.management.base import BaseCommand
@@ -43,11 +48,21 @@ def main_help_text(self, commands_only: bool = False) -> str:
4348
if (aliases := settings.ALIASES)
4449
else []
4550
)
51+
modules = get_commands_from_modules_and_submodules()
52+
modules_usage = (
53+
[
54+
style.NOTICE(f"[django-management-commands: {module}]"),
55+
*[f" {file}" for file in modules[module]],
56+
"",
57+
]
58+
for module in modules
59+
)
4660

4761
usage_list = usage.split("\n")
4862
usage_list.append("")
4963
usage_list.extend(commands_usage)
5064
usage_list.extend(aliases_usage)
65+
usage_list.extend(itertools.chain(*modules_usage))
5166

5267
return "\n".join(usage_list)
5368

0 commit comments

Comments
 (0)