| 
1 | 1 | from __future__ import annotations  | 
2 | 2 | 
 
  | 
 | 3 | +import contextlib  | 
 | 4 | +import importlib  | 
 | 5 | +import pkgutil  | 
3 | 6 | from contextlib import suppress  | 
4 | 7 | 
 
  | 
5 | 8 | from django.apps.registry import apps  | 
@@ -27,6 +30,49 @@ def import_command_class(dotted_path: str) -> type[BaseCommand]:  | 
27 | 30 |     return command_class  | 
28 | 31 | 
 
  | 
29 | 32 | 
 
  | 
 | 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 | + | 
30 | 76 | def get_command_paths(name: str, app_label: str | None = None) -> list[str]:  | 
31 | 77 |     if not app_label:  | 
32 | 78 |         app_names = [  | 
 | 
0 commit comments