web/lib/django/core/management/__init__.py
changeset 0 0d40e90630ef
child 29 cc9b7e14412b
equal deleted inserted replaced
-1:000000000000 0:0d40e90630ef
       
     1 import os
       
     2 import sys
       
     3 from optparse import OptionParser, NO_DEFAULT
       
     4 import imp
       
     5 
       
     6 import django
       
     7 from django.core.management.base import BaseCommand, CommandError, handle_default_options
       
     8 from django.utils.importlib import import_module
       
     9 
       
    10 # For backwards compatibility: get_version() used to be in this module.
       
    11 get_version = django.get_version
       
    12 
       
    13 # A cache of loaded commands, so that call_command
       
    14 # doesn't have to reload every time it's called.
       
    15 _commands = None
       
    16 
       
    17 def find_commands(management_dir):
       
    18     """
       
    19     Given a path to a management directory, returns a list of all the command
       
    20     names that are available.
       
    21 
       
    22     Returns an empty list if no commands are defined.
       
    23     """
       
    24     command_dir = os.path.join(management_dir, 'commands')
       
    25     try:
       
    26         return [f[:-3] for f in os.listdir(command_dir)
       
    27                 if not f.startswith('_') and f.endswith('.py')]
       
    28     except OSError:
       
    29         return []
       
    30 
       
    31 def find_management_module(app_name):
       
    32     """
       
    33     Determines the path to the management module for the given app_name,
       
    34     without actually importing the application or the management module.
       
    35 
       
    36     Raises ImportError if the management module cannot be found for any reason.
       
    37     """
       
    38     parts = app_name.split('.')
       
    39     parts.append('management')
       
    40     parts.reverse()
       
    41     part = parts.pop()
       
    42     path = None
       
    43 
       
    44     # When using manage.py, the project module is added to the path,
       
    45     # loaded, then removed from the path. This means that
       
    46     # testproject.testapp.models can be loaded in future, even if
       
    47     # testproject isn't in the path. When looking for the management
       
    48     # module, we need look for the case where the project name is part
       
    49     # of the app_name but the project directory itself isn't on the path.
       
    50     try:
       
    51         f, path, descr = imp.find_module(part,path)
       
    52     except ImportError,e:
       
    53         if os.path.basename(os.getcwd()) != part:
       
    54             raise e
       
    55 
       
    56     while parts:
       
    57         part = parts.pop()
       
    58         f, path, descr = imp.find_module(part, path and [path] or None)
       
    59     return path
       
    60 
       
    61 def load_command_class(app_name, name):
       
    62     """
       
    63     Given a command name and an application name, returns the Command
       
    64     class instance. All errors raised by the import process
       
    65     (ImportError, AttributeError) are allowed to propagate.
       
    66     """
       
    67     module = import_module('%s.management.commands.%s' % (app_name, name))
       
    68     return module.Command()
       
    69 
       
    70 def get_commands():
       
    71     """
       
    72     Returns a dictionary mapping command names to their callback applications.
       
    73 
       
    74     This works by looking for a management.commands package in django.core, and
       
    75     in each installed application -- if a commands package exists, all commands
       
    76     in that package are registered.
       
    77 
       
    78     Core commands are always included. If a settings module has been
       
    79     specified, user-defined commands will also be included, the
       
    80     startproject command will be disabled, and the startapp command
       
    81     will be modified to use the directory in which the settings module appears.
       
    82 
       
    83     The dictionary is in the format {command_name: app_name}. Key-value
       
    84     pairs from this dictionary can then be used in calls to
       
    85     load_command_class(app_name, command_name)
       
    86 
       
    87     If a specific version of a command must be loaded (e.g., with the
       
    88     startapp command), the instantiated module can be placed in the
       
    89     dictionary in place of the application name.
       
    90 
       
    91     The dictionary is cached on the first call and reused on subsequent
       
    92     calls.
       
    93     """
       
    94     global _commands
       
    95     if _commands is None:
       
    96         _commands = dict([(name, 'django.core') for name in find_commands(__path__[0])])
       
    97 
       
    98         # Find the installed apps
       
    99         try:
       
   100             from django.conf import settings
       
   101             apps = settings.INSTALLED_APPS
       
   102         except (AttributeError, EnvironmentError, ImportError):
       
   103             apps = []
       
   104 
       
   105         # Find the project directory
       
   106         try:
       
   107             from django.conf import settings
       
   108             module = import_module(settings.SETTINGS_MODULE)
       
   109             project_directory = setup_environ(module, settings.SETTINGS_MODULE)
       
   110         except (AttributeError, EnvironmentError, ImportError, KeyError):
       
   111             project_directory = None
       
   112 
       
   113         # Find and load the management module for each installed app.
       
   114         for app_name in apps:
       
   115             try:
       
   116                 path = find_management_module(app_name)
       
   117                 _commands.update(dict([(name, app_name)
       
   118                                        for name in find_commands(path)]))
       
   119             except ImportError:
       
   120                 pass # No management module - ignore this app
       
   121 
       
   122         if project_directory:
       
   123             # Remove the "startproject" command from self.commands, because
       
   124             # that's a django-admin.py command, not a manage.py command.
       
   125             del _commands['startproject']
       
   126 
       
   127             # Override the startapp command so that it always uses the
       
   128             # project_directory, not the current working directory
       
   129             # (which is default).
       
   130             from django.core.management.commands.startapp import ProjectCommand
       
   131             _commands['startapp'] = ProjectCommand(project_directory)
       
   132 
       
   133     return _commands
       
   134 
       
   135 def call_command(name, *args, **options):
       
   136     """
       
   137     Calls the given command, with the given options and args/kwargs.
       
   138 
       
   139     This is the primary API you should use for calling specific commands.
       
   140 
       
   141     Some examples:
       
   142         call_command('syncdb')
       
   143         call_command('shell', plain=True)
       
   144         call_command('sqlall', 'myapp')
       
   145     """
       
   146     # Load the command object.
       
   147     try:
       
   148         app_name = get_commands()[name]
       
   149         if isinstance(app_name, BaseCommand):
       
   150             # If the command is already loaded, use it directly.
       
   151             klass = app_name
       
   152         else:
       
   153             klass = load_command_class(app_name, name)
       
   154     except KeyError:
       
   155         raise CommandError, "Unknown command: %r" % name
       
   156 
       
   157     # Grab out a list of defaults from the options. optparse does this for us
       
   158     # when the script runs from the command line, but since call_command can
       
   159     # be called programatically, we need to simulate the loading and handling
       
   160     # of defaults (see #10080 for details).
       
   161     defaults = dict([(o.dest, o.default)
       
   162                      for o in klass.option_list
       
   163                      if o.default is not NO_DEFAULT])
       
   164     defaults.update(options)
       
   165 
       
   166     return klass.execute(*args, **defaults)
       
   167 
       
   168 class LaxOptionParser(OptionParser):
       
   169     """
       
   170     An option parser that doesn't raise any errors on unknown options.
       
   171 
       
   172     This is needed because the --settings and --pythonpath options affect
       
   173     the commands (and thus the options) that are available to the user.
       
   174     """
       
   175     def error(self, msg):
       
   176         pass
       
   177 
       
   178     def print_help(self):
       
   179         """Output nothing.
       
   180 
       
   181         The lax options are included in the normal option parser, so under
       
   182         normal usage, we don't need to print the lax options.
       
   183         """
       
   184         pass
       
   185 
       
   186     def print_lax_help(self):
       
   187         """Output the basic options available to every command.
       
   188 
       
   189         This just redirects to the default print_help() behaviour.
       
   190         """
       
   191         OptionParser.print_help(self)
       
   192 
       
   193     def _process_args(self, largs, rargs, values):
       
   194         """
       
   195         Overrides OptionParser._process_args to exclusively handle default
       
   196         options and ignore args and other options.
       
   197 
       
   198         This overrides the behavior of the super class, which stop parsing
       
   199         at the first unrecognized option.
       
   200         """
       
   201         while rargs:
       
   202             arg = rargs[0]
       
   203             try:
       
   204                 if arg[0:2] == "--" and len(arg) > 2:
       
   205                     # process a single long option (possibly with value(s))
       
   206                     # the superclass code pops the arg off rargs
       
   207                     self._process_long_opt(rargs, values)
       
   208                 elif arg[:1] == "-" and len(arg) > 1:
       
   209                     # process a cluster of short options (possibly with
       
   210                     # value(s) for the last one only)
       
   211                     # the superclass code pops the arg off rargs
       
   212                     self._process_short_opts(rargs, values)
       
   213                 else:
       
   214                     # it's either a non-default option or an arg
       
   215                     # either way, add it to the args list so we can keep
       
   216                     # dealing with options
       
   217                     del rargs[0]
       
   218                     raise Exception
       
   219             except:
       
   220                 largs.append(arg)
       
   221 
       
   222 class ManagementUtility(object):
       
   223     """
       
   224     Encapsulates the logic of the django-admin.py and manage.py utilities.
       
   225 
       
   226     A ManagementUtility has a number of commands, which can be manipulated
       
   227     by editing the self.commands dictionary.
       
   228     """
       
   229     def __init__(self, argv=None):
       
   230         self.argv = argv or sys.argv[:]
       
   231         self.prog_name = os.path.basename(self.argv[0])
       
   232 
       
   233     def main_help_text(self):
       
   234         """
       
   235         Returns the script's main help text, as a string.
       
   236         """
       
   237         usage = ['',"Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name,'']
       
   238         usage.append('Available subcommands:')
       
   239         commands = get_commands().keys()
       
   240         commands.sort()
       
   241         for cmd in commands:
       
   242             usage.append('  %s' % cmd)
       
   243         return '\n'.join(usage)
       
   244 
       
   245     def fetch_command(self, subcommand):
       
   246         """
       
   247         Tries to fetch the given subcommand, printing a message with the
       
   248         appropriate command called from the command line (usually
       
   249         "django-admin.py" or "manage.py") if it can't be found.
       
   250         """
       
   251         try:
       
   252             app_name = get_commands()[subcommand]
       
   253             if isinstance(app_name, BaseCommand):
       
   254                 # If the command is already loaded, use it directly.
       
   255                 klass = app_name
       
   256             else:
       
   257                 klass = load_command_class(app_name, subcommand)
       
   258         except KeyError:
       
   259             sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % \
       
   260                 (subcommand, self.prog_name))
       
   261             sys.exit(1)
       
   262         return klass
       
   263 
       
   264     def execute(self):
       
   265         """
       
   266         Given the command-line arguments, this figures out which subcommand is
       
   267         being run, creates a parser appropriate to that command, and runs it.
       
   268         """
       
   269         # Preprocess options to extract --settings and --pythonpath.
       
   270         # These options could affect the commands that are available, so they
       
   271         # must be processed early.
       
   272         parser = LaxOptionParser(usage="%prog subcommand [options] [args]",
       
   273                                  version=get_version(),
       
   274                                  option_list=BaseCommand.option_list)
       
   275         try:
       
   276             options, args = parser.parse_args(self.argv)
       
   277             handle_default_options(options)
       
   278         except:
       
   279             pass # Ignore any option errors at this point.
       
   280 
       
   281         try:
       
   282             subcommand = self.argv[1]
       
   283         except IndexError:
       
   284             sys.stderr.write("Type '%s help' for usage.\n" % self.prog_name)
       
   285             sys.exit(1)
       
   286 
       
   287         if subcommand == 'help':
       
   288             if len(args) > 2:
       
   289                 self.fetch_command(args[2]).print_help(self.prog_name, args[2])
       
   290             else:
       
   291                 parser.print_lax_help()
       
   292                 sys.stderr.write(self.main_help_text() + '\n')
       
   293                 sys.exit(1)
       
   294         # Special-cases: We want 'django-admin.py --version' and
       
   295         # 'django-admin.py --help' to work, for backwards compatibility.
       
   296         elif self.argv[1:] == ['--version']:
       
   297             # LaxOptionParser already takes care of printing the version.
       
   298             pass
       
   299         elif self.argv[1:] == ['--help']:
       
   300             parser.print_lax_help()
       
   301             sys.stderr.write(self.main_help_text() + '\n')
       
   302         else:
       
   303             self.fetch_command(subcommand).run_from_argv(self.argv)
       
   304 
       
   305 def setup_environ(settings_mod, original_settings_path=None):
       
   306     """
       
   307     Configures the runtime environment. This can also be used by external
       
   308     scripts wanting to set up a similar environment to manage.py.
       
   309     Returns the project directory (assuming the passed settings module is
       
   310     directly in the project directory).
       
   311 
       
   312     The "original_settings_path" parameter is optional, but recommended, since
       
   313     trying to work out the original path from the module can be problematic.
       
   314     """
       
   315     # Add this project to sys.path so that it's importable in the conventional
       
   316     # way. For example, if this file (manage.py) lives in a directory
       
   317     # "myproject", this code would add "/path/to/myproject" to sys.path.
       
   318     if '__init__.py' in settings_mod.__file__:
       
   319         p = os.path.dirname(settings_mod.__file__)
       
   320     else:
       
   321         p = settings_mod.__file__
       
   322     project_directory, settings_filename = os.path.split(p)
       
   323     if project_directory == os.curdir or not project_directory:
       
   324         project_directory = os.getcwd()
       
   325     project_name = os.path.basename(project_directory)
       
   326 
       
   327     # Strip filename suffix to get the module name.
       
   328     settings_name = os.path.splitext(settings_filename)[0]
       
   329 
       
   330     # Strip $py for Jython compiled files (like settings$py.class)
       
   331     if settings_name.endswith("$py"):
       
   332         settings_name = settings_name[:-3]
       
   333 
       
   334     # Set DJANGO_SETTINGS_MODULE appropriately.
       
   335     if original_settings_path:
       
   336         os.environ['DJANGO_SETTINGS_MODULE'] = original_settings_path
       
   337     else:
       
   338         os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name)
       
   339 
       
   340     # Import the project module. We add the parent directory to PYTHONPATH to
       
   341     # avoid some of the path errors new users can have.
       
   342     sys.path.append(os.path.join(project_directory, os.pardir))
       
   343     project_module = import_module(project_name)
       
   344     sys.path.pop()
       
   345 
       
   346     return project_directory
       
   347 
       
   348 def execute_from_command_line(argv=None):
       
   349     """
       
   350     A simple method that runs a ManagementUtility.
       
   351     """
       
   352     utility = ManagementUtility(argv)
       
   353     utility.execute()
       
   354 
       
   355 def execute_manager(settings_mod, argv=None):
       
   356     """
       
   357     Like execute_from_command_line(), but for use by manage.py, a
       
   358     project-specific django-admin.py utility.
       
   359     """
       
   360     setup_environ(settings_mod)
       
   361     utility = ManagementUtility(argv)
       
   362     utility.execute()