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

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 

14 

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 

27 

28 

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 ] 

40 

41 

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() 

50 

51 

52@functools.lru_cache(maxsize=None) 

53def get_commands(): 

54 """ 

55 Return a dictionary mapping command names to their callback applications. 

56 

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. 

60 

61 Core commands are always included. If a settings module has been 

62 specified, also include user-defined commands. 

63 

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) 

67 

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. 

71 

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])} 

76 

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 

79 

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)}) 

83 

84 return commands 

85 

86 

87def call_command(command_name, *args, **options): 

88 """ 

89 Call the given command, with the given options and args/kwargs. 

90 

91 This is the primary API you should use for calling specific commands. 

92 

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. 

96 

97 Some examples: 

98 call_command('migrate') 

99 call_command('shell', plain=True) 

100 call_command('sqlmigrate', 'myapp') 

101 

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) 

117 

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) 

123 

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)) 

139 

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 

148 

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 

197 

198 return command.execute(*args, **defaults) 

199 

200 

201class ManagementUtility: 

202 """ 

203 Encapsulate the logic of the django-admin and manage.py utilities. 

204 """ 

205 

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 

212 

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 ) 

247 

248 return "\n".join(usage) 

249 

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 

281 

282 def autocomplete(self): 

283 """ 

284 Output completion suggestions for BASH. 

285 

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. 

289 

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. 

293 

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. 

298 

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 

306 

307 cwords = os.environ["COMP_WORDS"].split()[1:] 

308 cword = int(os.environ["COMP_CWORD"]) 

309 

310 try: 

311 curr = cwords[cword - 1] 

312 except IndexError: 

313 curr = "" 

314 

315 subcommands = [*get_commands(), "help"] 

316 options = [("--help", False)] 

317 

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) 

344 

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) 

356 

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. 

366 

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. 

384 

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 

391 

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 

406 

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) 

417 

418 # In all other cases, django.setup() is required to succeed. 

419 else: 

420 django.setup() 

421 

422 self.autocomplete() 

423 

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) 

441 

442 

443def execute_from_command_line(argv=None): 

444 """Run a ManagementUtility.""" 

445 utility = ManagementUtility(argv) 

446 utility.execute()