--- a/app/django/core/management/__init__.py Tue Oct 14 12:36:55 2008 +0000
+++ b/app/django/core/management/__init__.py Tue Oct 14 16:00:59 2008 +0000
@@ -1,7 +1,7 @@
import os
import sys
from optparse import OptionParser
-from imp import find_module
+import imp
import django
from django.core.management.base import BaseCommand, CommandError, handle_default_options
@@ -37,10 +37,24 @@
parts = app_name.split('.')
parts.append('management')
parts.reverse()
+ part = parts.pop()
path = None
+
+ # When using manage.py, the project module is added to the path,
+ # loaded, then removed from the path. This means that
+ # testproject.testapp.models can be loaded in future, even if
+ # testproject isn't in the path. When looking for the management
+ # module, we need look for the case where the project name is part
+ # of the app_name but the project directory itself isn't on the path.
+ try:
+ f, path, descr = imp.find_module(part,path)
+ except ImportError,e:
+ if os.path.basename(os.getcwd()) != part:
+ raise e
+
while parts:
part = parts.pop()
- f, path, descr = find_module(part, path and [path] or None)
+ f, path, descr = imp.find_module(part, path and [path] or None)
return path
def load_command_class(app_name, name):
@@ -52,7 +66,7 @@
return getattr(__import__('%s.management.commands.%s' % (app_name, name),
{}, {}, ['Command']), 'Command')()
-def get_commands(load_user_commands=True, project_directory=None):
+def get_commands():
"""
Returns a dictionary mapping command names to their callback applications.
@@ -63,7 +77,7 @@
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.
+ will be modified to use the directory in which the settings 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
@@ -80,15 +94,33 @@
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.
+ # Find the installed apps
+ try:
+ from django.conf import settings
+ apps = settings.INSTALLED_APPS
+ except (AttributeError, EnvironmentError, ImportError):
+ apps = []
+
+ # Find the project directory
+ try:
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.
+ project_directory = setup_environ(
+ __import__(
+ settings.SETTINGS_MODULE, {}, {},
+ (settings.SETTINGS_MODULE.split(".")[-1],)
+ ), settings.SETTINGS_MODULE
+ )
+ except (AttributeError, EnvironmentError, ImportError):
+ project_directory = None
+
+ # Find and load the management module for each installed app.
+ for app_name in 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
@@ -135,6 +167,50 @@
def error(self, msg):
pass
+ def print_help(self):
+ """Output nothing.
+
+ The lax options are included in the normal option parser, so under
+ normal usage, we don't need to print the lax options.
+ """
+ pass
+
+ def print_lax_help(self):
+ """Output the basic options available to every command.
+
+ This just redirects to the default print_help() behaviour.
+ """
+ OptionParser.print_help(self)
+
+ def _process_args(self, largs, rargs, values):
+ """
+ Overrides OptionParser._process_args to exclusively handle default
+ options and ignore args and other options.
+
+ This overrides the behavior of the super class, which stop parsing
+ at the first unrecognized option.
+ """
+ while rargs:
+ arg = rargs[0]
+ try:
+ if arg[0:2] == "--" and len(arg) > 2:
+ # process a single long option (possibly with value(s))
+ # the superclass code pops the arg off rargs
+ self._process_long_opt(rargs, values)
+ elif arg[:1] == "-" and len(arg) > 1:
+ # process a cluster of short options (possibly with
+ # value(s) for the last one only)
+ # the superclass code pops the arg off rargs
+ self._process_short_opts(rargs, values)
+ else:
+ # it's either a non-default option or an arg
+ # either way, add it to the args list so we can keep
+ # dealing with options
+ del rargs[0]
+ raise error
+ except:
+ largs.append(arg)
+
class ManagementUtility(object):
"""
Encapsulates the logic of the django-admin.py and manage.py utilities.
@@ -145,18 +221,14 @@
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 <subcommand> [options] [args]' % self.prog_name]
- usage.append('Django command line tool, version %s' % django.get_version())
- usage.append("Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name)
+ usage = ['',"Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name,'']
usage.append('Available subcommands:')
- commands = get_commands(self.user_commands, self.project_directory).keys()
+ commands = get_commands().keys()
commands.sort()
for cmd in commands:
usage.append(' %s' % cmd)
@@ -169,7 +241,7 @@
"django-admin.py" or "manage.py") if it can't be found.
"""
try:
- app_name = get_commands(self.user_commands, self.project_directory)[subcommand]
+ app_name = get_commands()[subcommand]
if isinstance(app_name, BaseCommand):
# If the command is already loaded, use it directly.
klass = app_name
@@ -189,7 +261,9 @@
# 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)
+ parser = LaxOptionParser(usage="%prog subcommand [options] [args]",
+ version=get_version(),
+ option_list=BaseCommand.option_list)
try:
options, args = parser.parse_args(self.argv)
handle_default_options(options)
@@ -206,6 +280,7 @@
if len(args) > 2:
self.fetch_command(args[2]).print_help(self.prog_name, args[2])
else:
+ parser.print_lax_help()
sys.stderr.write(self.main_help_text() + '\n')
sys.exit(1)
# Special-cases: We want 'django-admin.py --version' and
@@ -214,30 +289,20 @@
# LaxOptionParser already takes care of printing the version.
pass
elif self.argv[1:] == ['--help']:
+ parser.print_lax_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):
+def setup_environ(settings_mod, original_settings_path=None):
"""
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).
+
+ The "original_settings_path" parameter is optional, but recommended, since
+ trying to work out the original path from the module can be problematic.
"""
# 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
@@ -252,7 +317,10 @@
sys.path.pop()
# Set DJANGO_SETTINGS_MODULE appropriately.
- os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name)
+ if original_settings_path:
+ os.environ['DJANGO_SETTINGS_MODULE'] = original_settings_path
+ else:
+ os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name)
return project_directory
def execute_from_command_line(argv=None):
@@ -267,6 +335,6 @@
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)
+ setup_environ(settings_mod)
+ utility = ManagementUtility(argv)
utility.execute()