1 | import django
|
---|
2 | from optparse import OptionParser
|
---|
3 | import os
|
---|
4 | import sys
|
---|
5 | import textwrap
|
---|
6 |
|
---|
7 | # For backwards compatibility: get_version() used to be in this module.
|
---|
8 | get_version = django.get_version
|
---|
9 |
|
---|
10 | def find_commands(path):
|
---|
11 | """
|
---|
12 | Given a path to a management directory, return a list of all the command names
|
---|
13 | that are available. Returns an empty list if no commands are defined.
|
---|
14 | """
|
---|
15 | command_dir = os.path.join(path, 'commands')
|
---|
16 | try:
|
---|
17 | return [f[:-3] for f in os.listdir(command_dir) if not f.startswith('_') and f.endswith('.py')]
|
---|
18 | except OSError:
|
---|
19 | return []
|
---|
20 |
|
---|
21 | def load_default_commands():
|
---|
22 | """
|
---|
23 | Returns a dictionary of instances of all available Command classes.
|
---|
24 |
|
---|
25 | This works by looking for and loading all Python modules in the
|
---|
26 | django.core.management.commands package.
|
---|
27 |
|
---|
28 | The dictionary is in the format {name: command_instance}.
|
---|
29 | """
|
---|
30 | return dict([(name, getattr(__import__('django.core.management.commands.%s' % (name), {}, {}, ['Command']), 'Command')()) for name in find_commands(__path__[0])])
|
---|
31 |
|
---|
32 | def load_project_commands():
|
---|
33 | """
|
---|
34 | Returns a dictionary of instances of all available Command classes.
|
---|
35 |
|
---|
36 | It looks for a management.commands package in each installed application
|
---|
37 | -- if a commands package exists, it loads all commands in that
|
---|
38 | application and raises an AttributeError if a command doesn't contain a
|
---|
39 | Command instance.
|
---|
40 |
|
---|
41 | The dictionary is in the format {name: command_instance}.
|
---|
42 | """
|
---|
43 |
|
---|
44 | commands = {}
|
---|
45 |
|
---|
46 | if 'DJANGO_SETTINGS_MODULE' in os.environ:
|
---|
47 | from django.db import models
|
---|
48 |
|
---|
49 | # Get commands from all installed apps
|
---|
50 | for app in models.get_apps():
|
---|
51 | try:
|
---|
52 | app_name = '.'.join(app.__name__.split('.')[:-1])
|
---|
53 | path = os.path.join(os.path.dirname(app.__file__),'management')
|
---|
54 | commands.update(dict([(name, getattr(__import__('%s.management.commands.%s' % (app_name, name), {}, {}, ['Command']), 'Command')()) for name in find_commands(path)]))
|
---|
55 | except AttributeError:
|
---|
56 | sys.stderr.write("Management command '%s' in application '%s' doesn't contain a Command instance.\n" % (name, app_name))
|
---|
57 | sys.exit(1)
|
---|
58 |
|
---|
59 | return commands
|
---|
60 |
|
---|
61 | def call_command(name, *args, **options):
|
---|
62 | """
|
---|
63 | Calls the given command, with the given options and args/kwargs.
|
---|
64 |
|
---|
65 | This is the primary API you should use for calling specific commands.
|
---|
66 |
|
---|
67 | Some examples:
|
---|
68 | call_command('syncdb')
|
---|
69 | call_command('shell', plain=True)
|
---|
70 | call_command('sqlall', 'myapp')
|
---|
71 | """
|
---|
72 | klass = getattr(__import__(_DJANGO_COMMANDS[name].__module__, {}, {}, ['Command']), 'Command')()
|
---|
73 | return klass.execute(*args, **options)
|
---|
74 |
|
---|
75 | class ManagementUtility(object):
|
---|
76 | """
|
---|
77 | Encapsulates the logic of the django-admin.py and manage.py utilities.
|
---|
78 |
|
---|
79 | A ManagementUtility has a number of commands, which can be manipulated
|
---|
80 | by editing the self.commands dictionary.
|
---|
81 | """
|
---|
82 | def __init__(self):
|
---|
83 | self.commands = _DJANGO_COMMANDS
|
---|
84 |
|
---|
85 | def usage(self):
|
---|
86 | """
|
---|
87 | Returns a usage string, for use with optparse.
|
---|
88 |
|
---|
89 | The string doesn't include the options (e.g., "--verbose"), because
|
---|
90 | optparse puts those in automatically.
|
---|
91 | """
|
---|
92 | usage = ["%prog command [options]\nactions:"]
|
---|
93 | commands = self.commands.items()
|
---|
94 | commands.sort()
|
---|
95 | for name, cmd in commands:
|
---|
96 | usage.append(' %s %s' % (name, cmd.args))
|
---|
97 | usage.extend(textwrap.wrap(cmd.help, initial_indent=' ', subsequent_indent=' '))
|
---|
98 | usage.append('')
|
---|
99 | return '\n'.join(usage[:-1]) # Cut off the last list element, an empty space.
|
---|
100 |
|
---|
101 | def execute(self, argv=None):
|
---|
102 | """
|
---|
103 | Parses the given argv from the command line, determines which command
|
---|
104 | to run and runs the command.
|
---|
105 | """
|
---|
106 | if argv is None:
|
---|
107 | argv = sys.argv
|
---|
108 |
|
---|
109 | # Create the parser object and parse the command-line args.
|
---|
110 | # TODO: Ideally each Command class would register its own options for
|
---|
111 | # add_option(), but we'd need to figure out how to allow for multiple
|
---|
112 | # Commands using the same options. The optparse library gets in the way
|
---|
113 | # by checking for conflicts:
|
---|
114 | # http://docs.python.org/lib/optparse-conflicts-between-options.html
|
---|
115 | parser = OptionParser(usage=self.usage(), version=get_version())
|
---|
116 | parser.add_option('--settings',
|
---|
117 | help='The Python path to a settings module, e.g. "myproject.settings.main". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.')
|
---|
118 | parser.add_option('--pythonpath',
|
---|
119 | help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".')
|
---|
120 | parser.add_option('--plain', action='store_true', dest='plain',
|
---|
121 | help='When using "shell": Tells Django to use plain Python, not IPython.')
|
---|
122 | parser.add_option('--noinput', action='store_false', dest='interactive', default=True,
|
---|
123 | help='Tells Django to NOT prompt the user for input of any kind.')
|
---|
124 | parser.add_option('--noreload', action='store_false', dest='use_reloader', default=True,
|
---|
125 | help='When using "runserver": Tells Django to NOT use the auto-reloader.')
|
---|
126 | parser.add_option('--format', default='json', dest='format',
|
---|
127 | help='Specifies the output serialization format for fixtures')
|
---|
128 | parser.add_option('--indent', default=None, dest='indent',
|
---|
129 | type='int', help='Specifies the indent level to use when pretty-printing output')
|
---|
130 | parser.add_option('--verbosity', action='store', dest='verbosity', default='1',
|
---|
131 | type='choice', choices=['0', '1', '2'],
|
---|
132 | help='Verbosity level; 0=minimal output, 1=normal output, 2=all output')
|
---|
133 | parser.add_option('--adminmedia', dest='admin_media_path', default='',
|
---|
134 | help='When using "runserver": Specifies the directory from which to serve admin media.')
|
---|
135 | options, args = parser.parse_args(argv[1:])
|
---|
136 |
|
---|
137 | # If the 'settings' or 'pythonpath' options were submitted, activate those.
|
---|
138 | if options.settings:
|
---|
139 | os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
|
---|
140 | if options.pythonpath:
|
---|
141 | sys.path.insert(0, options.pythonpath)
|
---|
142 |
|
---|
143 | # Run the appropriate command.
|
---|
144 | try:
|
---|
145 | command_name = args[0]
|
---|
146 | except IndexError:
|
---|
147 | sys.stderr.write("Type '%s --help' for usage.\n" % os.path.basename(argv[0]))
|
---|
148 | sys.exit(1)
|
---|
149 | try:
|
---|
150 | command = self.commands[command_name]
|
---|
151 | except KeyError:
|
---|
152 | sys.stderr.write("Unknown command: %r\nType '%s --help' for usage.\n" % (command_name, os.path.basename(argv[0])))
|
---|
153 | sys.exit(1)
|
---|
154 | command.execute(*args[1:], **options.__dict__)
|
---|
155 |
|
---|
156 | class ProjectManagementUtility(ManagementUtility):
|
---|
157 | """
|
---|
158 | A ManagementUtility that is specific to a particular Django project.
|
---|
159 | As such, its commands are slightly different than those of its parent
|
---|
160 | class.
|
---|
161 |
|
---|
162 | In practice, this class represents manage.py, whereas ManagementUtility
|
---|
163 | represents django-admin.py.
|
---|
164 | """
|
---|
165 | def __init__(self, project_directory):
|
---|
166 | super(ProjectManagementUtility, self).__init__()
|
---|
167 |
|
---|
168 | # Remove the "startproject" command from self.commands, because
|
---|
169 | # that's a django-admin.py command, not a manage.py command.
|
---|
170 | del self.commands['startproject']
|
---|
171 |
|
---|
172 | # Override the startapp command so that it always uses the
|
---|
173 | # project_directory, not the current working directory (which is default).
|
---|
174 | from django.core.management.commands.startapp import ProjectCommand
|
---|
175 | self.commands['startapp'] = ProjectCommand(project_directory)
|
---|
176 |
|
---|
177 | def setup_environ(settings_mod):
|
---|
178 | """
|
---|
179 | Configure the runtime environment. This can also be used by external
|
---|
180 | scripts wanting to set up a similar environment to manage.py.
|
---|
181 | """
|
---|
182 | # Add this project to sys.path so that it's importable in the conventional
|
---|
183 | # way. For example, if this file (manage.py) lives in a directory
|
---|
184 | # "myproject", this code would add "/path/to/myproject" to sys.path.
|
---|
185 | project_directory, settings_filename = os.path.split(settings_mod.__file__)
|
---|
186 | project_name = os.path.basename(project_directory)
|
---|
187 | settings_name = os.path.splitext(settings_filename)[0]
|
---|
188 | sys.path.append(os.path.join(project_directory, '..'))
|
---|
189 | project_module = __import__(project_name, {}, {}, [''])
|
---|
190 | sys.path.pop()
|
---|
191 |
|
---|
192 | # Set DJANGO_SETTINGS_MODULE appropriately.
|
---|
193 | os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name)
|
---|
194 |
|
---|
195 | # Update default commands with project commands for django internal
|
---|
196 | # webservers and the manage.py utility
|
---|
197 | _DJANGO_COMMANDS.update(load_project_commands())
|
---|
198 |
|
---|
199 | return project_directory
|
---|
200 |
|
---|
201 | def execute_from_command_line(argv=None):
|
---|
202 | """
|
---|
203 | A simple method that runs a ManagementUtility.
|
---|
204 | """
|
---|
205 | utility = ManagementUtility()
|
---|
206 | utility.execute(argv)
|
---|
207 |
|
---|
208 | def execute_manager(settings_mod, argv=None):
|
---|
209 | """
|
---|
210 | Like execute_from_command_line(), but for use by manage.py, a
|
---|
211 | project-specific django-admin.py utility.
|
---|
212 | """
|
---|
213 | project_directory = setup_environ(settings_mod)
|
---|
214 | utility = ProjectManagementUtility(project_directory)
|
---|
215 | utility.execute(argv)
|
---|
216 |
|
---|
217 | # Add default commands to a dict
|
---|
218 | _DJANGO_COMMANDS = load_default_commands()
|
---|
219 |
|
---|
220 | # Update default commands with project commands
|
---|
221 | # when DJANGO_SETTINGS_MODULE is already set.
|
---|
222 | _DJANGO_COMMANDS.update(load_project_commands())
|
---|
223 |
|
---|