diff -r 57b4279d8c4e -r 03e267d67478 app/django/core/management/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/django/core/management/__init__.py Fri Jul 18 18:22:23 2008 +0000 @@ -0,0 +1,272 @@ +import os +import sys +from optparse import OptionParser +from imp import find_module + +import django +from django.core.management.base import BaseCommand, CommandError, handle_default_options + +# For backwards compatibility: get_version() used to be in this module. +get_version = django.get_version + +# A cache of loaded commands, so that call_command +# doesn't have to reload every time it's called. +_commands = None + +def find_commands(management_dir): + """ + Given a path to a management directory, returns a list of all the command + names that are available. + + Returns an empty list if no commands are defined. + """ + command_dir = os.path.join(management_dir, 'commands') + try: + return [f[:-3] for f in os.listdir(command_dir) + if not f.startswith('_') and f.endswith('.py')] + except OSError: + return [] + +def find_management_module(app_name): + """ + Determines the path to the management module for the given app_name, + without actually importing the application or the management module. + + Raises ImportError if the management module cannot be found for any reason. + """ + parts = app_name.split('.') + parts.append('management') + parts.reverse() + path = None + while parts: + part = parts.pop() + f, path, descr = find_module(part, path and [path] or None) + return path + +def load_command_class(app_name, name): + """ + Given a command name and an application name, returns the Command + class instance. All errors raised by the import process + (ImportError, AttributeError) are allowed to propagate. + """ + return getattr(__import__('%s.management.commands.%s' % (app_name, name), + {}, {}, ['Command']), 'Command')() + +def get_commands(load_user_commands=True, project_directory=None): + """ + Returns a dictionary mapping command names to their callback applications. + + This works by looking for a management.commands package in django.core, and + in each installed application -- if a commands package exists, all commands + in that package are registered. + + Core commands are always included. If a settings module has been + specified, user-defined commands will also be included, the + startproject command will be disabled, and the startapp command + will be modified to use the directory in which that module appears. + + The dictionary is in the format {command_name: app_name}. Key-value + pairs from this dictionary can then be used in calls to + load_command_class(app_name, command_name) + + If a specific version of a command must be loaded (e.g., with the + startapp command), the instantiated module can be placed in the + dictionary in place of the application name. + + The dictionary is cached on the first call and reused on subsequent + calls. + """ + global _commands + if _commands is None: + _commands = dict([(name, 'django.core') for name in find_commands(__path__[0])]) + + if load_user_commands: + # Get commands from all installed apps. + from django.conf import settings + for app_name in settings.INSTALLED_APPS: + try: + path = find_management_module(app_name) + _commands.update(dict([(name, app_name) for name in find_commands(path)])) + except ImportError: + pass # No management module -- ignore this app. + + if project_directory: + # Remove the "startproject" command from self.commands, because + # that's a django-admin.py command, not a manage.py command. + del _commands['startproject'] + + # Override the startapp command so that it always uses the + # project_directory, not the current working directory + # (which is default). + from django.core.management.commands.startapp import ProjectCommand + _commands['startapp'] = ProjectCommand(project_directory) + + return _commands + +def call_command(name, *args, **options): + """ + Calls the given command, with the given options and args/kwargs. + + This is the primary API you should use for calling specific commands. + + Some examples: + call_command('syncdb') + call_command('shell', plain=True) + call_command('sqlall', 'myapp') + """ + try: + app_name = get_commands()[name] + if isinstance(app_name, BaseCommand): + # If the command is already loaded, use it directly. + klass = app_name + else: + klass = load_command_class(app_name, name) + except KeyError: + raise CommandError, "Unknown command: %r" % name + return klass.execute(*args, **options) + +class LaxOptionParser(OptionParser): + """ + An option parser that doesn't raise any errors on unknown options. + + This is needed because the --settings and --pythonpath options affect + the commands (and thus the options) that are available to the user. + """ + def error(self, msg): + pass + +class ManagementUtility(object): + """ + Encapsulates the logic of the django-admin.py and manage.py utilities. + + A ManagementUtility has a number of commands, which can be manipulated + by editing the self.commands dictionary. + """ + def __init__(self, argv=None): + self.argv = argv or sys.argv[:] + self.prog_name = os.path.basename(self.argv[0]) + self.project_directory = None + self.user_commands = False + + def main_help_text(self): + """ + Returns the script's main help text, as a string. + """ + usage = ['%s [options] [args]' % self.prog_name] + usage.append('Django command line tool, version %s' % django.get_version()) + usage.append("Type '%s help ' for help on a specific subcommand." % self.prog_name) + usage.append('Available subcommands:') + commands = get_commands(self.user_commands, self.project_directory).keys() + commands.sort() + for cmd in commands: + usage.append(' %s' % cmd) + return '\n'.join(usage) + + def fetch_command(self, subcommand): + """ + Tries to fetch the given subcommand, printing a message with the + appropriate command called from the command line (usually + "django-admin.py" or "manage.py") if it can't be found. + """ + try: + app_name = get_commands(self.user_commands, self.project_directory)[subcommand] + if isinstance(app_name, BaseCommand): + # If the command is already loaded, use it directly. + klass = app_name + else: + klass = load_command_class(app_name, subcommand) + except KeyError: + sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % \ + (subcommand, self.prog_name)) + sys.exit(1) + return klass + + def execute(self): + """ + Given the command-line arguments, this figures out which subcommand is + being run, creates a parser appropriate to that command, and runs it. + """ + # Preprocess options to extract --settings and --pythonpath. + # These options could affect the commands that are available, so they + # must be processed early. + parser = LaxOptionParser(version=get_version(), option_list=BaseCommand.option_list) + try: + options, args = parser.parse_args(self.argv) + handle_default_options(options) + except: + pass # Ignore any option errors at this point. + + try: + subcommand = self.argv[1] + except IndexError: + sys.stderr.write("Type '%s help' for usage.\n" % self.prog_name) + sys.exit(1) + + if subcommand == 'help': + if len(args) > 2: + self.fetch_command(args[2]).print_help(self.prog_name, args[2]) + else: + sys.stderr.write(self.main_help_text() + '\n') + sys.exit(1) + # Special-cases: We want 'django-admin.py --version' and + # 'django-admin.py --help' to work, for backwards compatibility. + elif self.argv[1:] == ['--version']: + # LaxOptionParser already takes care of printing the version. + pass + elif self.argv[1:] == ['--help']: + sys.stderr.write(self.main_help_text() + '\n') + else: + self.fetch_command(subcommand).run_from_argv(self.argv) + +class ProjectManagementUtility(ManagementUtility): + """ + A ManagementUtility that is specific to a particular Django project. + As such, its commands are slightly different than those of its parent + class. + + In practice, this class represents manage.py, whereas ManagementUtility + represents django-admin.py. + """ + def __init__(self, argv, project_directory): + super(ProjectManagementUtility, self).__init__(argv) + self.project_directory = project_directory + self.user_commands = True + +def setup_environ(settings_mod): + """ + Configures the runtime environment. This can also be used by external + scripts wanting to set up a similar environment to manage.py. + Returns the project directory (assuming the passed settings module is + directly in the project directory). + """ + # Add this project to sys.path so that it's importable in the conventional + # way. For example, if this file (manage.py) lives in a directory + # "myproject", this code would add "/path/to/myproject" to sys.path. + project_directory, settings_filename = os.path.split(settings_mod.__file__) + if project_directory == os.curdir or not project_directory: + project_directory = os.getcwd() + project_name = os.path.basename(project_directory) + settings_name = os.path.splitext(settings_filename)[0] + sys.path.append(os.path.join(project_directory, os.pardir)) + project_module = __import__(project_name, {}, {}, ['']) + sys.path.pop() + + # Set DJANGO_SETTINGS_MODULE appropriately. + os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name) + return project_directory + +def execute_from_command_line(argv=None): + """ + A simple method that runs a ManagementUtility. + """ + utility = ManagementUtility(argv) + utility.execute() + +def execute_manager(settings_mod, argv=None): + """ + Like execute_from_command_line(), but for use by manage.py, a + project-specific django-admin.py utility. + """ + project_directory = setup_environ(settings_mod) + utility = ProjectManagementUtility(argv, project_directory) + utility.execute()