|
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() |