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