35 Raises ImportError if the management module cannot be found for any reason. |
35 Raises ImportError if the management module cannot be found for any reason. |
36 """ |
36 """ |
37 parts = app_name.split('.') |
37 parts = app_name.split('.') |
38 parts.append('management') |
38 parts.append('management') |
39 parts.reverse() |
39 parts.reverse() |
|
40 part = parts.pop() |
40 path = None |
41 path = None |
|
42 |
|
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. |
|
49 try: |
|
50 f, path, descr = imp.find_module(part,path) |
|
51 except ImportError,e: |
|
52 if os.path.basename(os.getcwd()) != part: |
|
53 raise e |
|
54 |
41 while parts: |
55 while parts: |
42 part = parts.pop() |
56 part = parts.pop() |
43 f, path, descr = find_module(part, path and [path] or None) |
57 f, path, descr = imp.find_module(part, path and [path] or None) |
44 return path |
58 return path |
45 |
59 |
46 def load_command_class(app_name, name): |
60 def load_command_class(app_name, name): |
47 """ |
61 """ |
48 Given a command name and an application name, returns the Command |
62 Given a command name and an application name, returns the Command |
50 (ImportError, AttributeError) are allowed to propagate. |
64 (ImportError, AttributeError) are allowed to propagate. |
51 """ |
65 """ |
52 return getattr(__import__('%s.management.commands.%s' % (app_name, name), |
66 return getattr(__import__('%s.management.commands.%s' % (app_name, name), |
53 {}, {}, ['Command']), 'Command')() |
67 {}, {}, ['Command']), 'Command')() |
54 |
68 |
55 def get_commands(load_user_commands=True, project_directory=None): |
69 def get_commands(): |
56 """ |
70 """ |
57 Returns a dictionary mapping command names to their callback applications. |
71 Returns a dictionary mapping command names to their callback applications. |
58 |
72 |
59 This works by looking for a management.commands package in django.core, and |
73 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 |
74 in each installed application -- if a commands package exists, all commands |
61 in that package are registered. |
75 in that package are registered. |
62 |
76 |
63 Core commands are always included. If a settings module has been |
77 Core commands are always included. If a settings module has been |
64 specified, user-defined commands will also be included, the |
78 specified, user-defined commands will also be included, the |
65 startproject command will be disabled, and the startapp command |
79 startproject command will be disabled, and the startapp command |
66 will be modified to use the directory in which that module appears. |
80 will be modified to use the directory in which the settings module appears. |
67 |
81 |
68 The dictionary is in the format {command_name: app_name}. Key-value |
82 The dictionary is in the format {command_name: app_name}. Key-value |
69 pairs from this dictionary can then be used in calls to |
83 pairs from this dictionary can then be used in calls to |
70 load_command_class(app_name, command_name) |
84 load_command_class(app_name, command_name) |
71 |
85 |
78 """ |
92 """ |
79 global _commands |
93 global _commands |
80 if _commands is None: |
94 if _commands is None: |
81 _commands = dict([(name, 'django.core') for name in find_commands(__path__[0])]) |
95 _commands = dict([(name, 'django.core') for name in find_commands(__path__[0])]) |
82 |
96 |
83 if load_user_commands: |
97 # Find the installed apps |
84 # Get commands from all installed apps. |
98 try: |
85 from django.conf import settings |
99 from django.conf import settings |
86 for app_name in settings.INSTALLED_APPS: |
100 apps = settings.INSTALLED_APPS |
87 try: |
101 except (AttributeError, EnvironmentError, ImportError): |
88 path = find_management_module(app_name) |
102 apps = [] |
89 _commands.update(dict([(name, app_name) for name in find_commands(path)])) |
103 |
90 except ImportError: |
104 # Find the project directory |
91 pass # No management module -- ignore this app. |
105 try: |
|
106 from django.conf import settings |
|
107 project_directory = setup_environ( |
|
108 __import__( |
|
109 settings.SETTINGS_MODULE, {}, {}, |
|
110 (settings.SETTINGS_MODULE.split(".")[-1],) |
|
111 ), settings.SETTINGS_MODULE |
|
112 ) |
|
113 except (AttributeError, EnvironmentError, ImportError): |
|
114 project_directory = None |
|
115 |
|
116 # Find and load the management module for each installed app. |
|
117 for app_name in apps: |
|
118 try: |
|
119 path = find_management_module(app_name) |
|
120 _commands.update(dict([(name, app_name) |
|
121 for name in find_commands(path)])) |
|
122 except ImportError: |
|
123 pass # No management module - ignore this app |
92 |
124 |
93 if project_directory: |
125 if project_directory: |
94 # Remove the "startproject" command from self.commands, because |
126 # Remove the "startproject" command from self.commands, because |
95 # that's a django-admin.py command, not a manage.py command. |
127 # that's a django-admin.py command, not a manage.py command. |
96 del _commands['startproject'] |
128 del _commands['startproject'] |
133 the commands (and thus the options) that are available to the user. |
165 the commands (and thus the options) that are available to the user. |
134 """ |
166 """ |
135 def error(self, msg): |
167 def error(self, msg): |
136 pass |
168 pass |
137 |
169 |
|
170 def print_help(self): |
|
171 """Output nothing. |
|
172 |
|
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. |
|
175 """ |
|
176 pass |
|
177 |
|
178 def print_lax_help(self): |
|
179 """Output the basic options available to every command. |
|
180 |
|
181 This just redirects to the default print_help() behaviour. |
|
182 """ |
|
183 OptionParser.print_help(self) |
|
184 |
|
185 def _process_args(self, largs, rargs, values): |
|
186 """ |
|
187 Overrides OptionParser._process_args to exclusively handle default |
|
188 options and ignore args and other options. |
|
189 |
|
190 This overrides the behavior of the super class, which stop parsing |
|
191 at the first unrecognized option. |
|
192 """ |
|
193 while rargs: |
|
194 arg = rargs[0] |
|
195 try: |
|
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) |
|
205 else: |
|
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 |
|
209 del rargs[0] |
|
210 raise error |
|
211 except: |
|
212 largs.append(arg) |
|
213 |
138 class ManagementUtility(object): |
214 class ManagementUtility(object): |
139 """ |
215 """ |
140 Encapsulates the logic of the django-admin.py and manage.py utilities. |
216 Encapsulates the logic of the django-admin.py and manage.py utilities. |
141 |
217 |
142 A ManagementUtility has a number of commands, which can be manipulated |
218 A ManagementUtility has a number of commands, which can be manipulated |
143 by editing the self.commands dictionary. |
219 by editing the self.commands dictionary. |
144 """ |
220 """ |
145 def __init__(self, argv=None): |
221 def __init__(self, argv=None): |
146 self.argv = argv or sys.argv[:] |
222 self.argv = argv or sys.argv[:] |
147 self.prog_name = os.path.basename(self.argv[0]) |
223 self.prog_name = os.path.basename(self.argv[0]) |
148 self.project_directory = None |
|
149 self.user_commands = False |
|
150 |
224 |
151 def main_help_text(self): |
225 def main_help_text(self): |
152 """ |
226 """ |
153 Returns the script's main help text, as a string. |
227 Returns the script's main help text, as a string. |
154 """ |
228 """ |
155 usage = ['%s <subcommand> [options] [args]' % self.prog_name] |
229 usage = ['',"Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name,''] |
156 usage.append('Django command line tool, version %s' % django.get_version()) |
|
157 usage.append("Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name) |
|
158 usage.append('Available subcommands:') |
230 usage.append('Available subcommands:') |
159 commands = get_commands(self.user_commands, self.project_directory).keys() |
231 commands = get_commands().keys() |
160 commands.sort() |
232 commands.sort() |
161 for cmd in commands: |
233 for cmd in commands: |
162 usage.append(' %s' % cmd) |
234 usage.append(' %s' % cmd) |
163 return '\n'.join(usage) |
235 return '\n'.join(usage) |
164 |
236 |
167 Tries to fetch the given subcommand, printing a message with the |
239 Tries to fetch the given subcommand, printing a message with the |
168 appropriate command called from the command line (usually |
240 appropriate command called from the command line (usually |
169 "django-admin.py" or "manage.py") if it can't be found. |
241 "django-admin.py" or "manage.py") if it can't be found. |
170 """ |
242 """ |
171 try: |
243 try: |
172 app_name = get_commands(self.user_commands, self.project_directory)[subcommand] |
244 app_name = get_commands()[subcommand] |
173 if isinstance(app_name, BaseCommand): |
245 if isinstance(app_name, BaseCommand): |
174 # If the command is already loaded, use it directly. |
246 # If the command is already loaded, use it directly. |
175 klass = app_name |
247 klass = app_name |
176 else: |
248 else: |
177 klass = load_command_class(app_name, subcommand) |
249 klass = load_command_class(app_name, subcommand) |
187 being run, creates a parser appropriate to that command, and runs it. |
259 being run, creates a parser appropriate to that command, and runs it. |
188 """ |
260 """ |
189 # Preprocess options to extract --settings and --pythonpath. |
261 # Preprocess options to extract --settings and --pythonpath. |
190 # These options could affect the commands that are available, so they |
262 # These options could affect the commands that are available, so they |
191 # must be processed early. |
263 # must be processed early. |
192 parser = LaxOptionParser(version=get_version(), option_list=BaseCommand.option_list) |
264 parser = LaxOptionParser(usage="%prog subcommand [options] [args]", |
|
265 version=get_version(), |
|
266 option_list=BaseCommand.option_list) |
193 try: |
267 try: |
194 options, args = parser.parse_args(self.argv) |
268 options, args = parser.parse_args(self.argv) |
195 handle_default_options(options) |
269 handle_default_options(options) |
196 except: |
270 except: |
197 pass # Ignore any option errors at this point. |
271 pass # Ignore any option errors at this point. |
204 |
278 |
205 if subcommand == 'help': |
279 if subcommand == 'help': |
206 if len(args) > 2: |
280 if len(args) > 2: |
207 self.fetch_command(args[2]).print_help(self.prog_name, args[2]) |
281 self.fetch_command(args[2]).print_help(self.prog_name, args[2]) |
208 else: |
282 else: |
|
283 parser.print_lax_help() |
209 sys.stderr.write(self.main_help_text() + '\n') |
284 sys.stderr.write(self.main_help_text() + '\n') |
210 sys.exit(1) |
285 sys.exit(1) |
211 # Special-cases: We want 'django-admin.py --version' and |
286 # Special-cases: We want 'django-admin.py --version' and |
212 # 'django-admin.py --help' to work, for backwards compatibility. |
287 # 'django-admin.py --help' to work, for backwards compatibility. |
213 elif self.argv[1:] == ['--version']: |
288 elif self.argv[1:] == ['--version']: |
214 # LaxOptionParser already takes care of printing the version. |
289 # LaxOptionParser already takes care of printing the version. |
215 pass |
290 pass |
216 elif self.argv[1:] == ['--help']: |
291 elif self.argv[1:] == ['--help']: |
|
292 parser.print_lax_help() |
217 sys.stderr.write(self.main_help_text() + '\n') |
293 sys.stderr.write(self.main_help_text() + '\n') |
218 else: |
294 else: |
219 self.fetch_command(subcommand).run_from_argv(self.argv) |
295 self.fetch_command(subcommand).run_from_argv(self.argv) |
220 |
296 |
221 class ProjectManagementUtility(ManagementUtility): |
297 def setup_environ(settings_mod, original_settings_path=None): |
222 """ |
|
223 A ManagementUtility that is specific to a particular Django project. |
|
224 As such, its commands are slightly different than those of its parent |
|
225 class. |
|
226 |
|
227 In practice, this class represents manage.py, whereas ManagementUtility |
|
228 represents django-admin.py. |
|
229 """ |
|
230 def __init__(self, argv, project_directory): |
|
231 super(ProjectManagementUtility, self).__init__(argv) |
|
232 self.project_directory = project_directory |
|
233 self.user_commands = True |
|
234 |
|
235 def setup_environ(settings_mod): |
|
236 """ |
298 """ |
237 Configures the runtime environment. This can also be used by external |
299 Configures the runtime environment. This can also be used by external |
238 scripts wanting to set up a similar environment to manage.py. |
300 scripts wanting to set up a similar environment to manage.py. |
239 Returns the project directory (assuming the passed settings module is |
301 Returns the project directory (assuming the passed settings module is |
240 directly in the project directory). |
302 directly in the project directory). |
|
303 |
|
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. |
241 """ |
306 """ |
242 # Add this project to sys.path so that it's importable in the conventional |
307 # Add this project to sys.path so that it's importable in the conventional |
243 # way. For example, if this file (manage.py) lives in a directory |
308 # way. For example, if this file (manage.py) lives in a directory |
244 # "myproject", this code would add "/path/to/myproject" to sys.path. |
309 # "myproject", this code would add "/path/to/myproject" to sys.path. |
245 project_directory, settings_filename = os.path.split(settings_mod.__file__) |
310 project_directory, settings_filename = os.path.split(settings_mod.__file__) |
250 sys.path.append(os.path.join(project_directory, os.pardir)) |
315 sys.path.append(os.path.join(project_directory, os.pardir)) |
251 project_module = __import__(project_name, {}, {}, ['']) |
316 project_module = __import__(project_name, {}, {}, ['']) |
252 sys.path.pop() |
317 sys.path.pop() |
253 |
318 |
254 # Set DJANGO_SETTINGS_MODULE appropriately. |
319 # Set DJANGO_SETTINGS_MODULE appropriately. |
255 os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name) |
320 if original_settings_path: |
|
321 os.environ['DJANGO_SETTINGS_MODULE'] = original_settings_path |
|
322 else: |
|
323 os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name) |
256 return project_directory |
324 return project_directory |
257 |
325 |
258 def execute_from_command_line(argv=None): |
326 def execute_from_command_line(argv=None): |
259 """ |
327 """ |
260 A simple method that runs a ManagementUtility. |
328 A simple method that runs a ManagementUtility. |