|
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