3
from optparse import OptionParser
4
from imp import find_module
7
from django.core.management.base import BaseCommand, CommandError, handle_default_options
9
# For backwards compatibility: get_version() used to be in this module.
10
get_version = django.get_version
12
# A cache of loaded commands, so that call_command
13
# doesn't have to reload every time it's called.
16
def find_commands(management_dir):
18
Given a path to a management directory, returns a list of all the command
19
names that are available.
21
Returns an empty list if no commands are defined.
23
command_dir = os.path.join(management_dir, 'commands')
25
return [f[:-3] for f in os.listdir(command_dir)
26
if not f.startswith('_') and f.endswith('.py')]
30
def find_management_module(app_name):
32
Determines the path to the management module for the given app_name,
33
without actually importing the application or the management module.
35
Raises ImportError if the management module cannot be found for any reason.
37
parts = app_name.split('.')
38
parts.append('management')
43
f, path, descr = find_module(part, path and [path] or None)
46
def load_command_class(app_name, name):
48
Given a command name and an application name, returns the Command
49
class instance. All errors raised by the import process
50
(ImportError, AttributeError) are allowed to propagate.
52
return getattr(__import__('%s.management.commands.%s' % (app_name, name),
53
{}, {}, ['Command']), 'Command')()
55
def get_commands(load_user_commands=True, project_directory=None):
57
Returns a dictionary mapping command names to their callback applications.
59
This works by looking for a management.commands package in django.core, and
60
in each installed application -- if a commands package exists, all commands
61
in that package are registered.
63
Core commands are always included. If a settings module has been
64
specified, user-defined commands will also be included, the
65
startproject command will be disabled, and the startapp command
66
will be modified to use the directory in which that module appears.
68
The dictionary is in the format {command_name: app_name}. Key-value
69
pairs from this dictionary can then be used in calls to
70
load_command_class(app_name, command_name)
72
If a specific version of a command must be loaded (e.g., with the
73
startapp command), the instantiated module can be placed in the
74
dictionary in place of the application name.
76
The dictionary is cached on the first call and reused on subsequent
81
_commands = dict([(name, 'django.core') for name in find_commands(__path__[0])])
83
if load_user_commands:
84
# Get commands from all installed apps.
85
from django.conf import settings
86
for app_name in settings.INSTALLED_APPS:
88
path = find_management_module(app_name)
89
_commands.update(dict([(name, app_name) for name in find_commands(path)]))
91
pass # No management module -- ignore this app.
94
# Remove the "startproject" command from self.commands, because
95
# that's a django-admin.py command, not a manage.py command.
96
del _commands['startproject']
98
# Override the startapp command so that it always uses the
99
# project_directory, not the current working directory
100
# (which is default).
101
from django.core.management.commands.startapp import ProjectCommand
102
_commands['startapp'] = ProjectCommand(project_directory)
106
def call_command(name, *args, **options):
108
Calls the given command, with the given options and args/kwargs.
110
This is the primary API you should use for calling specific commands.
113
call_command('syncdb')
114
call_command('shell', plain=True)
115
call_command('sqlall', 'myapp')
118
app_name = get_commands()[name]
119
if isinstance(app_name, BaseCommand):
120
# If the command is already loaded, use it directly.
123
klass = load_command_class(app_name, name)
125
raise CommandError, "Unknown command: %r" % name
126
return klass.execute(*args, **options)
128
class LaxOptionParser(OptionParser):
130
An option parser that doesn't raise any errors on unknown options.
132
This is needed because the --settings and --pythonpath options affect
133
the commands (and thus the options) that are available to the user.
135
def error(self, msg):
138
def _process_args(self, largs, rargs, values):
140
Overrides OptionParser._process_args to exclusively handle default
141
options and ignore args and other options.
143
This overrides the behavior of the super class, which stop parsing
144
at the first unrecognized option.
149
if arg[0:2] == "--" and len(arg) > 2:
150
# process a single long option (possibly with value(s))
151
# the superclass code pops the arg off rargs
152
self._process_long_opt(rargs, values)
153
elif arg[:1] == "-" and len(arg) > 1:
154
# process a cluster of short options (possibly with
155
# value(s) for the last one only)
156
# the superclass code pops the arg off rargs
157
self._process_short_opts(rargs, values)
159
# it's either a non-default option or an arg
160
# either way, add it to the args list so we can keep
161
# dealing with options
167
class ManagementUtility(object):
169
Encapsulates the logic of the django-admin.py and manage.py utilities.
171
A ManagementUtility has a number of commands, which can be manipulated
172
by editing the self.commands dictionary.
174
def __init__(self, argv=None):
175
self.argv = argv or sys.argv[:]
176
self.prog_name = os.path.basename(self.argv[0])
177
self.project_directory = None
178
self.user_commands = False
180
def main_help_text(self):
182
Returns the script's main help text, as a string.
184
usage = ['%s <subcommand> [options] [args]' % self.prog_name]
185
usage.append('Django command line tool, version %s' % django.get_version())
186
usage.append("Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name)
187
usage.append('Available subcommands:')
188
commands = get_commands(self.user_commands, self.project_directory).keys()
191
usage.append(' %s' % cmd)
192
return '\n'.join(usage)
194
def fetch_command(self, subcommand):
196
Tries to fetch the given subcommand, printing a message with the
197
appropriate command called from the command line (usually
198
"django-admin.py" or "manage.py") if it can't be found.
201
app_name = get_commands(self.user_commands, self.project_directory)[subcommand]
202
if isinstance(app_name, BaseCommand):
203
# If the command is already loaded, use it directly.
206
klass = load_command_class(app_name, subcommand)
208
sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % \
209
(subcommand, self.prog_name))
215
Given the command-line arguments, this figures out which subcommand is
216
being run, creates a parser appropriate to that command, and runs it.
218
# Preprocess options to extract --settings and --pythonpath.
219
# These options could affect the commands that are available, so they
220
# must be processed early.
221
parser = LaxOptionParser(version=get_version(), option_list=BaseCommand.option_list)
223
options, args = parser.parse_args(self.argv)
224
handle_default_options(options)
226
pass # Ignore any option errors at this point.
229
subcommand = self.argv[1]
231
sys.stderr.write("Type '%s help' for usage.\n" % self.prog_name)
234
if subcommand == 'help':
236
self.fetch_command(args[2]).print_help(self.prog_name, args[2])
238
sys.stderr.write(self.main_help_text() + '\n')
240
# Special-cases: We want 'django-admin.py --version' and
241
# 'django-admin.py --help' to work, for backwards compatibility.
242
elif self.argv[1:] == ['--version']:
243
# LaxOptionParser already takes care of printing the version.
245
elif self.argv[1:] == ['--help']:
246
sys.stderr.write(self.main_help_text() + '\n')
248
self.fetch_command(subcommand).run_from_argv(self.argv)
250
class ProjectManagementUtility(ManagementUtility):
252
A ManagementUtility that is specific to a particular Django project.
253
As such, its commands are slightly different than those of its parent
256
In practice, this class represents manage.py, whereas ManagementUtility
257
represents django-admin.py.
259
def __init__(self, argv, project_directory):
260
super(ProjectManagementUtility, self).__init__(argv)
261
self.project_directory = project_directory
262
self.user_commands = True
264
def setup_environ(settings_mod):
266
Configures the runtime environment. This can also be used by external
267
scripts wanting to set up a similar environment to manage.py.
268
Returns the project directory (assuming the passed settings module is
269
directly in the project directory).
271
# Add this project to sys.path so that it's importable in the conventional
272
# way. For example, if this file (manage.py) lives in a directory
273
# "myproject", this code would add "/path/to/myproject" to sys.path.
274
project_directory, settings_filename = os.path.split(settings_mod.__file__)
275
if project_directory == os.curdir or not project_directory:
276
project_directory = os.getcwd()
277
project_name = os.path.basename(project_directory)
278
settings_name = os.path.splitext(settings_filename)[0]
279
sys.path.append(os.path.join(project_directory, os.pardir))
280
project_module = __import__(project_name, {}, {}, [''])
283
# Set DJANGO_SETTINGS_MODULE appropriately.
284
os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name)
285
return project_directory
287
def execute_from_command_line(argv=None):
289
A simple method that runs a ManagementUtility.
291
utility = ManagementUtility(argv)
294
def execute_manager(settings_mod, argv=None):
296
Like execute_from_command_line(), but for use by manage.py, a
297
project-specific django-admin.py utility.
299
project_directory = setup_environ(settings_mod)
300
utility = ProjectManagementUtility(argv, project_directory)