Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

__init__.py 15KB

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