Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/core/management/__init__.py: 43%
205 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1import functools
2import os
3import pkgutil
4import sys
5from argparse import (
6 _AppendConstAction,
7 _CountAction,
8 _StoreConstAction,
9 _SubParsersAction,
10)
11from collections import defaultdict
12from difflib import get_close_matches
13from importlib import import_module
15import django
16from django.apps import apps
17from django.conf import settings
18from django.core.exceptions import ImproperlyConfigured
19from django.core.management.base import (
20 BaseCommand,
21 CommandError,
22 CommandParser,
23 handle_default_options,
24)
25from django.core.management.color import color_style
26from django.utils import autoreload
29def find_commands(management_dir):
30 """
31 Given a path to a management directory, return a list of all the command
32 names that are available.
33 """
34 command_dir = os.path.join(management_dir, "commands")
35 return [
36 name
37 for _, name, is_pkg in pkgutil.iter_modules([command_dir])
38 if not is_pkg and not name.startswith("_")
39 ]
42def load_command_class(app_name, name):
43 """
44 Given a command name and an application name, return the Command
45 class instance. Allow all errors raised by the import process
46 (ImportError, AttributeError) to propagate.
47 """
48 module = import_module("%s.management.commands.%s" % (app_name, name))
49 return module.Command()
52@functools.lru_cache(maxsize=None)
53def get_commands():
54 """
55 Return a dictionary mapping command names to their callback applications.
57 Look for a management.commands package in django.core, and in each
58 installed application -- if a commands package exists, register all
59 commands in that package.
61 Core commands are always included. If a settings module has been
62 specified, also include user-defined commands.
64 The dictionary is in the format {command_name: app_name}. Key-value
65 pairs from this dictionary can then be used in calls to
66 load_command_class(app_name, command_name)
68 If a specific version of a command must be loaded (e.g., with the
69 startapp command), the instantiated module can be placed in the
70 dictionary in place of the application name.
72 The dictionary is cached on the first call and reused on subsequent
73 calls.
74 """
75 commands = {name: "django.core" for name in find_commands(__path__[0])}
77 if not settings.configured: 77 ↛ 78line 77 didn't jump to line 78, because the condition on line 77 was never true
78 return commands
80 for app_config in reversed(list(apps.get_app_configs())):
81 path = os.path.join(app_config.path, "management")
82 commands.update({name: app_config.name for name in find_commands(path)})
84 return commands
87def call_command(command_name, *args, **options):
88 """
89 Call the given command, with the given options and args/kwargs.
91 This is the primary API you should use for calling specific commands.
93 `command_name` may be a string or a command object. Using a string is
94 preferred unless the command object is required for further processing or
95 testing.
97 Some examples:
98 call_command('migrate')
99 call_command('shell', plain=True)
100 call_command('sqlmigrate', 'myapp')
102 from django.core.management.commands import flush
103 cmd = flush.Command()
104 call_command(cmd, verbosity=0, interactive=False)
105 # Do something with cmd ...
106 """
107 if isinstance(command_name, BaseCommand): 107 ↛ 109line 107 didn't jump to line 109, because the condition on line 107 was never true
108 # Command object passed in.
109 command = command_name
110 command_name = command.__class__.__module__.split(".")[-1]
111 else:
112 # Load the command object by name.
113 try:
114 app_name = get_commands()[command_name]
115 except KeyError:
116 raise CommandError("Unknown command: %r" % command_name)
118 if isinstance(app_name, BaseCommand): 118 ↛ 120line 118 didn't jump to line 120, because the condition on line 118 was never true
119 # If the command is already loaded, use it directly.
120 command = app_name
121 else:
122 command = load_command_class(app_name, command_name)
124 # Simulate argument parsing to get the option defaults (see #10080 for details).
125 parser = command.create_parser("", command_name)
126 # Use the `dest` option name from the parser option
127 opt_mapping = {
128 min(s_opt.option_strings).lstrip("-").replace("-", "_"): s_opt.dest
129 for s_opt in parser._actions
130 if s_opt.option_strings
131 }
132 arg_options = {opt_mapping.get(key, key): value for key, value in options.items()}
133 parse_args = []
134 for arg in args: 134 ↛ 135line 134 didn't jump to line 135, because the loop on line 134 never started
135 if isinstance(arg, (list, tuple)):
136 parse_args += map(str, arg)
137 else:
138 parse_args.append(str(arg))
140 def get_actions(parser):
141 # Parser actions and actions from sub-parser choices.
142 for opt in parser._actions:
143 if isinstance(opt, _SubParsersAction): 143 ↛ 144line 143 didn't jump to line 144, because the condition on line 143 was never true
144 for sub_opt in opt.choices.values():
145 yield from get_actions(sub_opt)
146 else:
147 yield opt
149 parser_actions = list(get_actions(parser))
150 mutually_exclusive_required_options = {
151 opt
152 for group in parser._mutually_exclusive_groups
153 for opt in group._group_actions
154 if group.required
155 }
156 # Any required arguments which are passed in via **options must be passed
157 # to parse_args().
158 for opt in parser_actions:
159 if opt.dest in options and ( 159 ↛ 162line 159 didn't jump to line 162, because the condition on line 159 was never true
160 opt.required or opt in mutually_exclusive_required_options
161 ):
162 opt_dest_count = sum(v == opt.dest for v in opt_mapping.values())
163 if opt_dest_count > 1:
164 raise TypeError(
165 f"Cannot pass the dest {opt.dest!r} that matches multiple "
166 f"arguments via **options."
167 )
168 parse_args.append(min(opt.option_strings))
169 if isinstance(opt, (_AppendConstAction, _CountAction, _StoreConstAction)):
170 continue
171 value = arg_options[opt.dest]
172 if isinstance(value, (list, tuple)):
173 parse_args += map(str, value)
174 else:
175 parse_args.append(str(value))
176 defaults = parser.parse_args(args=parse_args)
177 defaults = dict(defaults._get_kwargs(), **arg_options)
178 # Raise an error if any unknown options were passed.
179 stealth_options = set(command.base_stealth_options + command.stealth_options)
180 dest_parameters = {action.dest for action in parser_actions}
181 valid_options = (dest_parameters | stealth_options).union(opt_mapping)
182 unknown_options = set(options) - valid_options
183 if unknown_options: 183 ↛ 184line 183 didn't jump to line 184, because the condition on line 183 was never true
184 raise TypeError(
185 "Unknown option(s) for %s command: %s. "
186 "Valid options are: %s."
187 % (
188 command_name,
189 ", ".join(sorted(unknown_options)),
190 ", ".join(sorted(valid_options)),
191 )
192 )
193 # Move positional args out of options to mimic legacy optparse
194 args = defaults.pop("args", ())
195 if "skip_checks" not in options: 195 ↛ 198line 195 didn't jump to line 198, because the condition on line 195 was never false
196 defaults["skip_checks"] = True
198 return command.execute(*args, **defaults)
201class ManagementUtility:
202 """
203 Encapsulate the logic of the django-admin and manage.py utilities.
204 """
206 def __init__(self, argv=None):
207 self.argv = argv or sys.argv[:]
208 self.prog_name = os.path.basename(self.argv[0])
209 if self.prog_name == "__main__.py": 209 ↛ 210line 209 didn't jump to line 210, because the condition on line 209 was never true
210 self.prog_name = "python -m django"
211 self.settings_exception = None
213 def main_help_text(self, commands_only=False):
214 """Return the script's main help text, as a string."""
215 if commands_only:
216 usage = sorted(get_commands())
217 else:
218 usage = [
219 "",
220 "Type '%s help <subcommand>' for help on a specific subcommand."
221 % self.prog_name,
222 "",
223 "Available subcommands:",
224 ]
225 commands_dict = defaultdict(lambda: [])
226 for name, app in get_commands().items():
227 if app == "django.core":
228 app = "django"
229 else:
230 app = app.rpartition(".")[-1]
231 commands_dict[app].append(name)
232 style = color_style()
233 for app in sorted(commands_dict):
234 usage.append("")
235 usage.append(style.NOTICE("[%s]" % app))
236 for name in sorted(commands_dict[app]):
237 usage.append(" %s" % name)
238 # Output an extra note if settings are not properly configured
239 if self.settings_exception is not None:
240 usage.append(
241 style.NOTICE(
242 "Note that only Django core commands are listed "
243 "as settings are not properly configured (error: %s)."
244 % self.settings_exception
245 )
246 )
248 return "\n".join(usage)
250 def fetch_command(self, subcommand):
251 """
252 Try to fetch the given subcommand, printing a message with the
253 appropriate command called from the command line (usually
254 "django-admin" or "manage.py") if it can't be found.
255 """
256 # Get commands outside of try block to prevent swallowing exceptions
257 commands = get_commands()
258 try:
259 app_name = commands[subcommand]
260 except KeyError:
261 if os.environ.get("DJANGO_SETTINGS_MODULE"):
262 # If `subcommand` is missing due to misconfigured settings, the
263 # following line will retrigger an ImproperlyConfigured exception
264 # (get_commands() swallows the original one) so the user is
265 # informed about it.
266 settings.INSTALLED_APPS
267 elif not settings.configured:
268 sys.stderr.write("No Django settings specified.\n")
269 possible_matches = get_close_matches(subcommand, commands)
270 sys.stderr.write("Unknown command: %r" % subcommand)
271 if possible_matches:
272 sys.stderr.write(". Did you mean %s?" % possible_matches[0])
273 sys.stderr.write("\nType '%s help' for usage.\n" % self.prog_name)
274 sys.exit(1)
275 if isinstance(app_name, BaseCommand): 275 ↛ 277line 275 didn't jump to line 277, because the condition on line 275 was never true
276 # If the command is already loaded, use it directly.
277 klass = app_name
278 else:
279 klass = load_command_class(app_name, subcommand)
280 return klass
282 def autocomplete(self):
283 """
284 Output completion suggestions for BASH.
286 The output of this function is passed to BASH's `COMREPLY` variable and
287 treated as completion suggestions. `COMREPLY` expects a space
288 separated string as the result.
290 The `COMP_WORDS` and `COMP_CWORD` BASH environment variables are used
291 to get information about the cli input. Please refer to the BASH
292 man-page for more information about this variables.
294 Subcommand options are saved as pairs. A pair consists of
295 the long option string (e.g. '--exclude') and a boolean
296 value indicating if the option requires arguments. When printing to
297 stdout, an equal sign is appended to options which require arguments.
299 Note: If debugging this function, it is recommended to write the debug
300 output in a separate file. Otherwise the debug output will be treated
301 and formatted as potential completion suggestions.
302 """
303 # Don't complete if user hasn't sourced bash_completion file.
304 if "DJANGO_AUTO_COMPLETE" not in os.environ: 304 ↛ 307line 304 didn't jump to line 307, because the condition on line 304 was never false
305 return
307 cwords = os.environ["COMP_WORDS"].split()[1:]
308 cword = int(os.environ["COMP_CWORD"])
310 try:
311 curr = cwords[cword - 1]
312 except IndexError:
313 curr = ""
315 subcommands = [*get_commands(), "help"]
316 options = [("--help", False)]
318 # subcommand
319 if cword == 1:
320 print(" ".join(sorted(filter(lambda x: x.startswith(curr), subcommands))))
321 # subcommand options
322 # special case: the 'help' subcommand has no options
323 elif cwords[0] in subcommands and cwords[0] != "help":
324 subcommand_cls = self.fetch_command(cwords[0])
325 # special case: add the names of installed apps to options
326 if cwords[0] in ("dumpdata", "sqlmigrate", "sqlsequencereset", "test"):
327 try:
328 app_configs = apps.get_app_configs()
329 # Get the last part of the dotted path as the app name.
330 options.extend((app_config.label, 0) for app_config in app_configs)
331 except ImportError:
332 # Fail silently if DJANGO_SETTINGS_MODULE isn't set. The
333 # user will find out once they execute the command.
334 pass
335 parser = subcommand_cls.create_parser("", cwords[0])
336 options.extend(
337 (min(s_opt.option_strings), s_opt.nargs != 0)
338 for s_opt in parser._actions
339 if s_opt.option_strings
340 )
341 # filter out previously specified options from available options
342 prev_opts = {x.split("=")[0] for x in cwords[1 : cword - 1]}
343 options = (opt for opt in options if opt[0] not in prev_opts)
345 # filter options by current input
346 options = sorted((k, v) for k, v in options if k.startswith(curr))
347 for opt_label, require_arg in options:
348 # append '=' to options which require args
349 if require_arg:
350 opt_label += "="
351 print(opt_label)
352 # Exit code of the bash completion function is never passed back to
353 # the user, so it's safe to always exit with 0.
354 # For more details see #25420.
355 sys.exit(0)
357 def execute(self):
358 """
359 Given the command-line arguments, figure out which subcommand is being
360 run, create a parser appropriate to that command, and run it.
361 """
362 try:
363 subcommand = self.argv[1]
364 except IndexError:
365 subcommand = "help" # Display help if no arguments were given.
367 # Preprocess options to extract --settings and --pythonpath.
368 # These options could affect the commands that are available, so they
369 # must be processed early.
370 parser = CommandParser(
371 prog=self.prog_name,
372 usage="%(prog)s subcommand [options] [args]",
373 add_help=False,
374 allow_abbrev=False,
375 )
376 parser.add_argument("--settings")
377 parser.add_argument("--pythonpath")
378 parser.add_argument("args", nargs="*") # catch-all
379 try:
380 options, args = parser.parse_known_args(self.argv[2:])
381 handle_default_options(options)
382 except CommandError:
383 pass # Ignore any option errors at this point.
385 try:
386 settings.INSTALLED_APPS
387 except ImproperlyConfigured as exc:
388 self.settings_exception = exc
389 except ImportError as exc:
390 self.settings_exception = exc
392 if settings.configured: 392 ↛ 422line 392 didn't jump to line 422, because the condition on line 392 was never false
393 # Start the auto-reloading dev server even if the code is broken.
394 # The hardcoded condition is a code smell but we can't rely on a
395 # flag on the command class because we haven't located it yet.
396 if subcommand == "runserver" and "--noreload" not in self.argv: 396 ↛ 397line 396 didn't jump to line 397, because the condition on line 396 was never true
397 try:
398 autoreload.check_errors(django.setup)()
399 except Exception:
400 # The exception will be raised later in the child process
401 # started by the autoreloader. Pretend it didn't happen by
402 # loading an empty list of applications.
403 apps.all_models = defaultdict(dict)
404 apps.app_configs = {}
405 apps.apps_ready = apps.models_ready = apps.ready = True
407 # Remove options not compatible with the built-in runserver
408 # (e.g. options for the contrib.staticfiles' runserver).
409 # Changes here require manually testing as described in
410 # #27522.
411 _parser = self.fetch_command("runserver").create_parser(
412 "django", "runserver"
413 )
414 _options, _args = _parser.parse_known_args(self.argv[2:])
415 for _arg in _args:
416 self.argv.remove(_arg)
418 # In all other cases, django.setup() is required to succeed.
419 else:
420 django.setup()
422 self.autocomplete()
424 if subcommand == "help": 424 ↛ 425line 424 didn't jump to line 425, because the condition on line 424 was never true
425 if "--commands" in args:
426 sys.stdout.write(self.main_help_text(commands_only=True) + "\n")
427 elif not options.args:
428 sys.stdout.write(self.main_help_text() + "\n")
429 else:
430 self.fetch_command(options.args[0]).print_help(
431 self.prog_name, options.args[0]
432 )
433 # Special-cases: We want 'django-admin --version' and
434 # 'django-admin --help' to work, for backwards compatibility.
435 elif subcommand == "version" or self.argv[1:] == ["--version"]: 435 ↛ 436line 435 didn't jump to line 436, because the condition on line 435 was never true
436 sys.stdout.write(django.get_version() + "\n")
437 elif self.argv[1:] in (["--help"], ["-h"]): 437 ↛ 438line 437 didn't jump to line 438, because the condition on line 437 was never true
438 sys.stdout.write(self.main_help_text() + "\n")
439 else:
440 self.fetch_command(subcommand).run_from_argv(self.argv)
443def execute_from_command_line(argv=None):
444 """Run a ManagementUtility."""
445 utility = ManagementUtility(argv)
446 utility.execute()