3
from optparse import OptionParser
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
# When using manage.py, the project module is added to the path,
44
# loaded, then removed from the path. This means that
45
# testproject.testapp.models can be loaded in future, even if
46
# testproject isn't in the path. When looking for the management
47
# module, we need look for the case where the project name is part
48
# of the app_name but the project directory itself isn't on the path.
50
f, path, descr = imp.find_module(part,path)
52
if os.path.basename(os.getcwd()) != part:
57
f, path, descr = imp.find_module(part, path and [path] or None)
60
def load_command_class(app_name, name):
62
Given a command name and an application name, returns the Command
63
class instance. All errors raised by the import process
64
(ImportError, AttributeError) are allowed to propagate.
66
return getattr(__import__('%s.management.commands.%s' % (app_name, name),
67
{}, {}, ['Command']), 'Command')()
71
Returns a dictionary mapping command names to their callback applications.
73
This works by looking for a management.commands package in django.core, and
74
in each installed application -- if a commands package exists, all commands
75
in that package are registered.
77
Core commands are always included. If a settings module has been
78
specified, user-defined commands will also be included, the
79
startproject command will be disabled, and the startapp command
80
will be modified to use the directory in which the settings module appears.
82
The dictionary is in the format {command_name: app_name}. Key-value
83
pairs from this dictionary can then be used in calls to
84
load_command_class(app_name, command_name)
86
If a specific version of a command must be loaded (e.g., with the
87
startapp command), the instantiated module can be placed in the
88
dictionary in place of the application name.
90
The dictionary is cached on the first call and reused on subsequent
95
_commands = dict([(name, 'django.core') for name in find_commands(__path__[0])])
97
# Find the installed apps
99
from django.conf import settings
100
apps = settings.INSTALLED_APPS
101
except (AttributeError, EnvironmentError, ImportError):
104
# Find the project directory
106
from django.conf import settings
107
project_directory = setup_environ(
109
settings.SETTINGS_MODULE, {}, {},
110
(settings.SETTINGS_MODULE.split(".")[-1],)
111
), settings.SETTINGS_MODULE
113
except (AttributeError, EnvironmentError, ImportError):
114
project_directory = None
116
# Find and load the management module for each installed app.
117
for app_name in apps:
119
path = find_management_module(app_name)
120
_commands.update(dict([(name, app_name)
121
for name in find_commands(path)]))
123
pass # No management module - ignore this app
125
if project_directory:
126
# Remove the "startproject" command from self.commands, because
127
# that's a django-admin.py command, not a manage.py command.
128
del _commands['startproject']
130
# Override the startapp command so that it always uses the
131
# project_directory, not the current working directory
132
# (which is default).
133
from django.core.management.commands.startapp import ProjectCommand
134
_commands['startapp'] = ProjectCommand(project_directory)
138
def call_command(name, *args, **options):
140
Calls the given command, with the given options and args/kwargs.
142
This is the primary API you should use for calling specific commands.
145
call_command('syncdb')
146
call_command('shell', plain=True)
147
call_command('sqlall', 'myapp')
150
app_name = get_commands()[name]
151
if isinstance(app_name, BaseCommand):
152
# If the command is already loaded, use it directly.
155
klass = load_command_class(app_name, name)
157
raise CommandError, "Unknown command: %r" % name
158
return klass.execute(*args, **options)
160
class LaxOptionParser(OptionParser):
162
An option parser that doesn't raise any errors on unknown options.
164
This is needed because the --settings and --pythonpath options affect
165
the commands (and thus the options) that are available to the user.
167
def error(self, msg):
170
def print_help(self):
173
The lax options are included in the normal option parser, so under
174
normal usage, we don't need to print the lax options.
178
def print_lax_help(self):
179
"""Output the basic options available to every command.
181
This just redirects to the default print_help() behaviour.
183
OptionParser.print_help(self)
185
def _process_args(self, largs, rargs, values):
187
Overrides OptionParser._process_args to exclusively handle default
188
options and ignore args and other options.
190
This overrides the behavior of the super class, which stop parsing
191
at the first unrecognized option.
196
if arg[0:2] == "--" and len(arg) > 2:
197
# process a single long option (possibly with value(s))
198
# the superclass code pops the arg off rargs
199
self._process_long_opt(rargs, values)
200
elif arg[:1] == "-" and len(arg) > 1:
201
# process a cluster of short options (possibly with
202
# value(s) for the last one only)
203
# the superclass code pops the arg off rargs
204
self._process_short_opts(rargs, values)
206
# it's either a non-default option or an arg
207
# either way, add it to the args list so we can keep
208
# dealing with options
214
class ManagementUtility(object):
216
Encapsulates the logic of the django-admin.py and manage.py utilities.
218
A ManagementUtility has a number of commands, which can be manipulated
219
by editing the self.commands dictionary.
221
def __init__(self, argv=None):
222
self.argv = argv or sys.argv[:]
223
self.prog_name = os.path.basename(self.argv[0])
225
def main_help_text(self):
227
Returns the script's main help text, as a string.
229
usage = ['',"Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name,'']
230
usage.append('Available subcommands:')
231
commands = get_commands().keys()
234
usage.append(' %s' % cmd)
235
return '\n'.join(usage)
237
def fetch_command(self, subcommand):
239
Tries to fetch the given subcommand, printing a message with the
240
appropriate command called from the command line (usually
241
"django-admin.py" or "manage.py") if it can't be found.
244
app_name = get_commands()[subcommand]
245
if isinstance(app_name, BaseCommand):
246
# If the command is already loaded, use it directly.
249
klass = load_command_class(app_name, subcommand)
251
sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % \
252
(subcommand, self.prog_name))
258
Given the command-line arguments, this figures out which subcommand is
259
being run, creates a parser appropriate to that command, and runs it.
261
# Preprocess options to extract --settings and --pythonpath.
262
# These options could affect the commands that are available, so they
263
# must be processed early.
264
parser = LaxOptionParser(usage="%prog subcommand [options] [args]",
265
version=get_version(),
266
option_list=BaseCommand.option_list)
268
options, args = parser.parse_args(self.argv)
269
handle_default_options(options)
271
pass # Ignore any option errors at this point.
274
subcommand = self.argv[1]
276
sys.stderr.write("Type '%s help' for usage.\n" % self.prog_name)
279
if subcommand == 'help':
281
self.fetch_command(args[2]).print_help(self.prog_name, args[2])
283
parser.print_lax_help()
284
sys.stderr.write(self.main_help_text() + '\n')
286
# Special-cases: We want 'django-admin.py --version' and
287
# 'django-admin.py --help' to work, for backwards compatibility.
288
elif self.argv[1:] == ['--version']:
289
# LaxOptionParser already takes care of printing the version.
291
elif self.argv[1:] == ['--help']:
292
parser.print_lax_help()
293
sys.stderr.write(self.main_help_text() + '\n')
295
self.fetch_command(subcommand).run_from_argv(self.argv)
297
def setup_environ(settings_mod, original_settings_path=None):
299
Configures the runtime environment. This can also be used by external
300
scripts wanting to set up a similar environment to manage.py.
301
Returns the project directory (assuming the passed settings module is
302
directly in the project directory).
304
The "original_settings_path" parameter is optional, but recommended, since
305
trying to work out the original path from the module can be problematic.
307
# Add this project to sys.path so that it's importable in the conventional
308
# way. For example, if this file (manage.py) lives in a directory
309
# "myproject", this code would add "/path/to/myproject" to sys.path.
310
project_directory, settings_filename = os.path.split(settings_mod.__file__)
311
if project_directory == os.curdir or not project_directory:
312
project_directory = os.getcwd()
313
project_name = os.path.basename(project_directory)
314
settings_name = os.path.splitext(settings_filename)[0]
315
sys.path.append(os.path.join(project_directory, os.pardir))
316
project_module = __import__(project_name, {}, {}, [''])
319
# Set DJANGO_SETTINGS_MODULE appropriately.
320
if original_settings_path:
321
os.environ['DJANGO_SETTINGS_MODULE'] = original_settings_path
323
os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name)
324
return project_directory
326
def execute_from_command_line(argv=None):
328
A simple method that runs a ManagementUtility.
330
utility = ManagementUtility(argv)
333
def execute_manager(settings_mod, argv=None):
335
Like execute_from_command_line(), but for use by manage.py, a
336
project-specific django-admin.py utility.
338
setup_environ(settings_mod)
339
utility = ManagementUtility(argv)